Skip to content

Commit ab13a46

Browse files
authored
feat: add support for {#snippet} and {@render} (#431)
1 parent af1bae5 commit ab13a46

File tree

52 files changed

+35491
-34
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+35491
-34
lines changed

.changeset/grumpy-pans-guess.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": minor
3+
---
4+
5+
feat: add support for `{#snippet}` and `{@render}`

docs/AST.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,12 @@ type Child =
104104
| SvelteMustacheTag
105105
| SvelteDebugTag
106106
| SvelteConstTag
107+
| SvelteRenderTag
107108
| SvelteIfBlock
108109
| SvelteEachBlock
109110
| SvelteAwaitBlock
110111
| SvelteKeyBlock
112+
| SvelteSnippetBlock
111113
| SvelteHTMLComment;
112114
```
113115

@@ -421,6 +423,18 @@ interface SvelteConstTag extends Node {
421423
}
422424
```
423425

426+
### SvelteRenderTag
427+
428+
This is the `{@render}` tag node.
429+
430+
```ts
431+
interface SvelteRenderTag extends Node {
432+
type: "SvelteRenderTag";
433+
callee: Identifier;
434+
argument: Expression | null;
435+
}
436+
```
437+
424438
[VariableDeclarator] is a node defined in ESTree.
425439

426440
### SvelteIfBlock
@@ -533,6 +547,19 @@ interface SvelteKeyBlock extends Node {
533547
}
534548
```
535549

550+
### SvelteSnippetBlock
551+
552+
This is the `{#snippet}` tag node.
553+
554+
```ts
555+
interface SvelteSnippetBlock extends Node {
556+
type: "SvelteSnippetBlock";
557+
id: Identifier;
558+
context: null | Pattern;
559+
children: Child[];
560+
}
561+
```
562+
536563
## Comments
537564

538565
### SvelteHTMLComment

explorer-v2/package.json

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,26 @@
1212
"preview": "vite preview"
1313
},
1414
"dependencies": {
15-
"@fontsource/fira-mono": "^5.0.0",
16-
"@typescript-eslint/parser": "^6.0.0",
17-
"eslint": "^8.0.0",
18-
"eslint-scope": "^7.0.0",
15+
"@fontsource/fira-mono": "^5.0.8",
16+
"@typescript-eslint/parser": "^6.11.0",
17+
"eslint": "^8.54.0",
18+
"eslint-scope": "^7.2.2",
1919
"esquery": "^1.5.0",
20-
"pako": "^2.0.3",
21-
"svelte": "^5.0.0-next.2",
20+
"pako": "^2.1.0",
21+
"svelte": "^5.0.0-next.8",
2222
"svelte-eslint-parser": "link:..",
23-
"tslib": "^2.5.0"
23+
"tslib": "^2.6.2"
2424
},
2525
"devDependencies": {
26-
"@sveltejs/adapter-static": "^2.0.0",
27-
"@sveltejs/kit": "^1.0.0-next.456",
28-
"prettier": "^3.0.0",
29-
"prettier-plugin-svelte": "^3.0.0",
30-
"string-replace-loader": "^3.0.1",
31-
"typescript": "^5.0.4",
26+
"@sveltejs/adapter-static": "^2.0.3",
27+
"@sveltejs/kit": "^1.27.6",
28+
"prettier": "^3.1.0",
29+
"prettier-plugin-svelte": "^3.1.0",
30+
"string-replace-loader": "^3.1.0",
31+
"typescript": "^5.2.2",
3232
"vite": "^5.0.0",
33-
"webpack": "^5.82.1",
34-
"webpack-cli": "^5.0.0",
35-
"wrapper-webpack-plugin": "^2.1.0"
33+
"webpack": "^5.89.0",
34+
"webpack-cli": "^5.1.4",
35+
"wrapper-webpack-plugin": "^2.2.2"
3636
}
3737
}

explorer-v2/src/app.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html lang="en">
33
<head>
44
<meta charset="utf-8" />

explorer-v2/src/lib/MonacoEditor.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
renderValidationDecorations: 'on',
9595
renderWhitespace: 'boundary',
9696
scrollBeyondLastLine: false,
97+
useInlineViewWhenSpaceIsLimited: false,
9798
...editorOptions
9899
};
99100

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@
7070
"@types/eslint": "^8.40.1",
7171
"@types/eslint-scope": "^3.7.4",
7272
"@types/eslint-visitor-keys": "^1.0.0",
73-
"@types/estree": "^1.0.1",
73+
"@types/estree": "^1.0.5",
7474
"@types/mocha": "^10.0.1",
7575
"@types/node": "^20.0.0",
7676
"@types/semver": "^7.5.0",
77-
"@typescript-eslint/eslint-plugin": "^6.9.0",
77+
"@typescript-eslint/eslint-plugin": "^6.10.0",
7878
"@typescript-eslint/parser": "~6.10.0",
7979
"@typescript-eslint/types": "~6.10.0",
8080
"benchmark": "^2.1.4",

src/ast/html.ts

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export type SvelteHTMLNode =
1616
| SvelteMustacheTag
1717
| SvelteDebugTag
1818
| SvelteConstTag
19+
| SvelteRenderTag
1920
| SvelteIfBlock
2021
| SvelteElseBlock
2122
| SvelteEachBlock
@@ -24,6 +25,7 @@ export type SvelteHTMLNode =
2425
| SvelteAwaitThenBlock
2526
| SvelteAwaitCatchBlock
2627
| SvelteKeyBlock
28+
| SvelteSnippetBlock
2729
| SvelteAttribute
2830
| SvelteShorthandAttribute
2931
| SvelteSpreadAttribute
@@ -87,7 +89,8 @@ export interface SvelteHTMLElement extends BaseSvelteElement {
8789
| SvelteAwaitPendingBlock
8890
| SvelteAwaitThenBlock
8991
| SvelteAwaitCatchBlock
90-
| SvelteKeyBlock;
92+
| SvelteKeyBlock
93+
| SvelteSnippetBlock;
9194
}
9295
/** Node of Svelte component element. */
9396
export interface SvelteComponentElement extends BaseSvelteElement {
@@ -106,7 +109,8 @@ export interface SvelteComponentElement extends BaseSvelteElement {
106109
| SvelteAwaitPendingBlock
107110
| SvelteAwaitThenBlock
108111
| SvelteAwaitCatchBlock
109-
| SvelteKeyBlock;
112+
| SvelteKeyBlock
113+
| SvelteSnippetBlock;
110114
}
111115
/** Node of Svelte special component element. e.g. `<svelte:window>` */
112116
export interface SvelteSpecialElement extends BaseSvelteElement {
@@ -125,7 +129,8 @@ export interface SvelteSpecialElement extends BaseSvelteElement {
125129
| SvelteAwaitPendingBlock
126130
| SvelteAwaitThenBlock
127131
| SvelteAwaitCatchBlock
128-
| SvelteKeyBlock;
132+
| SvelteKeyBlock
133+
| SvelteSnippetBlock;
129134
}
130135
/** Node of start tag. */
131136
export interface SvelteStartTag extends BaseNode {
@@ -174,10 +179,12 @@ type Child =
174179
| SvelteMustacheTag
175180
| SvelteDebugTag
176181
| SvelteConstTag
182+
| SvelteRenderTag
177183
| SvelteIfBlockAlone
178184
| SvelteEachBlock
179185
| SvelteAwaitBlock
180186
| SvelteKeyBlock
187+
| SvelteSnippetBlock
181188
| SvelteHTMLComment;
182189

183190
/** Node of text. like HTML text. */
@@ -194,7 +201,8 @@ export interface SvelteText extends BaseNode {
194201
| SvelteAwaitPendingBlock
195202
| SvelteAwaitThenBlock
196203
| SvelteAwaitCatchBlock
197-
| SvelteKeyBlock;
204+
| SvelteKeyBlock
205+
| SvelteSnippetBlock;
198206
}
199207
/** Node of literal. */
200208
export interface SvelteLiteral extends BaseNode {
@@ -219,6 +227,7 @@ interface BaseSvelteMustacheTag extends BaseNode {
219227
| SvelteAwaitThenBlock
220228
| SvelteAwaitCatchBlock
221229
| SvelteKeyBlock
230+
| SvelteSnippetBlock
222231
| SvelteAttribute
223232
| SvelteStyleDirective;
224233
}
@@ -244,6 +253,7 @@ export interface SvelteDebugTag extends BaseNode {
244253
| SvelteAwaitThenBlock
245254
| SvelteAwaitCatchBlock
246255
| SvelteKeyBlock
256+
| SvelteSnippetBlock
247257
| SvelteAttribute;
248258
}
249259
/** Node of const tag. e.g. `{@const}` */
@@ -260,8 +270,26 @@ export interface SvelteConstTag extends BaseNode {
260270
| SvelteAwaitThenBlock
261271
| SvelteAwaitCatchBlock
262272
| SvelteKeyBlock
273+
| SvelteSnippetBlock
263274
| SvelteAttribute;
264275
}
276+
/** Node of render tag. e.g. `{@render}` */
277+
export interface SvelteRenderTag extends BaseNode {
278+
type: "SvelteRenderTag";
279+
callee: ESTree.Identifier;
280+
argument: ESTree.Expression | null;
281+
parent:
282+
| SvelteProgram
283+
| SvelteElement
284+
| SvelteIfBlock
285+
| SvelteElseBlockAlone
286+
| SvelteEachBlock
287+
| SvelteAwaitPendingBlock
288+
| SvelteAwaitThenBlock
289+
| SvelteAwaitCatchBlock
290+
| SvelteKeyBlock
291+
| SvelteSnippetBlock;
292+
}
265293
/** Node of if block. e.g. `{#if}` */
266294
export type SvelteIfBlock = SvelteIfBlockAlone | SvelteIfBlockElseIf;
267295
interface BaseSvelteIfBlock extends BaseNode {
@@ -279,7 +307,8 @@ interface BaseSvelteIfBlock extends BaseNode {
279307
| SvelteAwaitPendingBlock
280308
| SvelteAwaitThenBlock
281309
| SvelteAwaitCatchBlock
282-
| SvelteKeyBlock;
310+
| SvelteKeyBlock
311+
| SvelteSnippetBlock;
283312
}
284313
/** Node of if block. e.g. `{#if}` */
285314
export interface SvelteIfBlockAlone extends BaseSvelteIfBlock {
@@ -328,7 +357,8 @@ export interface SvelteEachBlock extends BaseNode {
328357
| SvelteAwaitPendingBlock
329358
| SvelteAwaitThenBlock
330359
| SvelteAwaitCatchBlock
331-
| SvelteKeyBlock;
360+
| SvelteKeyBlock
361+
| SvelteSnippetBlock;
332362
}
333363
/** Node of await block. e.g. `{#await}`, `{#await ... then ... }`, `{#await ... catch ... }` */
334364
export type SvelteAwaitBlock =
@@ -351,7 +381,8 @@ interface BaseSvelteAwaitBlock extends BaseNode {
351381
| SvelteAwaitPendingBlock
352382
| SvelteAwaitThenBlock
353383
| SvelteAwaitCatchBlock
354-
| SvelteKeyBlock;
384+
| SvelteKeyBlock
385+
| SvelteSnippetBlock;
355386
}
356387
/** Node of await block. e.g. `{#await}` */
357388
export interface SvelteAwaitBlockAwaitPending extends BaseSvelteAwaitBlock {
@@ -442,7 +473,26 @@ export interface SvelteKeyBlock extends BaseNode {
442473
| SvelteAwaitPendingBlock
443474
| SvelteAwaitThenBlock
444475
| SvelteAwaitCatchBlock
445-
| SvelteKeyBlock;
476+
| SvelteKeyBlock
477+
| SvelteSnippetBlock;
478+
}
479+
/** Node of snippet block. e.g. `{#snippet}` */
480+
export interface SvelteSnippetBlock extends BaseNode {
481+
type: "SvelteSnippetBlock";
482+
id: ESTree.Identifier;
483+
context: null | ESTree.Pattern;
484+
children: Child[];
485+
parent:
486+
| SvelteProgram
487+
| SvelteElement
488+
| SvelteIfBlock
489+
| SvelteElseBlockAlone
490+
| SvelteEachBlock
491+
| SvelteAwaitPendingBlock
492+
| SvelteAwaitThenBlock
493+
| SvelteAwaitCatchBlock
494+
| SvelteKeyBlock
495+
| SvelteSnippetBlock;
446496
}
447497
/** Node of HTML comment. */
448498
export interface SvelteHTMLComment extends BaseNode {
@@ -457,7 +507,8 @@ export interface SvelteHTMLComment extends BaseNode {
457507
| SvelteAwaitPendingBlock
458508
| SvelteAwaitThenBlock
459509
| SvelteAwaitCatchBlock
460-
| SvelteKeyBlock;
510+
| SvelteKeyBlock
511+
| SvelteSnippetBlock;
461512
}
462513
/** Node of HTML attribute. */
463514
export interface SvelteAttribute extends BaseNode {

src/context/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
SvelteHTMLElement,
77
SvelteName,
88
SvelteScriptElement,
9+
SvelteSnippetBlock,
910
SvelteStyleElement,
1011
Token,
1112
} from "../ast";
@@ -142,6 +143,8 @@ export class Context {
142143
| SvAST.Title
143144
>();
144145

146+
public readonly snippets: SvelteSnippetBlock[] = [];
147+
145148
// ----- States ------
146149
private readonly state: { isTypeScript?: boolean } = {};
147150

src/context/script-let.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
SvelteIfBlock,
1111
SvelteName,
1212
SvelteNode,
13+
SvelteSnippetBlock,
1314
Token,
1415
} from "../ast";
1516
import type { ESLintExtendedProgram } from "../parser";
@@ -148,6 +149,15 @@ export class ScriptLetContext {
148149
...callbacks: ScriptLetCallback<E>[]
149150
): ScriptLetCallback<E>[] {
150151
const range = getNodeRange(expression);
152+
return this.addExpressionFromRange(range, parent, typing, ...callbacks);
153+
}
154+
155+
public addExpressionFromRange<E extends ESTree.Expression>(
156+
range: [number, number],
157+
parent: SvelteNode,
158+
typing?: string | null,
159+
...callbacks: ScriptLetCallback<E>[]
160+
): ScriptLetCallback<E>[] {
151161
const part = this.ctx.code.slice(...range);
152162
const isTS = typing && this.ctx.isTypeScript();
153163
this.appendScript(
@@ -414,6 +424,45 @@ export class ScriptLetContext {
414424
this.pushScope(restore, "});");
415425
}
416426

427+
public nestSnippetBlock(
428+
id: ESTree.Identifier,
429+
closeParentIndex: number,
430+
snippetBlock: SvelteSnippetBlock,
431+
callback: (id: ESTree.Identifier, ctx: ESTree.Pattern | null) => void,
432+
): void {
433+
const idRange = getNodeRange(id);
434+
const part = this.ctx.code.slice(idRange[0], closeParentIndex + 1);
435+
const restore = this.appendScript(
436+
`function ${part}{`,
437+
idRange[0] - 9,
438+
(st, tokens, _comments, result) => {
439+
const fnDecl = st as ESTree.FunctionDeclaration;
440+
const idNode = fnDecl.id;
441+
const context = fnDecl.params.length > 0 ? fnDecl.params[0] : null;
442+
const scope = result.getScope(fnDecl);
443+
444+
// Process for nodes
445+
callback(idNode, context);
446+
(idNode as any).parent = snippetBlock;
447+
if (context) {
448+
(context as any).parent = snippetBlock;
449+
}
450+
451+
// Process for scope
452+
result.registerNodeToScope(snippetBlock, scope);
453+
454+
tokens.shift(); // function
455+
tokens.pop(); // {
456+
tokens.pop(); // }
457+
458+
// Disconnect the tree structure.
459+
fnDecl.id = null as never;
460+
fnDecl.params = [];
461+
},
462+
);
463+
this.pushScope(restore, "}");
464+
}
465+
417466
public nestBlock(
418467
block: SvelteNode,
419468
params?:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy