Skip to content

Commit f686b4d

Browse files
committed
more robust
1 parent afd422d commit f686b4d

File tree

3 files changed

+127
-54
lines changed

3 files changed

+127
-54
lines changed

packages/eslint-plugin-svelte/src/utils/get-package-json.ts

+17-13
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ type PackageJson = {
1414
};
1515

1616
const isRunOnBrowser = !fs.readFileSync;
17-
const cache = createCache<PackageJson | null>();
17+
const packageJsonCache = createCache<PackageJson | null>();
18+
const packageJsonsCache = createCache<PackageJson[]>();
1819

1920
/**
2021
* Reads the `package.json` data in a given path.
@@ -49,34 +50,37 @@ function readPackageJson(dir: string): PackageJson | null {
4950
* @returns A found `package.json` data or `null`.
5051
* This object have additional property `filePath`.
5152
*/
52-
export function getPackageJson(startPath = 'a.js'): PackageJson | null {
53-
if (isRunOnBrowser) return null;
53+
export function getPackageJsons(startPath = 'a.js'): PackageJson[] {
54+
if (isRunOnBrowser) return [];
55+
56+
const cached = packageJsonsCache.get(startPath);
57+
if (cached) {
58+
return cached;
59+
}
60+
61+
const packageJsons: PackageJson[] = [];
5462
const startDir = path.dirname(path.resolve(startPath));
5563
let dir = startDir;
5664
let prevDir = '';
5765
let data = null;
5866

5967
do {
60-
data = cache.get(dir);
68+
data = packageJsonCache.get(dir);
6169
if (data) {
62-
if (dir !== startDir) {
63-
cache.set(startDir, data);
64-
}
65-
return data;
70+
packageJsons.push(data);
6671
}
6772

6873
data = readPackageJson(dir);
6974
if (data) {
70-
cache.set(dir, data);
71-
cache.set(startDir, data);
72-
return data;
75+
packageJsonCache.set(dir, data);
76+
packageJsons.push(data);
7377
}
7478

7579
// Go to next.
7680
prevDir = dir;
7781
dir = path.resolve(dir, '..');
7882
} while (dir !== prevDir);
7983

80-
cache.set(startDir, null);
81-
return null;
84+
packageJsonsCache.set(startDir, packageJsons);
85+
return packageJsons;
8286
}

packages/eslint-plugin-svelte/src/utils/svelte-context.ts

+109-40
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,40 @@
11
import type { RuleContext } from '../types.js';
22
import fs from 'fs';
33
import path from 'path';
4-
import { getPackageJson } from './get-package-json.js';
4+
import { getPackageJsons } from './get-package-json.js';
55
import { getFilename, getSourceCode } from './compat.js';
66

77
const isRunInBrowser = !fs.readFileSync;
88

9-
export type SvelteContext = {
10-
svelteVersion: '3/4' | '5' | 'undetermined';
11-
svelteFileType: '.svelte' | '.svelte.[js|ts]' | null;
12-
runes: boolean | 'undetermined';
13-
svelteKitVersion: '1-next' | '1' | '2' | null;
9+
export type SvelteContext = (
10+
| ({
11+
svelteVersion: '3/4';
12+
} & {
13+
svelteFileType: '.svelte' | 'other';
14+
runes: null;
15+
})
16+
| ({
17+
svelteVersion: '5';
18+
} & (
19+
| {
20+
svelteFileType: '.svelte' | '.svelte.[js|ts]';
21+
/** If a user uses a parser other than `svelte-eslint-parser`, `undetermined` will be set. */
22+
runes: boolean | 'undetermined';
23+
}
24+
| {
25+
/** e.g. `foo.js` / `package.json` */
26+
svelteFileType: 'other';
27+
runes: null;
28+
}
29+
))
30+
| {
31+
/** For projects that do not use Svelte. */
32+
svelteVersion: null;
33+
svelteFileType: null;
34+
runes: null;
35+
}
36+
) & {
37+
svelteKitVersion: '1.0.0-next' | '1' | '2' | null;
1438
svelteKitFileType:
1539
| '+page.svelte'
1640
| '+page.js'
@@ -23,7 +47,7 @@ export type SvelteContext = {
2347
| null;
2448
};
2549

26-
function getSvelteFileType(filePath: string): SvelteContext['svelteFileType'] | null {
50+
function getSvelteFileType(filePath: string): NonNullable<SvelteContext['svelteFileType']> {
2751
if (filePath.endsWith('.svelte')) {
2852
return '.svelte';
2953
}
@@ -32,7 +56,7 @@ function getSvelteFileType(filePath: string): SvelteContext['svelteFileType'] |
3256
return '.svelte.[js|ts]';
3357
}
3458

35-
return null;
59+
return 'other';
3660
}
3761

3862
function getSvelteKitFileTypeFromFilePath(filePath: string): SvelteContext['svelteKitFileType'] {
@@ -112,6 +136,29 @@ function getSvelteKitContext(
112136
};
113137
}
114138

139+
function getSvelteVersion(filePath: string): SvelteContext['svelteVersion'] {
140+
// Hack: if it runs in browser, it regards as Svelte project.
141+
if (isRunInBrowser) return '5';
142+
try {
143+
const packageJsons = getPackageJsons(filePath);
144+
for (const packageJson of packageJsons) {
145+
const version = packageJson.dependencies?.svelte ?? packageJson.devDependencies?.svelte;
146+
if (typeof version !== 'string') {
147+
continue;
148+
}
149+
const major = version.split('.')[0];
150+
if (major === '3' || major === '4') {
151+
return '3/4';
152+
}
153+
return major as SvelteContext['svelteVersion'];
154+
}
155+
} catch {
156+
/** do nothing */
157+
}
158+
159+
return null;
160+
}
161+
115162
/**
116163
* Check givin file is under SvelteKit project.
117164
*
@@ -124,41 +171,28 @@ function getSvelteKitVersion(filePath: string): SvelteContext['svelteKitVersion'
124171
// Hack: if it runs in browser, it regards as SvelteKit project.
125172
if (isRunInBrowser) return '2';
126173
try {
127-
const packageJson = getPackageJson(filePath);
128-
if (!packageJson) return null;
129-
if (packageJson.name === 'eslint-plugin-svelte')
174+
const packageJsons = getPackageJsons(filePath);
175+
if (packageJsons.length === 0) return null;
176+
if (packageJsons[0].name === 'eslint-plugin-svelte') {
130177
// Hack: CI removes `@sveltejs/kit` and it returns false and test failed.
131-
// So always it returns true if it runs on the package.
178+
// So always it returns 2 if it runs on the package.
132179
return '2';
133-
134-
const version =
135-
packageJson.dependencies?.['@sveltejs/kit'] ?? packageJson.devDependencies?.['@sveltejs/kit'];
136-
if (typeof version !== 'string') {
137-
return null;
138180
}
139-
if (version.startsWith('1.0.0-next.')) {
140-
return '1-next';
141-
} else if (version.startsWith('1.')) {
142-
return '1';
143-
} else if (version.startsWith('2.')) {
144-
return '2';
181+
182+
for (const packageJson of packageJsons) {
183+
const version =
184+
packageJson.dependencies?.['@sveltejs/kit'] ??
185+
packageJson.devDependencies?.['@sveltejs/kit'];
186+
if (typeof version !== 'string') {
187+
return null;
188+
}
189+
return version.split('.')[0] as SvelteContext['svelteKitVersion'];
145190
}
146-
// If unknown version, it recognize as v2.
147-
return '2';
148191
} catch {
149-
return null;
192+
/** do nothing */
150193
}
151-
}
152194

153-
function getSvelteVersion(compilerVersion: string | undefined): SvelteContext['svelteVersion'] {
154-
if (compilerVersion == null) {
155-
return 'undetermined';
156-
}
157-
const version = parseInt(compilerVersion.split('.')[0], 10);
158-
if (version === 3 || version === 4) {
159-
return '3/4';
160-
}
161-
return String(version) as '5';
195+
return null;
162196
}
163197

164198
/**
@@ -168,22 +202,57 @@ function getSvelteVersion(compilerVersion: string | undefined): SvelteContext['s
168202
*/
169203
function getProjectRootDir(filePath: string): string | null {
170204
if (isRunInBrowser) return null;
171-
const packageJsonFilePath = getPackageJson(filePath)?.filePath;
205+
const packageJsons = getPackageJsons(filePath);
206+
if (packageJsons.length === 0) {
207+
return null;
208+
}
209+
const packageJsonFilePath = packageJsons[0].filePath;
172210
if (!packageJsonFilePath) return null;
173211
return path.dirname(path.resolve(packageJsonFilePath));
174212
}
175213

176214
export function getSvelteContext(context: RuleContext): SvelteContext | null {
177215
const { parserServices } = getSourceCode(context);
178216
const { svelteParseContext } = parserServices;
179-
const compilerVersion = svelteParseContext?.compilerVersion;
180217
const filePath = getFilename(context);
181218
const svelteKitContext = getSvelteKitContext(context);
219+
const svelteVersion = getSvelteVersion(filePath);
220+
const svelteFileType = getSvelteFileType(filePath);
221+
222+
if (svelteVersion == null) {
223+
return {
224+
svelteVersion: null,
225+
svelteFileType: null,
226+
runes: null,
227+
svelteKitVersion: svelteKitContext.svelteKitVersion,
228+
svelteKitFileType: svelteKitContext.svelteKitFileType
229+
};
230+
}
231+
232+
if (svelteFileType === 'other') {
233+
return {
234+
svelteVersion,
235+
svelteFileType,
236+
runes: null,
237+
svelteKitVersion: svelteKitContext.svelteKitVersion,
238+
svelteKitFileType: svelteKitContext.svelteKitFileType
239+
};
240+
}
241+
242+
if (svelteVersion === '3/4') {
243+
return {
244+
svelteVersion,
245+
svelteFileType: svelteFileType === '.svelte' ? '.svelte' : 'other',
246+
runes: null,
247+
svelteKitVersion: svelteKitContext.svelteKitVersion,
248+
svelteKitFileType: svelteKitContext.svelteKitFileType
249+
};
250+
}
182251

183252
return {
184-
svelteVersion: getSvelteVersion(compilerVersion),
253+
svelteVersion,
185254
runes: svelteParseContext?.runes ?? 'undetermined',
186-
svelteFileType: getSvelteFileType(filePath),
255+
svelteFileType,
187256
svelteKitVersion: svelteKitContext.svelteKitVersion,
188257
svelteKitFileType: svelteKitContext.svelteKitFileType
189258
};

packages/eslint-plugin-svelte/tests/src/index.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { shouldRun } from '../../src/utils/index.js';
44
const actualSvelte3: Parameters<typeof shouldRun>[0] = {
55
svelteVersion: '3/4',
66
svelteFileType: '.svelte',
7-
runes: false,
7+
runes: null,
88
svelteKitVersion: null,
99
svelteKitFileType: null
1010
};

0 commit comments

Comments
 (0)