Skip to content

Commit 6ff7516

Browse files
authored
feat: improve scoping of snippet declarations acting as slot properties (#645)
1 parent 7a5f487 commit 6ff7516

File tree

9 files changed

+139
-1750
lines changed

9 files changed

+139
-1750
lines changed

.changeset/blue-eggs-work.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": minor
3+
---
4+
5+
feat: improve scoping of snippet declarations acting as slot properties

src/parser/analyze-scope.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import type {
77
SvelteScriptElement,
88
SvelteSnippetBlock,
99
} from "../ast/index.js";
10-
import { addReference, addVariable, getScopeFromNode } from "../scope/index.js";
10+
import {
11+
addReference,
12+
addVariable,
13+
getScopeFromNode,
14+
removeIdentifierVariable,
15+
} from "../scope/index.js";
1116
import { addElementToSortedArray } from "../utils/index.js";
1217
import type { NormalizedParserOptions } from "./parser-options.js";
1318
import type { SvelteParseContext } from "./svelte-parse-context.js";
@@ -304,11 +309,27 @@ export function analyzeSnippetsScope(
304309
if (!upperScope) continue;
305310
const variable = upperScope.set.get(snippet.id.name);
306311
if (!variable) continue;
307-
// Add the virtual reference for reading.
308-
const reference = addVirtualReference(snippet.id, variable, upperScope, {
309-
read: true,
310-
});
311-
(reference as any).svelteSnippetReference = true;
312+
const defIds = variable.defs.map((d) => d.name);
313+
const refs = variable.references.filter(
314+
(id) => !defIds.includes(id.identifier),
315+
);
316+
317+
if (refs.length <= 0) {
318+
// If the snippet is not referenced,
319+
// remove the a variable from the upperScope.
320+
removeIdentifierVariable(snippet.id, upperScope);
321+
} else {
322+
// Add the virtual reference for reading.
323+
const reference = addVirtualReference(
324+
snippet.id,
325+
variable,
326+
upperScope,
327+
{
328+
read: true,
329+
},
330+
);
331+
(reference as any).svelteSnippetReference = true;
332+
}
312333
}
313334
}
314335
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script lang="ts">
2+
import Foo from './Foo.svelte';
3+
</script>
4+
5+
<div>
6+
<!-- The snippet is not used. -->
7+
{#snippet children()}
8+
<th>fruit</th>
9+
<th>qty</th>
10+
<th>price</th>
11+
<th>total</th>
12+
{/snippet}
13+
</div>
14+
15+
<Foo>
16+
<!-- The snippet is used and does not add any variables. -->
17+
{#snippet children(arg)}
18+
<p>{arg}</p>
19+
{/snippet}
20+
{#snippet c()}
21+
<Foo>
22+
<!-- The snippet is used and does not add any variables. -->
23+
{#snippet children(arg)}
24+
<p>{arg}</p>
25+
{/snippet}
26+
</Foo>
27+
{/snippet}
28+
</Foo>
29+
30+
<!-- The snippet is used and add a variable. -->
31+
{#snippet bar(arg)}
32+
<p>{arg}</p>
33+
{/snippet}
34+
<Foo children={bar}>
35+
{#snippet c()}
36+
<!-- The snippet is used and add a variable. -->
37+
{#snippet bar(arg)}
38+
<p>{arg}</p>
39+
{/snippet}
40+
<Foo children={bar}>
41+
</Foo>
42+
{/snippet}
43+
</Foo>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[
2+
{
3+
"ruleId": "no-unused-vars",
4+
"code": "children",
5+
"line": 7,
6+
"column": 12,
7+
"message": "'children' is defined but never used."
8+
},
9+
{
10+
"ruleId": "no-shadow",
11+
"code": "bar",
12+
"line": 37,
13+
"column": 13,
14+
"message": "'bar' is already declared in the upper scope on line 31 column 11."
15+
},
16+
{
17+
"ruleId": "@typescript-eslint/no-shadow",
18+
"code": "bar",
19+
"line": 37,
20+
"column": 13,
21+
"message": "'bar' is already declared in the upper scope on line 31 column 11."
22+
}
23+
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"parse": {
3+
"svelte": ">=5.0.0-0"
4+
}
5+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { Linter } from "eslint";
2+
import { generateParserOptions } from "../../../src/parser/test-utils.js";
3+
import { rules } from "@typescript-eslint/eslint-plugin";
4+
import * as parser from "../../../../src/index.js";
5+
import globals from "globals";
6+
7+
export function getConfig(): Linter.Config {
8+
return {
9+
plugins: {
10+
"@typescript-eslint": {
11+
rules: rules as any,
12+
},
13+
},
14+
languageOptions: {
15+
parser,
16+
parserOptions: {
17+
...generateParserOptions(),
18+
svelteFeatures: { runes: true },
19+
},
20+
globals: {
21+
...globals.browser,
22+
...globals.es2021,
23+
},
24+
},
25+
rules: {
26+
"no-shadow": "error",
27+
"@typescript-eslint/no-shadow": "error",
28+
"no-undef": "error",
29+
"no-unused-vars": "error",
30+
},
31+
};
32+
}

tests/fixtures/integrations/snippet-scope/ts-snippet-hoist-scope-setup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Linter } from "eslint";
2-
import { generateParserOptions } from "../../../src/parser/test-utils";
2+
import { generateParserOptions } from "../../../src/parser/test-utils.js";
33
import { rules } from "@typescript-eslint/eslint-plugin";
4-
import * as parser from "../../../../src";
4+
import * as parser from "../../../../src/index.js";
55
import globals from "globals";
66

77
export function getConfig(): Linter.Config {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy