Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: apply runes to *.svelte.js and *.svelte.ts #426

Merged
merged 15 commits into from
Nov 17, 2023
5 changes: 5 additions & 0 deletions .changeset/brown-cheetahs-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-eslint-parser": minor
---

feat: apply runes to `*.svelte.js` and `*.svelte.ts`.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,44 @@ module.exports = {
}
```

### Runes support

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

If you install Svelte v5 the parser will be able to parse runes, and will also be able to parse `*.js` and `*.ts` files.

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

```json
{
"overrides": [
{
"files": ["*.svelte"],
"parser": "svelte-eslint-parser",
"parserOptions": {
"parser": "...",
...
}
},
{
"files": ["*.svelte.js"],
"parser": "svelte-eslint-parser",
"parserOptions": {
...
}
},
{
"files": ["*.svelte.ts"],
"parser": "svelte-eslint-parser",
"parserOptions": {
"parser": "...(ts parser)...",
...
}
}
]
}
```

## :computer: Editor Integrations

### Visual Studio Code
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"release": "changeset publish",
"test": "pnpm run mocha \"tests/src/**/*.ts\" --reporter dot --timeout 60000",
"ts": "node -r esbuild-register",
"update-fixtures": "pnpm i -D svelte@4 && pnpm run run-update-fixtures && git checkout package.json && pnpm i && pnpm run run-update-fixtures",
"update-fixtures": "git add package.json && pnpm i -D svelte@4 && git checkout package.json && pnpm run run-update-fixtures && pnpm i && pnpm run run-update-fixtures",
"run-update-fixtures": "pnpm run ts ./tools/update-fixtures.ts",
"version:ci": "env-cmd -e version-ci pnpm run build:meta && changeset version"
},
Expand Down
59 changes: 7 additions & 52 deletions src/context/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import fs from "fs";
import path from "path";
import type {
Comment,
Locations,
Expand All @@ -15,19 +13,13 @@ import type ESTree from "estree";
import type * as SvAST from "../parser/svelte-ast-types";
import { ScriptLetContext } from "./script-let";
import { LetDirectiveCollections } from "./let-directive-collection";
import { getParserForLang } from "../parser/resolve-parser";
import type { AttributeToken } from "../parser/html";
import { parseAttributes } from "../parser/html";
import {
isTSESLintParserObject,
maybeTSESLintParserObject,
} from "../parser/parser-object";
import { sortedLastIndex } from "../utils";

const TS_PARSER_NAMES = [
"@typescript-eslint/parser",
"typescript-eslint-parser-for-extra-files",
];
import {
isTypeScript,
type NormalizedParserOptions,
} from "../parser/parser-options";

export class ScriptsSourceCode {
private raw: string;
Expand Down Expand Up @@ -116,7 +108,7 @@ export type ContextSourceCode = {
export class Context {
public readonly code: string;

public readonly parserOptions: any;
public readonly parserOptions: NormalizedParserOptions;

// ----- Source Code ------
public readonly sourceCode: ContextSourceCode;
Expand Down Expand Up @@ -155,7 +147,7 @@ export class Context {

private readonly blocks: Block[] = [];

public constructor(code: string, parserOptions: any) {
public constructor(code: string, parserOptions: NormalizedParserOptions) {
this.code = code;
this.parserOptions = parserOptions;
this.locs = new LinesAndColumns(code);
Expand Down Expand Up @@ -287,44 +279,7 @@ export class Context {
return this.state.isTypeScript;
}
const lang = this.sourceCode.scripts.attrs.lang;
if (!lang) {
return (this.state.isTypeScript = false);
}
const parserValue = getParserForLang(
this.sourceCode.scripts.attrs,
this.parserOptions?.parser,
);
if (typeof parserValue !== "string") {
return (this.state.isTypeScript =
maybeTSESLintParserObject(parserValue) ||
isTSESLintParserObject(parserValue));
}
const parserName = parserValue;
if (TS_PARSER_NAMES.includes(parserName)) {
return (this.state.isTypeScript = true);
}
if (TS_PARSER_NAMES.some((nm) => parserName.includes(nm))) {
let targetPath = parserName;
while (targetPath) {
const pkgPath = path.join(targetPath, "package.json");
if (fs.existsSync(pkgPath)) {
try {
return (this.state.isTypeScript = TS_PARSER_NAMES.includes(
JSON.parse(fs.readFileSync(pkgPath, "utf-8"))?.name,
));
} catch {
return (this.state.isTypeScript = false);
}
}
const parent = path.dirname(targetPath);
if (targetPath === parent) {
break;
}
targetPath = parent;
}
}

return (this.state.isTypeScript = false);
return (this.state.isTypeScript = isTypeScript(this.parserOptions, lang));
}

public stripScriptCode(start: number, end: number): void {
Expand Down
3 changes: 2 additions & 1 deletion src/parser/analyze-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import { getFallbackKeys } from "../traverse";
import type { SvelteReactiveStatement, SvelteScriptElement } from "../ast";
import { addReference, addVariable } from "../scope";
import { addElementToSortedArray } from "../utils";
import type { NormalizedParserOptions } from "./parser-options";
/**
* Analyze scope
*/
export function analyzeScope(
node: ESTree.Node,
parserOptions: any = {},
parserOptions: NormalizedParserOptions,
): ScopeManager {
const ecmaVersion = parserOptions.ecmaVersion || 2020;
const ecmaFeatures = parserOptions.ecmaFeatures || {};
Expand Down
18 changes: 9 additions & 9 deletions src/parser/globals.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { VERSION as SVELTE_VERSION } from "svelte/compiler";
import { svelteVersion } from "./svelte-version";

const globalsForSvelte4: Readonly<string[]> = [
"$$slots",
"$$props",
"$$restProps",
] as const;
export const globalsForSvelte5 = [
const globalsForSvelte4 = ["$$slots", "$$props", "$$restProps"] as const;
export const globalsForRunes = [
"$state",
"$derived",
"$effect",
"$props",
] as const;
export const globals = SVELTE_VERSION.startsWith("5")
? [...globalsForSvelte4, ...globalsForSvelte5]
const globalsForSvelte5 = [...globalsForSvelte4, ...globalsForRunes];
export const globals = svelteVersion.gte(5)
? globalsForSvelte5
: globalsForSvelte4;
export const globalsForSvelteScript = svelteVersion.gte(5)
? globalsForRunes
: [];
Loading
Loading