Skip to content

Commit

Permalink
feat: add eslint flat config support (#132)
Browse files Browse the repository at this point in the history
This adds support for ESLint's new flat config feature.

There are now 3 exported configs:

- `recommended` (legacy/eslintrc)
- `best-practice` (legacy/eslintrc)
- `flat/recommended` (flat)
- `flat/best-practice` (flat)

In future, we will drop the legacy ones and replace them with the flat
configs in a breaking change.
  • Loading branch information
43081j authored Apr 8, 2024
1 parent 8deeb40 commit 1b50de0
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 28 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ Then extend the recommended eslint config:
}
```

Or if you're using ESLint flat configs, add this to your `eslint.config.js`:

```ts
import {configs} from 'eslint-plugin-wc';

export default [
configs.recommended,

// or if you want to specify `files`, or other options
{
...configs.recommended,
files: ['test/**/*.js']
}
];
```

You should also specify settings that will be shared across all the plugin rules. ([More about eslint shared settings](https://eslint.org/docs/user-guide/configuring/configuration-files#adding-shared-settings))

```jsonc
Expand Down
37 changes: 20 additions & 17 deletions src/configs/best-practice.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
const config = {
extends: ['plugin:wc/recommended'],
plugins: ['wc'],
import type {ESLint, Linter} from 'eslint';
import {configFactory as recommendedFactory} from './recommended.js';

rules: {
'wc/attach-shadow-constructor': 'error',
'wc/guard-super-call': 'error',
'wc/no-child-traversal-in-attributechangedcallback': 'error',
'wc/no-child-traversal-in-connectedcallback': 'error',
'wc/no-closed-shadow-root': 'error',
'wc/no-constructor-params': 'error',
'wc/no-customized-built-in-elements': 'error',
'wc/no-invalid-extends': 'error',
'wc/no-typos': 'error',
'wc/require-listener-teardown': 'error'
}
export const configFactory = (plugin: ESLint.Plugin): Linter.FlatConfig => {
const base = recommendedFactory(plugin);
return {
...base,
rules: {
...base.rules,
'wc/attach-shadow-constructor': 'error',
'wc/guard-super-call': 'error',
'wc/no-child-traversal-in-attributechangedcallback': 'error',
'wc/no-child-traversal-in-connectedcallback': 'error',
'wc/no-closed-shadow-root': 'error',
'wc/no-constructor-params': 'error',
'wc/no-customized-built-in-elements': 'error',
'wc/no-invalid-extends': 'error',
'wc/no-typos': 'error',
'wc/require-listener-teardown': 'error'
}
};
};

export default config;
19 changes: 19 additions & 0 deletions src/configs/legacy-best-practice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type {ESLint} from 'eslint';

export const config: ESLint.ConfigData = {
extends: ['plugin:wc/recommended'],
plugins: ['wc'],

rules: {
'wc/attach-shadow-constructor': 'error',
'wc/guard-super-call': 'error',
'wc/no-child-traversal-in-attributechangedcallback': 'error',
'wc/no-child-traversal-in-connectedcallback': 'error',
'wc/no-closed-shadow-root': 'error',
'wc/no-constructor-params': 'error',
'wc/no-customized-built-in-elements': 'error',
'wc/no-invalid-extends': 'error',
'wc/no-typos': 'error',
'wc/require-listener-teardown': 'error'
}
};
11 changes: 11 additions & 0 deletions src/configs/legacy-recommended.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type {ESLint} from 'eslint';

export const config: ESLint.ConfigData = {
plugins: ['wc'],

rules: {
'wc/no-constructor-attributes': 'error',
'wc/no-invalid-element-name': 'error',
'wc/no-self-class': 'error'
}
};
13 changes: 6 additions & 7 deletions src/configs/recommended.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
const config = {
plugins: ['wc'],
parserOptions: {
sourceType: 'module'
import type {ESLint, Linter} from 'eslint';

export const configFactory = (plugin: ESLint.Plugin): Linter.FlatConfig => ({
plugins: {
wc: plugin
},

rules: {
'wc/no-constructor-attributes': 'error',
'wc/no-invalid-element-name': 'error',
'wc/no-self-class': 'error'
}
};

export default config;
});
15 changes: 11 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {ESLint} from 'eslint';
import attachShadowConstructor from './rules/attach-shadow-constructor';
import bestPractice from './configs/best-practice';
import defineTagAfterClassDef from './rules/define-tag-after-class-definition';
import exposeClassOnGlobal from './rules/expose-class-on-global';
import filenameMatches from './rules/file-name-matches-element';
Expand All @@ -19,7 +19,10 @@ import noInvalidExtends from './rules/no-invalid-extends';
import noOnPrefix from './rules/no-method-prefixed-with-on';
import noSelfClass from './rules/no-self-class';
import noTypos from './rules/no-typos';
import recommended from './configs/recommended';
import {configFactory as configRecommended} from './configs/recommended';
import {configFactory as configBestPractice} from './configs/best-practice';
import {config as configLegacyRecommended} from './configs/legacy-recommended';
import {config as configLegacyBestPractice} from './configs/legacy-best-practice';
import requireListenerTeardown from './rules/require-listener-teardown';
import tagMatchesClass from './rules/tag-name-matches-class';

Expand Down Expand Up @@ -48,7 +51,11 @@ export const rules = {
'tag-name-matches-class': tagMatchesClass
};

const plugin: ESLint.Plugin = {rules};

export const configs = {
recommended,
'best-practice': bestPractice
recommended: configLegacyRecommended,
'best-practice': configLegacyBestPractice,
'flat/recommended': configRecommended(plugin),
'flat/best-practice': configBestPractice(plugin)
};
22 changes: 22 additions & 0 deletions src/test/configs_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type {ESLint, Linter} from 'eslint';
import {expect} from 'chai';
import {configs} from '../index';

type ConfigLike = Linter.FlatConfig | ESLint.ConfigData;

const isFlatConfig = (config: ConfigLike): config is Linter.FlatConfig =>
!Array.isArray(config.plugins);

describe('configs', () => {
it('should define configs correctly', () => {
expect(configs['recommended']).to.be.ok;
expect(configs['best-practice']).to.be.ok;
expect(configs['flat/recommended']).to.be.ok;
expect(configs['flat/best-practice']).to.be.ok;

expect(isFlatConfig(configs['flat/recommended'])).to.equal(true);
expect(isFlatConfig(configs['flat/best-practice'])).to.equal(true);
expect(isFlatConfig(configs['recommended'])).to.equal(false);
expect(isFlatConfig(configs['best-practice'])).to.equal(false);
});
});

0 comments on commit 1b50de0

Please sign in to comment.