Skip to content

Commit 5ed0609

Browse files
authored
fix: recognize script as module for Typescript type checking (#604)
1 parent ca0cb19 commit 5ed0609

9 files changed

+1710
-11
lines changed

.changeset/purple-otters-hammer.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": patch
3+
---
4+
5+
fix: recognize script as module for Typescript type checking

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@
9595
"eslint-plugin-regexp": "^2.7.0",
9696
"eslint-plugin-svelte": "^2.46.1",
9797
"eslint-plugin-yml": "^1.15.0",
98-
"estree-walker": "^3.0.3",
9998
"globals": "^15.12.0",
10099
"locate-character": "^3.0.0",
101100
"magic-string": "^0.30.14",

src/parser/typescript/analyze/index.ts

+65-10
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ export function analyzeTypeScriptInSvelte(
8080

8181
analyzeRenderScopes(code, ctx);
8282

83+
// When performing type checking on TypeScript code that is not a module, the error `Cannot redeclare block-scoped variable 'xxx'`. occurs. To fix this, add an `export`.
84+
// see: https://github.com/sveltejs/svelte-eslint-parser/issues/557
85+
if (!hasExportDeclaration(result.ast)) {
86+
appendDummyExport(ctx);
87+
}
88+
8389
ctx.appendOriginalToEnd();
8490

8591
return ctx;
@@ -118,6 +124,18 @@ export function analyzeTypeScript(
118124
return ctx;
119125
}
120126

127+
function hasExportDeclaration(ast: TSESParseForESLintResult["ast"]): boolean {
128+
for (const node of ast.body) {
129+
if (
130+
node.type === "ExportNamedDeclaration" ||
131+
node.type === "ExportDefaultDeclaration"
132+
) {
133+
return true;
134+
}
135+
}
136+
return false;
137+
}
138+
121139
/**
122140
* Analyze the store reference names.
123141
* Insert type definitions code to provide correct type information for variables that begin with `$`.
@@ -319,6 +337,34 @@ function analyzeDollarDollarVariables(
319337
}
320338
}
321339

340+
/** Append dummy export */
341+
function appendDummyExport(ctx: VirtualTypeScriptContext) {
342+
ctx.appendVirtualScript(`export namespace SvelteEslintParserModuleMarker {}`);
343+
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
344+
if (
345+
node.type !== "ExportNamedDeclaration" ||
346+
node.declaration?.type !== "TSModuleDeclaration" ||
347+
node.declaration.kind !== "namespace" ||
348+
node.declaration.id.type !== "Identifier" ||
349+
node.declaration.id.name !== "SvelteEslintParserModuleMarker"
350+
) {
351+
return false;
352+
}
353+
const program = result.ast;
354+
program.body.splice(program.body.indexOf(node), 1);
355+
356+
const scopeManager = result.scopeManager as ScopeManager;
357+
358+
// Remove `declare` variable
359+
removeAllScopeAndVariableAndReference(node, {
360+
visitorKeys: result.visitorKeys,
361+
scopeManager,
362+
});
363+
364+
return true;
365+
});
366+
}
367+
322368
/**
323369
* Analyze Runes.
324370
* Insert type definitions code to provide correct type information for Runes.
@@ -569,24 +615,29 @@ function analyzeRenderScopes(
569615
) {
570616
ctx.appendOriginal(code.script.length);
571617
const renderFunctionName = ctx.generateUniqueId("render");
572-
ctx.appendVirtualScript(`function ${renderFunctionName}(){`);
618+
ctx.appendVirtualScript(`export function ${renderFunctionName}(){`);
573619
ctx.appendOriginal(code.script.length + code.render.length);
574620
ctx.appendVirtualScript(`}`);
575621
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
576622
if (
577-
node.type !== "FunctionDeclaration" ||
578-
node.id.name !== renderFunctionName
623+
node.type !== "ExportNamedDeclaration" ||
624+
node.declaration?.type !== "FunctionDeclaration" ||
625+
node.declaration?.id?.name !== renderFunctionName
579626
) {
580627
return false;
581628
}
582629
const program = result.ast;
583-
program.body.splice(program.body.indexOf(node), 1, ...node.body.body);
584-
for (const body of node.body.body) {
630+
program.body.splice(
631+
program.body.indexOf(node),
632+
1,
633+
...node.declaration.body.body,
634+
);
635+
for (const body of node.declaration.body.body) {
585636
body.parent = program;
586637
}
587638

588639
const scopeManager = result.scopeManager as ScopeManager;
589-
removeFunctionScope(node, scopeManager);
640+
removeFunctionScope(node.declaration, scopeManager);
590641
return true;
591642
});
592643
}
@@ -843,7 +894,7 @@ function transformForReactiveStatement(
843894
const functionId = ctx.generateUniqueId("reactiveStatementScopeFunction");
844895
const originalBody = statement.body;
845896
ctx.appendOriginal(originalBody.range[0]);
846-
ctx.appendVirtualScript(`function ${functionId}(){`);
897+
ctx.appendVirtualScript(`export function ${functionId}(){`);
847898
ctx.appendOriginal(originalBody.range[1]);
848899
ctx.appendVirtualScript(`}`);
849900
ctx.appendOriginal(statement.range[1]);
@@ -854,14 +905,18 @@ function transformForReactiveStatement(
854905
}
855906
const reactiveStatement = node as TSESTree.LabeledStatement;
856907
const body = reactiveStatement.body;
857-
if (body.type !== "FunctionDeclaration" || body.id.name !== functionId) {
908+
if (
909+
body.type !== "ExportNamedDeclaration" ||
910+
body.declaration?.type !== "FunctionDeclaration" ||
911+
body.declaration?.id?.name !== functionId
912+
) {
858913
return false;
859914
}
860-
reactiveStatement.body = body.body.body[0];
915+
reactiveStatement.body = body.declaration.body.body[0];
861916
reactiveStatement.body.parent = reactiveStatement;
862917

863918
const scopeManager = result.scopeManager as ScopeManager;
864-
removeFunctionScope(body, scopeManager);
919+
removeFunctionScope(body.declaration, scopeManager);
865920
return true;
866921
});
867922
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script lang="ts">
2+
let { name }: { name: string } = $props();
3+
</script>
4+
5+
{name}

0 commit comments

Comments
 (0)