Skip to content

feat: add support for flat config #708

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 4 commits into from
Mar 18, 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/gentle-apricots-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-svelte": minor
---

feat: add support for flat config
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ module.exports = {
'mdx/code-blocks': true
}
},
{
files: ['*.md/**', '**/*.md/**'],
rules: {
'n/no-missing-import': 'off'
}
},
{
files: ['*.mjs'],
parserOptions: {
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,38 @@ npm install --save-dev eslint eslint-plugin-svelte svelte

### Configuration

#### For ESLint>=v9 Config (Flat Config)

Use `eslint.config.js` file to configure rules. See also: <https://eslint.org/docs/latest/use/configure/configuration-files-new>.

Example **eslint.config.js**:

```mjs
import eslintPluginSvelte from 'eslint-plugin-svelte';
export default [
// add more generic rule sets here, such as:
// js.configs.recommended,
...eslintPluginSvelte.configs['flat/recommended'],
{
rules: {
// override/add rules settings here, such as:
// 'svelte/rule-name': 'error'
}
}
];
```

This plugin provides configs:

- `eslintPluginSvelte.configs['flat/base']` ... Configuration to enable correct Svelte parsing.
- `eslintPluginSvelte.configs['flat/recommended']` ... Above, plus rules to prevent errors or unintended behavior.
- `eslintPluginSvelte.configs['flat/prettier']` ... Turns off rules that may conflict with [Prettier](https://prettier.io/) (You still need to configure prettier to work with svelte yourself, for example by using [prettier-plugin-svelte](https://github.com/sveltejs/prettier-plugin-svelte).).
- `eslintPluginSvelte.configs['flat/all']` ... All rules. This configuration is not recommended for production use because it changes with every minor and major version of `eslint-plugin-svelte`. Use it at your own risk.

See [the rule list](https://sveltejs.github.io/eslint-plugin-svelte/rules/) to get the `rules` that this plugin provides.

#### Legacy Config (ESLint<v9)

Use `.eslintrc.*` file to configure rules. See also: <https://eslint.org/docs/user-guide/configuring>.

Example **.eslintrc.js**:
Expand Down
32 changes: 32 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,38 @@ npm install --save-dev eslint eslint-plugin-svelte svelte

### Configuration

#### For ESLint>=v9 Config (Flat Config)

Use `eslint.config.js` file to configure rules. See also: <https://eslint.org/docs/latest/use/configure/configuration-files-new>.

Example **eslint.config.js**:

```mjs
import eslintPluginSvelte from 'eslint-plugin-svelte';
export default [
// add more generic rule sets here, such as:
// js.configs.recommended,
...eslintPluginSvelte.configs['flat/recommended'],
{
rules: {
// override/add rules settings here, such as:
// 'svelte/rule-name': 'error'
}
}
];
```

This plugin provides configs:

- `eslintPluginSvelte.configs['flat/base']` ... Configuration to enable correct Svelte parsing.
- `eslintPluginSvelte.configs['flat/recommended']` ... Above, plus rules to prevent errors or unintended behavior.
- `eslintPluginSvelte.configs['flat/prettier']` ... Turns off rules that may conflict with [Prettier](https://prettier.io/) (You still need to configure prettier to work with svelte yourself, for example by using [prettier-plugin-svelte](https://github.com/sveltejs/prettier-plugin-svelte).).
- `eslintPluginSvelte.configs['flat/all']` ... All rules. This configuration is not recommended for production use because it changes with every minor and major version of `eslint-plugin-svelte`. Use it at your own risk.

See [the rule list](./rules.md) to get the `rules` that this plugin provides.

#### Legacy Config (ESLint<v9)

Use `.eslintrc.*` file to configure rules. See also: <https://eslint.org/docs/user-guide/configuring>.

Example **.eslintrc.js**:
Expand Down
18 changes: 18 additions & 0 deletions src/configs/flat/all.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { rules } from '../../utils/rules';
import base from './base';
export default [
...base,
{
rules: Object.fromEntries(
rules
.map((rule) => [`svelte/${rule.meta.docs.ruleName}`, 'error'])
.filter(
([ruleName]) =>
![
// Does not work without options.
'svelte/no-restricted-html-elements'
].includes(ruleName)
)
)
}
];
30 changes: 30 additions & 0 deletions src/configs/flat/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// IMPORTANT!
// This file has been automatically generated,
// in order to update its content execute "pnpm run update"
import type { ESLint } from 'eslint';
export default [
{
files: ['*.svelte', '**/*.svelte'],
plugins: {
get svelte(): ESLint.Plugin {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- ignore
return require('../../index');
}
},
languageOptions: {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- ignore
parser: require('svelte-eslint-parser')
},
rules: {
// ESLint core rules known to cause problems with `.svelte`.
'no-inner-declarations': 'off', // The AST generated by svelte-eslint-parser will false positives in it rule because the root node of the script is not the `Program`.
// "no-irregular-whitespace": "off",
// Self assign is one of way to update reactive value in Svelte.
'no-self-assign': 'off',

// eslint-plugin-svelte rules
'svelte/comment-directive': 'error',
'svelte/system': 'error'
}
}
];
23 changes: 23 additions & 0 deletions src/configs/flat/prettier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// IMPORTANT!
// This file has been automatically generated,
// in order to update its content execute "pnpm run update"
import base from './base';
export default [
...base,
{
rules: {
// eslint-plugin-svelte rules
'svelte/first-attribute-linebreak': 'off',
'svelte/html-closing-bracket-spacing': 'off',
'svelte/html-quotes': 'off',
'svelte/html-self-closing': 'off',
'svelte/indent': 'off',
'svelte/max-attributes-per-line': 'off',
'svelte/mustache-spacing': 'off',
'svelte/no-spaces-around-equal-signs-in-attribute': 'off',
'svelte/no-trailing-spaces': 'off',
'svelte/shorthand-attribute': 'off',
'svelte/shorthand-directive': 'off'
}
}
];
26 changes: 26 additions & 0 deletions src/configs/flat/recommended.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// IMPORTANT!
// This file has been automatically generated,
// in order to update its content execute "pnpm run update"
import base from './base';
export default [
...base,
{
rules: {
// eslint-plugin-svelte rules
'svelte/comment-directive': 'error',
'svelte/no-at-debug-tags': 'warn',
'svelte/no-at-html-tags': 'error',
'svelte/no-dupe-else-if-blocks': 'error',
'svelte/no-dupe-style-properties': 'error',
'svelte/no-dynamic-slot-name': 'error',
'svelte/no-inner-declarations': 'error',
'svelte/no-not-function-handler': 'error',
'svelte/no-object-in-text-mustaches': 'error',
'svelte/no-shorthand-style-property-overrides': 'error',
'svelte/no-unknown-style-directive-property': 'error',
'svelte/no-unused-svelte-ignore': 'error',
'svelte/system': 'error',
'svelte/valid-compile': 'error'
}
}
];
10 changes: 9 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@ import base from './configs/base';
import recommended from './configs/recommended';
import prettier from './configs/prettier';
import all from './configs/all';
import flatBase from './configs/flat/base';
import flatRecommended from './configs/flat/recommended';
import flatPrettier from './configs/flat/prettier';
import flatAll from './configs/flat/all';
import * as processor from './processor';
import * as meta from './meta';

const configs = {
base,
recommended,
prettier,
all
all,
'flat/base': flatBase,
'flat/recommended': flatRecommended,
'flat/prettier': flatPrettier,
'flat/all': flatAll
};

const rules = ruleList.reduce(
Expand Down
30 changes: 27 additions & 3 deletions tests/src/configs/all.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import assert from 'assert';
import semver from 'semver';
import plugin from '../../../src/index';
import { LegacyESLint } from '../../utils/eslint-compat';
import { LegacyESLint, ESLint } from '../../utils/eslint-compat';

describe('`all` config', () => {
it('`all` config should work. ', async () => {
it('legacy `all` config should work. ', async () => {
const code = `<script>const a = 1, b = 2;</script>{@html a+b}`;

const linter = new LegacyESLint({
Expand All @@ -22,10 +23,33 @@ describe('`all` config', () => {
const messages = result[0].messages;

assert.deepStrictEqual(
messages.map((m) => ({ ruleId: m.ruleId, line: m.line })),
messages.map((m) => ({ ruleId: m.ruleId, line: m.line, message: m.message })),
[
{
ruleId: 'svelte/no-at-html-tags',
message: '`{@html}` can lead to XSS attack.',
line: 1
}
]
);
});
it('`all` config should work. ', async () => {
if (semver.satisfies(ESLint.version, '<8.0.0')) return;
const code = `<script>const a = 1, b = 2;</script>{@html a+b}`;

const linter = new ESLint({
overrideConfigFile: true as never,
overrideConfig: plugin.configs['flat/all'] as never
});
const result = await linter.lintText(code, { filePath: 'test.svelte' });
const messages = result[0].messages;

assert.deepStrictEqual(
messages.map((m) => ({ ruleId: m.ruleId, line: m.line, message: m.message })),
[
{
ruleId: 'svelte/no-at-html-tags',
message: '`{@html}` can lead to XSS attack.',
line: 1
}
]
Expand Down
58 changes: 58 additions & 0 deletions tests/src/configs/recommended.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import assert from 'assert';
import semver from 'semver';
import plugin from '../../../src/index';
import { LegacyESLint, ESLint } from '../../utils/eslint-compat';

describe('`all` config', () => {
it('legacy `all` config should work. ', async () => {
const code = `<script>const a = 1, b = 2;</script>{@html a+b}`;

const linter = new LegacyESLint({
plugins: {
svelte: plugin as never
},
baseConfig: {
parserOptions: {
ecmaVersion: 2020
},
extends: ['plugin:svelte/recommended']
},
useEslintrc: false
});
const result = await linter.lintText(code, { filePath: 'test.svelte' });
const messages = result[0].messages;

assert.deepStrictEqual(
messages.map((m) => ({ ruleId: m.ruleId, line: m.line, message: m.message })),
[
{
ruleId: 'svelte/no-at-html-tags',
message: '`{@html}` can lead to XSS attack.',
line: 1
}
]
);
});
it('`all` config should work. ', async () => {
if (semver.satisfies(ESLint.version, '<8.0.0')) return;
const code = `<script>const a = 1, b = 2;</script>{@html a+b}`;

const linter = new ESLint({
overrideConfigFile: true as never,
overrideConfig: plugin.configs['flat/recommended'] as never
});
const result = await linter.lintText(code, { filePath: 'test.svelte' });
const messages = result[0].messages;

assert.deepStrictEqual(
messages.map((m) => ({ ruleId: m.ruleId, line: m.line, message: m.message })),
[
{
ruleId: 'svelte/no-at-html-tags',
message: '`{@html}` can lead to XSS attack.',
line: 1
}
]
);
});
});
Loading
Loading