Skip to content

Commit 1c533d8

Browse files
authoredMar 27, 2025··
fix: resolved issue of $props incorrectly detected as store when using variables named after runes like $props and props (#692)
1 parent c5fe769 commit 1c533d8

11 files changed

+2439
-4
lines changed
 

‎.changeset/icy-pianos-film.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": patch
3+
---
4+
5+
fix: resolved issue of `$props` incorrectly detected as store when using variables named after runes like `$props` and `props`

‎src/parser/analyze-scope.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import { addElementToSortedArray } from "../utils/index.js";
1717
import type { NormalizedParserOptions } from "./parser-options.js";
1818
import type { SvelteParseContext } from "./svelte-parse-context.js";
19+
import { getGlobalsForSvelte } from "./globals.js";
1920
/**
2021
* Analyze scope
2122
*/
@@ -105,7 +106,10 @@ export function analyzeReactiveScope(scopeManager: ScopeManager): void {
105106
/**
106107
* Analyze store scope. e.g. $count
107108
*/
108-
export function analyzeStoreScope(scopeManager: ScopeManager): void {
109+
export function analyzeStoreScope(
110+
scopeManager: ScopeManager,
111+
svelteParseContext: SvelteParseContext,
112+
): void {
109113
const moduleScope = scopeManager.scopes.find(
110114
(scope) => scope.type === "module",
111115
);
@@ -114,8 +118,13 @@ export function analyzeStoreScope(scopeManager: ScopeManager): void {
114118
}
115119
const toBeMarkAsUsedReferences: Reference[] = [];
116120

121+
const globals = getGlobalsForSvelte(svelteParseContext);
122+
117123
for (const reference of [...scopeManager.globalScope.through]) {
118-
if (reference.identifier.name.startsWith("$")) {
124+
if (
125+
reference.identifier.name.startsWith("$") &&
126+
!globals.includes(reference.identifier.name as never)
127+
) {
119128
const realName = reference.identifier.name.slice(1);
120129
const variable = moduleScope.set.get(realName);
121130
if (variable) {

‎src/parser/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ function parseAsSvelte(
170170
sortNodes(ctx.comments);
171171
sortNodes(ctx.tokens);
172172
extractTokens(ctx);
173-
analyzeStoreScope(resultScript.scopeManager!);
173+
analyzeStoreScope(resultScript.scopeManager!, svelteParseContext);
174174
analyzeReactiveScope(resultScript.scopeManager!);
175-
analyzeStoreScope(resultScript.scopeManager!); // for reactive vars
175+
analyzeStoreScope(resultScript.scopeManager!, svelteParseContext); // for reactive vars
176176
analyzeSnippetsScope(ctx.snippets, resultScript.scopeManager!);
177177

178178
// Add $$xxx variable
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"svelteConfig": {
3+
"compilerOptions": {
4+
"runes": true
5+
}
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
// It should not be recognized as a store.
3+
const props = $props();
4+
</script>
5+
6+
<span>{props}</span>

‎tests/fixtures/parser/ast/svelte5/$props-without-destructuring-runes-output.json

+785
Large diffs are not rendered by default.

‎tests/fixtures/parser/ast/svelte5/$props-without-destructuring-runes-scope-output.json

+429
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"svelteConfig": {
3+
"compilerOptions": {
4+
"runes": false
5+
}
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
// It should be recognized as a store.
3+
const props = $props();
4+
</script>
5+
6+
<span>{props}</span>

‎tests/fixtures/parser/ast/svelte5/$props-without-destructuring-without-runes-output.json

+785
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
1+
{
2+
"type": "global",
3+
"variables": [
4+
{
5+
"name": "$$slots",
6+
"identifiers": [],
7+
"defs": [],
8+
"references": []
9+
},
10+
{
11+
"name": "$$props",
12+
"identifiers": [],
13+
"defs": [],
14+
"references": []
15+
},
16+
{
17+
"name": "$$restProps",
18+
"identifiers": [],
19+
"defs": [],
20+
"references": []
21+
}
22+
],
23+
"references": [],
24+
"childScopes": [
25+
{
26+
"type": "module",
27+
"variables": [
28+
{
29+
"name": "props",
30+
"identifiers": [
31+
{
32+
"type": "Identifier",
33+
"name": "props",
34+
"range": [
35+
56,
36+
61
37+
],
38+
"loc": {
39+
"start": {
40+
"line": 3,
41+
"column": 7
42+
},
43+
"end": {
44+
"line": 3,
45+
"column": 12
46+
}
47+
}
48+
}
49+
],
50+
"defs": [
51+
{
52+
"type": "Variable",
53+
"name": {
54+
"type": "Identifier",
55+
"name": "props",
56+
"range": [
57+
56,
58+
61
59+
],
60+
"loc": {
61+
"start": {
62+
"line": 3,
63+
"column": 7
64+
},
65+
"end": {
66+
"line": 3,
67+
"column": 12
68+
}
69+
}
70+
},
71+
"node": {
72+
"type": "VariableDeclarator",
73+
"id": {
74+
"type": "Identifier",
75+
"name": "props",
76+
"range": [
77+
56,
78+
61
79+
],
80+
"loc": {
81+
"start": {
82+
"line": 3,
83+
"column": 7
84+
},
85+
"end": {
86+
"line": 3,
87+
"column": 12
88+
}
89+
}
90+
},
91+
"init": {
92+
"type": "CallExpression",
93+
"arguments": [],
94+
"callee": {
95+
"type": "Identifier",
96+
"name": "$props",
97+
"range": [
98+
64,
99+
70
100+
],
101+
"loc": {
102+
"start": {
103+
"line": 3,
104+
"column": 15
105+
},
106+
"end": {
107+
"line": 3,
108+
"column": 21
109+
}
110+
}
111+
},
112+
"optional": false,
113+
"range": [
114+
64,
115+
72
116+
],
117+
"loc": {
118+
"start": {
119+
"line": 3,
120+
"column": 15
121+
},
122+
"end": {
123+
"line": 3,
124+
"column": 23
125+
}
126+
}
127+
},
128+
"range": [
129+
56,
130+
72
131+
],
132+
"loc": {
133+
"start": {
134+
"line": 3,
135+
"column": 7
136+
},
137+
"end": {
138+
"line": 3,
139+
"column": 23
140+
}
141+
}
142+
}
143+
}
144+
],
145+
"references": [
146+
{
147+
"identifier": {
148+
"type": "Identifier",
149+
"name": "props",
150+
"range": [
151+
56,
152+
61
153+
],
154+
"loc": {
155+
"start": {
156+
"line": 3,
157+
"column": 7
158+
},
159+
"end": {
160+
"line": 3,
161+
"column": 12
162+
}
163+
}
164+
},
165+
"from": "module",
166+
"init": true,
167+
"resolved": {
168+
"type": "Identifier",
169+
"name": "props",
170+
"range": [
171+
56,
172+
61
173+
],
174+
"loc": {
175+
"start": {
176+
"line": 3,
177+
"column": 7
178+
},
179+
"end": {
180+
"line": 3,
181+
"column": 12
182+
}
183+
}
184+
}
185+
},
186+
{
187+
"identifier": {
188+
"type": "Identifier",
189+
"name": "$props",
190+
"range": [
191+
64,
192+
70
193+
],
194+
"loc": {
195+
"start": {
196+
"line": 3,
197+
"column": 15
198+
},
199+
"end": {
200+
"line": 3,
201+
"column": 21
202+
}
203+
}
204+
},
205+
"from": "module",
206+
"init": null,
207+
"resolved": {
208+
"type": "Identifier",
209+
"name": "props",
210+
"range": [
211+
56,
212+
61
213+
],
214+
"loc": {
215+
"start": {
216+
"line": 3,
217+
"column": 7
218+
},
219+
"end": {
220+
"line": 3,
221+
"column": 12
222+
}
223+
}
224+
}
225+
},
226+
{
227+
"identifier": {
228+
"type": "Identifier",
229+
"name": "props",
230+
"range": [
231+
92,
232+
97
233+
],
234+
"loc": {
235+
"start": {
236+
"line": 6,
237+
"column": 7
238+
},
239+
"end": {
240+
"line": 6,
241+
"column": 12
242+
}
243+
}
244+
},
245+
"from": "module",
246+
"init": null,
247+
"resolved": {
248+
"type": "Identifier",
249+
"name": "props",
250+
"range": [
251+
56,
252+
61
253+
],
254+
"loc": {
255+
"start": {
256+
"line": 3,
257+
"column": 7
258+
},
259+
"end": {
260+
"line": 3,
261+
"column": 12
262+
}
263+
}
264+
}
265+
}
266+
]
267+
}
268+
],
269+
"references": [
270+
{
271+
"identifier": {
272+
"type": "Identifier",
273+
"name": "props",
274+
"range": [
275+
56,
276+
61
277+
],
278+
"loc": {
279+
"start": {
280+
"line": 3,
281+
"column": 7
282+
},
283+
"end": {
284+
"line": 3,
285+
"column": 12
286+
}
287+
}
288+
},
289+
"from": "module",
290+
"init": true,
291+
"resolved": {
292+
"type": "Identifier",
293+
"name": "props",
294+
"range": [
295+
56,
296+
61
297+
],
298+
"loc": {
299+
"start": {
300+
"line": 3,
301+
"column": 7
302+
},
303+
"end": {
304+
"line": 3,
305+
"column": 12
306+
}
307+
}
308+
}
309+
},
310+
{
311+
"identifier": {
312+
"type": "Identifier",
313+
"name": "$props",
314+
"range": [
315+
64,
316+
70
317+
],
318+
"loc": {
319+
"start": {
320+
"line": 3,
321+
"column": 15
322+
},
323+
"end": {
324+
"line": 3,
325+
"column": 21
326+
}
327+
}
328+
},
329+
"from": "module",
330+
"init": null,
331+
"resolved": {
332+
"type": "Identifier",
333+
"name": "props",
334+
"range": [
335+
56,
336+
61
337+
],
338+
"loc": {
339+
"start": {
340+
"line": 3,
341+
"column": 7
342+
},
343+
"end": {
344+
"line": 3,
345+
"column": 12
346+
}
347+
}
348+
}
349+
},
350+
{
351+
"identifier": {
352+
"type": "Identifier",
353+
"name": "props",
354+
"range": [
355+
92,
356+
97
357+
],
358+
"loc": {
359+
"start": {
360+
"line": 6,
361+
"column": 7
362+
},
363+
"end": {
364+
"line": 6,
365+
"column": 12
366+
}
367+
}
368+
},
369+
"from": "module",
370+
"init": null,
371+
"resolved": {
372+
"type": "Identifier",
373+
"name": "props",
374+
"range": [
375+
56,
376+
61
377+
],
378+
"loc": {
379+
"start": {
380+
"line": 3,
381+
"column": 7
382+
},
383+
"end": {
384+
"line": 3,
385+
"column": 12
386+
}
387+
}
388+
}
389+
}
390+
],
391+
"childScopes": [],
392+
"through": []
393+
}
394+
],
395+
"through": []
396+
}

0 commit comments

Comments
 (0)
Please sign in to comment.