From df461c3019b5b964a83c45fc16888fe8b706a3a1 Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Mon, 13 Jan 2025 16:12:55 +0900 Subject: [PATCH] fix: resolve issues in Runes mode detection causing parser malfunctions (#638) --- .changeset/empty-turtles-report.md | 5 + src/parser/analyze-scope.ts | 3 +- src/parser/globals.ts | 6 +- src/parser/typescript/analyze/index.ts | 8 +- .../unknown-runes-mode/$props-input.svelte | 5 + .../unknown-runes-mode/$props-output.json | 860 ++++++++++++++++++ .../$props-scope-output.json | 487 ++++++++++ .../unknown-runes-mode/svelte.config.js | 2 + 8 files changed, 1371 insertions(+), 5 deletions(-) create mode 100644 .changeset/empty-turtles-report.md create mode 100644 tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-input.svelte create mode 100644 tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-output.json create mode 100644 tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-scope-output.json create mode 100644 tests/fixtures/parser/ast/svelte5/unknown-runes-mode/svelte.config.js diff --git a/.changeset/empty-turtles-report.md b/.changeset/empty-turtles-report.md new file mode 100644 index 00000000..d59a7eb2 --- /dev/null +++ b/.changeset/empty-turtles-report.md @@ -0,0 +1,5 @@ +--- +"svelte-eslint-parser": patch +--- + +fix: resolve issues in Runes mode detection causing parser malfunctions diff --git a/src/parser/analyze-scope.ts b/src/parser/analyze-scope.ts index b502b034..3f69b665 100644 --- a/src/parser/analyze-scope.ts +++ b/src/parser/analyze-scope.ts @@ -191,7 +191,8 @@ export function analyzePropsScope( } } } else if (node.type === "VariableDeclaration") { - if (svelteParseContext.runes) { + // Process if not confirmed as non-Runes mode. + if (svelteParseContext.runes !== false) { // Process for Svelte v5 Runes props. e.g. `let { x = $bindable() } = $props()`; for (const decl of node.declarations) { if ( diff --git a/src/parser/globals.ts b/src/parser/globals.ts index be66a663..a86831ba 100644 --- a/src/parser/globals.ts +++ b/src/parser/globals.ts @@ -16,7 +16,8 @@ type Global = export function getGlobalsForSvelte( svelteParseContext: SvelteParseContext, ): readonly Global[] { - if (svelteParseContext.runes) { + // Process if not confirmed as non-Runes mode. + if (svelteParseContext.runes !== false) { return [...globalsForSvelte, ...globalsForRunes]; } return globalsForSvelte; @@ -24,7 +25,8 @@ export function getGlobalsForSvelte( export function getGlobalsForSvelteScript( svelteParseContext: SvelteParseContext, ): readonly Global[] { - if (svelteParseContext.runes) { + // Process if not confirmed as non-Runes mode. + if (svelteParseContext.runes !== false) { return globalsForRunes; } return []; diff --git a/src/parser/typescript/analyze/index.ts b/src/parser/typescript/analyze/index.ts index 19d7ab7f..d70cdbe7 100644 --- a/src/parser/typescript/analyze/index.ts +++ b/src/parser/typescript/analyze/index.ts @@ -377,7 +377,10 @@ function analyzeRuneVariables( ctx: VirtualTypeScriptContext, svelteParseContext: SvelteParseContext, ) { - if (!svelteParseContext.runes) return; + // No processing is needed if the user is determined not to be in Runes mode. + if (svelteParseContext.runes === false) { + return; + } const scopeManager = result.scopeManager; for (const globalName of globalsForRunes) { if ( @@ -583,7 +586,8 @@ function* analyzeDollarDerivedScopes( result: TSESParseForESLintResult, svelteParseContext: SvelteParseContext, ): Iterable { - if (!svelteParseContext.runes) return; + // No processing is needed if the user is determined not to be in Runes mode. + if (svelteParseContext.runes === false) return; const scopeManager = result.scopeManager; const derivedReferences = scopeManager.globalScope!.through.filter( (reference) => reference.identifier.name === "$derived", diff --git a/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-input.svelte b/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-input.svelte new file mode 100644 index 00000000..60106664 --- /dev/null +++ b/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-input.svelte @@ -0,0 +1,5 @@ + + +{p} diff --git a/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-output.json b/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-output.json new file mode 100644 index 00000000..0ccd50d3 --- /dev/null +++ b/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-output.json @@ -0,0 +1,860 @@ +{ + "type": "Program", + "body": [ + { + "type": "SvelteScriptElement", + "name": { + "type": "SvelteName", + "name": "script", + "range": [ + 1, + 7 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 7 + } + } + }, + "startTag": { + "type": "SvelteStartTag", + "attributes": [], + "selfClosing": false, + "range": [ + 0, + 8 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 8 + } + } + }, + "body": [ + { + "type": "VariableDeclaration", + "kind": "const", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "ObjectPattern", + "properties": [ + { + "type": "Property", + "kind": "init", + "computed": false, + "key": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + }, + "method": false, + "shorthand": true, + "value": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + }, + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + } + ], + "range": [ + 16, + 21 + ], + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 2, + "column": 12 + } + } + }, + "init": { + "type": "CallExpression", + "arguments": [], + "callee": { + "type": "Identifier", + "name": "$props", + "range": [ + 24, + 30 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 21 + } + } + }, + "optional": false, + "range": [ + 24, + 32 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 23 + } + } + }, + "range": [ + 16, + 32 + ], + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 2, + "column": 23 + } + } + } + ], + "range": [ + 10, + 33 + ], + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 24 + } + } + } + ], + "endTag": { + "type": "SvelteEndTag", + "range": [ + 34, + 43 + ], + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 9 + } + } + }, + "range": [ + 0, + 43 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 9 + } + } + }, + { + "type": "SvelteText", + "value": "\n\n", + "range": [ + 43, + 45 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 5, + "column": 0 + } + } + }, + { + "type": "SvelteElement", + "kind": "html", + "name": { + "type": "SvelteName", + "name": "span", + "range": [ + 46, + 50 + ], + "loc": { + "start": { + "line": 5, + "column": 1 + }, + "end": { + "line": 5, + "column": 5 + } + } + }, + "startTag": { + "type": "SvelteStartTag", + "attributes": [], + "selfClosing": false, + "range": [ + 45, + 51 + ], + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 6 + } + } + }, + "children": [ + { + "type": "SvelteMustacheTag", + "kind": "text", + "expression": { + "type": "Identifier", + "name": "p", + "range": [ + 52, + 53 + ], + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 5, + "column": 8 + } + } + }, + "range": [ + 51, + 54 + ], + "loc": { + "start": { + "line": 5, + "column": 6 + }, + "end": { + "line": 5, + "column": 9 + } + } + } + ], + "endTag": { + "type": "SvelteEndTag", + "range": [ + 54, + 61 + ], + "loc": { + "start": { + "line": 5, + "column": 9 + }, + "end": { + "line": 5, + "column": 16 + } + } + }, + "range": [ + 45, + 61 + ], + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 16 + } + } + } + ], + "sourceType": "module", + "comments": [], + "tokens": [ + { + "type": "Punctuator", + "value": "<", + "range": [ + 0, + 1 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "HTMLIdentifier", + "value": "script", + "range": [ + 1, + 7 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 7 + } + } + }, + { + "type": "Punctuator", + "value": ">", + "range": [ + 7, + 8 + ], + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 8 + } + } + }, + { + "type": "Keyword", + "value": "const", + "range": [ + 10, + 15 + ], + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 6 + } + } + }, + { + "type": "Punctuator", + "value": "{", + "range": [ + 16, + 17 + ], + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 2, + "column": 8 + } + } + }, + { + "type": "Identifier", + "value": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + }, + { + "type": "Punctuator", + "value": "}", + "range": [ + 20, + 21 + ], + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 2, + "column": 12 + } + } + }, + { + "type": "Punctuator", + "value": "=", + "range": [ + 22, + 23 + ], + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 14 + } + } + }, + { + "type": "Identifier", + "value": "$props", + "range": [ + 24, + 30 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 21 + } + } + }, + { + "type": "Punctuator", + "value": "(", + "range": [ + 30, + 31 + ], + "loc": { + "start": { + "line": 2, + "column": 21 + }, + "end": { + "line": 2, + "column": 22 + } + } + }, + { + "type": "Punctuator", + "value": ")", + "range": [ + 31, + 32 + ], + "loc": { + "start": { + "line": 2, + "column": 22 + }, + "end": { + "line": 2, + "column": 23 + } + } + }, + { + "type": "Punctuator", + "value": ";", + "range": [ + 32, + 33 + ], + "loc": { + "start": { + "line": 2, + "column": 23 + }, + "end": { + "line": 2, + "column": 24 + } + } + }, + { + "type": "Punctuator", + "value": "<", + "range": [ + 34, + 35 + ], + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + } + } + }, + { + "type": "Punctuator", + "value": "/", + "range": [ + 35, + 36 + ], + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 3, + "column": 2 + } + } + }, + { + "type": "HTMLIdentifier", + "value": "script", + "range": [ + 36, + 42 + ], + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 8 + } + } + }, + { + "type": "Punctuator", + "value": ">", + "range": [ + 42, + 43 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 9 + } + } + }, + { + "type": "HTMLText", + "value": "\n\n", + "range": [ + 43, + 45 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 5, + "column": 0 + } + } + }, + { + "type": "Punctuator", + "value": "<", + "range": [ + 45, + 46 + ], + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 1 + } + } + }, + { + "type": "HTMLIdentifier", + "value": "span", + "range": [ + 46, + 50 + ], + "loc": { + "start": { + "line": 5, + "column": 1 + }, + "end": { + "line": 5, + "column": 5 + } + } + }, + { + "type": "Punctuator", + "value": ">", + "range": [ + 50, + 51 + ], + "loc": { + "start": { + "line": 5, + "column": 5 + }, + "end": { + "line": 5, + "column": 6 + } + } + }, + { + "type": "Punctuator", + "value": "{", + "range": [ + 51, + 52 + ], + "loc": { + "start": { + "line": 5, + "column": 6 + }, + "end": { + "line": 5, + "column": 7 + } + } + }, + { + "type": "Identifier", + "value": "p", + "range": [ + 52, + 53 + ], + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 5, + "column": 8 + } + } + }, + { + "type": "Punctuator", + "value": "}", + "range": [ + 53, + 54 + ], + "loc": { + "start": { + "line": 5, + "column": 8 + }, + "end": { + "line": 5, + "column": 9 + } + } + }, + { + "type": "Punctuator", + "value": "<", + "range": [ + 54, + 55 + ], + "loc": { + "start": { + "line": 5, + "column": 9 + }, + "end": { + "line": 5, + "column": 10 + } + } + }, + { + "type": "Punctuator", + "value": "/", + "range": [ + 55, + 56 + ], + "loc": { + "start": { + "line": 5, + "column": 10 + }, + "end": { + "line": 5, + "column": 11 + } + } + }, + { + "type": "HTMLIdentifier", + "value": "span", + "range": [ + 56, + 60 + ], + "loc": { + "start": { + "line": 5, + "column": 11 + }, + "end": { + "line": 5, + "column": 15 + } + } + }, + { + "type": "Punctuator", + "value": ">", + "range": [ + 60, + 61 + ], + "loc": { + "start": { + "line": 5, + "column": 15 + }, + "end": { + "line": 5, + "column": 16 + } + } + } + ], + "range": [ + 0, + 62 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 0 + } + } +} \ No newline at end of file diff --git a/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-scope-output.json b/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-scope-output.json new file mode 100644 index 00000000..c8c1da71 --- /dev/null +++ b/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/$props-scope-output.json @@ -0,0 +1,487 @@ +{ + "type": "global", + "variables": [ + { + "name": "$$slots", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$$props", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$$restProps", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$state", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$derived", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$effect", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$props", + "identifiers": [], + "defs": [], + "references": [ + { + "identifier": { + "type": "Identifier", + "name": "$props", + "range": [ + 24, + 30 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 21 + } + } + }, + "from": "module", + "init": null, + "resolved": null + } + ] + }, + { + "name": "$bindable", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$inspect", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$host", + "identifiers": [], + "defs": [], + "references": [] + } + ], + "references": [], + "childScopes": [ + { + "type": "module", + "variables": [ + { + "name": "p", + "identifiers": [ + { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + } + ], + "defs": [ + { + "type": "Variable", + "name": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + }, + "node": { + "type": "VariableDeclarator", + "id": { + "type": "ObjectPattern", + "properties": [ + { + "type": "Property", + "kind": "init", + "computed": false, + "key": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + }, + "method": false, + "shorthand": true, + "value": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + }, + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + } + ], + "range": [ + 16, + 21 + ], + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 2, + "column": 12 + } + } + }, + "init": { + "type": "CallExpression", + "arguments": [], + "callee": { + "type": "Identifier", + "name": "$props", + "range": [ + 24, + 30 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 21 + } + } + }, + "optional": false, + "range": [ + 24, + 32 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 23 + } + } + }, + "range": [ + 16, + 32 + ], + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 2, + "column": 23 + } + } + } + } + ], + "references": [ + { + "identifier": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + }, + "from": "module", + "init": true, + "resolved": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + } + }, + { + "identifier": { + "type": "Identifier", + "name": "p", + "range": [ + 52, + 53 + ], + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 5, + "column": 8 + } + } + }, + "from": "module", + "init": null, + "resolved": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + } + } + ] + } + ], + "references": [ + { + "identifier": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + }, + "from": "module", + "init": true, + "resolved": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + } + }, + { + "identifier": { + "type": "Identifier", + "name": "$props", + "range": [ + 24, + 30 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 21 + } + } + }, + "from": "module", + "init": null, + "resolved": null + }, + { + "identifier": { + "type": "Identifier", + "name": "p", + "range": [ + 52, + 53 + ], + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 5, + "column": 8 + } + } + }, + "from": "module", + "init": null, + "resolved": { + "type": "Identifier", + "name": "p", + "range": [ + 18, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + } + } + } + ], + "childScopes": [], + "through": [ + { + "identifier": { + "type": "Identifier", + "name": "$props", + "range": [ + 24, + 30 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 21 + } + } + }, + "from": "module", + "init": null, + "resolved": null + } + ] + } + ], + "through": [] +} \ No newline at end of file diff --git a/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/svelte.config.js b/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/svelte.config.js new file mode 100644 index 00000000..cbc7a6c8 --- /dev/null +++ b/tests/fixtures/parser/ast/svelte5/unknown-runes-mode/svelte.config.js @@ -0,0 +1,2 @@ +/** Config for testing */ +export default {};