Skip to content

Commit a85b09e

Browse files
committed
handle wacky identifier names in templates
1 parent 66b64e2 commit a85b09e

File tree

5 files changed

+79
-4
lines changed

5 files changed

+79
-4
lines changed

src/parse/index.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import { isIdentifierStart, isIdentifierChar } from 'acorn';
12
import { locate, Location } from 'locate-character';
23
import fragment from './state/fragment';
34
import { whitespace } from '../utils/patterns';
45
import { trimStart, trimEnd } from '../utils/trim';
56
import getCodeFrame from '../utils/getCodeFrame';
67
import reservedNames from '../utils/reservedNames';
8+
import fullCharCodeAt from '../utils/fullCharCodeAt';
79
import hash from './utils/hash';
810
import { Node, Parsed } from '../interfaces';
911
import CompileError from '../utils/CompileError';
@@ -147,7 +149,22 @@ export class Parser {
147149

148150
readIdentifier() {
149151
const start = this.index;
150-
const identifier = this.read(/[a-zA-Z_$][a-zA-Z0-9_$]*/);
152+
153+
let i = this.index;
154+
155+
const code = fullCharCodeAt(this.template, i);
156+
if (!isIdentifierStart(code, true)) return null;
157+
158+
i += code <= 0xffff ? 1 : 2;
159+
160+
while (i < this.template.length) {
161+
const code = fullCharCodeAt(this.template, i);
162+
163+
if (!isIdentifierChar(code, true)) break;
164+
i += code <= 0xffff ? 1 : 2;
165+
}
166+
167+
const identifier = this.template.slice(this.index, this.index = i);
151168

152169
if (reservedNames.has(identifier)) {
153170
this.error(`'${identifier}' is a reserved word in JavaScript and cannot be used here`, start);

src/utils/fullCharCodeAt.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Adapted from https://github.com/acornjs/acorn/blob/6584815dca7440e00de841d1dad152302fdd7ca5/src/tokenize.js
2+
// Reproduced under MIT License https://github.com/acornjs/acorn/blob/master/LICENSE
3+
4+
export default function fullCharCodeAt(str: string, i: number): number {
5+
let code = str.charCodeAt(i)
6+
if (code <= 0xd7ff || code >= 0xe000) return code;
7+
8+
let next = str.charCodeAt(i + 1);
9+
return (code << 10) + next - 0x35fdc00;
10+
}

src/utils/isValidIdentifier.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { isIdentifierStart, isIdentifierChar } from 'acorn';
2+
import fullCharCodeAt from './fullCharCodeAt';
23

34
export default function isValidIdentifier(str: string): boolean {
4-
if (!isIdentifierStart(str.charCodeAt(0), true)) return false;
5+
let i = 0;
56

6-
for (let i = 0; i < str.length; i += 1) {
7-
if (!isIdentifierChar(str.charCodeAt(i), true)) return false;
7+
while (i < str.length) {
8+
const code = fullCharCodeAt(str, i);
9+
if (!(i === 0 ? isIdentifierStart : isIdentifierChar)(code, true)) return false;
10+
11+
i += code <= 0xffff ? 1 : 2;
812
}
913

1014
return true;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{{#each things as 𐊧}}
2+
<p>𐊧</p>
3+
{{/each}}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"hash": 2991613308,
3+
"html": {
4+
"start": 0,
5+
"end": 43,
6+
"type": "Fragment",
7+
"children": [
8+
{
9+
"start": 0,
10+
"end": 43,
11+
"type": "EachBlock",
12+
"expression": {
13+
"type": "Identifier",
14+
"start": 8,
15+
"end": 14,
16+
"name": "things"
17+
},
18+
"children": [
19+
{
20+
"start": 24,
21+
"end": 33,
22+
"type": "Element",
23+
"name": "p",
24+
"attributes": [],
25+
"children": [
26+
{
27+
"start": 27,
28+
"end": 29,
29+
"type": "Text",
30+
"data": "𐊧"
31+
}
32+
]
33+
}
34+
],
35+
"context": "𐊧"
36+
}
37+
]
38+
},
39+
"css": null,
40+
"js": null
41+
}

0 commit comments

Comments
 (0)