Skip to content

Commit 64bd95a

Browse files
committed
fix
1 parent 30b5a47 commit 64bd95a

File tree

17 files changed

+61
-160
lines changed

17 files changed

+61
-160
lines changed

.changeset/brown-cheetahs-greet.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"svelte-eslint-parser": minor
33
---
44

5-
feat: add `svelteFeatures.runes` option and add rune symbols to global scope
5+
feat: apply runes to `*.svelte.js` and `*.svelte.ts`.

README.md

Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -169,37 +169,11 @@ 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-
"runes": false
182-
}
183-
}
184-
}
185-
```
186-
187-
#### parserOptions.svelteFeatures.runes
172+
### Runes support
188173

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

191-
If set to `true`, Rune symbols will be parsed. In this mode, the parser also parses files other than `*.svelte`.
192-
193-
```json
194-
{
195-
"parser": "svelte-eslint-parser",
196-
"parserOptions": {
197-
"svelteFeatures": {
198-
"runes": true // Default `false`
199-
}
200-
}
201-
}
202-
```
176+
If you install Svelte v5 the parser will be able to parse runes, and will also be able to parse `*.js` and `*.ts` files.
203177

204178
When using this mode in an ESLint configuration, it is recommended to set it per file pattern as below.
205179

@@ -210,9 +184,6 @@ When using this mode in an ESLint configuration, it is recommended to set it per
210184
"files": ["*.svelte"],
211185
"parser": "svelte-eslint-parser",
212186
"parserOptions": {
213-
"svelteFeatures": {
214-
"runes": true,
215-
},
216187
"parser": "...",
217188
...
218189
}
@@ -221,19 +192,13 @@ When using this mode in an ESLint configuration, it is recommended to set it per
221192
"files": ["*.svelte.js"],
222193
"parser": "svelte-eslint-parser",
223194
"parserOptions": {
224-
"svelteFeatures": {
225-
"runes": true,
226-
},
227195
...
228196
}
229197
},
230198
{
231199
"files": ["*.svelte.ts"],
232200
"parser": "svelte-eslint-parser",
233201
"parserOptions": {
234-
"svelteFeatures": {
235-
"runes": true,
236-
},
237202
"parser": "...(ts parser)...",
238203
...
239204
}
@@ -242,18 +207,6 @@ When using this mode in an ESLint configuration, it is recommended to set it per
242207
}
243208
```
244209

245-
Even if `runes` is not set to `true`, if it is enabled in the `<svelte:option>` of the `*.svelte` file, it will be parsed as `runes` mode is enabled
246-
247-
```svelte
248-
<svelte:options runes={true} />
249-
```
250-
251-
Also, even if `runes` is set to `true`, if it is disabled in the `<svelte:option>` of the `*.svelte` file, it will be parsed as `runes` mode is disabled.
252-
253-
```svelte
254-
<svelte:options runes={false} />
255-
```
256-
257210
## :computer: Editor Integrations
258211

259212
### Visual Studio Code

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
"prettier-plugin-svelte": "^3.0.0",
105105
"rimraf": "^5.0.1",
106106
"semver": "^7.5.1",
107-
"svelte": "^5.0.0-next.2",
107+
"svelte": "^4.2.4",
108108
"svelte2tsx": "^0.6.25",
109109
"typescript": "~5.1.3",
110110
"typescript-eslint-parser-for-extra-files": "^0.5.0"

src/context/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,6 @@ export class Context {
136136

137137
public readonly slots = new Set<SvelteHTMLElement>();
138138

139-
public runes: boolean | null = null;
140-
141139
public readonly elements = new Map<
142140
SvelteElement,
143141
| SvAST.InlineComponent

src/parser/analyze-scope.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -217,30 +217,6 @@ export function analyzePropsScope(
217217
}
218218
}
219219

220-
/** Analyze Runes. e.g. $state() */
221-
export function analyzeRunesScope(scopeManager: ScopeManager): void {
222-
const globalScope = scopeManager.globalScope;
223-
// https://svelte-5-preview.vercel.app/docs/runes
224-
for (const $name of ["$state", "$derived", "$effect", "$props"]) {
225-
if (globalScope.set.has($name)) continue;
226-
const variable = new Variable();
227-
variable.name = $name;
228-
(variable as any).scope = globalScope;
229-
globalScope.variables.push(variable);
230-
globalScope.set.set($name, variable);
231-
globalScope.through = globalScope.through.filter((reference) => {
232-
if (reference.identifier.name === $name) {
233-
// Links the variable and the reference.
234-
// And this reference is removed from `Scope#through`.
235-
reference.resolved = variable;
236-
addReference(variable.references, reference);
237-
return false;
238-
}
239-
return true;
240-
});
241-
}
242-
}
243-
244220
/** Remove reference from through */
245221
function removeReferenceFromThrough(reference: Reference, baseScope: Scope) {
246222
const variable = reference.resolved!;

src/parser/converts/element.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -810,27 +810,7 @@ function convertOptionsElement(
810810
parent: SvelteSpecialElement["parent"],
811811
ctx: Context,
812812
): SvelteSpecialElement {
813-
const element = convertSpecialElement(node, parent, ctx);
814-
815-
// Extract rune mode from options.
816-
for (const attr of node.attributes) {
817-
if (attr.type === "Attribute" && attr.name === "runes") {
818-
if (attr.value === true) {
819-
ctx.runes = true;
820-
} else if (attr.value.length === 1) {
821-
const val = attr.value[0];
822-
if (
823-
val.type === "MustacheTag" &&
824-
val.expression.type === "Literal" &&
825-
typeof val.expression.value === "boolean"
826-
) {
827-
ctx.runes = val.expression.value;
828-
}
829-
}
830-
}
831-
}
832-
833-
return element;
813+
return convertSpecialElement(node, parent, ctx);
834814
}
835815

836816
/** Convert for <svelte:fragment> element. */

src/parser/globals.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
import { VERSION as SVELTE_VERSION } from "svelte/compiler";
1+
import { svelteVersion } from "./svelte-version";
22

33
const globalsForSvelte4: Readonly<string[]> = [
44
"$$slots",
55
"$$props",
66
"$$restProps",
77
] as const;
8-
export const globalsForSvelte5 = [
8+
export const globalsForRunes = [
99
"$state",
1010
"$derived",
1111
"$effect",
1212
"$props",
1313
] as const;
14-
export const globals = SVELTE_VERSION.startsWith("5")
15-
? [...globalsForSvelte4, ...globalsForSvelte5]
14+
const globalsForSvelte5 = [...globalsForSvelte4, ...globalsForRunes];
15+
export const globals = svelteVersion.gte(5)
16+
? globalsForSvelte5
1617
: globalsForSvelte4;
18+
export const globalsForSvelteScript = svelteVersion.gte(5)
19+
? globalsForRunes
20+
: [];

src/parser/index.ts

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { parseTemplate } from "./template";
1717
import {
1818
analyzePropsScope,
1919
analyzeReactiveScope,
20-
analyzeRunesScope,
2120
analyzeStoreScope,
2221
} from "./analyze-scope";
2322
import { ParseError } from "../errors";
@@ -33,7 +32,8 @@ import {
3332
styleNodeLoc,
3433
styleNodeRange,
3534
} from "./style-context";
36-
import { globals } from "./globals";
35+
import { globals, globalsForSvelteScript } from "./globals";
36+
import { svelteVersion } from "./svelte-version";
3737

3838
export {
3939
StyleContext,
@@ -66,11 +66,11 @@ type ParseResult = {
6666
(
6767
| {
6868
isSvelte: true;
69-
svelteOptions: { runes: boolean };
69+
isSvelteScript: false;
7070
getSvelteHtmlAst: () => SvAST.Fragment;
7171
getStyleContext: () => StyleContext;
7272
}
73-
| { isSvelte: false; svelteOptions: { runes: boolean } }
73+
| { isSvelte: false; isSvelteScript: true }
7474
);
7575
visitorKeys: { [type: string]: string[] };
7676
scopeManager: ScopeManager;
@@ -82,9 +82,9 @@ export function parseForESLint(code: string, options?: any): ParseResult {
8282
const parserOptions = normalizeParserOptions(options);
8383

8484
if (
85+
svelteVersion.hasRunes &&
8586
parserOptions.filePath &&
86-
!parserOptions.filePath.endsWith(".svelte") &&
87-
parserOptions.svelteFeatures.runes
87+
!parserOptions.filePath.endsWith(".svelte")
8888
) {
8989
const trimmed = code.trim();
9090
if (!trimmed.startsWith("<") && !trimmed.endsWith(">")) {
@@ -109,8 +109,6 @@ function parseAsSvelte(
109109
parserOptions,
110110
);
111111

112-
const runes = ctx.runes ?? parserOptions.svelteFeatures.runes;
113-
114112
const scripts = ctx.sourceCode.scripts;
115113
const resultScript = ctx.isTypeScript()
116114
? parseTypeScript(
@@ -155,10 +153,6 @@ function parseAsSvelte(
155153
});
156154
}
157155

158-
if (runes) {
159-
analyzeRunesScope(resultScript.scopeManager!);
160-
}
161-
162156
const ast = resultTemplate.ast;
163157

164158
const statements = [...resultScript.ast.body];
@@ -212,7 +206,7 @@ function parseAsSvelte(
212206
resultScript.ast = ast as any;
213207
resultScript.services = Object.assign(resultScript.services || {}, {
214208
isSvelte: true,
215-
svelteOptions: { runes },
209+
isSvelteScript: false,
216210
getSvelteHtmlAst() {
217211
return resultTemplate.svelteAst.html;
218212
},
@@ -239,10 +233,31 @@ function parseAsScript(
239233
): ParseResult {
240234
const lang = parserOptions.filePath?.split(".").pop() || "js";
241235
const resultScript = parseScript(code, { lang }, parserOptions);
242-
analyzeRunesScope(resultScript.scopeManager!);
236+
237+
// Add $$xxx variable
238+
const globalScope = resultScript.scopeManager!.globalScope;
239+
for (const $$name of globalsForSvelteScript) {
240+
if (globalScope.set.has($$name)) continue;
241+
const variable = new Variable();
242+
variable.name = $$name;
243+
(variable as any).scope = globalScope;
244+
globalScope.variables.push(variable);
245+
globalScope.set.set($$name, variable);
246+
globalScope.through = globalScope.through.filter((reference) => {
247+
if (reference.identifier.name === $$name) {
248+
// Links the variable and the reference.
249+
// And this reference is removed from `Scope#through`.
250+
reference.resolved = variable;
251+
addReference(variable.references, reference);
252+
return false;
253+
}
254+
return true;
255+
});
256+
}
257+
243258
resultScript.services = Object.assign(resultScript.services || {}, {
244259
isSvelte: false,
245-
svelteOptions: { runes: true },
260+
isSvelteScript: true,
246261
});
247262
resultScript.visitorKeys = Object.assign({}, KEYS, resultScript.visitorKeys);
248263
return resultScript as any;
@@ -258,7 +273,6 @@ type NormalizedParserOptions = {
258273
comment: boolean;
259274
eslintVisitorKeys: boolean;
260275
eslintScopeManager: boolean;
261-
svelteFeatures: { runes: boolean };
262276
filePath?: string;
263277
};
264278

@@ -274,10 +288,6 @@ function normalizeParserOptions(options: any): NormalizedParserOptions {
274288
comment: true,
275289
eslintVisitorKeys: true,
276290
eslintScopeManager: true,
277-
svelteFeatures: {
278-
rune: false,
279-
...(options?.svelteFeatures || {}),
280-
},
281291
...(options || {}),
282292
};
283293
parserOptions.sourceType = "module";

src/parser/svelte-version.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { VERSION as SVELTE_VERSION } from "svelte/compiler";
2+
3+
const verStrings = SVELTE_VERSION.split(".");
4+
5+
export const svelteVersion = {
6+
gte(v: number): boolean {
7+
return Number(verStrings[0]) >= v;
8+
},
9+
hasRunes: Number(verStrings[0]) >= 5,
10+
};

src/parser/typescript/analyze/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ 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";
19+
import { globalsForRunes, globals } from "../../../parser/globals";
2020

2121
export type AnalyzeTypeScriptContext = {
2222
slots: Set<SvelteHTMLElement>;
@@ -215,10 +215,10 @@ function analyzeDollarDollarVariables(
215215
);
216216
}
217217

218-
addSvelte5Globals();
218+
addSvelteRuneGlobals();
219219

220-
function addSvelte5Globals() {
221-
for (const svelte5Global of globalsForSvelte5) {
220+
function addSvelteRuneGlobals() {
221+
for (const svelte5Global of globalsForRunes) {
222222
if (
223223
!scopeManager.globalScope!.through.some(
224224
(reference) => reference.identifier.name === svelte5Global,

tests/fixtures/parser/ast/svelte-5-preview/docs/fine-grained-reactivity/_config.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/fixtures/parser/ast/svelte-5-preview/docs/functions/_config.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/fixtures/parser/ast/svelte-5-preview/docs/old-vs-new/_config.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/fixtures/parser/ast/svelte-5-preview/docs/runes/_config.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/fixtures/parser/ast/svelte-5-preview/docs/universal-reactivity/_config.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/fixtures/parser/ast/svelte-5-preview/optin-runes01-config.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)