Skip to content

Commit df8674c

Browse files
committed
feat: add experimental virtual code cache for faster type-aware linting
1 parent e8c5d7d commit df8674c

25 files changed

+2475
-31
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,6 @@ dist
107107
/lib
108108
/.nyc_output
109109
/coverage
110+
111+
# svelte-eslint-parser virtual code cache
112+
.svelte-eslint-parser/

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default [
2525
"!.github",
2626
"explorer-v2/.svelte-kit",
2727
".changeset/pre.json",
28+
".svelte-eslint-parser",
2829
],
2930
},
3031
...myPlugin.config({

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,17 @@
6262
"eslint-scope": "^8.2.0",
6363
"eslint-visitor-keys": "^4.0.0",
6464
"espree": "^10.0.0",
65+
"ignore": "^7.0.5",
6566
"postcss": "^8.4.49",
6667
"postcss-scss": "^4.0.9",
67-
"postcss-selector-parser": "^7.0.0"
68+
"postcss-selector-parser": "^7.0.0",
69+
"proper-lockfile": "^4.1.2"
6870
},
6971
"devDependencies": {
7072
"@changesets/changelog-github": "^0.5.1",
7173
"@changesets/cli": "^2.29.7",
7274
"@changesets/get-release-plan": "^4.0.13",
75+
"@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
7376
"@ota-meshi/eslint-plugin": "^0.19.0",
7477
"@ota-meshi/test-snapshot": "^1.1.1",
7578
"@types/benchmark": "^2.1.5",
@@ -80,6 +83,7 @@
8083
"@types/estree": "^1.0.8",
8184
"@types/mocha": "^10.0.10",
8285
"@types/node": "^24.0.0",
86+
"@types/proper-lockfile": "^4.1.4",
8387
"@types/semver": "^7.7.1",
8488
"@typescript-eslint/parser": "^8.46.1",
8589
"@typescript-eslint/scope-manager": "^8.46.1",
@@ -91,7 +95,6 @@
9195
"esbuild": "^0.27.0",
9296
"eslint": "^9.37.0",
9397
"eslint-config-prettier": "^10.1.8",
94-
"eslint-plugin-eslint-comments": "^3.2.0",
9598
"eslint-plugin-jsdoc": "^62.0.0",
9699
"eslint-plugin-json-schema-validator": "^6.0.0",
97100
"eslint-plugin-jsonc": "^2.20.1",

pnpm-lock.yaml

Lines changed: 50 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/parser/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ import {
5757
} from "./svelte-parse-context.js";
5858
import type { SvelteConfig } from "../svelte-config/index.js";
5959
import { resolveSvelteConfigFromOption } from "../svelte-config/index.js";
60+
import { getVirtualCodeCacheManager } from "../virtual-code/index.js";
61+
import { initializeVirtualCodeCache } from "./virtual-code-initializer.js";
6062

6163
export {
6264
StyleContext,
@@ -116,6 +118,21 @@ export function parseForESLint(code: string, options?: any): ParseResult {
116118
const svelteConfig = resolveSvelteConfigFromOption(options);
117119
const parserOptions = normalizeParserOptions(options);
118120

121+
// Initialize virtual code cache for TypeScript type-aware linting
122+
// Only enabled when svelteFeatures.experimentalGenerateVirtualCodeCache is true
123+
// Note: We only initialize the cache here; the actual tsconfig/filePath replacement
124+
// happens in parseTypeScriptInSvelte to ensure we have a valid virtual file path
125+
if (
126+
parserOptions.svelteFeatures?.experimentalGenerateVirtualCodeCache &&
127+
(parserOptions.projectService || parserOptions.project)
128+
) {
129+
const cacheManager = getVirtualCodeCacheManager();
130+
131+
if (!cacheManager.isInitialized() && parserOptions.filePath) {
132+
initializeVirtualCodeCache(parserOptions.filePath, parserOptions);
133+
}
134+
}
135+
119136
if (
120137
parserOptions.filePath &&
121138
(parserOptions.filePath.endsWith(".svelte.js") ||

src/parser/parser-options.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ export type NormalizedParserOptions = {
2727
// If not configured this option, The parser will try to read the option from `compilerOptions.runes` from `svelte.config.js`.
2828
// If `parserOptions.svelteConfig` is not specified and the file cannot be parsed by static analysis, it will behave as `true`.
2929
runes?: boolean;
30+
// Enable virtual code caching to speed up type-aware linting.
31+
// When enabled, the parser generates virtual TypeScript files for all Svelte files
32+
// in the project and creates a tsconfig that includes them.
33+
// Default: false
34+
experimentalGenerateVirtualCodeCache?: boolean;
3035
};
3136
loc: boolean;
3237
range: boolean;
@@ -36,6 +41,9 @@ export type NormalizedParserOptions = {
3641
eslintVisitorKeys: boolean;
3742
eslintScopeManager: boolean;
3843
filePath?: string;
44+
45+
// Internal use: tsconfig paths for import rewriting
46+
__tsconfigPaths?: Record<string, string[]> | null;
3947
};
4048

4149
/** Normalize parserOptions */

src/parser/typescript/analyze/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import {
1515
sortedLastIndex,
1616
} from "../../../utils/index.js";
1717
import { parseScriptWithoutAnalyzeScope } from "../../script.js";
18-
import { VirtualTypeScriptContext } from "../context.js";
18+
import {
19+
VirtualTypeScriptContext,
20+
extractSvelteImportsFromAST,
21+
} from "../context.js";
1922
import type { TSESParseForESLintResult } from "../types.js";
2023
import type ESTree from "estree";
2124
import type { SvelteAttribute, SvelteHTMLElement } from "../../../ast/index.js";
@@ -99,6 +102,10 @@ export function analyzeTypeScriptInSvelte(
99102

100103
ctx.appendOriginalToEnd();
101104

105+
// Extract .svelte imports from AST and compute their positions in virtual code
106+
const svelteImportPaths = extractSvelteImportsFromAST(result.ast);
107+
ctx.computeSvelteImportPositions(svelteImportPaths);
108+
102109
return ctx;
103110
}
104111
/**
@@ -132,6 +139,10 @@ export function analyzeTypeScript(
132139

133140
ctx.appendOriginalToEnd();
134141

142+
// Extract .svelte imports from AST and compute their positions in virtual code
143+
const svelteImportPaths = extractSvelteImportsFromAST(result.ast);
144+
ctx.computeSvelteImportPositions(svelteImportPaths);
145+
135146
return ctx;
136147
}
137148

0 commit comments

Comments
 (0)