Skip to content

feat: improve svelte/valid-compile to use svelte.config.js's onwarn #796

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

Merged
merged 2 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/onwarn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': minor
---

feat: improve `svelte/valid-compile` to use `svelte.config.js`'s `onwarn` from the parser.
39 changes: 39 additions & 0 deletions docs/rules/valid-compile.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,45 @@ This rule uses Svelte compiler to check the source code.

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

### Using `svelte.config.js`

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.

```js
import svelteConfig from './svelte.config.js';
export default [
// ...
{
files: ['**/*.svelte', '*.svelte'],
languageOptions: {
parserOptions: {
svelteConfig: svelteConfig
}
}
}
];
```

See also [User Guide > Specify `svelte.config.js`](../user-guide.md#specify-svelte-config-js)

#### onwarn

This rule can use [`onwarn` like `vite-plugin-svelte`](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#onwarn).

Example:

```js
// svelte.config.js
export default {
onwarn: (warning, handler) => {
if (warning.code === 'a11y-distracting-elements') return;
if (warning.code === 'a11y_distracting_elements') return; // for Svelte v5

handler(warning);
}
};
```

## :wrench: Options

```json
Expand Down
30 changes: 23 additions & 7 deletions packages/eslint-plugin-svelte/src/rules/valid-compile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createRule } from '../utils';
import type { Warning } from '../shared/svelte-compile-warns';
import type { SvelteCompileWarnings, Warning } from '../shared/svelte-compile-warns';
import { getSvelteCompileWarnings } from '../shared/svelte-compile-warns';
import { getSourceCode } from '../utils/compat';

Expand All @@ -23,9 +23,21 @@ export default createRule('valid-compile', {
type: 'problem'
},
create(context) {
if (!getSourceCode(context).parserServices.isSvelte) {
const sourceCode = getSourceCode(context);
if (!sourceCode.parserServices.isSvelte) {
return {};
}
const onwarn = sourceCode.parserServices.svelteParseContext?.svelteConfig?.onwarn;

const transform: (warning: Warning) => Warning | null = onwarn
? (warning) => {
if (!warning.code) return warning;
let result: Warning | null = null;
onwarn(warning, (reportWarn) => (result = reportWarn));
return result;
}
: (warning) => warning;

const ignoreWarnings = Boolean(context.options[0]?.ignoreWarnings);

const ignores = [
Expand All @@ -39,17 +51,21 @@ export default createRule('valid-compile', {
/**
* report
*/
function report(warnings: Warning[]) {
function report({ warnings, kind }: SvelteCompileWarnings) {
for (const warn of warnings) {
if (warn.code && ignores.includes(warn.code)) {
continue;
}
const reportWarn = kind === 'warn' ? transform(warn) : warn;
if (!reportWarn) {
continue;
}
context.report({
loc: {
start: warn.start || warn.end || { line: 1, column: 0 },
end: warn.end || warn.start || { line: 1, column: 0 }
start: reportWarn.start || reportWarn.end || { line: 1, column: 0 },
end: reportWarn.end || reportWarn.start || { line: 1, column: 0 }
},
message: `${warn.message}${warn.code ? `(${warn.code})` : ''}`
message: `${reportWarn.message}${reportWarn.code ? `(${reportWarn.code})` : ''}`
});
}
}
Expand All @@ -60,7 +76,7 @@ export default createRule('valid-compile', {
if (ignoreWarnings && result.kind === 'warn') {
return;
}
report(result.warnings);
report(result);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { AST } from 'svelte-eslint-parser';
import type {} from 'svelte'; // FIXME: Workaround to get type information for "svelte/compiler"
import * as compiler from 'svelte/compiler';
import type { SourceMapMappings } from '@jridgewell/sourcemap-codec';
import { decode } from '@jridgewell/sourcemap-codec';
Expand Down Expand Up @@ -60,16 +59,25 @@ export type Loc = {
start?: {
line: number;
column: number;
character: number;
};
end?: {
line: number;
column: number;
character: number;
};
};
export type Warning = {
code?: string;
message: string;
} & Loc;
export type Warning = (
| {
code: string;
message: string;
}
| {
code?: undefined;
message: string;
}
) &
Loc;

/**
* Get svelte compile warnings
Expand Down Expand Up @@ -228,51 +236,24 @@ function getSvelteCompileWarningsWithoutCache(context: RuleContext): SvelteCompi
}
}

public remapLocs(points: {
start?: {
line: number;
column: number;
};
end?: {
line: number;
column: number;
};
}): {
start?: {
line: number;
column: number;
};
end?: {
line: number;
column: number;
};
} {
public remapLocs(points: Loc): Loc {
const mapIndexes = this.mapIndexes;
const locs = (this.locs = this.locs ?? new LinesAndColumns(this.code));
let start:
| {
line: number;
column: number;
}
| undefined = undefined;
let end:
| {
line: number;
column: number;
}
| undefined = undefined;
let start: Loc['start'] | undefined = undefined;
let end: Loc['end'] | undefined = undefined;
if (points.start) {
const index = locs.getIndexFromLoc(points.start);
const remapped = remapIndex(index);
if (remapped) {
start = sourceCode.getLocFromIndex(remapped);
start = { ...sourceCode.getLocFromIndex(remapped), character: remapped };
}
}
if (points.end) {
const index = locs.getIndexFromLoc(points.end);
const remapped = remapIndex(index - 1 /* include index */);
if (remapped) {
end = sourceCode.getLocFromIndex(remapped + 1 /* restore */);
const character = remapped + 1; /* restore */
end = { ...sourceCode.getLocFromIndex(character), character };
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @typedef {import("svelte/compiler").Warning} Warning
*/
module.exports = {
languageOptions: {
parserOptions: {
svelteConfig: {
/**
* @param {Warning} warning
* @param {(warning: Warning) => void} handler
* @returns {void}
*/
onwarn(warning, handler) {
// transform code
handler({ ...warning, code: 'foo' });
}
}
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- message: '`<img>` element should have an alt attribute(foo)'
line: 5
column: 1
suggestions: null
- message: Avoid using autofocus(foo)
line: 5
column: 12
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
let src = 'tutorial/image.gif';
</script>

<img {src} autofocus />
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"svelte": ">=5.0.0-0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- message: 'A11y: <img> element should have an alt attribute(foo)'
line: 5
column: 1
suggestions: null
- message: 'A11y: Avoid using autofocus(foo)'
line: 5
column: 12
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
let src = 'tutorial/image.gif';
</script>

<img {src} autofocus />
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"svelte": "^3.0.0 || ^4.0.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @typedef {import("svelte/compiler").Warning} Warning
*/
module.exports = {
languageOptions: {
parserOptions: {
svelteConfig: {
/**
* @param {Warning} warning
* @param {(warning: Warning) => void} handler
* @returns {void}
*/
onwarn(warning, handler) {
if (
warning.code === 'a11y_missing_attribute' ||
warning.code === 'a11y-missing-attribute'
)
return;
handler(warning);
}
}
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: Avoid using autofocus(a11y_autofocus)
line: 5
column: 12
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
let src = 'tutorial/image.gif';
</script>

<img {src} autofocus />
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"svelte": ">=5.0.0-0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: 'A11y: Avoid using autofocus(a11y-autofocus)'
line: 5
column: 12
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
let src = 'tutorial/image.gif';
</script>

<img {src} autofocus />
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"svelte": "^3.0.0 || ^4.0.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @typedef {import("svelte/compiler").Warning} Warning
*/
module.exports = {
languageOptions: {
parserOptions: {
svelteConfig: {
/**
* @param {Warning} warning
* @param {(warning: Warning) => void} handler
* @returns {void}
*/
onwarn(warning, handler) {
if (
warning.code === 'a11y_missing_attribute' ||
warning.code === 'a11y-missing-attribute'
)
return;
handler(warning);
}
}
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
let src = 'tutorial/image.gif';
</script>

<img {src} />
16 changes: 10 additions & 6 deletions packages/eslint-plugin-svelte/tests/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,16 @@ function getConfig(ruleName: string, inputFile: string) {
const filename = inputFile.slice(inputFile.indexOf(ruleName));
const code = fs.readFileSync(inputFile, 'utf8');
let config;
let configFile: string = inputFile.replace(/input\.[a-z]+$/u, 'config.json');
if (!fs.existsSync(configFile)) {
configFile = path.join(path.dirname(inputFile), '_config.json');
}
if (fs.existsSync(configFile)) {
config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
let configFile = [
inputFile.replace(/input\.[a-z]+$/u, 'config.json'),
path.join(path.dirname(inputFile), '_config.json'),
inputFile.replace(/input\.[a-z]+$/u, 'config.js'),
path.join(path.dirname(inputFile), '_config.js')
].find((f) => fs.existsSync(f));
if (configFile) {
config = configFile.endsWith('.js')
? require(configFile)
: JSON.parse(fs.readFileSync(configFile, 'utf8'));
}
const parser =
path.extname(filename) === '.svelte'
Expand Down
Loading