Skip to content

Commit 64b882e

Browse files
authored
[feat] improve parser errors (#6612)
1 parent 1292bdc commit 64b882e

File tree

8 files changed

+158
-1
lines changed

8 files changed

+158
-1
lines changed

src/compiler/parse/errors.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ export default {
157157
code: 'missing-component-definition',
158158
message: '<svelte:component> must have a \'this\' attribute'
159159
},
160+
missing_attribute_value: {
161+
code: 'missing-attribute-value',
162+
message: 'Expected value for the attribute'
163+
},
160164
unclosed_script: {
161165
code: 'unclosed-script',
162166
message: '<script> must have a closing tag'
@@ -169,6 +173,10 @@ export default {
169173
code: 'unclosed-comment',
170174
message: 'comment was left open, expected -->'
171175
},
176+
unclosed_attribute_value: (token: string) => ({
177+
code: 'unclosed-attribute-value',
178+
message: `Expected to close the attribute value with ${token}`
179+
}),
172180
unexpected_block_close: {
173181
code: 'unexpected-block-close',
174182
message: 'Unexpected block closing tag'

src/compiler/parse/state/tag.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,25 @@ function read_attribute_value(parser: Parser) {
438438
/(\/>|[\s"'=<>`])/
439439
);
440440

441-
const value = read_sequence(parser, () => !!parser.match_regex(regex));
441+
let value;
442+
try {
443+
value = read_sequence(parser, () => !!parser.match_regex(regex));
444+
} catch (error) {
445+
if (error.code === 'parse-error') {
446+
// if the attribute value didn't close + self-closing tag
447+
// eg: `<Component test={{a:1} />`
448+
// acorn may throw a `Unterminated regular expression` because of `/>`
449+
if (parser.template.slice(error.pos - 1, error.pos + 1) === '/>') {
450+
parser.index = error.pos;
451+
parser.error(parser_errors.unclosed_attribute_value(quote_mark || '}'));
452+
}
453+
}
454+
throw error;
455+
}
456+
457+
if (value.length === 0 && !quote_mark) {
458+
parser.error(parser_errors.missing_attribute_value);
459+
}
442460

443461
if (quote_mark) parser.index += 1;
444462
return value;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"code": "missing-attribute-value",
3+
"message": "Expected value for the attribute",
4+
"pos": 12,
5+
"start": {
6+
"character": 12,
7+
"column": 12,
8+
"line": 1
9+
}
10+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div class= ></div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div a="" b={''} c='' d="{''}" ></div>
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
{
2+
"html": {
3+
"start": 0,
4+
"end": 38,
5+
"type": "Fragment",
6+
"children": [
7+
{
8+
"start": 0,
9+
"end": 38,
10+
"type": "Element",
11+
"name": "div",
12+
"attributes": [
13+
{
14+
"start": 5,
15+
"end": 9,
16+
"type": "Attribute",
17+
"name": "a",
18+
"value": [
19+
{
20+
"start": 8,
21+
"end": 8,
22+
"type": "Text",
23+
"raw": "",
24+
"data": ""
25+
}
26+
]
27+
},
28+
{
29+
"start": 10,
30+
"end": 16,
31+
"type": "Attribute",
32+
"name": "b",
33+
"value": [
34+
{
35+
"start": 12,
36+
"end": 16,
37+
"type": "MustacheTag",
38+
"expression": {
39+
"type": "Literal",
40+
"start": 13,
41+
"end": 15,
42+
"loc": {
43+
"start": {
44+
"line": 1,
45+
"column": 13
46+
},
47+
"end": {
48+
"line": 1,
49+
"column": 15
50+
}
51+
},
52+
"value": "",
53+
"raw": "''"
54+
}
55+
}
56+
]
57+
},
58+
{
59+
"start": 17,
60+
"end": 21,
61+
"type": "Attribute",
62+
"name": "c",
63+
"value": [
64+
{
65+
"start": 20,
66+
"end": 20,
67+
"type": "Text",
68+
"raw": "",
69+
"data": ""
70+
}
71+
]
72+
},
73+
{
74+
"start": 22,
75+
"end": 30,
76+
"type": "Attribute",
77+
"name": "d",
78+
"value": [
79+
{
80+
"start": 25,
81+
"end": 29,
82+
"type": "MustacheTag",
83+
"expression": {
84+
"type": "Literal",
85+
"start": 26,
86+
"end": 28,
87+
"loc": {
88+
"start": {
89+
"line": 1,
90+
"column": 26
91+
},
92+
"end": {
93+
"line": 1,
94+
"column": 28
95+
}
96+
},
97+
"value": "",
98+
"raw": "''"
99+
}
100+
}
101+
]
102+
}
103+
],
104+
"children": []
105+
}
106+
]
107+
}
108+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"code": "unclosed-attribute-value",
3+
"message": "Expected to close the attribute value with }",
4+
"pos": 25,
5+
"start": {
6+
"character": 25,
7+
"column": 25,
8+
"line": 1
9+
}
10+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<Component test={{a: 1} />

0 commit comments

Comments
 (0)