Skip to content

Commit

Permalink
feat: add should-be-fine support for flat configs (#181)
Browse files Browse the repository at this point in the history
* feat: add should-be-fine support for flat configs

* refactor: use `Object.fromEntries` instead of `reduce`
  • Loading branch information
G-Rath authored Apr 20, 2024
1 parent 62e2c79 commit 7d106b0
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 39 deletions.
8 changes: 7 additions & 1 deletion .eslint-doc-generatorrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ const { prettier: prettierRC } = require('./package.json');

/** @type {import('eslint-doc-generator').GenerateOptions} */
const config = {
ignoreConfig: ['all'],
ignoreConfig: [
'all',
'flat/all',
'flat/recommended',
'flat/style',
'flat/snapshots',
],
ruleDocTitleFormat: 'desc-parens-name',
ruleDocSectionInclude: ['Rule details'],
ruleListColumns: [
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ $ yarn add --dev eslint eslint-plugin-jest-extended

## Usage

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

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

To enable this configuration with `eslint.config.js`, use
`jestExtended.configs['flat/all']`:

```js
const jestExtended = require('eslint-plugin-jest-extended');

module.exports = [
{
files: [
/* glob matching your test files */
],
...jestExtended.configs['flat/all'],
},
];
```

Note that the `all` configuration may change in any release and is thus unsuited
for installations requiring long-term consistency.

Expand Down
28 changes: 28 additions & 0 deletions src/__tests__/__snapshots__/rules.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,34 @@ exports[`rules should export configs that refer to actual rules 1`] = `
"jest-extended/prefer-to-have-been-called-once": "error",
},
},
"flat/all": {
"plugins": {
"jest-extended": ObjectContaining {
"meta": {
"name": "eslint-plugin-jest-extended",
"version": Any<String>,
},
},
},
"rules": {
"jest-extended/prefer-to-be-array": "error",
"jest-extended/prefer-to-be-false": "error",
"jest-extended/prefer-to-be-object": "error",
"jest-extended/prefer-to-be-true": "error",
"jest-extended/prefer-to-have-been-called-once": "error",
},
},
"flat/recommended": {
"plugins": {
"jest-extended": ObjectContaining {
"meta": {
"name": "eslint-plugin-jest-extended",
"version": Any<String>,
},
},
},
"rules": {},
},
"recommended": {
"plugins": [
"jest-extended",
Expand Down
28 changes: 25 additions & 3 deletions src/__tests__/rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,37 @@ describe('rules', () => {
});

it('should export configs that refer to actual rules', () => {
const expectJestExtendedPlugin = expect.objectContaining({
meta: {
name: 'eslint-plugin-jest-extended',
version: expect.any(String),
},
});

const recommendedConfigs = plugin.configs;

expect(recommendedConfigs).toMatchSnapshot();
expect(Object.keys(recommendedConfigs)).toEqual(['all', 'recommended']);
expect(recommendedConfigs).toMatchSnapshot({
'flat/recommended': {
plugins: { 'jest-extended': expectJestExtendedPlugin },
},
'flat/all': {
plugins: { 'jest-extended': expectJestExtendedPlugin },
},
});
expect(Object.keys(recommendedConfigs)).toEqual([
'all',
'recommended',
'flat/all',
'flat/recommended',
]);
expect(Object.keys(recommendedConfigs.all.rules)).toHaveLength(
ruleNames.length,
);
expect(Object.keys(recommendedConfigs['flat/all'].rules)).toHaveLength(
ruleNames.length,
);
const allConfigRules = Object.values(recommendedConfigs)
.map(config => Object.keys(config.rules))
.map(config => Object.keys(config.rules ?? {}))
.reduce((previousValue, currentValue) => [
...previousValue,
...currentValue,
Expand Down
76 changes: 41 additions & 35 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { readdirSync } from 'fs';
import { join, parse } from 'path';
import { TSESLint, TSESTree } from '@typescript-eslint/utils';
import {
name as packageName,
version as packageVersion,
} from '../package.json';

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

const rules = readdirSync(rulesDir)
.map(rule => parse(rule).name)
.filter(rule => !excludedFiles.includes(rule))
.reduce<Record<string, RuleModule>>(
(acc, curr) => ({
...acc,
[curr]: importDefault(join(rulesDir, curr)) as RuleModule,
}),
{},
);
const rules = Object.fromEntries(
readdirSync(rulesDir)
.map(rule => parse(rule).name)
.filter(rule => !excludedFiles.includes(rule))
.map(rule => [rule, importDefault(join(rulesDir, rule)) as RuleModule]),
);

const recommendedRules = Object.entries(rules)
.filter(([, rule]) => rule.meta.docs.recommended)
.reduce(
/* istanbul ignore next */
(acc, [name, rule]) => ({
...acc,
[`jest-extended/${name}`]: rule.meta.docs.recommended,
}),
{},
);
const recommendedRules = {} satisfies Record<string, TSESLint.Linter.RuleLevel>;

const allRules = Object.entries(rules)
.filter(([, rule]) => !rule.meta.deprecated)
.reduce(
(acc, [name]) => ({
...acc,
[`jest-extended/${name}`]: 'error',
}),
{},
);
const allRules = Object.fromEntries<TSESLint.Linter.RuleLevel>(
Object.entries(rules)
.filter(([, rule]) => !rule.meta.deprecated)
.map(([name]) => [`jest-extended/${name}`, 'error']),
);

const createConfig = (rules: Record<string, TSESLint.Linter.RuleLevel>) => ({
const plugin = {
meta: { name: packageName, version: packageVersion },
// ugly cast for now to keep TypeScript happy since
// we don't have types for flat config yet
configs: {} as Record<
'all' | 'recommended' | 'flat/all' | 'flat/recommended',
Pick<Required<TSESLint.Linter.Config>, 'rules'>
>,
rules,
};

const createRCConfig = (rules: Record<string, TSESLint.Linter.RuleLevel>) => ({
plugins: ['jest-extended'],
rules,
});

export = {
configs: {
all: createConfig(allRules),
recommended: createConfig(recommendedRules),
},
const createFlatConfig = (
rules: Record<string, TSESLint.Linter.RuleLevel>,
) => ({
plugins: { 'jest-extended': plugin },
rules,
});

plugin.configs = {
all: createRCConfig(allRules),
recommended: createRCConfig(recommendedRules),
'flat/all': createFlatConfig(allRules),
'flat/recommended': createFlatConfig(recommendedRules),
};

export = plugin;

0 comments on commit 7d106b0

Please sign in to comment.