Skip to content

feat: add support for {#snippet} and {@render} #431

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 4 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/grumpy-pans-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-eslint-parser": minor
---

feat: add support for `{#snippet}` and `{@render}`
27 changes: 27 additions & 0 deletions docs/AST.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,12 @@ type Child =
| SvelteMustacheTag
| SvelteDebugTag
| SvelteConstTag
| SvelteRenderTag
| SvelteIfBlock
| SvelteEachBlock
| SvelteAwaitBlock
| SvelteKeyBlock
| SvelteSnippetBlock
| SvelteHTMLComment;
```

Expand Down Expand Up @@ -421,6 +423,18 @@ interface SvelteConstTag extends Node {
}
```

### SvelteRenderTag

This is the `{@render}` tag node.

```ts
interface SvelteRenderTag extends Node {
type: "SvelteRenderTag";
callee: Identifier;
argument: Expression | null;
}
```

[VariableDeclarator] is a node defined in ESTree.

### SvelteIfBlock
Expand Down Expand Up @@ -533,6 +547,19 @@ interface SvelteKeyBlock extends Node {
}
```

### SvelteSnippetBlock

This is the `{#snippet}` tag node.

```ts
interface SvelteSnippetBlock extends Node {
type: "SvelteSnippetBlock";
id: Identifier;
context: null | Pattern;
children: Child[];
}
```

## Comments

### SvelteHTMLComment
Expand Down
32 changes: 16 additions & 16 deletions explorer-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@
"preview": "vite preview"
},
"dependencies": {
"@fontsource/fira-mono": "^5.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.0.0",
"eslint-scope": "^7.0.0",
"@fontsource/fira-mono": "^5.0.8",
"@typescript-eslint/parser": "^6.11.0",
"eslint": "^8.54.0",
"eslint-scope": "^7.2.2",
"esquery": "^1.5.0",
"pako": "^2.0.3",
"svelte": "^5.0.0-next.2",
"pako": "^2.1.0",
"svelte": "^5.0.0-next.8",
"svelte-eslint-parser": "link:..",
"tslib": "^2.5.0"
"tslib": "^2.6.2"
},
"devDependencies": {
"@sveltejs/adapter-static": "^2.0.0",
"@sveltejs/kit": "^1.0.0-next.456",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^3.0.0",
"string-replace-loader": "^3.0.1",
"typescript": "^5.0.4",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.27.6",
"prettier": "^3.1.0",
"prettier-plugin-svelte": "^3.1.0",
"string-replace-loader": "^3.1.0",
"typescript": "^5.2.2",
"vite": "^5.0.0",
"webpack": "^5.82.1",
"webpack-cli": "^5.0.0",
"wrapper-webpack-plugin": "^2.1.0"
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"wrapper-webpack-plugin": "^2.2.2"
}
}
2 changes: 1 addition & 1 deletion explorer-v2/src/app.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
Expand Down
1 change: 1 addition & 0 deletions explorer-v2/src/lib/MonacoEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
renderValidationDecorations: 'on',
renderWhitespace: 'boundary',
scrollBeyondLastLine: false,
useInlineViewWhenSpaceIsLimited: false,
...editorOptions
};

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@
"@types/eslint": "^8.40.1",
"@types/eslint-scope": "^3.7.4",
"@types/eslint-visitor-keys": "^1.0.0",
"@types/estree": "^1.0.1",
"@types/estree": "^1.0.5",
"@types/mocha": "^10.0.1",
"@types/node": "^20.0.0",
"@types/semver": "^7.5.0",
"@typescript-eslint/eslint-plugin": "^6.9.0",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "~6.10.0",
"@typescript-eslint/types": "~6.10.0",
"benchmark": "^2.1.4",
Expand Down
69 changes: 60 additions & 9 deletions src/ast/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type SvelteHTMLNode =
| SvelteMustacheTag
| SvelteDebugTag
| SvelteConstTag
| SvelteRenderTag
| SvelteIfBlock
| SvelteElseBlock
| SvelteEachBlock
Expand All @@ -24,6 +25,7 @@ export type SvelteHTMLNode =
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock
| SvelteSnippetBlock
| SvelteAttribute
| SvelteShorthandAttribute
| SvelteSpreadAttribute
Expand Down Expand Up @@ -87,7 +89,8 @@ export interface SvelteHTMLElement extends BaseSvelteElement {
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock;
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of Svelte component element. */
export interface SvelteComponentElement extends BaseSvelteElement {
Expand All @@ -106,7 +109,8 @@ export interface SvelteComponentElement extends BaseSvelteElement {
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock;
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of Svelte special component element. e.g. `<svelte:window>` */
export interface SvelteSpecialElement extends BaseSvelteElement {
Expand All @@ -125,7 +129,8 @@ export interface SvelteSpecialElement extends BaseSvelteElement {
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock;
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of start tag. */
export interface SvelteStartTag extends BaseNode {
Expand Down Expand Up @@ -174,10 +179,12 @@ type Child =
| SvelteMustacheTag
| SvelteDebugTag
| SvelteConstTag
| SvelteRenderTag
| SvelteIfBlockAlone
| SvelteEachBlock
| SvelteAwaitBlock
| SvelteKeyBlock
| SvelteSnippetBlock
| SvelteHTMLComment;

/** Node of text. like HTML text. */
Expand All @@ -194,7 +201,8 @@ export interface SvelteText extends BaseNode {
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock;
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of literal. */
export interface SvelteLiteral extends BaseNode {
Expand All @@ -219,6 +227,7 @@ interface BaseSvelteMustacheTag extends BaseNode {
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock
| SvelteSnippetBlock
| SvelteAttribute
| SvelteStyleDirective;
}
Expand All @@ -244,6 +253,7 @@ export interface SvelteDebugTag extends BaseNode {
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock
| SvelteSnippetBlock
| SvelteAttribute;
}
/** Node of const tag. e.g. `{@const}` */
Expand All @@ -260,8 +270,26 @@ export interface SvelteConstTag extends BaseNode {
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock
| SvelteSnippetBlock
| SvelteAttribute;
}
/** Node of render tag. e.g. `{@render}` */
export interface SvelteRenderTag extends BaseNode {
type: "SvelteRenderTag";
callee: ESTree.Identifier;
argument: ESTree.Expression | null;
parent:
| SvelteProgram
| SvelteElement
| SvelteIfBlock
| SvelteElseBlockAlone
| SvelteEachBlock
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of if block. e.g. `{#if}` */
export type SvelteIfBlock = SvelteIfBlockAlone | SvelteIfBlockElseIf;
interface BaseSvelteIfBlock extends BaseNode {
Expand All @@ -279,7 +307,8 @@ interface BaseSvelteIfBlock extends BaseNode {
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock;
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of if block. e.g. `{#if}` */
export interface SvelteIfBlockAlone extends BaseSvelteIfBlock {
Expand Down Expand Up @@ -328,7 +357,8 @@ export interface SvelteEachBlock extends BaseNode {
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock;
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of await block. e.g. `{#await}`, `{#await ... then ... }`, `{#await ... catch ... }` */
export type SvelteAwaitBlock =
Expand All @@ -351,7 +381,8 @@ interface BaseSvelteAwaitBlock extends BaseNode {
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock;
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of await block. e.g. `{#await}` */
export interface SvelteAwaitBlockAwaitPending extends BaseSvelteAwaitBlock {
Expand Down Expand Up @@ -442,7 +473,26 @@ export interface SvelteKeyBlock extends BaseNode {
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock;
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of snippet block. e.g. `{#snippet}` */
export interface SvelteSnippetBlock extends BaseNode {
type: "SvelteSnippetBlock";
id: ESTree.Identifier;
context: null | ESTree.Pattern;
children: Child[];
parent:
| SvelteProgram
| SvelteElement
| SvelteIfBlock
| SvelteElseBlockAlone
| SvelteEachBlock
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of HTML comment. */
export interface SvelteHTMLComment extends BaseNode {
Expand All @@ -457,7 +507,8 @@ export interface SvelteHTMLComment extends BaseNode {
| SvelteAwaitPendingBlock
| SvelteAwaitThenBlock
| SvelteAwaitCatchBlock
| SvelteKeyBlock;
| SvelteKeyBlock
| SvelteSnippetBlock;
}
/** Node of HTML attribute. */
export interface SvelteAttribute extends BaseNode {
Expand Down
3 changes: 3 additions & 0 deletions src/context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
SvelteHTMLElement,
SvelteName,
SvelteScriptElement,
SvelteSnippetBlock,
SvelteStyleElement,
Token,
} from "../ast";
Expand Down Expand Up @@ -142,6 +143,8 @@ export class Context {
| SvAST.Title
>();

public readonly snippets: SvelteSnippetBlock[] = [];

// ----- States ------
private readonly state: { isTypeScript?: boolean } = {};

Expand Down
49 changes: 49 additions & 0 deletions src/context/script-let.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
SvelteIfBlock,
SvelteName,
SvelteNode,
SvelteSnippetBlock,
Token,
} from "../ast";
import type { ESLintExtendedProgram } from "../parser";
Expand Down Expand Up @@ -148,6 +149,15 @@ export class ScriptLetContext {
...callbacks: ScriptLetCallback<E>[]
): ScriptLetCallback<E>[] {
const range = getNodeRange(expression);
return this.addExpressionFromRange(range, parent, typing, ...callbacks);
}

public addExpressionFromRange<E extends ESTree.Expression>(
range: [number, number],
parent: SvelteNode,
typing?: string | null,
...callbacks: ScriptLetCallback<E>[]
): ScriptLetCallback<E>[] {
const part = this.ctx.code.slice(...range);
const isTS = typing && this.ctx.isTypeScript();
this.appendScript(
Expand Down Expand Up @@ -414,6 +424,45 @@ export class ScriptLetContext {
this.pushScope(restore, "});");
}

public nestSnippetBlock(
id: ESTree.Identifier,
closeParentIndex: number,
snippetBlock: SvelteSnippetBlock,
callback: (id: ESTree.Identifier, ctx: ESTree.Pattern | null) => 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,
(st, tokens, _comments, result) => {
const fnDecl = st as ESTree.FunctionDeclaration;
const idNode = fnDecl.id;
const context = fnDecl.params.length > 0 ? fnDecl.params[0] : null;
const scope = result.getScope(fnDecl);

// Process for nodes
callback(idNode, context);
(idNode as any).parent = snippetBlock;
if (context) {
(context as any).parent = snippetBlock;
}

// Process for scope
result.registerNodeToScope(snippetBlock, scope);

tokens.shift(); // function
tokens.pop(); // {
tokens.pop(); // }

// Disconnect the tree structure.
fnDecl.id = null as never;
fnDecl.params = [];
},
);
this.pushScope(restore, "}");
}

public nestBlock(
block: SvelteNode,
params?:
Expand Down
Loading