Skip to content

Commit 7d106b0

Browse files
authored
feat: add should-be-fine support for flat configs (#181)
* feat: add should-be-fine support for flat configs * refactor: use `Object.fromEntries` instead of `reduce`
1 parent 62e2c79 commit 7d106b0

File tree

5 files changed

+126
-39
lines changed

5 files changed

+126
-39
lines changed

.eslint-doc-generatorrc.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ const { prettier: prettierRC } = require('./package.json');
33

44
/** @type {import('eslint-doc-generator').GenerateOptions} */
55
const config = {
6-
ignoreConfig: ['all'],
6+
ignoreConfig: [
7+
'all',
8+
'flat/all',
9+
'flat/recommended',
10+
'flat/style',
11+
'flat/snapshots',
12+
],
713
ruleDocTitleFormat: 'desc-parens-name',
814
ruleDocSectionInclude: ['Rule details'],
915
ruleListColumns: [

README.md

+25
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ $ yarn add --dev eslint eslint-plugin-jest-extended
2222

2323
## Usage
2424

25+
> [!NOTE]
26+
>
27+
> `eslint.config.js` is supported, though most of the plugin documentation still
28+
> currently uses `.eslintrc` syntax.
29+
>
30+
> Refer to the
31+
> [ESLint documentation on the new configuration file format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
32+
> for more.
33+
2534
Add `jest-extended` to the plugins section of your `.eslintrc` configuration
2635
file. You can omit the `eslint-plugin-` prefix:
2736

@@ -61,6 +70,22 @@ If you want to enable all rules instead of only some you can do so by adding the
6170
}
6271
```
6372

73+
To enable this configuration with `eslint.config.js`, use
74+
`jestExtended.configs['flat/all']`:
75+
76+
```js
77+
const jestExtended = require('eslint-plugin-jest-extended');
78+
79+
module.exports = [
80+
{
81+
files: [
82+
/* glob matching your test files */
83+
],
84+
...jestExtended.configs['flat/all'],
85+
},
86+
];
87+
```
88+
6489
Note that the `all` configuration may change in any release and is thus unsuited
6590
for installations requiring long-term consistency.
6691

src/__tests__/__snapshots__/rules.test.ts.snap

+28
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,34 @@ exports[`rules should export configs that refer to actual rules 1`] = `
1414
"jest-extended/prefer-to-have-been-called-once": "error",
1515
},
1616
},
17+
"flat/all": {
18+
"plugins": {
19+
"jest-extended": ObjectContaining {
20+
"meta": {
21+
"name": "eslint-plugin-jest-extended",
22+
"version": Any<String>,
23+
},
24+
},
25+
},
26+
"rules": {
27+
"jest-extended/prefer-to-be-array": "error",
28+
"jest-extended/prefer-to-be-false": "error",
29+
"jest-extended/prefer-to-be-object": "error",
30+
"jest-extended/prefer-to-be-true": "error",
31+
"jest-extended/prefer-to-have-been-called-once": "error",
32+
},
33+
},
34+
"flat/recommended": {
35+
"plugins": {
36+
"jest-extended": ObjectContaining {
37+
"meta": {
38+
"name": "eslint-plugin-jest-extended",
39+
"version": Any<String>,
40+
},
41+
},
42+
},
43+
"rules": {},
44+
},
1745
"recommended": {
1846
"plugins": [
1947
"jest-extended",

src/__tests__/rules.test.ts

+25-3
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,37 @@ describe('rules', () => {
2929
});
3030

3131
it('should export configs that refer to actual rules', () => {
32+
const expectJestExtendedPlugin = expect.objectContaining({
33+
meta: {
34+
name: 'eslint-plugin-jest-extended',
35+
version: expect.any(String),
36+
},
37+
});
38+
3239
const recommendedConfigs = plugin.configs;
3340

34-
expect(recommendedConfigs).toMatchSnapshot();
35-
expect(Object.keys(recommendedConfigs)).toEqual(['all', 'recommended']);
41+
expect(recommendedConfigs).toMatchSnapshot({
42+
'flat/recommended': {
43+
plugins: { 'jest-extended': expectJestExtendedPlugin },
44+
},
45+
'flat/all': {
46+
plugins: { 'jest-extended': expectJestExtendedPlugin },
47+
},
48+
});
49+
expect(Object.keys(recommendedConfigs)).toEqual([
50+
'all',
51+
'recommended',
52+
'flat/all',
53+
'flat/recommended',
54+
]);
3655
expect(Object.keys(recommendedConfigs.all.rules)).toHaveLength(
3756
ruleNames.length,
3857
);
58+
expect(Object.keys(recommendedConfigs['flat/all'].rules)).toHaveLength(
59+
ruleNames.length,
60+
);
3961
const allConfigRules = Object.values(recommendedConfigs)
40-
.map(config => Object.keys(config.rules))
62+
.map(config => Object.keys(config.rules ?? {}))
4163
.reduce((previousValue, currentValue) => [
4264
...previousValue,
4365
...currentValue,

src/index.ts

+41-35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { readdirSync } from 'fs';
22
import { join, parse } from 'path';
33
import { TSESLint, TSESTree } from '@typescript-eslint/utils';
4+
import {
5+
name as packageName,
6+
version as packageVersion,
7+
} from '../package.json';
48

59
type RuleModule = TSESLint.RuleModule<string, unknown[]> & {
610
meta: Required<Pick<TSESLint.RuleMetaData<string>, 'docs'>>;
@@ -36,47 +40,49 @@ const importDefault = (moduleName: string) =>
3640
const rulesDir = join(__dirname, 'rules');
3741
const excludedFiles = ['__tests__', 'utils'];
3842

39-
const rules = readdirSync(rulesDir)
40-
.map(rule => parse(rule).name)
41-
.filter(rule => !excludedFiles.includes(rule))
42-
.reduce<Record<string, RuleModule>>(
43-
(acc, curr) => ({
44-
...acc,
45-
[curr]: importDefault(join(rulesDir, curr)) as RuleModule,
46-
}),
47-
{},
48-
);
43+
const rules = Object.fromEntries(
44+
readdirSync(rulesDir)
45+
.map(rule => parse(rule).name)
46+
.filter(rule => !excludedFiles.includes(rule))
47+
.map(rule => [rule, importDefault(join(rulesDir, rule)) as RuleModule]),
48+
);
4949

50-
const recommendedRules = Object.entries(rules)
51-
.filter(([, rule]) => rule.meta.docs.recommended)
52-
.reduce(
53-
/* istanbul ignore next */
54-
(acc, [name, rule]) => ({
55-
...acc,
56-
[`jest-extended/${name}`]: rule.meta.docs.recommended,
57-
}),
58-
{},
59-
);
50+
const recommendedRules = {} satisfies Record<string, TSESLint.Linter.RuleLevel>;
6051

61-
const allRules = Object.entries(rules)
62-
.filter(([, rule]) => !rule.meta.deprecated)
63-
.reduce(
64-
(acc, [name]) => ({
65-
...acc,
66-
[`jest-extended/${name}`]: 'error',
67-
}),
68-
{},
69-
);
52+
const allRules = Object.fromEntries<TSESLint.Linter.RuleLevel>(
53+
Object.entries(rules)
54+
.filter(([, rule]) => !rule.meta.deprecated)
55+
.map(([name]) => [`jest-extended/${name}`, 'error']),
56+
);
7057

71-
const createConfig = (rules: Record<string, TSESLint.Linter.RuleLevel>) => ({
58+
const plugin = {
59+
meta: { name: packageName, version: packageVersion },
60+
// ugly cast for now to keep TypeScript happy since
61+
// we don't have types for flat config yet
62+
configs: {} as Record<
63+
'all' | 'recommended' | 'flat/all' | 'flat/recommended',
64+
Pick<Required<TSESLint.Linter.Config>, 'rules'>
65+
>,
66+
rules,
67+
};
68+
69+
const createRCConfig = (rules: Record<string, TSESLint.Linter.RuleLevel>) => ({
7270
plugins: ['jest-extended'],
7371
rules,
7472
});
7573

76-
export = {
77-
configs: {
78-
all: createConfig(allRules),
79-
recommended: createConfig(recommendedRules),
80-
},
74+
const createFlatConfig = (
75+
rules: Record<string, TSESLint.Linter.RuleLevel>,
76+
) => ({
77+
plugins: { 'jest-extended': plugin },
8178
rules,
79+
});
80+
81+
plugin.configs = {
82+
all: createRCConfig(allRules),
83+
recommended: createRCConfig(recommendedRules),
84+
'flat/all': createFlatConfig(allRules),
85+
'flat/recommended': createFlatConfig(recommendedRules),
8286
};
87+
88+
export = plugin;

0 commit comments

Comments
 (0)