@@ -28,34 +28,53 @@ private extension HTMLElement {
2828 if key == " acceptCharset " {
2929 key = " accept-charset "
3030 }
31- if let string: String = parse_attribute ( context: context, elementType: elementType, key: key, expression: child. expression) {
31+ if var string: String = parse_attribute ( context: context, elementType: elementType, key: key, argument: child) {
32+ string. escapeHTML ( attribute: true )
3233 attributes. append ( key + ( string. isEmpty ? " " : " = \\ \" " + string + " \\ \" " ) )
3334 }
3435 }
3536 // inner html
36- } else if let macro: MacroExpansionExprSyntax = child. expression. macroExpansion {
37- innerHTML. append ( parse_element_macro ( context: context, expression: macro) )
38- } else if let string: String = child. expression. stringLiteral? . string {
39- innerHTML. append ( string)
40- } else if let function: FunctionCallExprSyntax = child. expression. as ( FunctionCallExprSyntax . self) , function. calledExpression. as ( DeclReferenceExprSyntax . self) ? . baseName. text == " StaticString " {
41- innerHTML. append ( function. arguments. first!. expression. stringLiteral!. string)
42- } else {
43- context. diagnose ( Diagnostic ( node: child, message: ErrorDiagnostic ( id: " unallowedExpression " , message: " Expression not allowed. " ) ) )
37+ } else if let inner_html: String = parse_inner_html ( context: context, elementType: elementType, child: child) {
38+ innerHTML. append ( inner_html)
4439 }
4540 }
4641 }
4742 return ElementData ( attributes: attributes, innerHTML: innerHTML)
4843 }
44+ static func parse_inner_html( context: some MacroExpansionContext , elementType: HTMLElementType , child: LabeledExprSyntax ) -> String ? {
45+ if let macro: MacroExpansionExprSyntax = child. expression. macroExpansion {
46+ var string : String = parse_element_macro ( context: context, expression: macro)
47+ if elementType == . escapeHTML {
48+ string. escapeHTML ( attribute: false )
49+ }
50+ return string
51+ } else if var string: String = child. expression. stringLiteral? . string {
52+ string. escapeHTML ( attribute: false )
53+ return string
54+ } else if let function: FunctionCallExprSyntax = child. expression. as ( FunctionCallExprSyntax . self) , function. calledExpression. as ( DeclReferenceExprSyntax . self) ? . baseName. text == " StaticString " {
55+ return function. arguments. first!. expression. stringLiteral!. string. escapingHTML ( attribute: false )
56+ } else {
57+ context. diagnose ( Diagnostic ( node: child, message: ErrorDiagnostic ( id: " unallowedExpression " , message: " Expression not allowed. String interpolation is required when encoding runtime values. " ) , fixIts: [
58+ FixIt ( message: SimpleDiagnosticMessage ( id: " useStringInterpolation " , message: " Use String Interpolation. " , severity: . error) , changes: [
59+ FixIt . Change. replace (
60+ oldNode: Syntax ( child) ,
61+ newNode: Syntax ( StringLiteralExprSyntax ( content: " \\ ( \( child) ) " ) )
62+ )
63+ ] )
64+ ] ) )
65+ return nil
66+ }
67+ }
4968 static func parse_global_attributes( context: some MacroExpansionContext , elementType: HTMLElementType , array: ArrayExprSyntax ) -> [ String ] {
5069 var keys : Set < String > = [ ] , attributes : [ String ] = [ ]
5170 for element in array. elements {
52- let function : FunctionCallExprSyntax = element. expression. as ( FunctionCallExprSyntax . self) !, key_element : ExprSyntax = function. arguments. first!. expression
71+ let function : FunctionCallExprSyntax = element. expression. as ( FunctionCallExprSyntax . self) !, key_argument : LabeledExprSyntax = function. arguments. first!, key_element : ExprSyntax = key_argument . expression
5372 var key : String = function. calledExpression. memberAccess!. declName. baseName. text, value : String ? = nil
5473 switch key {
5574 case " custom " , " data " :
56- var ( literalValue, returnType) : ( String , LiteralReturnType ) = parse_literal_value ( elementType: elementType, key: key, expression : function. arguments. last!. expression ) !
57- if returnType == . interpolation {
58- literalValue = " \\ ( " + literalValue + " ) "
75+ var ( literalValue, returnType) : ( String , LiteralReturnType ) = parse_literal_value ( context : context , elementType: elementType, key: key, argument : function. arguments. last!) !
76+ if returnType == . string {
77+ literalValue. escapeHTML ( attribute : true )
5978 }
6079 value = literalValue
6180 if key == " custom " {
@@ -66,10 +85,10 @@ private extension HTMLElement {
6685 break
6786 case " event " :
6887 key = " on " + key_element. memberAccess!. declName. baseName. text
69- value = function. arguments. last!. expression. stringLiteral!. string
88+ value = function. arguments. last!. expression. stringLiteral!. string. escapingHTML ( attribute : true )
7089 break
7190 default :
72- if let string: String = parse_attribute ( context: context, elementType: elementType, key: key, expression : key_element ) {
91+ if let string: String = parse_attribute ( context: context, elementType: elementType, key: key, argument : key_argument ) {
7392 value = string
7493 }
7594 break
@@ -90,6 +109,12 @@ private extension HTMLElement {
90109 static func parse_element_macro( context: some MacroExpansionContext , expression: MacroExpansionExprSyntax ) -> String {
91110 guard let elementType: HTMLElementType = HTMLElementType ( rawValue: expression. macroName. text) else { return " \( expression) " }
92111 let childs : SyntaxChildren = expression. arguments. children ( viewMode: . all)
112+ if elementType == . escapeHTML {
113+ return childs. compactMap ( {
114+ guard let child: LabeledExprSyntax = $0. labeled else { return nil }
115+ return parse_inner_html ( context: context, elementType: elementType, child: child)
116+ } ) . joined ( )
117+ }
93118 let tag : String , isVoid : Bool
94119 var children : Slice < SyntaxChildren >
95120 if elementType == . custom {
@@ -129,34 +154,14 @@ private extension HTMLElement {
129154 }
130155 }
131156
132- static func parse_attribute( context: some MacroExpansionContext , elementType: HTMLElementType , key: String , expression: ExprSyntax ) -> String ? {
133- if let ( string, returnType) : ( String , LiteralReturnType ) = parse_literal_value ( elementType: elementType, key: key, expression: expression) {
157+ static func parse_attribute( context: some MacroExpansionContext , elementType: HTMLElementType , key: String , argument: LabeledExprSyntax ) -> String ? {
158+ let expression : ExprSyntax = argument. expression
159+ if let ( string, returnType) : ( String , LiteralReturnType ) = parse_literal_value ( context: context, elementType: elementType, key: key, argument: argument) {
134160 switch returnType {
135161 case . boolean: return string. elementsEqual ( " true " ) ? " " : nil
136162 case . string: return string
137- case . interpolation: return " \\ ( " + string + " ) "
138- }
139- }
140- let separator : String = get_separator ( key: key)
141- let string_return_logic : ( ExprSyntax , String ) -> String = {
142- if $1. contains ( separator) {
143- context. diagnose ( Diagnostic ( node: $0, message: ErrorDiagnostic ( id: " characterNotAllowedInDeclaration " , message: " Character \" " + separator + " \" is not allowed when declaring values for \" " + key + " \" . " ) ) )
163+ case . interpolation: return string
144164 }
145- return $1
146- }
147- if let value: String = expression. array? . elements. compactMap ( {
148- if let string: String = $0. expression. stringLiteral? . string {
149- return string_return_logic ( $0. expression, string)
150- }
151- if let string: String = $0. expression. integerLiteral? . literal. text {
152- return string
153- }
154- if let string: String = $0. expression. memberAccess? . declName. baseName. text {
155- return HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: string)
156- }
157- return nil
158- } ) . joined ( separator: separator) {
159- return value
160165 }
161166 func member( _ value: String ) -> String {
162167 var string : String = String ( value [ value. index ( after: value. startIndex) ... ] )
@@ -174,48 +179,89 @@ private extension HTMLElement {
174179 default : return " "
175180 }
176181 }
177- static func parse_literal_value( elementType: HTMLElementType , key: String , expression: ExprSyntax ) -> ( value: String , returnType: LiteralReturnType ) ? {
182+ static func parse_literal_value( context: some MacroExpansionContext , elementType: HTMLElementType , key: String , argument: LabeledExprSyntax ) -> ( value: String , returnType: LiteralReturnType ) ? {
183+ let expression : ExprSyntax = argument. expression
178184 if let boolean: String = expression. booleanLiteral? . literal. text {
179185 return ( boolean, . boolean)
180186 }
181- if let string: String = expression. stringLiteral? . string {
182- return ( string, . string)
183- }
184- if let integer: String = expression. integerLiteral? . literal. text {
185- return ( integer, . string)
186- }
187- if let float: String = expression. floatLiteral? . literal. text {
188- return ( float, . string)
189- }
190- if let function: FunctionCallExprSyntax = expression. as ( FunctionCallExprSyntax . self) {
191- switch key {
192- case " height " , " width " :
193- var value : String = " \( function) "
194- value = String ( value [ value. index ( after: value. startIndex) ... ] )
195- value = HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: value)
196- return ( value, . string)
197- default :
198- return ( " \( function) " , . interpolation)
187+ func return_string_or_interpolation( ) -> ( String , LiteralReturnType ) ? {
188+ if let string: String = expression. stringLiteral? . string {
189+ return ( string, . string)
199190 }
200- }
201- if let member: MemberAccessExprSyntax = expression. memberAccess {
202- let decl : String = member. declName. baseName. text
203- if let base: ExprSyntax = member. base {
204- if let integer: String = base. integerLiteral? . literal. text {
205- switch decl {
206- case " description " :
207- return ( integer, . string)
208- default :
209- return ( integer, . interpolation)
191+ if let integer: String = expression. integerLiteral? . literal. text {
192+ return ( integer, . string)
193+ }
194+ if let float: String = expression. floatLiteral? . literal. text {
195+ return ( float, . string)
196+ }
197+ if let function: FunctionCallExprSyntax = expression. as ( FunctionCallExprSyntax . self) {
198+ switch key {
199+ case " height " , " width " :
200+ var value : String = " \( function) "
201+ value = String ( value [ value. index ( after: value. startIndex) ... ] )
202+ value = HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: value)
203+ return ( value, . string)
204+ default :
205+ if function. calledExpression. as ( DeclReferenceExprSyntax . self) ? . baseName. text == " StaticString " {
206+ return ( function. arguments. first!. expression. stringLiteral!. string, . string)
210207 }
208+ return ( " \( function) " , . interpolation)
209+ }
210+ }
211+ if let member: MemberAccessExprSyntax = expression. memberAccess {
212+ let decl : String = member. declName. baseName. text
213+ if let _: ExprSyntax = member. base {
214+ /*if let integer:String = base.integerLiteral?.literal.text {
215+ switch decl {
216+ case "description":
217+ return (integer, .integer)
218+ default:
219+ return (integer, .interpolation)
220+ }
221+ } else {*/
222+ return ( " \( member) " , . interpolation)
223+ //}
211224 } else {
212- return ( " \( member) " , . interpolation)
225+ return ( HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: decl) , . string)
226+ }
227+ }
228+ let separator : String = get_separator ( key: key)
229+ let string_return_logic : ( ExprSyntax , String ) -> String = {
230+ if $1. contains ( separator) {
231+ context. diagnose ( Diagnostic ( node: $0, message: ErrorDiagnostic ( id: " characterNotAllowedInDeclaration " , message: " Character \" " + separator + " \" is not allowed when declaring values for \" " + key + " \" . " ) ) )
213232 }
214- } else {
215- return ( HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: decl) , . string)
233+ return $1
216234 }
235+ if let value: String = expression. array? . elements. compactMap ( {
236+ if let string: String = $0. expression. stringLiteral? . string {
237+ return string_return_logic ( $0. expression, string)
238+ }
239+ if let string: String = $0. expression. integerLiteral? . literal. text {
240+ return string
241+ }
242+ if let string: String = $0. expression. floatLiteral? . literal. text {
243+ return string
244+ }
245+ if let string: String = $0. expression. memberAccess? . declName. baseName. text {
246+ return HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: string)
247+ }
248+ return nil
249+ } ) . joined ( separator: separator) {
250+ return ( value, . string)
251+ }
252+ return nil
217253 }
218- return nil
254+ guard var ( string, returnType) : ( String , LiteralReturnType ) = return_string_or_interpolation ( ) else {
255+ //context.diagnose(Diagnostic(node: expression, message: ErrorDiagnostic(id: "somethingWentWrong", message: "Something went wrong. (" + expression.debugDescription + ")", severity: .warning)))
256+ return nil
257+ }
258+ if returnType == . interpolation {
259+ string = " \\ ( " + string + " ) "
260+ }
261+ if string. contains ( " \\ ( " ) {
262+ context. diagnose ( Diagnostic ( node: expression, message: ErrorDiagnostic ( id: " unsafeInterpolation " , message: " Interpolation may introduce raw HTML elements. " , severity: . warning) ) )
263+ }
264+ return ( string, returnType)
219265 }
220266}
221267
@@ -225,6 +271,7 @@ enum LiteralReturnType {
225271
226272// MARK: HTMLElementType
227273enum HTMLElementType : String {
274+ case escapeHTML
228275 case html
229276 case custom
230277
0 commit comments