Skip to content

Commit 5f2b111

Browse files
authored
feat: add experimental support for generics directive (#477)
1 parent 122d318 commit 5f2b111

File tree

61 files changed

+62992
-95
lines changed

Some content is hidden

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

61 files changed

+62992
-95
lines changed

.changeset/twelve-bulldogs-peel.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": minor
3+
---
4+
5+
feat: add experimental support for generics directive

README.md

+18
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,24 @@ module.exports = {
169169
}
170170
```
171171

172+
### parserOptions.svelteFeatures
173+
174+
You can use `parserOptions.svelteFeatures` property to specify how to parse related to Svelte features. For example:
175+
176+
```json
177+
{
178+
"parser": "svelte-eslint-parser",
179+
"parserOptions": {
180+
"svelteFeatures": {
181+
/* -- Experimental Svelte Features -- */
182+
// Whether to parse the `generics` attribute.
183+
// See https://github.com/sveltejs/rfcs/pull/38
184+
"experimentalGenerics": false
185+
}
186+
}
187+
}
188+
```
189+
172190
### Runes support
173191

174192
***This is an experimental feature. It may be changed or removed in minor versions without notice.***

docs/AST.md

+14
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ interface SvelteStartTag extends Node {
172172
| SvelteDirective
173173
| SvelteStyleDirective
174174
| SvelteSpecialDirective
175+
| SvelteGenericsDirective
175176
)[];
176177
selfClosing: boolean;
177178
}
@@ -362,6 +363,19 @@ interface SvelteSpecialDirectiveKey extends Node {
362363
}
363364
```
364365

366+
### SvelteGenericsDirective
367+
368+
This is the generics directive node.
369+
370+
```ts
371+
interface SvelteGenericsDirective extends BaseNode {
372+
type: "SvelteGenericsDirective";
373+
key: SvelteName & { name: "generics" };
374+
params: TSESTree.TSTypeParameter[];
375+
parent: SvelteStartTag & { parent: SvelteScriptElement };
376+
}
377+
```
378+
365379
## Texts and Literals
366380

367381
### SvelteText

src/ast/html.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type ESTree from "estree";
2+
import type { TSESTree } from "@typescript-eslint/types";
23
import type { BaseNode } from "./base";
34
import type { Token, Comment } from "./common";
45

@@ -32,6 +33,7 @@ export type SvelteHTMLNode =
3233
| SvelteDirective
3334
| SvelteStyleDirective
3435
| SvelteSpecialDirective
36+
| SvelteGenericsDirective
3537
| SvelteDirectiveKey
3638
| SvelteSpecialDirectiveKey
3739
| SvelteHTMLComment;
@@ -142,6 +144,7 @@ export interface SvelteStartTag extends BaseNode {
142144
| SvelteDirective
143145
| SvelteStyleDirective
144146
| SvelteSpecialDirective
147+
| SvelteGenericsDirective
145148
)[];
146149
selfClosing: boolean;
147150
parent: SvelteElement | SvelteScriptElement | SvelteStyleElement;
@@ -651,3 +654,9 @@ export interface SvelteSpecialDirective extends BaseNode {
651654
expression: ESTree.Expression;
652655
parent: SvelteStartTag /* & { parent: SvelteSpecialElement } */;
653656
}
657+
export interface SvelteGenericsDirective extends BaseNode {
658+
type: "SvelteGenericsDirective";
659+
key: SvelteName & { name: "generics" };
660+
params: TSESTree.TSTypeParameterDeclaration["params"];
661+
parent: SvelteStartTag /* & { parent: SvelteScriptElement } */;
662+
}

src/context/fix-locations.ts

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type * as ESTree from "estree";
2+
import type { Comment, Locations, Token } from "../ast";
3+
import type { Context } from ".";
4+
import { traverseNodes } from "../traverse";
5+
6+
/** Fix locations */
7+
export function fixLocations(
8+
node: ESTree.Node,
9+
tokens: Token[],
10+
comments: Comment[],
11+
offset: number,
12+
visitorKeys: { [type: string]: string[] } | undefined,
13+
ctx: Context,
14+
): void {
15+
if (offset === 0) {
16+
return;
17+
}
18+
const traversed = new Set<any>();
19+
traverseNodes(node, {
20+
visitorKeys,
21+
enterNode: (n) => {
22+
if (traversed.has(n)) {
23+
return;
24+
}
25+
traversed.add(n);
26+
if (traversed.has(n.range)) {
27+
if (!traversed.has(n.loc)) {
28+
// However, `Node#loc` may not be shared.
29+
const locs = ctx.getConvertLocation({
30+
start: n.range![0],
31+
end: n.range![1],
32+
});
33+
applyLocs(n, locs);
34+
traversed.add(n.loc);
35+
}
36+
} else {
37+
const start = n.range![0] + offset;
38+
const end = n.range![1] + offset;
39+
const locs = ctx.getConvertLocation({ start, end });
40+
applyLocs(n, locs);
41+
traversed.add(n.range);
42+
traversed.add(n.loc);
43+
}
44+
},
45+
leaveNode: Function.prototype as any,
46+
});
47+
for (const t of tokens) {
48+
const start = t.range[0] + offset;
49+
const end = t.range[1] + offset;
50+
const locs = ctx.getConvertLocation({ start, end });
51+
applyLocs(t, locs);
52+
}
53+
for (const t of comments) {
54+
const start = t.range[0] + offset;
55+
const end = t.range[1] + offset;
56+
const locs = ctx.getConvertLocation({ start, end });
57+
applyLocs(t, locs);
58+
}
59+
}
60+
61+
/**
62+
* applyLocs
63+
*/
64+
function applyLocs(target: Locations | ESTree.Node, locs: Locations) {
65+
target.loc = locs.loc;
66+
target.range = locs.range;
67+
if (typeof (target as any).start === "number") {
68+
delete (target as any).start;
69+
}
70+
if (typeof (target as any).end === "number") {
71+
delete (target as any).end;
72+
}
73+
}

src/context/index.ts

+40-17
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@ export class ScriptsSourceCode {
2929

3030
public readonly attrs: Record<string, string | undefined>;
3131

32-
private _separate = "";
33-
34-
private _appendScriptLets: string | null = null;
32+
private _appendScriptLets: {
33+
separate: string;
34+
beforeSpaces: string;
35+
render: string;
36+
generics: string;
37+
} | null = null;
3538

3639
public separateIndexes: number[] = [];
3740

@@ -49,16 +52,28 @@ export class ScriptsSourceCode {
4952
if (this._appendScriptLets == null) {
5053
return this.raw;
5154
}
52-
return this.trimmedRaw + this._separate + this._appendScriptLets;
55+
return (
56+
this.trimmedRaw +
57+
this._appendScriptLets.separate +
58+
this._appendScriptLets.beforeSpaces +
59+
this._appendScriptLets.render +
60+
this._appendScriptLets.generics
61+
);
5362
}
5463

55-
public getCurrentVirtualCodeInfo(): { script: string; render: string } {
64+
public getCurrentVirtualCodeInfo(): {
65+
script: string;
66+
render: string;
67+
generics: string;
68+
} {
5669
if (this._appendScriptLets == null) {
57-
return { script: this.raw, render: "" };
70+
return { script: this.raw, render: "", generics: "" };
5871
}
5972
return {
60-
script: this.trimmedRaw + this._separate,
61-
render: this._appendScriptLets,
73+
script: this.trimmedRaw + this._appendScriptLets.separate,
74+
render:
75+
this._appendScriptLets.beforeSpaces + this._appendScriptLets.render,
76+
generics: this._appendScriptLets.generics,
6277
};
6378
}
6479

@@ -68,22 +83,30 @@ export class ScriptsSourceCode {
6883
}
6984
return (
7085
this.trimmedRaw.length +
71-
this._separate.length +
72-
this._appendScriptLets.length
86+
this._appendScriptLets.separate.length +
87+
this._appendScriptLets.beforeSpaces.length +
88+
this._appendScriptLets.render.length +
89+
this._appendScriptLets.generics.length
7390
);
7491
}
7592

76-
public addLet(letCode: string): { start: number; end: number } {
93+
public addLet(
94+
letCode: string,
95+
kind: "generics" | "render",
96+
): { start: number; end: number } {
7797
if (this._appendScriptLets == null) {
78-
this._appendScriptLets = "";
79-
const currentLength = this.getCurrentVirtualCodeLength();
98+
const currentLength = this.trimmedRaw.length;
8099
this.separateIndexes = [currentLength, currentLength + 1];
81-
this._separate += "\n;";
82-
const after = this.raw.slice(this.getCurrentVirtualCodeLength());
83-
this._appendScriptLets += after;
100+
const after = this.raw.slice(currentLength + 2);
101+
this._appendScriptLets = {
102+
separate: "\n;",
103+
beforeSpaces: after,
104+
render: "",
105+
generics: "",
106+
};
84107
}
85108
const start = this.getCurrentVirtualCodeLength();
86-
this._appendScriptLets += letCode;
109+
this._appendScriptLets[kind] += letCode;
87110
return {
88111
start,
89112
end: this.getCurrentVirtualCodeLength(),

0 commit comments

Comments
 (0)