diff --git a/.changeset/onwarn.md b/.changeset/onwarn.md
new file mode 100644
index 000000000..3054ff559
--- /dev/null
+++ b/.changeset/onwarn.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-svelte': minor
+---
+
+feat: improve `svelte/valid-compile` to use `svelte.config.js`'s `onwarn` from the parser.
diff --git a/docs/rules/valid-compile.md b/docs/rules/valid-compile.md
index 920d072ba..1efdf6f49 100644
--- a/docs/rules/valid-compile.md
+++ b/docs/rules/valid-compile.md
@@ -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
diff --git a/packages/eslint-plugin-svelte/src/rules/valid-compile.ts b/packages/eslint-plugin-svelte/src/rules/valid-compile.ts
index e56e6ec7b..e202164f3 100644
--- a/packages/eslint-plugin-svelte/src/rules/valid-compile.ts
+++ b/packages/eslint-plugin-svelte/src/rules/valid-compile.ts
@@ -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';
@@ -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 = [
@@ -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})` : ''}`
});
}
}
@@ -60,7 +76,7 @@ export default createRule('valid-compile', {
if (ignoreWarnings && result.kind === 'warn') {
return;
}
- report(result.warnings);
+ report(result);
}
};
}
diff --git a/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/index.ts b/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/index.ts
index d5e3bcad4..01cc5449b 100644
--- a/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/index.ts
+++ b/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/index.ts
@@ -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';
@@ -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
@@ -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 };
}
}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/_config.js b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/_config.js
new file mode 100644
index 000000000..ffe2c0ded
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/_config.js
@@ -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' });
+ }
+ }
+ }
+ }
+};
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-errors.yaml
new file mode 100644
index 000000000..cda864620
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-errors.yaml
@@ -0,0 +1,8 @@
+- message: '`
` element should have an alt attribute(foo)'
+ line: 5
+ column: 1
+ suggestions: null
+- message: Avoid using autofocus(foo)
+ line: 5
+ column: 12
+ suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-input.svelte
new file mode 100644
index 000000000..ba1d8baf5
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-input.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-requirements.json
new file mode 100644
index 000000000..0192b1098
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-requirements.json
@@ -0,0 +1,3 @@
+{
+ "svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-svelte4-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-svelte4-errors.yaml
new file mode 100644
index 000000000..96be2047d
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-svelte4-errors.yaml
@@ -0,0 +1,8 @@
+- message: 'A11y:
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
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-svelte4-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-svelte4-input.svelte
new file mode 100644
index 000000000..ba1d8baf5
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-svelte4-input.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-svelte4-requirements.json
new file mode 100644
index 000000000..b650cc296
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-custom-warn/a11y-svelte4-requirements.json
@@ -0,0 +1,3 @@
+{
+ "svelte": "^3.0.0 || ^4.0.0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/_config.js b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/_config.js
new file mode 100644
index 000000000..293325f3a
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/_config.js
@@ -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);
+ }
+ }
+ }
+ }
+};
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-errors.yaml
new file mode 100644
index 000000000..00fe33d44
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-errors.yaml
@@ -0,0 +1,4 @@
+- message: Avoid using autofocus(a11y_autofocus)
+ line: 5
+ column: 12
+ suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-input.svelte
new file mode 100644
index 000000000..ba1d8baf5
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-input.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-requirements.json
new file mode 100644
index 000000000..0192b1098
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-requirements.json
@@ -0,0 +1,3 @@
+{
+ "svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-svelte4-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-svelte4-errors.yaml
new file mode 100644
index 000000000..2b5f040ec
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-svelte4-errors.yaml
@@ -0,0 +1,4 @@
+- message: 'A11y: Avoid using autofocus(a11y-autofocus)'
+ line: 5
+ column: 12
+ suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-svelte4-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-svelte4-input.svelte
new file mode 100644
index 000000000..ba1d8baf5
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-svelte4-input.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-svelte4-requirements.json
new file mode 100644
index 000000000..b650cc296
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/invalid/svelte-config-ignore/a11y-svelte4-requirements.json
@@ -0,0 +1,3 @@
+{
+ "svelte": "^3.0.0 || ^4.0.0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/valid/svelte-config-ignore/_config.js b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/valid/svelte-config-ignore/_config.js
new file mode 100644
index 000000000..293325f3a
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/valid/svelte-config-ignore/_config.js
@@ -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);
+ }
+ }
+ }
+ }
+};
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/valid/svelte-config-ignore/a11y-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/valid/svelte-config-ignore/a11y-input.svelte
new file mode 100644
index 000000000..d7954e785
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-compile/valid/svelte-config-ignore/a11y-input.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/utils/utils.ts b/packages/eslint-plugin-svelte/tests/utils/utils.ts
index 202a97896..91663b2d3 100644
--- a/packages/eslint-plugin-svelte/tests/utils/utils.ts
+++ b/packages/eslint-plugin-svelte/tests/utils/utils.ts
@@ -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'