Skip to content

Commit

Permalink
feat: improve svelte/valid-compile to use svelte.config.js's `onw…
Browse files Browse the repository at this point in the history
…arn` (#796)

close #311
  • Loading branch information
ota-meshi committed Jun 17, 2024
1 parent f6d4e4c commit 5e4d264
Show file tree
Hide file tree
Showing 21 changed files with 225 additions and 51 deletions.
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

0 comments on commit 5e4d264

Please sign in to comment.