Skip to content

Commit

Permalink
fix: parsing error for nesting {#snippet} (#540)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi authored Jun 23, 2024
1 parent 985ff60 commit 48a7001
Show file tree
Hide file tree
Showing 6 changed files with 2,205 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/young-clouds-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-eslint-parser": patch
---

fix: parsing error for nesting `{#snippet}`
31 changes: 26 additions & 5 deletions src/context/script-let.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,12 @@ function getNodeRange(
];
}

type StatementNodeType = `${TSESTree.Statement["type"]}`;

type RestoreCallback = {
start: number;
end: number;
nodeType: StatementNodeType;
callback: ScriptLetRestoreCallback;
};

Expand Down Expand Up @@ -167,6 +170,7 @@ export class ScriptLetContext {
`(${part})${isTS ? `as (${typing})` : ""};`,
range[0] - 1,
this.currentScriptScopeKind,
"ExpressionStatement",
(st, tokens, comments, result) => {
const exprSt = st as ESTree.ExpressionStatement;
const tsAs: TSAsExpression | null = isTS
Expand Down Expand Up @@ -223,6 +227,7 @@ export class ScriptLetContext {
`({${part}});`,
range[0] - 2,
this.currentScriptScopeKind,
"ExpressionStatement",
(st, tokens, _comments, result) => {
const exprSt = st as ESTree.ExpressionStatement;
const objectExpression: ESTree.ObjectExpression =
Expand Down Expand Up @@ -262,6 +267,7 @@ export class ScriptLetContext {
`const ${part};`,
range[0] - 6,
this.currentScriptScopeKind,
"VariableDeclaration",
(st, tokens, _comments, result) => {
const decl = st as ESTree.VariableDeclaration;
const node = decl.declarations[0];
Expand Down Expand Up @@ -309,6 +315,7 @@ export class ScriptLetContext {
scriptLet,
ranges.idRange[0] - 5,
"generics",
"TSTypeAliasDeclaration",
(st, tokens, _comments, result) => {
const decl = st as any as TSESTree.TSTypeAliasDeclaration;
const id = decl.id;
Expand Down Expand Up @@ -361,6 +368,7 @@ export class ScriptLetContext {
scriptLet,
ranges.defaultRange[0] - 5 - id.length - 1,
"generics",
"TSTypeAliasDeclaration",
(st, tokens, _comments, result) => {
const decl = st as any as TSESTree.TSTypeAliasDeclaration;
const typeAnnotation = decl.typeAnnotation;
Expand Down Expand Up @@ -396,6 +404,7 @@ export class ScriptLetContext {
`if(${part}){`,
range[0] - 3,
this.currentScriptScopeKind,
"IfStatement",
(st, tokens, _comments, result) => {
const ifSt = st as ESTree.IfStatement;
const node = ifSt.test;
Expand Down Expand Up @@ -451,6 +460,7 @@ export class ScriptLetContext {
source,
exprRange[0] - exprOffset,
this.currentScriptScopeKind,
"ExpressionStatement",
(st, tokens, comments, result) => {
const expSt = st as ESTree.ExpressionStatement;
const call = expSt.expression as ESTree.CallExpression;
Expand Down Expand Up @@ -534,15 +544,18 @@ export class ScriptLetContext {
id: ESTree.Identifier,
closeParentIndex: number,
snippetBlock: SvelteSnippetBlock,
kind: "snippet" | "render",
// If set to null, `currentScriptScopeKind` will be used.
kind: "snippet" | null,
callback: (id: ESTree.Identifier, params: ESTree.Pattern[]) => void,
): void {
const scopeKind = kind || this.currentScriptScopeKind;
const idRange = getNodeRange(id);
const part = this.ctx.code.slice(idRange[0], closeParentIndex + 1);
const restore = this.appendScript(
`function ${part}{`,
idRange[0] - 9,
kind,
scopeKind,
"FunctionDeclaration",
(st, tokens, _comments, result) => {
const fnDecl = st as ESTree.FunctionDeclaration;
const idNode = fnDecl.id;
Expand All @@ -568,7 +581,7 @@ export class ScriptLetContext {
fnDecl.params = [];
},
);
this.pushScope(restore, "}", kind);
this.pushScope(restore, "}", scopeKind);
}

public nestBlock(
Expand All @@ -577,7 +590,7 @@ export class ScriptLetContext {
| ScriptLetBlockParam[]
| ((helper: TypeGenHelper | null) => {
param: ScriptLetBlockParam;
preparationScript?: string[];
preparationScript?: { script: string; nodeType: StatementNodeType }[];
}),
): void {
let resolvedParams;
Expand All @@ -590,8 +603,9 @@ export class ScriptLetContext {
if (generatedTypes.preparationScript) {
for (const preparationScript of generatedTypes.preparationScript) {
this.appendScriptWithoutOffset(
preparationScript,
preparationScript.script,
this.currentScriptScopeKind,
preparationScript.nodeType,
(node, tokens, comments, result) => {
tokens.length = 0;
comments.length = 0;
Expand All @@ -612,6 +626,7 @@ export class ScriptLetContext {
`{`,
block.range[0],
this.currentScriptScopeKind,
"BlockStatement",
(st, tokens, _comments, result) => {
const blockSt = st as ESTree.BlockStatement;

Expand Down Expand Up @@ -668,6 +683,7 @@ export class ScriptLetContext {
`(${source})=>{`,
maps[0].range[0] - 1,
this.currentScriptScopeKind,
"ExpressionStatement",
(st, tokens, comments, result) => {
const exprSt = st as ESTree.ExpressionStatement;
const fn = exprSt.expression as ESTree.ArrowFunctionExpression;
Expand Down Expand Up @@ -742,6 +758,7 @@ export class ScriptLetContext {
text: string,
offset: number,
kind: "generics" | "snippet" | "render",
nodeType: StatementNodeType,
callback: (
node: ESTree.Node,
tokens: Token[],
Expand All @@ -752,6 +769,7 @@ export class ScriptLetContext {
const resultCallback = this.appendScriptWithoutOffset(
text,
kind,
nodeType,
(node, tokens, comments, result) => {
fixLocations(
node,
Expand All @@ -770,6 +788,7 @@ export class ScriptLetContext {
private appendScriptWithoutOffset(
text: string,
kind: "generics" | "snippet" | "render",
nodeType: StatementNodeType,
callback: (
node: ESTree.Node,
tokens: Token[],
Expand All @@ -785,6 +804,7 @@ export class ScriptLetContext {
const restoreCallback: RestoreCallback = {
start: startOffset,
end: endOffset,
nodeType,
callback,
};
this.restoreCallbacks.push(restoreCallback);
Expand Down Expand Up @@ -918,6 +938,7 @@ export class ScriptLetContext {
return;
}
if (
orderedRestoreCallback.nodeType === node.type &&
orderedRestoreCallback.start <= node.range![0] &&
node.range![1] <= orderedRestoreCallback.end
) {
Expand Down
18 changes: 14 additions & 4 deletions src/parser/converts/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,10 @@ export function convertAwaitBlock(
const id = typeCtx.generateUniqueId(expression);
return {
preparationScript: [
`const ${id} = ${expression};`,
{
script: `const ${id} = ${expression};`,
nodeType: "VariableDeclaration",
},
generateAwaitThenValueType(idAwaitThenValue),
],
param: {
Expand Down Expand Up @@ -655,7 +658,11 @@ export function convertSnippetBlock(
).end,
);

const scopeKind = parent.type === "Program" ? "snippet" : "render";
const scopeKind =
parent.type === "Program"
? "snippet"
: // use currentScriptScopeKind
null;

ctx.scriptLet.nestSnippetBlock(
node.expression,
Expand Down Expand Up @@ -735,13 +742,16 @@ function extractMustacheBlockTokens(

/** Generate Awaited like type code */
function generateAwaitThenValueType(id: string) {
return `type ${id}<T> = T extends null | undefined
return {
script: `type ${id}<T> = T extends null | undefined
? T
: T extends { then(value: infer F): any }
? F extends (value: infer V, ...args: any) => any
? ${id}<V>
: never
: T;`;
: T;`,
nodeType: "TSTypeAliasDeclaration" as const,
};
}

/** Checks whether the given name identifier is exists or not. */
Expand Down
10 changes: 10 additions & 0 deletions tests/fixtures/parser/ast/svelte5/snippet02-nesting-input.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script>
import Foo from '$lib/foo.svelte';
</script>

{#snippet foo()}
{#snippet bar()} Bar {/snippet}
Foo {@render bar()}
{/snippet}

<Foo {foo} />
Loading

0 comments on commit 48a7001

Please sign in to comment.