@@ -13,7 +13,7 @@ import HTMLKitUtilities
1313struct HTMLElement : ExpressionMacro {
1414 static func expansion( of node: some FreestandingMacroExpansionSyntax , in context: some MacroExpansionContext ) throws -> ExprSyntax {
1515 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)
1717 var string : String = ( type == . html ? " <!DOCTYPE html> " : " " ) + " < " + type. rawValue + data. attributes + " > " + data. innerHTML
1818 if !type. isVoid {
1919 string += " </ " + type. rawValue + " > "
@@ -23,25 +23,25 @@ struct HTMLElement : ExpressionMacro {
2323}
2424
2525private 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 {
2727 var attributes : [ String ] = [ ] , innerHTML : [ String ] = [ ]
2828 for element in arguments. children ( viewMode: . all) {
2929 if let child: LabeledExprSyntax = element. as ( LabeledExprSyntax . self) {
3030 if var key: String = child. label? . text { // attributes
3131 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) !) )
3333 } else {
3434 if key == " acceptCharset " {
3535 key = " accept-charset "
3636 }
3737 if let string: String = parse_attribute ( elementType: elementType, key: key, expression: child. expression) {
38- attributes. append ( string)
38+ attributes. append ( key + ( string. isEmpty ? " " : " = \\ \" " + string + " \\ \" " ) )
3939 }
4040 }
4141 } else if let array: ArrayElementListSyntax = child. expression. as ( ArrayExprSyntax . self) ? . elements { // inner html
4242 for yoink in array {
4343 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) )
4545 } else if let string: String = yoink. expression. as ( StringLiteralExprSyntax . self) ? . string {
4646 innerHTML. append ( string)
4747 }
@@ -51,30 +51,38 @@ private extension HTMLElement {
5151 }
5252 return ElementData ( attributes: attributes, innerHTML: innerHTML)
5353 }
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 ] = [ ]
5656 for element in array. elements {
5757 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
5959 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) !
6161 if returnType == . interpolation {
62- value = " \\ ( " + value + " ) "
62+ literalValue = " \\ ( " + literalValue + " ) "
6363 }
64+ value = literalValue
6465 key += " - \( function. arguments. first!. expression. as ( StringLiteralExprSyntax . self) !. string) "
65- attributes. append ( key + " = \\ \" " + value + " \\ \" " )
6666 } 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
6969 } 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+ }
7179 }
7280 }
7381 return attributes
7482 }
75- static func parse_element_macro( expression: MacroExpansionExprSyntax ) -> String {
83+ static func parse_element_macro( context : some MacroExpansionContext , expression: MacroExpansionExprSyntax ) -> String {
7684 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)
7886 return " < " + elementType. rawValue + data. attributes + " > " + data. innerHTML + ( elementType. isVoid ? " " : " </ " + elementType. rawValue + " > " )
7987 }
8088
@@ -98,12 +106,11 @@ private extension HTMLElement {
98106 }
99107
100108 static func parse_attribute( elementType: HTMLElementType , key: String , expression: ExprSyntax ) -> String ? {
101- func yup( _ value: String ) -> String { key + " = \\ \" " + value + " \\ \" " }
102109 if let ( string, returnType) : ( String , LiteralReturnType ) = parse_literal_value ( elementType: elementType, key: key, expression: expression) {
103110 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 + " ) "
107114 }
108115 }
109116 if let value: String = expression. as ( ArrayExprSyntax . self) ? . elements. compactMap ( {
@@ -118,12 +125,12 @@ private extension HTMLElement {
118125 }
119126 return nil
120127 } ) . joined ( separator: get_separator ( key: key) ) {
121- return yup ( value)
128+ return value
122129 }
123130 func member( _ value: String ) -> String {
124131 var string : String = String ( value [ value. index ( after: value. startIndex) ... ] )
125132 string = HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: string)
126- return yup ( string)
133+ return string
127134 }
128135 if let function: FunctionCallExprSyntax = expression. as ( FunctionCallExprSyntax . self) {
129136 return member ( " \( function) " )
0 commit comments