Skip to content

Commit 5e4d264

Browse files
authored
feat: improve svelte/valid-compile to use svelte.config.js's onwarn (#796)
close #311
1 parent f6d4e4c commit 5e4d264

File tree

21 files changed

+225
-51
lines changed

21 files changed

+225
-51
lines changed

.changeset/onwarn.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': minor
3+
---
4+
5+
feat: improve `svelte/valid-compile` to use `svelte.config.js`'s `onwarn` from the parser.

docs/rules/valid-compile.md

+39
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,45 @@ This rule uses Svelte compiler to check the source code.
3737

3838
Note that we exclude reports for some checks, such as `missing-declaration`, and `dynamic-slot-name`, which you can check with different ESLint rules.
3939

40+
### Using `svelte.config.js`
41+
42+
If you want to suppress messages using [`onwarn` like `vite-plugin-svelte`](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#onwarn), Use `eslint.config.js` and specify the information in `svelte.config.js` in your parser configuration.
43+
44+
```js
45+
import svelteConfig from './svelte.config.js';
46+
export default [
47+
// ...
48+
{
49+
files: ['**/*.svelte', '*.svelte'],
50+
languageOptions: {
51+
parserOptions: {
52+
svelteConfig: svelteConfig
53+
}
54+
}
55+
}
56+
];
57+
```
58+
59+
See also [User Guide > Specify `svelte.config.js`](../user-guide.md#specify-svelte-config-js)
60+
61+
#### onwarn
62+
63+
This rule can use [`onwarn` like `vite-plugin-svelte`](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#onwarn).
64+
65+
Example:
66+
67+
```js
68+
// svelte.config.js
69+
export default {
70+
onwarn: (warning, handler) => {
71+
if (warning.code === 'a11y-distracting-elements') return;
72+
if (warning.code === 'a11y_distracting_elements') return; // for Svelte v5
73+
74+
handler(warning);
75+
}
76+
};
77+
```
78+
4079
## :wrench: Options
4180

4281
```json

packages/eslint-plugin-svelte/src/rules/valid-compile.ts

+23-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createRule } from '../utils';
2-
import type { Warning } from '../shared/svelte-compile-warns';
2+
import type { SvelteCompileWarnings, Warning } from '../shared/svelte-compile-warns';
33
import { getSvelteCompileWarnings } from '../shared/svelte-compile-warns';
44
import { getSourceCode } from '../utils/compat';
55

@@ -23,9 +23,21 @@ export default createRule('valid-compile', {
2323
type: 'problem'
2424
},
2525
create(context) {
26-
if (!getSourceCode(context).parserServices.isSvelte) {
26+
const sourceCode = getSourceCode(context);
27+
if (!sourceCode.parserServices.isSvelte) {
2728
return {};
2829
}
30+
const onwarn = sourceCode.parserServices.svelteParseContext?.svelteConfig?.onwarn;
31+
32+
const transform: (warning: Warning) => Warning | null = onwarn
33+
? (warning) => {
34+
if (!warning.code) return warning;
35+
let result: Warning | null = null;
36+
onwarn(warning, (reportWarn) => (result = reportWarn));
37+
return result;
38+
}
39+
: (warning) => warning;
40+
2941
const ignoreWarnings = Boolean(context.options[0]?.ignoreWarnings);
3042

3143
const ignores = [
@@ -39,17 +51,21 @@ export default createRule('valid-compile', {
3951
/**
4052
* report
4153
*/
42-
function report(warnings: Warning[]) {
54+
function report({ warnings, kind }: SvelteCompileWarnings) {
4355
for (const warn of warnings) {
4456
if (warn.code && ignores.includes(warn.code)) {
4557
continue;
4658
}
59+
const reportWarn = kind === 'warn' ? transform(warn) : warn;
60+
if (!reportWarn) {
61+
continue;
62+
}
4763
context.report({
4864
loc: {
49-
start: warn.start || warn.end || { line: 1, column: 0 },
50-
end: warn.end || warn.start || { line: 1, column: 0 }
65+
start: reportWarn.start || reportWarn.end || { line: 1, column: 0 },
66+
end: reportWarn.end || reportWarn.start || { line: 1, column: 0 }
5167
},
52-
message: `${warn.message}${warn.code ? `(${warn.code})` : ''}`
68+
message: `${reportWarn.message}${reportWarn.code ? `(${reportWarn.code})` : ''}`
5369
});
5470
}
5571
}
@@ -60,7 +76,7 @@ export default createRule('valid-compile', {
6076
if (ignoreWarnings && result.kind === 'warn') {
6177
return;
6278
}
63-
report(result.warnings);
79+
report(result);
6480
}
6581
};
6682
}

packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/index.ts

+19-38
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { AST } from 'svelte-eslint-parser';
2-
import type {} from 'svelte'; // FIXME: Workaround to get type information for "svelte/compiler"
32
import * as compiler from 'svelte/compiler';
43
import type { SourceMapMappings } from '@jridgewell/sourcemap-codec';
54
import { decode } from '@jridgewell/sourcemap-codec';
@@ -60,16 +59,25 @@ export type Loc = {
6059
start?: {
6160
line: number;
6261
column: number;
62+
character: number;
6363
};
6464
end?: {
6565
line: number;
6666
column: number;
67+
character: number;
6768
};
6869
};
69-
export type Warning = {
70-
code?: string;
71-
message: string;
72-
} & Loc;
70+
export type Warning = (
71+
| {
72+
code: string;
73+
message: string;
74+
}
75+
| {
76+
code?: undefined;
77+
message: string;
78+
}
79+
) &
80+
Loc;
7381

7482
/**
7583
* Get svelte compile warnings
@@ -228,51 +236,24 @@ function getSvelteCompileWarningsWithoutCache(context: RuleContext): SvelteCompi
228236
}
229237
}
230238

231-
public remapLocs(points: {
232-
start?: {
233-
line: number;
234-
column: number;
235-
};
236-
end?: {
237-
line: number;
238-
column: number;
239-
};
240-
}): {
241-
start?: {
242-
line: number;
243-
column: number;
244-
};
245-
end?: {
246-
line: number;
247-
column: number;
248-
};
249-
} {
239+
public remapLocs(points: Loc): Loc {
250240
const mapIndexes = this.mapIndexes;
251241
const locs = (this.locs = this.locs ?? new LinesAndColumns(this.code));
252-
let start:
253-
| {
254-
line: number;
255-
column: number;
256-
}
257-
| undefined = undefined;
258-
let end:
259-
| {
260-
line: number;
261-
column: number;
262-
}
263-
| undefined = undefined;
242+
let start: Loc['start'] | undefined = undefined;
243+
let end: Loc['end'] | undefined = undefined;
264244
if (points.start) {
265245
const index = locs.getIndexFromLoc(points.start);
266246
const remapped = remapIndex(index);
267247
if (remapped) {
268-
start = sourceCode.getLocFromIndex(remapped);
248+
start = { ...sourceCode.getLocFromIndex(remapped), character: remapped };
269249
}
270250
}
271251
if (points.end) {
272252
const index = locs.getIndexFromLoc(points.end);
273253
const remapped = remapIndex(index - 1 /* include index */);
274254
if (remapped) {
275-
end = sourceCode.getLocFromIndex(remapped + 1 /* restore */);
255+
const character = remapped + 1; /* restore */
256+
end = { ...sourceCode.getLocFromIndex(character), character };
276257
}
277258
}
278259

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @typedef {import("svelte/compiler").Warning} Warning
3+
*/
4+
module.exports = {
5+
languageOptions: {
6+
parserOptions: {
7+
svelteConfig: {
8+
/**
9+
* @param {Warning} warning
10+
* @param {(warning: Warning) => void} handler
11+
* @returns {void}
12+
*/
13+
onwarn(warning, handler) {
14+
// transform code
15+
handler({ ...warning, code: 'foo' });
16+
}
17+
}
18+
}
19+
}
20+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
- message: '`<img>` element should have an alt attribute(foo)'
2+
line: 5
3+
column: 1
4+
suggestions: null
5+
- message: Avoid using autofocus(foo)
6+
line: 5
7+
column: 12
8+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let src = 'tutorial/image.gif';
3+
</script>
4+
5+
<img {src} autofocus />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"svelte": ">=5.0.0-0"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
- message: 'A11y: <img> element should have an alt attribute(foo)'
2+
line: 5
3+
column: 1
4+
suggestions: null
5+
- message: 'A11y: Avoid using autofocus(foo)'
6+
line: 5
7+
column: 12
8+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let src = 'tutorial/image.gif';
3+
</script>
4+
5+
<img {src} autofocus />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"svelte": "^3.0.0 || ^4.0.0"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @typedef {import("svelte/compiler").Warning} Warning
3+
*/
4+
module.exports = {
5+
languageOptions: {
6+
parserOptions: {
7+
svelteConfig: {
8+
/**
9+
* @param {Warning} warning
10+
* @param {(warning: Warning) => void} handler
11+
* @returns {void}
12+
*/
13+
onwarn(warning, handler) {
14+
if (
15+
warning.code === 'a11y_missing_attribute' ||
16+
warning.code === 'a11y-missing-attribute'
17+
)
18+
return;
19+
handler(warning);
20+
}
21+
}
22+
}
23+
}
24+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Avoid using autofocus(a11y_autofocus)
2+
line: 5
3+
column: 12
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let src = 'tutorial/image.gif';
3+
</script>
4+
5+
<img {src} autofocus />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"svelte": ">=5.0.0-0"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: 'A11y: Avoid using autofocus(a11y-autofocus)'
2+
line: 5
3+
column: 12
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let src = 'tutorial/image.gif';
3+
</script>
4+
5+
<img {src} autofocus />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"svelte": "^3.0.0 || ^4.0.0"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @typedef {import("svelte/compiler").Warning} Warning
3+
*/
4+
module.exports = {
5+
languageOptions: {
6+
parserOptions: {
7+
svelteConfig: {
8+
/**
9+
* @param {Warning} warning
10+
* @param {(warning: Warning) => void} handler
11+
* @returns {void}
12+
*/
13+
onwarn(warning, handler) {
14+
if (
15+
warning.code === 'a11y_missing_attribute' ||
16+
warning.code === 'a11y-missing-attribute'
17+
)
18+
return;
19+
handler(warning);
20+
}
21+
}
22+
}
23+
}
24+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let src = 'tutorial/image.gif';
3+
</script>
4+
5+
<img {src} />

packages/eslint-plugin-svelte/tests/utils/utils.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,16 @@ function getConfig(ruleName: string, inputFile: string) {
289289
const filename = inputFile.slice(inputFile.indexOf(ruleName));
290290
const code = fs.readFileSync(inputFile, 'utf8');
291291
let config;
292-
let configFile: string = inputFile.replace(/input\.[a-z]+$/u, 'config.json');
293-
if (!fs.existsSync(configFile)) {
294-
configFile = path.join(path.dirname(inputFile), '_config.json');
295-
}
296-
if (fs.existsSync(configFile)) {
297-
config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
292+
let configFile = [
293+
inputFile.replace(/input\.[a-z]+$/u, 'config.json'),
294+
path.join(path.dirname(inputFile), '_config.json'),
295+
inputFile.replace(/input\.[a-z]+$/u, 'config.js'),
296+
path.join(path.dirname(inputFile), '_config.js')
297+
].find((f) => fs.existsSync(f));
298+
if (configFile) {
299+
config = configFile.endsWith('.js')
300+
? require(configFile)
301+
: JSON.parse(fs.readFileSync(configFile, 'utf8'));
298302
}
299303
const parser =
300304
path.extname(filename) === '.svelte'

0 commit comments

Comments
 (0)