@@ -8,6 +8,15 @@ import (
8
8
9
9
var scriptElement = scriptElementParser {}
10
10
11
+ type jsQuote string
12
+
13
+ const (
14
+ jsQuoteNone jsQuote = ""
15
+ jsQuoteSingle jsQuote = `'`
16
+ jsQuoteDouble jsQuote = `"`
17
+ jsQuoteBacktick jsQuote = "`"
18
+ )
19
+
11
20
type scriptElementParser struct {}
12
21
13
22
func (p scriptElementParser ) Parse (pi * parse.Input ) (n Node , ok bool , err error ) {
@@ -51,7 +60,7 @@ func (p scriptElementParser) Parse(pi *parse.Input) (n Node, ok bool, err error)
51
60
52
61
// Parse the contents, we should get script text or Go expressions up until the closing tag.
53
62
var sb strings.Builder
54
- var isInsideStringLiteral bool
63
+ var stringLiteralDelimiter jsQuote
55
64
56
65
loop:
57
66
for {
82
91
return nil , false , err
83
92
}
84
93
if ok {
85
- e .Contents = append (e .Contents , NewScriptContentsGo (code .(GoCode ), isInsideStringLiteral ))
94
+ e .Contents = append (e .Contents , NewScriptContentsGo (code .(GoCode ), stringLiteralDelimiter != jsQuoteNone ))
86
95
continue loop
87
96
}
88
97
@@ -108,13 +117,18 @@ loop:
108
117
if ok {
109
118
_ , isEOF , _ := parse .EOF [string ]().Parse (pi )
110
119
if c == `"` || c == "'" || c == "`" {
111
- isInsideStringLiteral = ! isInsideStringLiteral
120
+ // Start or exit a string literal.
121
+ if stringLiteralDelimiter == jsQuoteNone {
122
+ stringLiteralDelimiter = jsQuote (c )
123
+ } else if stringLiteralDelimiter == jsQuote (c ) {
124
+ stringLiteralDelimiter = jsQuoteNone
125
+ }
112
126
}
113
127
peeked , _ := pi .Peek (1 )
114
128
peeked = c + peeked
115
129
116
130
breakForGo := peeked == "{{"
117
- breakForHTML := ! isInsideStringLiteral && (peeked == "</" || peeked == "//" || peeked == "/*" )
131
+ breakForHTML := stringLiteralDelimiter == jsQuoteNone && (peeked == "</" || peeked == "//" || peeked == "/*" )
118
132
119
133
if isEOF || breakForGo || breakForHTML {
120
134
if sb .Len () > 0 {
@@ -143,7 +157,21 @@ var endTagStart = parse.String("</")
143
157
144
158
var jsCharacter = parse .Any (jsEscapedCharacter , parse .AnyRune )
145
159
146
- var jsEscapedCharacter = parse .StringFrom (parse .String ("\\ " ), parse .AnyRune )
160
+ // \uXXXX Unicode code point escape '\u0061' = 'a'
161
+ var hexDigit = parse .Any (parse .ZeroToNine , parse .RuneIn ("abcdef" ), parse .RuneIn ("ABCDEF" ))
162
+ var jsUnicodeEscape = parse .StringFrom (parse .String ("\\ u" ), hexDigit , hexDigit , hexDigit , hexDigit )
163
+
164
+ // \u{X...} ES6+ extended Unicode escape '\u{1F600}' = '😀'
165
+ var jsExtendedUnicodeEscape = parse .StringFrom (parse .String ("\\ u{" ), hexDigit , parse .StringFrom (parse .AtLeast (1 , parse .ZeroOrMore (hexDigit ))), parse .String ("}" ))
166
+
167
+ // \xXX Hex code (2-digit) '\x41' = 'A'
168
+ var jsHexEscape = parse .StringFrom (parse .String ("\\ x" ), hexDigit , hexDigit )
169
+
170
+ // \x Backslash escape '\\' = '\'
171
+ var jsBackslashEscape = parse .StringFrom (parse .String ("\\ " ), parse .AnyRune )
172
+
173
+ // All escapes.
174
+ var jsEscapedCharacter = parse .Any (jsBackslashEscape , jsUnicodeEscape , jsHexEscape , jsExtendedUnicodeEscape )
147
175
148
176
var jsComment = parse .Any (jsSingleLineComment , jsMultiLineComment )
149
177
0 commit comments