Skip to content

Commit 273b1e9

Browse files
committed
feat: Implement util to conditionally run lint based on Svelte version and SvelteKit routes etc.
1 parent b384d57 commit 273b1e9

Some content is hidden

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

59 files changed

+394
-91
lines changed

packages/eslint-plugin-svelte/src/rules/no-export-load-in-svelte-module-in-kit-pages.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { TSESTree } from '@typescript-eslint/types';
22
import { createRule } from '../utils/index.js';
3-
import { isKitPageComponent } from '../utils/svelte-kit.js';
3+
import { getSvelteContext } from '../utils/svelte-context.js';
44

55
export default createRule('no-export-load-in-svelte-module-in-kit-pages', {
66
meta: {
@@ -19,9 +19,8 @@ export default createRule('no-export-load-in-svelte-module-in-kit-pages', {
1919
type: 'problem'
2020
},
2121
create(context) {
22-
if (!isKitPageComponent(context)) {
23-
return {};
24-
}
22+
const svelteContext = getSvelteContext(context);
23+
if (svelteContext == null || svelteContext.svelteKitFileType == null) return {};
2524
let isModule = false;
2625
return {
2726
// <script context="module">

packages/eslint-plugin-svelte/src/rules/valid-prop-names-in-kit-pages.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { AST } from 'svelte-eslint-parser';
22
import type { TSESTree } from '@typescript-eslint/types';
33
import { createRule } from '../utils/index.js';
4-
import { isKitPageComponent } from '../utils/svelte-kit.js';
4+
import { getSvelteContext } from '../utils/svelte-context.js';
55
import type { RuleContext } from '../types.js';
66

77
const EXPECTED_PROP_NAMES = ['data', 'errors', 'form', 'snapshot'];
@@ -38,7 +38,8 @@ export default createRule('valid-prop-names-in-kit-pages', {
3838
type: 'problem'
3939
},
4040
create(context) {
41-
if (!isKitPageComponent(context)) return {};
41+
const svelteContext = getSvelteContext(context);
42+
if (svelteContext == null || svelteContext.svelteKitFileType == null) return {};
4243
let isScript = false;
4344
return {
4445
// <script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import type { RuleContext } from '../types.js';
2+
import fs from 'fs';
3+
import path from 'path';
4+
import { getPackageJson } from './get-package-json.js';
5+
import { getFilename, getSourceCode } from './compat.js';
6+
7+
const isRunOnBrowser = !fs.readFileSync;
8+
9+
type FileType = '.svelte' | '.svelte.[js|ts]';
10+
export type SvelteContext = {
11+
svelteKitFileType:
12+
| '+page.svelte'
13+
| '+page.js'
14+
| '+page.server.js'
15+
| '+error.svelte'
16+
| '+layout.svelte'
17+
| '+layout.js'
18+
| '+layout.server.js'
19+
| '+server.js'
20+
| null;
21+
} & (
22+
| {
23+
version: 3 | 4;
24+
}
25+
| {
26+
version: 5;
27+
runes: boolean;
28+
fileType: FileType;
29+
}
30+
);
31+
32+
function getFileType(filePath: string): FileType | null {
33+
if (filePath.endsWith('.svelte')) {
34+
return '.svelte';
35+
}
36+
37+
if (filePath.endsWith('.svelte.js') || filePath.endsWith('.svelte.ts')) {
38+
return '.svelte.[js|ts]';
39+
}
40+
41+
return null;
42+
}
43+
44+
function getSvelteKitFileTypeFromFilePath(filePath: string): SvelteContext['svelteKitFileType'] {
45+
const fileName = filePath.split('/').pop();
46+
switch (fileName) {
47+
case '+page.svelte': {
48+
return '+page.svelte';
49+
}
50+
case '+page.js':
51+
case '+page.ts': {
52+
return '+page.js';
53+
}
54+
case '+page.server.js':
55+
case '+page.server.ts': {
56+
return '+page.server.js';
57+
}
58+
case '+error.svelte': {
59+
return '+error.svelte';
60+
}
61+
case '+layout.svelte': {
62+
return '+layout.svelte';
63+
}
64+
case '+layout.js':
65+
case '+layout.ts': {
66+
return '+layout.js';
67+
}
68+
case '+layout.server.js':
69+
case '+layout.server.ts': {
70+
return '+layout.server.js';
71+
}
72+
case '+server.js':
73+
case '+server.ts': {
74+
return '+server.js';
75+
}
76+
default: {
77+
return null;
78+
}
79+
}
80+
}
81+
82+
function getSvelteKitFileType(context: RuleContext): SvelteContext['svelteKitFileType'] {
83+
const filePath = getFilename(context);
84+
85+
// Hack: if it runs on browser, it regards as SvelteKit project.
86+
if (isRunOnBrowser) {
87+
return getSvelteKitFileTypeFromFilePath(filePath);
88+
}
89+
90+
if (!hasSvelteKit(getFilename(context))) {
91+
return null;
92+
}
93+
94+
const routes =
95+
(
96+
context.settings?.svelte?.kit?.files?.routes ??
97+
getSourceCode(context).parserServices.svelteParseContext?.svelteConfig?.kit?.files?.routes
98+
)?.replace(/^\//, '') ?? 'src/routes';
99+
const projectRootDir = getProjectRootDir(getFilename(context)) ?? '';
100+
101+
if (!filePath.startsWith(path.join(projectRootDir, routes))) {
102+
return null;
103+
}
104+
105+
return getSvelteKitFileTypeFromFilePath(filePath);
106+
}
107+
108+
/**
109+
* Check givin file is under SvelteKit project.
110+
*
111+
* If it runs on browser, it always returns true.
112+
*
113+
* @param filePath A file path.
114+
* @returns
115+
*/
116+
function hasSvelteKit(filePath: string): boolean {
117+
// Hack: if it runs on browser, it regards as SvelteKit project.
118+
if (isRunOnBrowser) return true;
119+
try {
120+
const packageJson = getPackageJson(filePath);
121+
if (!packageJson) return false;
122+
if (packageJson.name === 'eslint-plugin-svelte')
123+
// Hack: CI removes `@sveltejs/kit` and it returns false and test failed.
124+
// So always it returns true if it runs on the package.
125+
return true;
126+
return Boolean(
127+
packageJson.dependencies?.['@sveltejs/kit'] ?? packageJson.devDependencies?.['@sveltejs/kit']
128+
);
129+
} catch {
130+
return false;
131+
}
132+
}
133+
134+
/**
135+
* Gets a project root folder path.
136+
* @param filePath A file path to lookup.
137+
* @returns A found project root folder path or null.
138+
*/
139+
function getProjectRootDir(filePath: string): string | null {
140+
if (isRunOnBrowser) return null;
141+
const packageJsonFilePath = getPackageJson(filePath)?.filePath;
142+
if (!packageJsonFilePath) return null;
143+
return path.dirname(path.resolve(packageJsonFilePath));
144+
}
145+
146+
export function getSvelteContext(context: RuleContext): SvelteContext | null {
147+
const { parserServices } = getSourceCode(context);
148+
const { svelteParseContext } = parserServices;
149+
if (svelteParseContext === undefined) {
150+
return null;
151+
}
152+
153+
const { compilerVersion } = svelteParseContext;
154+
if (compilerVersion === undefined) {
155+
return null;
156+
}
157+
158+
const filePath = getFilename(context);
159+
const svelteKitFileType = getSvelteKitFileType(context);
160+
161+
if (compilerVersion.startsWith('3')) {
162+
return {
163+
version: 3,
164+
svelteKitFileType
165+
};
166+
}
167+
168+
if (compilerVersion.startsWith('4')) {
169+
return {
170+
version: 4,
171+
svelteKitFileType
172+
};
173+
}
174+
175+
const runes = svelteParseContext.runes === true;
176+
const fileType = getFileType(filePath);
177+
if (fileType === null) {
178+
return null;
179+
}
180+
181+
return {
182+
version: 5,
183+
runes,
184+
fileType,
185+
svelteKitFileType
186+
};
187+
}

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

-73
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"languageOptions": {
3+
"parserOptions": {
4+
"svelteConfig": {
5+
"kit": {
6+
"files": {
7+
"routes": "tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/valid/test01"
8+
}
9+
}
10+
}
11+
}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"languageOptions": {
3+
"parserOptions": {
4+
"svelteConfig": {
5+
"kit": {
6+
"files": {
7+
"routes": "tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/valid/test02"
8+
}
9+
}
10+
}
11+
}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"languageOptions": {
3+
"parserOptions": {
4+
"svelteConfig": {
5+
"kit": {
6+
"files": {
7+
"routes": "tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/valid/test03"
8+
}
9+
}
10+
}
11+
}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"languageOptions": {
3+
"parserOptions": {
4+
"svelteConfig": {
5+
"kit": {
6+
"files": {
7+
"routes": "tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/valid/test04"
8+
}
9+
}
10+
}
11+
}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"languageOptions": {
3+
"parserOptions": {
4+
"svelteConfig": {
5+
"kit": {
6+
"files": {
7+
"routes": "tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/valid/test05"
8+
}
9+
}
10+
}
11+
}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"languageOptions": {
3+
"parserOptions": {
4+
"svelteConfig": {
5+
"kit": {
6+
"files": {
7+
"routes": "tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/valid/test06"
8+
}
9+
}
10+
}
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)