Skip to content

Commit

Permalink
fix: recognize script as module for Typescript type checking (#604)
Browse files Browse the repository at this point in the history
  • Loading branch information
baseballyama authored Dec 1, 2024
1 parent ca0cb19 commit 5ed0609
Show file tree
Hide file tree
Showing 9 changed files with 1,710 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/purple-otters-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-eslint-parser": patch
---

fix: recognize script as module for Typescript type checking
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@
"eslint-plugin-regexp": "^2.7.0",
"eslint-plugin-svelte": "^2.46.1",
"eslint-plugin-yml": "^1.15.0",
"estree-walker": "^3.0.3",
"globals": "^15.12.0",
"locate-character": "^3.0.0",
"magic-string": "^0.30.14",
Expand Down
75 changes: 65 additions & 10 deletions src/parser/typescript/analyze/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ export function analyzeTypeScriptInSvelte(

analyzeRenderScopes(code, ctx);

// 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`.
// see: https://github.com/sveltejs/svelte-eslint-parser/issues/557
if (!hasExportDeclaration(result.ast)) {
appendDummyExport(ctx);
}

ctx.appendOriginalToEnd();

return ctx;
Expand Down Expand Up @@ -118,6 +124,18 @@ export function analyzeTypeScript(
return ctx;
}

function hasExportDeclaration(ast: TSESParseForESLintResult["ast"]): boolean {
for (const node of ast.body) {
if (
node.type === "ExportNamedDeclaration" ||
node.type === "ExportDefaultDeclaration"
) {
return true;
}
}
return false;
}

/**
* Analyze the store reference names.
* Insert type definitions code to provide correct type information for variables that begin with `$`.
Expand Down Expand Up @@ -319,6 +337,34 @@ function analyzeDollarDollarVariables(
}
}

/** Append dummy export */
function appendDummyExport(ctx: VirtualTypeScriptContext) {
ctx.appendVirtualScript(`export namespace SvelteEslintParserModuleMarker {}`);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
if (
node.type !== "ExportNamedDeclaration" ||
node.declaration?.type !== "TSModuleDeclaration" ||
node.declaration.kind !== "namespace" ||
node.declaration.id.type !== "Identifier" ||
node.declaration.id.name !== "SvelteEslintParserModuleMarker"
) {
return false;
}
const program = result.ast;
program.body.splice(program.body.indexOf(node), 1);

const scopeManager = result.scopeManager as ScopeManager;

// Remove `declare` variable
removeAllScopeAndVariableAndReference(node, {
visitorKeys: result.visitorKeys,
scopeManager,
});

return true;
});
}

/**
* Analyze Runes.
* Insert type definitions code to provide correct type information for Runes.
Expand Down Expand Up @@ -569,24 +615,29 @@ function analyzeRenderScopes(
) {
ctx.appendOriginal(code.script.length);
const renderFunctionName = ctx.generateUniqueId("render");
ctx.appendVirtualScript(`function ${renderFunctionName}(){`);
ctx.appendVirtualScript(`export function ${renderFunctionName}(){`);
ctx.appendOriginal(code.script.length + code.render.length);
ctx.appendVirtualScript(`}`);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
if (
node.type !== "FunctionDeclaration" ||
node.id.name !== renderFunctionName
node.type !== "ExportNamedDeclaration" ||
node.declaration?.type !== "FunctionDeclaration" ||
node.declaration?.id?.name !== renderFunctionName
) {
return false;
}
const program = result.ast;
program.body.splice(program.body.indexOf(node), 1, ...node.body.body);
for (const body of node.body.body) {
program.body.splice(
program.body.indexOf(node),
1,
...node.declaration.body.body,
);
for (const body of node.declaration.body.body) {
body.parent = program;
}

const scopeManager = result.scopeManager as ScopeManager;
removeFunctionScope(node, scopeManager);
removeFunctionScope(node.declaration, scopeManager);
return true;
});
}
Expand Down Expand Up @@ -843,7 +894,7 @@ function transformForReactiveStatement(
const functionId = ctx.generateUniqueId("reactiveStatementScopeFunction");
const originalBody = statement.body;
ctx.appendOriginal(originalBody.range[0]);
ctx.appendVirtualScript(`function ${functionId}(){`);
ctx.appendVirtualScript(`export function ${functionId}(){`);
ctx.appendOriginal(originalBody.range[1]);
ctx.appendVirtualScript(`}`);
ctx.appendOriginal(statement.range[1]);
Expand All @@ -854,14 +905,18 @@ function transformForReactiveStatement(
}
const reactiveStatement = node as TSESTree.LabeledStatement;
const body = reactiveStatement.body;
if (body.type !== "FunctionDeclaration" || body.id.name !== functionId) {
if (
body.type !== "ExportNamedDeclaration" ||
body.declaration?.type !== "FunctionDeclaration" ||
body.declaration?.id?.name !== functionId
) {
return false;
}
reactiveStatement.body = body.body.body[0];
reactiveStatement.body = body.declaration.body.body[0];
reactiveStatement.body.parent = reactiveStatement;

const scopeManager = result.scopeManager as ScopeManager;
removeFunctionScope(body, scopeManager);
removeFunctionScope(body.declaration, scopeManager);
return true;
});
}
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/parser/ast/svelte5/ts-$props02-input.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts">
let { name }: { name: string } = $props();
</script>

{name}
Loading

0 comments on commit 5ed0609

Please sign in to comment.