@@ -13,7 +13,7 @@ import HTMLKitUtilities
13
13
struct HTMLElement : ExpressionMacro {
14
14
static func expansion( of node: some FreestandingMacroExpansionSyntax , in context: some MacroExpansionContext ) throws -> ExprSyntax {
15
15
let type : HTMLElementType = HTMLElementType ( rawValue: node. macroName. text) !
16
- let data : ElementData = parse_arguments ( elementType: type, arguments: node. arguments)
16
+ let data : ElementData = parse_arguments ( context : context , elementType: type, arguments: node. arguments)
17
17
var string : String = ( type == . html ? " <!DOCTYPE html> " : " " ) + " < " + type. rawValue + data. attributes + " > " + data. innerHTML
18
18
if !type. isVoid {
19
19
string += " </ " + type. rawValue + " > "
@@ -23,25 +23,25 @@ struct HTMLElement : ExpressionMacro {
23
23
}
24
24
25
25
private extension HTMLElement {
26
- static func parse_arguments( elementType: HTMLElementType , arguments: LabeledExprListSyntax ) -> ElementData {
26
+ static func parse_arguments( context : some MacroExpansionContext , elementType: HTMLElementType , arguments: LabeledExprListSyntax ) -> ElementData {
27
27
var attributes : [ String ] = [ ] , innerHTML : [ String ] = [ ]
28
28
for element in arguments. children ( viewMode: . all) {
29
29
if let child: LabeledExprSyntax = element. as ( LabeledExprSyntax . self) {
30
30
if var key: String = child. label? . text { // attributes
31
31
if key == " attributes " {
32
- attributes. append ( contentsOf: parse_global_attributes ( elementType: elementType, array: child. expression. as ( ArrayExprSyntax . self) !) )
32
+ attributes. append ( contentsOf: parse_global_attributes ( context : context , elementType: elementType, array: child. expression. as ( ArrayExprSyntax . self) !) )
33
33
} else {
34
34
if key == " acceptCharset " {
35
35
key = " accept-charset "
36
36
}
37
37
if let string: String = parse_attribute ( elementType: elementType, key: key, expression: child. expression) {
38
- attributes. append ( string)
38
+ attributes. append ( key + ( string. isEmpty ? " " : " = \\ \" " + string + " \\ \" " ) )
39
39
}
40
40
}
41
41
} else if let array: ArrayElementListSyntax = child. expression. as ( ArrayExprSyntax . self) ? . elements { // inner html
42
42
for yoink in array {
43
43
if let macro: MacroExpansionExprSyntax = yoink. expression. as ( MacroExpansionExprSyntax . self) {
44
- innerHTML. append ( parse_element_macro ( expression: macro) )
44
+ innerHTML. append ( parse_element_macro ( context : context , expression: macro) )
45
45
} else if let string: String = yoink. expression. as ( StringLiteralExprSyntax . self) ? . string {
46
46
innerHTML. append ( string)
47
47
}
@@ -51,30 +51,38 @@ private extension HTMLElement {
51
51
}
52
52
return ElementData ( attributes: attributes, innerHTML: innerHTML)
53
53
}
54
- static func parse_global_attributes( elementType: HTMLElementType , array: ArrayExprSyntax ) -> [ String ] {
55
- var attributes : [ String ] = [ ]
54
+ static func parse_global_attributes( context : some MacroExpansionContext , elementType: HTMLElementType , array: ArrayExprSyntax ) -> [ String ] {
55
+ var keys : Set < String > = [ ] , attributes : [ String ] = [ ]
56
56
for element in array. elements {
57
57
let function : FunctionCallExprSyntax = element. expression. as ( FunctionCallExprSyntax . self) !
58
- var key : String = function. calledExpression. as ( MemberAccessExprSyntax . self) !. declName. baseName. text
58
+ var key : String = function. calledExpression. as ( MemberAccessExprSyntax . self) !. declName. baseName. text, value : String ? = nil
59
59
if key == " data " {
60
- var ( value , returnType) : ( String , LiteralReturnType ) = parse_literal_value ( elementType: elementType, key: " data " , expression: function. arguments. last!. expression) !
60
+ var ( literalValue , returnType) : ( String , LiteralReturnType ) = parse_literal_value ( elementType: elementType, key: " data " , expression: function. arguments. last!. expression) !
61
61
if returnType == . interpolation {
62
- value = " \\ ( " + value + " ) "
62
+ literalValue = " \\ ( " + literalValue + " ) "
63
63
}
64
+ value = literalValue
64
65
key += " - \( function. arguments. first!. expression. as ( StringLiteralExprSyntax . self) !. string) "
65
- attributes. append ( key + " = \\ \" " + value + " \\ \" " )
66
66
} else if key == " event " {
67
- key = function. arguments. first!. expression. as ( MemberAccessExprSyntax . self) !. declName. baseName. text
68
- attributes . append ( " on " + key + " = \\ \" " + function. arguments. last!. expression. as ( StringLiteralExprSyntax . self) !. string + " \\ \" " )
67
+ key = " on " + function. arguments. first!. expression. as ( MemberAccessExprSyntax . self) !. declName. baseName. text
68
+ value = function. arguments. last!. expression. as ( StringLiteralExprSyntax . self) !. string
69
69
} else if let string: String = parse_attribute ( elementType: elementType, key: key, expression: function. arguments. first!. expression) {
70
- attributes. append ( string)
70
+ value = string
71
+ }
72
+ if let value: String = value {
73
+ if keys. contains ( key) {
74
+ context. diagnose ( Diagnostic ( node: element, message: ErrorDiagnostic ( id: " globalAttributeAlreadyDefined " , message: " Global attribute is already defined. " ) ) )
75
+ } else {
76
+ attributes. append ( key + ( value. isEmpty ? " " : " = \\ \" " + value + " \\ \" " ) )
77
+ keys. insert ( key)
78
+ }
71
79
}
72
80
}
73
81
return attributes
74
82
}
75
- static func parse_element_macro( expression: MacroExpansionExprSyntax ) -> String {
83
+ static func parse_element_macro( context : some MacroExpansionContext , expression: MacroExpansionExprSyntax ) -> String {
76
84
guard let elementType: HTMLElementType = HTMLElementType ( rawValue: expression. macroName. text) else { return " \( expression) " }
77
- let data : ElementData = parse_arguments ( elementType: elementType, arguments: expression. arguments)
85
+ let data : ElementData = parse_arguments ( context : context , elementType: elementType, arguments: expression. arguments)
78
86
return " < " + elementType. rawValue + data. attributes + " > " + data. innerHTML + ( elementType. isVoid ? " " : " </ " + elementType. rawValue + " > " )
79
87
}
80
88
@@ -98,12 +106,11 @@ private extension HTMLElement {
98
106
}
99
107
100
108
static func parse_attribute( elementType: HTMLElementType , key: String , expression: ExprSyntax ) -> String ? {
101
- func yup( _ value: String ) -> String { key + " = \\ \" " + value + " \\ \" " }
102
109
if let ( string, returnType) : ( String , LiteralReturnType ) = parse_literal_value ( elementType: elementType, key: key, expression: expression) {
103
110
switch returnType {
104
- case . boolean: return string. elementsEqual ( " true " ) ? key : nil
105
- case . string: return yup ( string)
106
- case . interpolation: return yup ( " \\ ( " + string + " ) " )
111
+ case . boolean: return string. elementsEqual ( " true " ) ? " " : nil
112
+ case . string: return string
113
+ case . interpolation: return " \\ ( " + string + " ) "
107
114
}
108
115
}
109
116
if let value: String = expression. as ( ArrayExprSyntax . self) ? . elements. compactMap ( {
@@ -118,12 +125,12 @@ private extension HTMLElement {
118
125
}
119
126
return nil
120
127
} ) . joined ( separator: get_separator ( key: key) ) {
121
- return yup ( value)
128
+ return value
122
129
}
123
130
func member( _ value: String ) -> String {
124
131
var string : String = String ( value [ value. index ( after: value. startIndex) ... ] )
125
132
string = HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: string)
126
- return yup ( string)
133
+ return string
127
134
}
128
135
if let function: FunctionCallExprSyntax = expression. as ( FunctionCallExprSyntax . self) {
129
136
return member ( " \( function) " )
0 commit comments