Skip to content

Commit

Permalink
fix: wrong scope in top level snippets (#486)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Mar 8, 2024
1 parent 1b13acb commit 79a4fb7
Show file tree
Hide file tree
Showing 28 changed files with 15,697 additions and 2,141 deletions.
5 changes: 5 additions & 0 deletions .changeset/hot-impalas-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-eslint-parser": patch
---

fix: wrong scope in top level snippets
13 changes: 9 additions & 4 deletions src/context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class ScriptsSourceCode {
separate: string;
beforeSpaces: string;
render: string;
snippet: string;
generics: string;
} | null = null;

Expand All @@ -57,23 +58,25 @@ export class ScriptsSourceCode {
this._appendScriptLets.separate +
this._appendScriptLets.beforeSpaces +
this._appendScriptLets.render +
this._appendScriptLets.snippet +
this._appendScriptLets.generics
);
}

public getCurrentVirtualCodeInfo(): {
script: string;
render: string;
generics: string;
rootScope: string;
} {
if (this._appendScriptLets == null) {
return { script: this.raw, render: "", generics: "" };
return { script: this.raw, render: "", rootScope: "" };
}
return {
script: this.trimmedRaw + this._appendScriptLets.separate,
render:
this._appendScriptLets.beforeSpaces + this._appendScriptLets.render,
generics: this._appendScriptLets.generics,
rootScope:
this._appendScriptLets.snippet + this._appendScriptLets.generics,
};
}

Expand All @@ -86,13 +89,14 @@ export class ScriptsSourceCode {
this._appendScriptLets.separate.length +
this._appendScriptLets.beforeSpaces.length +
this._appendScriptLets.render.length +
this._appendScriptLets.snippet.length +
this._appendScriptLets.generics.length
);
}

public addLet(
letCode: string,
kind: "generics" | "render",
kind: "generics" | "snippet" | "render",
): { start: number; end: number } {
if (this._appendScriptLets == null) {
const currentLength = this.trimmedRaw.length;
Expand All @@ -102,6 +106,7 @@ export class ScriptsSourceCode {
separate: "\n;",
beforeSpaces: after,
render: "",
snippet: "",
generics: "",
};
}
Expand Down
58 changes: 40 additions & 18 deletions src/context/script-let.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ export class ScriptLetContext {

private readonly unique = new UniqueIdGenerator();

private currentScriptScopeKind: "render" | "snippet" = "render";

public constructor(ctx: Context) {
this.script = ctx.sourceCode.scripts;
this.ctx = ctx;
Expand All @@ -164,7 +166,7 @@ export class ScriptLetContext {
this.appendScript(
`(${part})${isTS ? `as (${typing})` : ""};`,
range[0] - 1,
"render",
this.currentScriptScopeKind,
(st, tokens, comments, result) => {
const exprSt = st as ESTree.ExpressionStatement;
const tsAs: TSAsExpression | null = isTS
Expand Down Expand Up @@ -220,7 +222,7 @@ export class ScriptLetContext {
this.appendScript(
`({${part}});`,
range[0] - 2,
"render",
this.currentScriptScopeKind,
(st, tokens, _comments, result) => {
const exprSt = st as ESTree.ExpressionStatement;
const objectExpression: ESTree.ObjectExpression =
Expand Down Expand Up @@ -259,7 +261,7 @@ export class ScriptLetContext {
this.appendScript(
`const ${part};`,
range[0] - 6,
"render",
this.currentScriptScopeKind,
(st, tokens, _comments, result) => {
const decl = st as ESTree.VariableDeclaration;
const node = decl.declarations[0];
Expand Down Expand Up @@ -393,7 +395,7 @@ export class ScriptLetContext {
const restore = this.appendScript(
`if(${part}){`,
range[0] - 3,
"render",
this.currentScriptScopeKind,
(st, tokens, _comments, result) => {
const ifSt = st as ESTree.IfStatement;
const node = ifSt.test;
Expand All @@ -417,7 +419,7 @@ export class ScriptLetContext {
ifSt.consequent = null as never;
},
);
this.pushScope(restore, "}");
this.pushScope(restore, "}", this.currentScriptScopeKind);
}

public nestEachBlock(
Expand Down Expand Up @@ -448,7 +450,7 @@ export class ScriptLetContext {
const restore = this.appendScript(
source,
exprRange[0] - exprOffset,
"render",
this.currentScriptScopeKind,
(st, tokens, comments, result) => {
const expSt = st as ESTree.ExpressionStatement;
const call = expSt.expression as ESTree.CallExpression;
Expand Down Expand Up @@ -525,21 +527,22 @@ export class ScriptLetContext {
expSt.expression = null as never;
},
);
this.pushScope(restore, "});");
this.pushScope(restore, "});", this.currentScriptScopeKind);
}

public nestSnippetBlock(
id: ESTree.Identifier,
closeParentIndex: number,
snippetBlock: SvelteSnippetBlock,
kind: "snippet" | "render",
callback: (id: ESTree.Identifier, params: ESTree.Pattern[]) => void,
): void {
const idRange = getNodeRange(id);
const part = this.ctx.code.slice(idRange[0], closeParentIndex + 1);
const restore = this.appendScript(
`function ${part}{`,
idRange[0] - 9,
"render",
kind,
(st, tokens, _comments, result) => {
const fnDecl = st as ESTree.FunctionDeclaration;
const idNode = fnDecl.id;
Expand All @@ -565,7 +568,7 @@ export class ScriptLetContext {
fnDecl.params = [];
},
);
this.pushScope(restore, "}");
this.pushScope(restore, "}", kind);
}

public nestBlock(
Expand All @@ -588,7 +591,7 @@ export class ScriptLetContext {
for (const preparationScript of generatedTypes.preparationScript) {
this.appendScriptWithoutOffset(
preparationScript,
"render",
this.currentScriptScopeKind,
(node, tokens, comments, result) => {
tokens.length = 0;
comments.length = 0;
Expand All @@ -608,7 +611,7 @@ export class ScriptLetContext {
const restore = this.appendScript(
`{`,
block.range[0],
"render",
this.currentScriptScopeKind,
(st, tokens, _comments, result) => {
const blockSt = st as ESTree.BlockStatement;

Expand All @@ -622,7 +625,7 @@ export class ScriptLetContext {
blockSt.body = null as never;
},
);
this.pushScope(restore, "}");
this.pushScope(restore, "}", this.currentScriptScopeKind);
} else {
const sortedParams = [...resolvedParams]
.map((d) => {
Expand Down Expand Up @@ -664,7 +667,7 @@ export class ScriptLetContext {
const restore = this.appendScript(
`(${source})=>{`,
maps[0].range[0] - 1,
"render",
this.currentScriptScopeKind,
(st, tokens, comments, result) => {
const exprSt = st as ESTree.ExpressionStatement;
const fn = exprSt.expression as ESTree.ArrowFunctionExpression;
Expand Down Expand Up @@ -723,7 +726,7 @@ export class ScriptLetContext {
exprSt.expression = null as never;
},
);
this.pushScope(restore, "};");
this.pushScope(restore, "};", this.currentScriptScopeKind);
}
}

Expand All @@ -738,7 +741,7 @@ export class ScriptLetContext {
private appendScript(
text: string,
offset: number,
kind: "generics" | "render",
kind: "generics" | "snippet" | "render",
callback: (
node: ESTree.Node,
tokens: Token[],
Expand Down Expand Up @@ -766,7 +769,7 @@ export class ScriptLetContext {

private appendScriptWithoutOffset(
text: string,
kind: "generics" | "render",
kind: "generics" | "snippet" | "render",
callback: (
node: ESTree.Node,
tokens: Token[],
Expand All @@ -788,9 +791,16 @@ export class ScriptLetContext {
return restoreCallback;
}

private pushScope(restoreCallback: RestoreCallback, closeToken: string) {
private pushScope(
restoreCallback: RestoreCallback,
closeToken: string,
kind: "snippet" | "render",
) {
const upper = this.currentScriptScopeKind;
this.currentScriptScopeKind = kind;
this.closeScopeCallbacks.push(() => {
this.script.addLet(closeToken, "render");
this.script.addLet(closeToken, kind);
this.currentScriptScopeKind = upper;
restoreCallback.end = this.script.getCurrentVirtualCodeLength();
});
}
Expand Down Expand Up @@ -825,7 +835,19 @@ export class ScriptLetContext {
// If we replace the `scope.block` at this time,
// the scope restore calculation will not work, so we will replace the `scope.block` later.
postprocessList.push(() => {
const beforeBlock = scope.block;
scope.block = node;

for (const variable of [
...scope.variables,
...(scope.upper?.variables ?? []),
]) {
for (const def of variable.defs) {
if (def.node === beforeBlock) {
def.node = node;
}
}
}
});

const scopes = nodeToScope.get(node);
Expand Down
19 changes: 9 additions & 10 deletions src/parser/analyze-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,16 +235,15 @@ export function analyzeSnippetsScope(
(parent.kind === "special" && parent.name.name === "svelte:component"))
) {
const scope = getScopeFromNode(scopeManager, snippet.id);
const variable = scope.upper
? scope.upper.set.get(snippet.id.name)
: null;
if (variable) {
// Add the virtual reference for reading.
const reference = addVirtualReference(snippet.id, variable, scope, {
read: true,
});
(reference as any).svelteSnippetReference = true;
}
const upperScope = scope.upper;
if (!upperScope) continue;
const variable = upperScope.set.get(snippet.id.name);
if (!variable) continue;
// Add the virtual reference for reading.
const reference = addVirtualReference(snippet.id, variable, upperScope, {
read: true,
});
(reference as any).svelteSnippetReference = true;
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/parser/converts/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -655,10 +655,13 @@ export function convertSnippetBlock(
).end,
);

const scopeKind = parent.type === "Program" ? "snippet" : "render";

ctx.scriptLet.nestSnippetBlock(
node.expression,
closeParenIndex,
snippetBlock,
scopeKind,
(id, params) => {
snippetBlock.id = id;
snippetBlock.params = params;
Expand Down
24 changes: 19 additions & 5 deletions src/parser/converts/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export function convertSvelteRoot(
...ctx.getConvertLocation({ start: 0, end: ctx.code.length }),
};
const body = ast.body;
const snippetChildren: Compiler.SnippetBlock[] = [];
const fragment = getFragmentFromRoot(svelteAst);
if (fragment) {
let children = getChildren(fragment);
Expand All @@ -63,11 +64,21 @@ export function convertSvelteRoot(
children.push(options);
}
}
body.push(...convertChildren({ nodes: children }, ast, ctx));
const nonSnippetChildren: typeof children = [];
for (const child of children) {
if (child.type === "SnippetBlock") {
snippetChildren.push(child);
} else {
nonSnippetChildren.push(child);
}
}

body.push(...convertChildren({ nodes: nonSnippetChildren }, ast, ctx));
}
let script: SvelteScriptElement | null = null;
const instance = getInstanceFromRoot(svelteAst);
if (instance) {
const script: SvelteScriptElement = {
script = {
type: "SvelteScriptElement",
name: null as any,
startTag: null as any,
Expand All @@ -77,15 +88,13 @@ export function convertSvelteRoot(
...ctx.getConvertLocation(instance),
};
extractAttributes(script, ctx);
if (ctx.parserOptions.svelteFeatures?.experimentalGenerics)
convertGenericsAttribute(script, ctx);
extractElementTags(script, ctx, {
buildNameNode: (openTokenRange) => {
ctx.addToken("HTMLIdentifier", openTokenRange);
const name: SvelteName = {
type: "SvelteName",
name: "script",
parent: script,
parent: script!,
...ctx.getConvertLocation(openTokenRange),
};
return name;
Expand Down Expand Up @@ -163,6 +172,9 @@ export function convertSvelteRoot(

body.push(style);
}
body.push(...convertChildren({ nodes: snippetChildren }, ast, ctx));
if (script && ctx.parserOptions.svelteFeatures?.experimentalGenerics)
convertGenericsAttribute(script, ctx);

// Set the scope of the Program node.
ctx.scriptLet.addProgramRestore(
Expand All @@ -188,6 +200,8 @@ export function convertSvelteRoot(
},
);

sortNodes(body);

return ast;
}

Expand Down
2 changes: 0 additions & 2 deletions src/parser/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type * as Compiler from "svelte/compiler";
import type * as SvAST from "./svelte-ast-types";
import type { Context } from "../context";
import { convertSvelteRoot } from "./converts/index";
import { sortNodes } from "./sort";
import type { SvelteProgram } from "../ast";
import { ParseError } from "..";
import type { NormalizedParserOptions } from "./parser-options";
Expand All @@ -27,7 +26,6 @@ export function parseTemplate(
...(svelteVersion.gte(5) ? { modern: true } : {}),
}) as never as Compiler.Root | SvAST.AstLegacy;
const ast = convertSvelteRoot(svelteAst, ctx);
sortNodes(ast.body);

return {
ast,
Expand Down

0 comments on commit 79a4fb7

Please sign in to comment.