Skip to content

Commit 6efd9d6

Browse files
authored
fix: correctly backport svelte:element to old AST (#11917)
Both `<svelte:element this="div">` and `<svelte:element this={"div"}>` were backported as `tag: "div"` for the old AST. That's wrong because the latter should result in `tag: { type: 'Literal', .. }`. Fixing this makes all the tests in prettier-plugin-svelte pass with Svelte 5. Also cleaned up a bit of code in the parser.
1 parent b1cbfc3 commit 6efd9d6

File tree

6 files changed

+157
-19
lines changed

6 files changed

+157
-19
lines changed

.changeset/thin-colts-yawn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: correctly backport `svelte:element` to old AST

packages/svelte/src/compiler/legacy.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,11 @@ export function convert(source, ast) {
478478
SvelteElement(node, { visit }) {
479479
/** @type {import('estree').Expression | string} */
480480
let tag = node.tag;
481-
if (tag.type === 'Literal' && typeof tag.value === 'string') {
481+
if (
482+
tag.type === 'Literal' &&
483+
typeof tag.value === 'string' &&
484+
source[/** @type {number} */ (node.tag.start) - 1] !== '{'
485+
) {
482486
tag = tag.value;
483487
}
484488

packages/svelte/src/compiler/phases/1-parse/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import full_char_code_at from './utils/full_char_code_at.js';
77
import * as e from '../../errors.js';
88
import { create_fragment } from './utils/create.js';
99
import read_options from './read/options.js';
10-
import { locator } from '../../state.js';
1110

1211
const regex_position_indicator = / \(\d+:\d+\)$/;
1312

packages/svelte/src/compiler/phases/1-parse/state/element.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
1414
/** Invalid attribute characters if the attribute is not surrounded by quotes */
1515
const regex_starts_with_invalid_attr_value = /^(\/>|[\s"'=<>`])/;
1616

17-
/** @type {Map<string, import('#compiler').SvelteNode['type']>} */
17+
/** @type {Map<string, import('#compiler').ElementLike['type']>} */
1818
const root_only_meta_tags = new Map([
1919
['svelte:head', 'SvelteHead'],
2020
['svelte:options', 'SvelteOptions'],
@@ -23,7 +23,7 @@ const root_only_meta_tags = new Map([
2323
['svelte:body', 'SvelteBody']
2424
]);
2525

26-
/** @type {Map<string, import('#compiler').SvelteNode['type']>} */
26+
/** @type {Map<string, import('#compiler').ElementLike['type']>} */
2727
const meta_tags = new Map([
2828
...root_only_meta_tags,
2929
['svelte:element', 'SvelteElement'],
@@ -132,24 +132,25 @@ export default function tag(parser) {
132132
: 'RegularElement';
133133

134134
/** @type {import('#compiler').ElementLike} */
135-
// @ts-expect-error TODO can't figure out this error
136135
const element =
137136
type === 'RegularElement'
138137
? {
139-
type: /** @type {import('#compiler').ElementLike['type']} */ (type),
138+
type: type,
140139
start,
141140
end: -1,
142141
name,
143142
attributes: [],
144143
fragment: create_fragment(true),
145144
metadata: {
146145
svg: false,
146+
mathml: false,
147+
scoped: false,
147148
has_spread: false
148149
},
149150
parent: null
150151
}
151-
: {
152-
type: /** @type {import('#compiler').ElementLike['type']} */ (type),
152+
: /** @type {import('#compiler').ElementLike} */ ({
153+
type,
153154
start,
154155
end: -1,
155156
name,
@@ -159,7 +160,7 @@ export default function tag(parser) {
159160
metadata: {
160161
svg: false
161162
}
162-
};
163+
});
163164

164165
parser.allow_whitespace();
165166

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1+
<svelte:element this="div"></svelte:element>
2+
<!-- prettier-ignore -->
3+
<svelte:element this='div'></svelte:element>
14
<svelte:element this={"div"}></svelte:element>
5+
<!-- prettier-ignore -->
6+
<svelte:element this={'div'}></svelte:element>
27
<svelte:element this={"div"} class="foo"></svelte:element>

packages/svelte/tests/parser-legacy/samples/dynamic-element-string/output.json

Lines changed: 134 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,164 @@
22
"html": {
33
"type": "Fragment",
44
"start": 0,
5-
"end": 105,
5+
"end": 292,
66
"children": [
77
{
88
"type": "Element",
99
"name": "svelte:element",
1010
"start": 0,
11-
"end": 46,
11+
"end": 44,
1212
"tag": "div",
1313
"attributes": [],
1414
"children": []
1515
},
1616
{
1717
"type": "Text",
18-
"start": 46,
19-
"end": 47,
18+
"start": 44,
19+
"end": 45,
20+
"raw": "\n",
21+
"data": "\n"
22+
},
23+
{
24+
"type": "Comment",
25+
"start": 45,
26+
"end": 69,
27+
"data": " prettier-ignore ",
28+
"ignores": []
29+
},
30+
{
31+
"type": "Text",
32+
"start": 69,
33+
"end": 70,
2034
"raw": "\n",
2135
"data": "\n"
2236
},
2337
{
2438
"type": "Element",
2539
"name": "svelte:element",
26-
"start": 47,
27-
"end": 105,
40+
"start": 70,
41+
"end": 114,
2842
"tag": "div",
43+
"attributes": [],
44+
"children": []
45+
},
46+
{
47+
"type": "Text",
48+
"start": 114,
49+
"end": 115,
50+
"raw": "\n",
51+
"data": "\n"
52+
},
53+
{
54+
"type": "Element",
55+
"name": "svelte:element",
56+
"start": 115,
57+
"end": 161,
58+
"tag": {
59+
"type": "Literal",
60+
"start": 137,
61+
"end": 142,
62+
"loc": {
63+
"start": {
64+
"line": 4,
65+
"column": 22
66+
},
67+
"end": {
68+
"line": 4,
69+
"column": 27
70+
}
71+
},
72+
"value": "div",
73+
"raw": "\"div\""
74+
},
75+
"attributes": [],
76+
"children": []
77+
},
78+
{
79+
"type": "Text",
80+
"start": 161,
81+
"end": 162,
82+
"raw": "\n",
83+
"data": "\n"
84+
},
85+
{
86+
"type": "Comment",
87+
"start": 162,
88+
"end": 186,
89+
"data": " prettier-ignore ",
90+
"ignores": []
91+
},
92+
{
93+
"type": "Text",
94+
"start": 186,
95+
"end": 187,
96+
"raw": "\n",
97+
"data": "\n"
98+
},
99+
{
100+
"type": "Element",
101+
"name": "svelte:element",
102+
"start": 187,
103+
"end": 233,
104+
"tag": {
105+
"type": "Literal",
106+
"start": 209,
107+
"end": 214,
108+
"loc": {
109+
"start": {
110+
"line": 6,
111+
"column": 22
112+
},
113+
"end": {
114+
"line": 6,
115+
"column": 27
116+
}
117+
},
118+
"value": "div",
119+
"raw": "'div'"
120+
},
121+
"attributes": [],
122+
"children": []
123+
},
124+
{
125+
"type": "Text",
126+
"start": 233,
127+
"end": 234,
128+
"raw": "\n",
129+
"data": "\n"
130+
},
131+
{
132+
"type": "Element",
133+
"name": "svelte:element",
134+
"start": 234,
135+
"end": 292,
136+
"tag": {
137+
"type": "Literal",
138+
"start": 256,
139+
"end": 261,
140+
"loc": {
141+
"start": {
142+
"line": 7,
143+
"column": 22
144+
},
145+
"end": {
146+
"line": 7,
147+
"column": 27
148+
}
149+
},
150+
"value": "div",
151+
"raw": "\"div\""
152+
},
29153
"attributes": [
30154
{
31155
"type": "Attribute",
32-
"start": 76,
33-
"end": 87,
156+
"start": 263,
157+
"end": 274,
34158
"name": "class",
35159
"value": [
36160
{
37-
"start": 83,
38-
"end": 86,
161+
"start": 270,
162+
"end": 273,
39163
"type": "Text",
40164
"raw": "foo",
41165
"data": "foo"

0 commit comments

Comments
 (0)