Skip to content

Commit ff242c4

Browse files
authored
feat: Support runes (#425)
1 parent 9f02d12 commit ff242c4

File tree

142 files changed

+106377
-16
lines changed

Some content is hidden

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

142 files changed

+106377
-16
lines changed

.changeset/blue-pets-play.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": minor
3+
---
4+
5+
feat: Support runes

.eslintignore

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
/node_modules
55
/tests/fixtures/**/*.json
66
/tests/fixtures/**/*.svelte
7+
/tests/fixtures/**/*.js
8+
/tests/fixtures/**/*.ts
79
/explorer/dist
810
/explorer/node_modules
911
/explorer-v2/build

.eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module.exports = {
2020
"no-lonely-if": "off",
2121
"no-shadow": "off",
2222
"no-warning-comments": "warn",
23-
"require-jsdoc": "error",
23+
"require-jsdoc": "off",
2424
"prettier/prettier": [
2525
"error",
2626
{},

.github/workflows/NodeCI.yml

+21-1
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,27 @@ jobs:
3535
run: pnpm install
3636
- name: Test
3737
run: pnpm run test
38-
test-for-svelte-v4:
38+
test-for-svelte-v5:
3939
runs-on: ubuntu-latest
4040
steps:
4141
- uses: actions/checkout@v4
4242
- uses: pnpm/action-setup@v2
4343
- name: Use Node.js
4444
uses: actions/setup-node@v4
45+
- name: Install Packages
46+
run: pnpm install
47+
- name: Test
48+
run: pnpm run test
49+
50+
test-for-svelte-v4:
51+
runs-on: ubuntu-latest
52+
steps:
53+
- uses: actions/checkout@v4
54+
- uses: pnpm/action-setup@v2
55+
- name: Use Node.js ${{ matrix.node-version }}
56+
uses: actions/setup-node@v4
57+
with:
58+
node-version: ${{ matrix.node-version }}
4559
- name: Install Svelte v4
4660
run: |+
4761
pnpm install -D svelte@4
@@ -50,6 +64,7 @@ jobs:
5064
run: pnpm install
5165
- name: Test
5266
run: pnpm run test
67+
5368
test-for-svelte-v3:
5469
runs-on: ubuntu-latest
5570
strategy:
@@ -138,12 +153,17 @@ jobs:
138153
- uses: actions/setup-node@v4
139154
with:
140155
node-version: 18
156+
- name: Install Svelte v4
157+
run: |+
158+
pnpm install -D svelte@4
159+
rm -rf node_modules
141160
- name: Install Packages
142161
run: pnpm install
143162
- name: Update fixtures
144163
run: pnpm run update-fixtures
145164
- name: Check changes
146165
run: |
166+
git checkout package.json && \
147167
git add --all && \
148168
git diff-index --cached HEAD --stat --exit-code
149169
test-and-coverage:

benchmark/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// eslint-disable-next-line eslint-comments/disable-enable-pair -- ignore
2-
/* eslint-disable require-jsdoc, no-console -- ignore */
2+
/* eslint-disable no-console -- ignore */
33
import * as Benchmark from "benchmark";
44
import fs from "fs";
55
import { parseForESLint } from "../src/index";

explorer-v2/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"eslint-scope": "^7.0.0",
1919
"esquery": "^1.5.0",
2020
"pako": "^2.0.3",
21-
"svelte": "^4.0.0",
21+
"svelte": "^5.0.0-next.2",
2222
"svelte-eslint-parser": "link:..",
2323
"tslib": "^2.5.0"
2424
},

src/parser/globals.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { VERSION as SVELTE_VERSION } from "svelte/compiler";
2+
3+
const globalsForSvelte4: Readonly<string[]> = [
4+
"$$slots",
5+
"$$props",
6+
"$$restProps",
7+
] as const;
8+
export const globalsForSvelte5 = [
9+
"$state",
10+
"$derived",
11+
"$effect",
12+
"$props",
13+
] as const;
14+
export const globals = SVELTE_VERSION.startsWith("5")
15+
? [...globalsForSvelte4, ...globalsForSvelte5]
16+
: globalsForSvelte4;

src/parser/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
styleNodeLoc,
3333
styleNodeRange,
3434
} from "./style-context";
35+
import { globals } from "./globals";
3536

3637
export {
3738
StyleContext,
@@ -122,7 +123,7 @@ export function parseForESLint(
122123
analyzeStoreScope(resultScript.scopeManager!); // for reactive vars
123124

124125
// Add $$xxx variable
125-
for (const $$name of ["$$slots", "$$props", "$$restProps"]) {
126+
for (const $$name of globals) {
126127
const globalScope = resultScript.scopeManager!.globalScope;
127128
const variable = new Variable();
128129
variable.name = $$name;

src/parser/typescript/analyze/index.ts

+109-3
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import { VirtualTypeScriptContext } from "../context";
1616
import type { TSESParseForESLintResult } from "../types";
1717
import type ESTree from "estree";
1818
import type { SvelteAttribute, SvelteHTMLElement } from "../../../ast";
19+
import { globalsForSvelte5, globals } from "../../../parser/globals";
1920

2021
export type AnalyzeTypeScriptContext = {
2122
slots: Set<SvelteHTMLElement>;
2223
};
2324

24-
const RESERVED_NAMES = new Set<string>(["$$props", "$$restProps", "$$slots"]);
2525
/**
2626
* Analyze TypeScript source code.
2727
* Generate virtual code to provide correct type information for Svelte store reference namess and scopes.
@@ -75,8 +75,8 @@ function analyzeStoreReferenceNames(
7575
if (
7676
// Begin with `$`.
7777
reference.identifier.name.startsWith("$") &&
78-
// Ignore it is a reserved variable.
79-
!RESERVED_NAMES.has(reference.identifier.name) &&
78+
// Ignore globals
79+
!globals.includes(reference.identifier.name) &&
8080
// Ignore if it is already defined.
8181
!programScope.set.has(reference.identifier.name)
8282
) {
@@ -215,6 +215,59 @@ function analyzeDollarDollarVariables(
215215
);
216216
}
217217

218+
addSvelte5Globals();
219+
220+
function addSvelte5Globals() {
221+
for (const svelte5Global of globalsForSvelte5) {
222+
if (
223+
!scopeManager.globalScope!.through.some(
224+
(reference) => reference.identifier.name === svelte5Global,
225+
)
226+
) {
227+
continue;
228+
}
229+
switch (svelte5Global) {
230+
case "$state": {
231+
appendDeclareFunctionVirtualScript(
232+
svelte5Global,
233+
"<T>(initial: T): T",
234+
);
235+
appendDeclareFunctionVirtualScript(
236+
svelte5Global,
237+
"<T>(): T | undefined",
238+
);
239+
break;
240+
}
241+
case "$derived": {
242+
appendDeclareFunctionVirtualScript(
243+
svelte5Global,
244+
"<T>(expression: T): T",
245+
);
246+
break;
247+
}
248+
case "$effect": {
249+
appendDeclareFunctionVirtualScript(
250+
svelte5Global,
251+
"(fn: () => void | (() => void)): void",
252+
);
253+
appendDeclareNamespaceVirtualScript(
254+
svelte5Global,
255+
"export function pre(fn: () => void | (() => void)): void;",
256+
);
257+
break;
258+
}
259+
case "$props": {
260+
appendDeclareFunctionVirtualScript(svelte5Global, "<T>(): T");
261+
break;
262+
}
263+
default: {
264+
const _: never = svelte5Global;
265+
throw Error(`Unknown global: ${_}`);
266+
}
267+
}
268+
}
269+
}
270+
218271
/** Append declare virtual script */
219272
function appendDeclareVirtualScript(name: string, type: string) {
220273
ctx.appendVirtualScript(`declare let ${name}: ${type};`);
@@ -242,6 +295,59 @@ function analyzeDollarDollarVariables(
242295
return true;
243296
});
244297
}
298+
299+
/** Append declare virtual script */
300+
function appendDeclareFunctionVirtualScript(name: string, type: string) {
301+
ctx.appendVirtualScript(`declare function ${name}${type};`);
302+
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
303+
if (
304+
node.type !== "TSDeclareFunction" ||
305+
!node.declare ||
306+
node.id?.type !== "Identifier" ||
307+
node.id.name !== name
308+
) {
309+
return false;
310+
}
311+
const program = result.ast;
312+
program.body.splice(program.body.indexOf(node), 1);
313+
314+
const scopeManager = result.scopeManager as ScopeManager;
315+
316+
// Remove `declare` variable
317+
removeAllScopeAndVariableAndReference(node, {
318+
visitorKeys: result.visitorKeys,
319+
scopeManager,
320+
});
321+
322+
return true;
323+
});
324+
}
325+
326+
function appendDeclareNamespaceVirtualScript(name: string, script: string) {
327+
ctx.appendVirtualScript(`declare namespace $effect { ${script} }`);
328+
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
329+
if (
330+
node.type !== "TSModuleDeclaration" ||
331+
!node.declare ||
332+
node.id?.type !== "Identifier" ||
333+
node.id.name !== name
334+
) {
335+
return false;
336+
}
337+
const program = result.ast;
338+
program.body.splice(program.body.indexOf(node), 1);
339+
340+
const scopeManager = result.scopeManager as ScopeManager;
341+
342+
// Remove `declare` variable
343+
removeAllScopeAndVariableAndReference(node, {
344+
visitorKeys: result.visitorKeys,
345+
scopeManager,
346+
});
347+
348+
return true;
349+
});
350+
}
245351
}
246352

247353
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"variables": [
3+
{
4+
"name": "$$slots",
5+
"identifiers": [],
6+
"defs": [],
7+
"references": [
8+
{
9+
"identifier": {
10+
"type": "Identifier",
11+
"name": "$$slots",
12+
"range": [5, 12],
13+
"loc": {
14+
"start": {
15+
"line": 1,
16+
"column": 5
17+
},
18+
"end": {
19+
"line": 1,
20+
"column": 12
21+
}
22+
}
23+
},
24+
"from": "module",
25+
"init": null,
26+
"resolved": null
27+
}
28+
]
29+
},
30+
{
31+
"name": "$$props",
32+
"identifiers": [],
33+
"defs": [],
34+
"references": []
35+
},
36+
{
37+
"name": "$$restProps",
38+
"identifiers": [],
39+
"defs": [],
40+
"references": []
41+
},
42+
{
43+
"name": "$state",
44+
"identifiers": [],
45+
"defs": [],
46+
"references": []
47+
},
48+
{
49+
"name": "$derived",
50+
"identifiers": [],
51+
"defs": [],
52+
"references": []
53+
},
54+
{
55+
"name": "$effect",
56+
"identifiers": [],
57+
"defs": [],
58+
"references": []
59+
},
60+
{
61+
"name": "$props",
62+
"identifiers": [],
63+
"defs": [],
64+
"references": []
65+
}
66+
]
67+
}

0 commit comments

Comments
 (0)