@vijayhardaha/dev-config 1.1.4 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -14
- package/package.json +8 -12
- package/src/eslint/index.js +1 -4
- package/src/eslint/lib/build-config.js +144 -51
- package/src/eslint/lib/build-config.test.js +26 -4
- package/src/eslint/lib/index.js +0 -1
- package/src/eslint/lib/index.test.js +0 -9
- package/src/eslint/lib/rules.js +71 -67
- package/src/eslint/lib/rules.test.js +22 -32
- package/src/eslint/lib/setup.js +0 -21
- package/src/eslint/lib/setup.test.js +0 -9
- package/src/eslint/next.js +15 -8
- package/src/eslint/react.js +9 -7
- package/src/eslint/typescript.js +3 -4
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
Reusable development configuration package for Next.js + TypeScript projects.
|
|
8
8
|
|
|
9
|
+
> **v2.0.0** — Requires ESLint >=10. Native flat config only. No FlatCompat.
|
|
10
|
+
|
|
9
11
|
## Features
|
|
10
12
|
|
|
11
13
|
- **ESLint** - Modular flat config with support for JavaScript, TypeScript, React, and Next.js
|
|
@@ -25,23 +27,11 @@ bun install @vijayhardaha/dev-config --dev
|
|
|
25
27
|
### Install Required Packages
|
|
26
28
|
|
|
27
29
|
```bash
|
|
28
|
-
bun add --dev eslint
|
|
30
|
+
bun add --dev eslint @eslint/compat prettier @prettier/plugin-xml eslint-plugin-prettier globals eslint-plugin-jsdoc eslint-plugin-import-x typescript typescript-eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser
|
|
29
31
|
```
|
|
30
32
|
|
|
31
33
|
### Install Optional Packages
|
|
32
34
|
|
|
33
|
-
### Typescript
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
bun add --dev typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Prettier
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
bun add --dev prettier @prettier/plugin-xml
|
|
43
|
-
```
|
|
44
|
-
|
|
45
35
|
#### Stylelint
|
|
46
36
|
|
|
47
37
|
```bash
|
|
@@ -57,7 +47,7 @@ bun add --dev eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a1
|
|
|
57
47
|
#### Next.js
|
|
58
48
|
|
|
59
49
|
```bash
|
|
60
|
-
bun add --dev @next/eslint-plugin-next
|
|
50
|
+
bun add --dev @next/eslint-plugin-next eslint-config-next
|
|
61
51
|
```
|
|
62
52
|
|
|
63
53
|
#### Commitlint
|
|
@@ -72,6 +62,25 @@ bun add --dev husky @commitlint/cli @commitlint/config-conventional @commitlint/
|
|
|
72
62
|
bun add --dev next-sitemap
|
|
73
63
|
```
|
|
74
64
|
|
|
65
|
+
## Migrating from v1
|
|
66
|
+
|
|
67
|
+
v2 drops FlatCompat and uses native ESLint 10 flat configs throughout.
|
|
68
|
+
|
|
69
|
+
### What changed
|
|
70
|
+
|
|
71
|
+
- **ESLint 10 required** — no longer compatible with ESLint 8/9
|
|
72
|
+
- **No FlatCompat** — all configs import flat config arrays/objects directly
|
|
73
|
+
- **`eslint-plugin-import` → `eslint-plugin-import-x`** — the ESLint 10-compatible fork
|
|
74
|
+
- **`eslint-config-prettier` removed** — bundled in `eslint-plugin-prettier/recommended`
|
|
75
|
+
- **`@eslint/js` removed** — no longer needed without FlatCompat
|
|
76
|
+
- **`plugins` option now accepts flat config arrays/objects** — no string-based plugin names
|
|
77
|
+
|
|
78
|
+
### Required updates
|
|
79
|
+
|
|
80
|
+
1. Install ESLint 10+: `bun add --dev eslint@10`
|
|
81
|
+
2. Replace `eslint-plugin-import` with `eslint-plugin-import-x`
|
|
82
|
+
3. Remove unused deps: `@eslint/compat`, `@eslint/eslintrc`, `@eslint/js`, `eslint-config-prettier`, `eslint-import-resolver-typescript`
|
|
83
|
+
|
|
75
84
|
## Quick Start
|
|
76
85
|
|
|
77
86
|
### ESLint
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vijayhardaha/dev-config",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Reusable development configurations for Next.js + TypeScript projects",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"lint": "eslint .",
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
"typescript"
|
|
59
59
|
],
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@commitlint/cli": "^21.0.
|
|
62
|
-
"@commitlint/config-conventional": "^21.0.
|
|
61
|
+
"@commitlint/cli": "^21.0.2",
|
|
62
|
+
"@commitlint/config-conventional": "^21.0.2",
|
|
63
63
|
"@commitlint/types": "^21.0.1",
|
|
64
|
-
"@prettier/plugin-xml": "^3.4.2",
|
|
65
64
|
"@vitest/coverage-v8": "^4.1.7",
|
|
65
|
+
"eslint-config-next": "^16.2.6",
|
|
66
66
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
67
67
|
"eslint-plugin-react": "^7.37.5",
|
|
68
68
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
@@ -72,26 +72,22 @@
|
|
|
72
72
|
"stylelint-config-property-sort-order-smacss": "^11.2.0",
|
|
73
73
|
"stylelint-config-standard-scss": "^17.0.0",
|
|
74
74
|
"stylelint-order": "^8.1.1",
|
|
75
|
-
"typescript": "^6.0.3",
|
|
76
75
|
"vitest": "^4.1.7"
|
|
77
76
|
},
|
|
78
77
|
"peerDependencies": {
|
|
79
78
|
"@eslint/compat": ">=2",
|
|
80
|
-
"@eslint/eslintrc": ">=3",
|
|
81
|
-
"@eslint/js": ">=9",
|
|
82
79
|
"@prettier/plugin-xml": ">=3",
|
|
83
80
|
"@typescript-eslint/eslint-plugin": ">=8",
|
|
84
81
|
"@typescript-eslint/parser": ">=8",
|
|
85
|
-
"eslint
|
|
86
|
-
"eslint-import-
|
|
87
|
-
"eslint-plugin-import": ">=2",
|
|
82
|
+
"eslint": ">=10",
|
|
83
|
+
"eslint-plugin-import-x": ">=4",
|
|
88
84
|
"eslint-plugin-jsdoc": ">=62",
|
|
89
85
|
"eslint-plugin-prettier": ">=5",
|
|
90
|
-
"eslint": ">=9",
|
|
91
86
|
"globals": ">=17",
|
|
92
87
|
"husky": ">=9",
|
|
93
88
|
"prettier": "^3.8.3",
|
|
94
|
-
"typescript": ">=6"
|
|
89
|
+
"typescript": ">=6",
|
|
90
|
+
"typescript-eslint": ">=8"
|
|
95
91
|
},
|
|
96
92
|
"peerDependenciesMeta": {
|
|
97
93
|
"@commitlint/cli": {
|
package/src/eslint/index.js
CHANGED
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
* =====================================================================
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
const { compat } = setup();
|
|
12
|
+
import { buildConfig, files } from './lib/index.js';
|
|
15
13
|
|
|
16
14
|
/**
|
|
17
15
|
* Creates an ESLint configuration object with optional features.
|
|
@@ -35,7 +33,6 @@ export const createConfig = (options = {}) => {
|
|
|
35
33
|
const { prettier = true, importOrder = true, jsdoc = true } = options;
|
|
36
34
|
|
|
37
35
|
return buildConfig({
|
|
38
|
-
compat,
|
|
39
36
|
files: [...files.withoutTs, ...(options.files || [])],
|
|
40
37
|
options: { ...options, prettier, importOrder, jsdoc },
|
|
41
38
|
});
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { fixupPluginRules } from '@eslint/compat';
|
|
1
2
|
import { defineConfig } from 'eslint/config';
|
|
3
|
+
import importX from 'eslint-plugin-import-x';
|
|
4
|
+
import jsdocPlugin from 'eslint-plugin-jsdoc';
|
|
5
|
+
import prettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
6
|
+
import tsEslint from 'typescript-eslint';
|
|
2
7
|
|
|
3
8
|
import { globalIgnores } from './ignores.js';
|
|
4
9
|
import { commonLanguageOptions } from './language-options.js';
|
|
@@ -6,13 +11,127 @@ import { commonRules } from './rules.js';
|
|
|
6
11
|
import { commonParser } from './setup.js';
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
|
-
*
|
|
14
|
+
* Filters and flattens conditional plugins based on user options.
|
|
15
|
+
*
|
|
16
|
+
* @param {object} conditionalPlugins - Plugin map keyed by option name.
|
|
17
|
+
* @param {object} options - User-provided options.
|
|
18
|
+
*
|
|
19
|
+
* @returns {object[]} Flat config objects for enabled plugins.
|
|
20
|
+
*/
|
|
21
|
+
const getEnabledPlugins = (conditionalPlugins, options) =>
|
|
22
|
+
Object.entries(conditionalPlugins)
|
|
23
|
+
.filter(([key]) => options[key])
|
|
24
|
+
.flatMap(([, value]) => (Array.isArray(value) ? value : [value]));
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Flattens a mixed list of config arrays and objects into a flat config array.
|
|
28
|
+
*
|
|
29
|
+
* @param {(Array|object)[]} plugins - Mixed list of flat config arrays and objects.
|
|
30
|
+
*
|
|
31
|
+
* @returns {object[]} Flattened config array.
|
|
32
|
+
*/
|
|
33
|
+
const flattenPlugins = (plugins) => {
|
|
34
|
+
const result = [];
|
|
35
|
+
|
|
36
|
+
for (const plugin of plugins) {
|
|
37
|
+
if (Array.isArray(plugin)) {
|
|
38
|
+
result.push(...plugin);
|
|
39
|
+
} else if (typeof plugin === 'object' && plugin !== null) {
|
|
40
|
+
result.push(plugin);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Wraps plugin rules with fixupPluginRules for ESLint 10 backward compatibility.
|
|
49
|
+
*
|
|
50
|
+
* @param {object[]} flatConfigs - Flat config array.
|
|
51
|
+
*
|
|
52
|
+
* @returns {object[]} Config array with wrapped plugin rules.
|
|
53
|
+
*/
|
|
54
|
+
const fixPlugins = (flatConfigs) =>
|
|
55
|
+
flatConfigs.map((config) => {
|
|
56
|
+
if (!config.plugins) return config;
|
|
57
|
+
|
|
58
|
+
const fixed = { ...config, plugins: {} };
|
|
59
|
+
|
|
60
|
+
for (const [name, plugin] of Object.entries(config.plugins)) {
|
|
61
|
+
fixed.plugins[name] = fixupPluginRules(plugin);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return fixed;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Merges user-provided global ignores with the common defaults.
|
|
69
|
+
*
|
|
70
|
+
* @param {string[]|undefined} userGlobalIgnores - User-provided global ignore patterns.
|
|
71
|
+
*
|
|
72
|
+
* @returns {object} ESLint ignores config object.
|
|
73
|
+
*/
|
|
74
|
+
const mergeGlobalIgnores = (userGlobalIgnores) =>
|
|
75
|
+
Array.isArray(userGlobalIgnores) ? globalIgnores(userGlobalIgnores) : globalIgnores();
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Builds the main ESLint config object with language options, settings, and rules.
|
|
79
|
+
*
|
|
80
|
+
* @param {object} ctx - Context object with all config parameters.
|
|
81
|
+
* @param {string[]} ctx.filePatterns - File patterns to apply the config to.
|
|
82
|
+
* @param {object} ctx.opts - Resolved user options.
|
|
83
|
+
* @param {boolean} ctx.typescript - Enable TypeScript support.
|
|
84
|
+
* @param {object} ctx.extraLanguageOptions - Additional language options.
|
|
85
|
+
* @param {object} ctx.parserOptions - Parser options.
|
|
86
|
+
* @param {object} ctx.extraSettings - Additional settings.
|
|
87
|
+
* @param {object} ctx.extraRules - Additional rules.
|
|
88
|
+
*
|
|
89
|
+
* @returns {import('eslint').Linter.Config} ESLint config object.
|
|
90
|
+
*/
|
|
91
|
+
const buildConfigObject = ({
|
|
92
|
+
filePatterns,
|
|
93
|
+
opts,
|
|
94
|
+
typescript,
|
|
95
|
+
extraLanguageOptions,
|
|
96
|
+
parserOptions,
|
|
97
|
+
extraSettings,
|
|
98
|
+
extraRules,
|
|
99
|
+
}) => {
|
|
100
|
+
const { ignores, rules, settings, languageOptions, extend } = opts;
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
files: [...filePatterns],
|
|
104
|
+
...(ignores && { ignores }),
|
|
105
|
+
...(typescript && { plugins: { '@typescript-eslint': tsEslint.plugin } }),
|
|
106
|
+
languageOptions: {
|
|
107
|
+
...commonLanguageOptions,
|
|
108
|
+
...(typescript && commonParser),
|
|
109
|
+
...extraLanguageOptions,
|
|
110
|
+
...languageOptions,
|
|
111
|
+
...(typescript && { parserOptions: { tsconfigRootDir: process.cwd(), ...parserOptions } }),
|
|
112
|
+
},
|
|
113
|
+
settings: {
|
|
114
|
+
...(opts.importOrder && { 'import-x/resolver': { typescript: {} } }),
|
|
115
|
+
...(opts.jsdoc && { jsdoc: { mode: 'typescript' } }),
|
|
116
|
+
...extraSettings,
|
|
117
|
+
...settings,
|
|
118
|
+
},
|
|
119
|
+
rules: {
|
|
120
|
+
...commonRules({ prettier: opts.prettier, importOrder: opts.importOrder, typescript, jsdoc: opts.jsdoc }),
|
|
121
|
+
...extraRules,
|
|
122
|
+
...rules,
|
|
123
|
+
},
|
|
124
|
+
...extend,
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Builds a flat ESLint configuration with support for various options.
|
|
10
130
|
*
|
|
11
131
|
* @param {object} config - Configuration options.
|
|
12
|
-
* @param {object} config.compat - FlatCompat instance.
|
|
13
132
|
* @param {string[]} config.files - File patterns to apply the config to.
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {object} config.conditionalPlugins - Conditional plugins based on options
|
|
133
|
+
* @param {(Array|object)[]} config.builtinPlugins - Flat config arrays or objects to always include.
|
|
134
|
+
* @param {object} config.conditionalPlugins - Conditional plugins based on options.
|
|
16
135
|
* @param {object} [config.languageOptions] - Additional language options.
|
|
17
136
|
* @param {object} [config.parserOptions] - Parser options.
|
|
18
137
|
* @param {object} [config.settings] - Settings object.
|
|
@@ -23,7 +142,6 @@ import { commonParser } from './setup.js';
|
|
|
23
142
|
* @returns {import('eslint').Linter.Config[]} ESLint configuration array.
|
|
24
143
|
*/
|
|
25
144
|
export const buildConfig = ({
|
|
26
|
-
compat,
|
|
27
145
|
files: filePatterns,
|
|
28
146
|
builtinPlugins = [],
|
|
29
147
|
conditionalPlugins = {},
|
|
@@ -34,57 +152,32 @@ export const buildConfig = ({
|
|
|
34
152
|
options = {},
|
|
35
153
|
typescript = false,
|
|
36
154
|
}) => {
|
|
37
|
-
const {
|
|
38
|
-
prettier = true,
|
|
39
|
-
importOrder = true,
|
|
40
|
-
jsdoc = true,
|
|
41
|
-
ignores,
|
|
42
|
-
rules,
|
|
43
|
-
settings,
|
|
44
|
-
languageOptions,
|
|
45
|
-
plugins: userPlugins,
|
|
46
|
-
globalIgnores: userGlobalIgnores,
|
|
47
|
-
extend,
|
|
48
|
-
} = options;
|
|
49
|
-
|
|
50
|
-
// ---- Build extends configs ----
|
|
51
|
-
const conditionalPluginList = Object.entries(conditionalPlugins)
|
|
52
|
-
.filter(([key]) => options[key])
|
|
53
|
-
.flatMap(([, value]) => (Array.isArray(value) ? value : [value]));
|
|
155
|
+
const opts = { prettier: true, importOrder: true, jsdoc: true, ...options };
|
|
54
156
|
|
|
55
|
-
const
|
|
157
|
+
const conditionalPluginList = getEnabledPlugins(conditionalPlugins, opts);
|
|
158
|
+
|
|
159
|
+
const mergedPlugins = [
|
|
56
160
|
...builtinPlugins,
|
|
57
|
-
importOrder &&
|
|
58
|
-
jsdoc && '
|
|
59
|
-
prettier &&
|
|
161
|
+
opts.importOrder && importX.flatConfigs.recommended,
|
|
162
|
+
opts.jsdoc && jsdocPlugin.configs['flat/recommended'],
|
|
163
|
+
opts.prettier && prettierRecommended,
|
|
60
164
|
...conditionalPluginList,
|
|
165
|
+
...(opts.plugins || []),
|
|
61
166
|
].filter(Boolean);
|
|
62
167
|
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const configObject = {
|
|
67
|
-
files: [...filePatterns],
|
|
68
|
-
...(ignores && { ignores }),
|
|
69
|
-
languageOptions: {
|
|
70
|
-
...commonLanguageOptions,
|
|
71
|
-
...(typescript && commonParser),
|
|
72
|
-
...extraLanguageOptions,
|
|
73
|
-
...languageOptions,
|
|
74
|
-
...(typescript && { parserOptions: { tsconfigRootDir: process.cwd(), ...parserOptions } }),
|
|
75
|
-
},
|
|
76
|
-
settings: {
|
|
77
|
-
...(importOrder && { 'import/resolver': { typescript: {} } }),
|
|
78
|
-
...(jsdoc && { jsdoc: { mode: 'typescript' } }),
|
|
79
|
-
...extraSettings,
|
|
80
|
-
...settings,
|
|
81
|
-
},
|
|
82
|
-
rules: { ...commonRules({ prettier, importOrder, typescript, jsdoc }), ...extraRules, ...rules },
|
|
83
|
-
...extend,
|
|
84
|
-
};
|
|
168
|
+
const flatConfigs = flattenPlugins(mergedPlugins);
|
|
169
|
+
const fixedConfigs = fixPlugins(flatConfigs);
|
|
170
|
+
const mergedGlobalIgnores = mergeGlobalIgnores(opts.globalIgnores);
|
|
85
171
|
|
|
86
|
-
|
|
87
|
-
|
|
172
|
+
const configObject = buildConfigObject({
|
|
173
|
+
filePatterns,
|
|
174
|
+
opts,
|
|
175
|
+
typescript,
|
|
176
|
+
extraLanguageOptions,
|
|
177
|
+
parserOptions,
|
|
178
|
+
extraSettings,
|
|
179
|
+
extraRules,
|
|
180
|
+
});
|
|
88
181
|
|
|
89
|
-
return defineConfig([...mergedGlobalIgnores, ...
|
|
182
|
+
return defineConfig([...mergedGlobalIgnores, ...fixedConfigs, configObject]);
|
|
90
183
|
};
|
|
@@ -17,14 +17,36 @@ describe('eslint/lib/build-config.js', () => {
|
|
|
17
17
|
const module = await import('./build-config.js');
|
|
18
18
|
|
|
19
19
|
// Import dependencies needed for the test.
|
|
20
|
-
const { setup } = await import('./setup.js');
|
|
21
20
|
const { files } = await import('./files.js');
|
|
22
|
-
const { compat } = setup();
|
|
23
21
|
|
|
24
|
-
// Call buildConfig with test parameters (
|
|
25
|
-
const result = module.buildConfig({
|
|
22
|
+
// Call buildConfig with test parameters (JS-only files, empty options).
|
|
23
|
+
const result = module.buildConfig({ files: files.withoutTs, options: {} });
|
|
26
24
|
|
|
27
25
|
// Verify that the result is an array (ESLint expects configs as an array).
|
|
28
26
|
expect(Array.isArray(result)).toBe(true);
|
|
29
27
|
});
|
|
28
|
+
|
|
29
|
+
// Test that buildConfig handles flat config arrays and objects.
|
|
30
|
+
it('should handle flat config arrays and objects', async () => {
|
|
31
|
+
const module = await import('./build-config.js');
|
|
32
|
+
const { files } = await import('./files.js');
|
|
33
|
+
|
|
34
|
+
// A flat config array (simulating eslint-config-next export)
|
|
35
|
+
const flatConfigArray = [{ name: 'test-flat-config', plugins: {}, rules: { 'no-console': 'warn' } }];
|
|
36
|
+
|
|
37
|
+
// A flat config object
|
|
38
|
+
const flatConfigObject = { name: 'test-flat-object', plugins: {}, rules: { 'no-debugger': 'warn' } };
|
|
39
|
+
|
|
40
|
+
const result = module.buildConfig({
|
|
41
|
+
files: files.withTs,
|
|
42
|
+
builtinPlugins: [flatConfigArray, flatConfigObject],
|
|
43
|
+
options: {},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(Array.isArray(result)).toBe(true);
|
|
47
|
+
// Should contain configs from the flat config array and object
|
|
48
|
+
const configNames = result.filter((c) => c.name).map((c) => c.name);
|
|
49
|
+
expect(configNames).toContain('test-flat-config');
|
|
50
|
+
expect(configNames).toContain('test-flat-object');
|
|
51
|
+
});
|
|
30
52
|
});
|
package/src/eslint/lib/index.js
CHANGED
|
@@ -2,15 +2,6 @@ import { describe, it, expect } from 'vitest';
|
|
|
2
2
|
|
|
3
3
|
// Test suite for the ESLint lib module's main entry point.
|
|
4
4
|
describe('eslint/lib/index.js', () => {
|
|
5
|
-
// Test that the module exports the setup function.
|
|
6
|
-
it('should export setup function', async () => {
|
|
7
|
-
// Dynamically import the index module to test its exports.
|
|
8
|
-
const module = await import('./index.js');
|
|
9
|
-
|
|
10
|
-
// Verify that setup is a function.
|
|
11
|
-
expect(typeof module.setup).toBe('function');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
5
|
// Test that the module exports a files object with TypeScript and non-TypeScript file arrays.
|
|
15
6
|
it('should export files object', async () => {
|
|
16
7
|
// Dynamically import the index module to test its exports.
|
package/src/eslint/lib/rules.js
CHANGED
|
@@ -1,3 +1,72 @@
|
|
|
1
|
+
// ---- JSDoc Rules: enforce documentation on public/exported APIs ----
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* JSDoc rules that enforce documentation presence on public/exported APIs.
|
|
5
|
+
* Covers `@param`, `@returns`, `@throws`, `@description`, and `@property-description`.
|
|
6
|
+
*
|
|
7
|
+
* @type {object}
|
|
8
|
+
*/
|
|
9
|
+
const JSDOC_REQUIRE_RULES = {
|
|
10
|
+
'jsdoc/require-jsdoc': [
|
|
11
|
+
'error',
|
|
12
|
+
{
|
|
13
|
+
publicOnly: true,
|
|
14
|
+
require: {
|
|
15
|
+
FunctionDeclaration: true,
|
|
16
|
+
MethodDefinition: true,
|
|
17
|
+
ClassDeclaration: true,
|
|
18
|
+
ArrowFunctionExpression: true,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
'jsdoc/require-description': 'error',
|
|
23
|
+
'jsdoc/require-param': 'error',
|
|
24
|
+
'jsdoc/require-param-name': 'error',
|
|
25
|
+
'jsdoc/require-param-description': 'error',
|
|
26
|
+
'jsdoc/require-param-type': 'error',
|
|
27
|
+
'jsdoc/require-returns': 'error',
|
|
28
|
+
'jsdoc/require-returns-description': 'error',
|
|
29
|
+
'jsdoc/require-returns-type': 'error',
|
|
30
|
+
'jsdoc/require-throws': 'error',
|
|
31
|
+
'jsdoc/require-property-description': 'warn',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* JSDoc rules that validate tag names, types, and undefined type references.
|
|
36
|
+
*
|
|
37
|
+
* @type {object}
|
|
38
|
+
*/
|
|
39
|
+
const JSDOC_CORRECTNESS_RULES = {
|
|
40
|
+
'jsdoc/check-tag-names': 'error',
|
|
41
|
+
'jsdoc/no-undefined-types': ['error', { definedTypes: ['JSX.Element'] }],
|
|
42
|
+
'jsdoc/valid-types': 'error',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* JSDoc rules that enforce consistent formatting and tag ordering.
|
|
47
|
+
*
|
|
48
|
+
* @type {object}
|
|
49
|
+
*/
|
|
50
|
+
const JSDOC_STYLE_RULES = {
|
|
51
|
+
'jsdoc/tag-lines': ['error', 'any', { startLines: 1, endLines: 0, applyToEndTag: true }],
|
|
52
|
+
'jsdoc/check-alignment': 'error',
|
|
53
|
+
'jsdoc/check-indentation': 'off',
|
|
54
|
+
'jsdoc/sort-tags': [
|
|
55
|
+
'warn',
|
|
56
|
+
{
|
|
57
|
+
tagSequence: [
|
|
58
|
+
{ tags: ['description'] },
|
|
59
|
+
{ tags: ['template'] },
|
|
60
|
+
{ tags: ['param'] },
|
|
61
|
+
{ tags: ['returns'] },
|
|
62
|
+
{ tags: ['example'] },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
'jsdoc/no-types': 'off',
|
|
67
|
+
'jsdoc/informative-docs': 'off',
|
|
68
|
+
};
|
|
69
|
+
|
|
1
70
|
/**
|
|
2
71
|
* Creates JSDoc rules for enforcing documentation on public/exported APIs.
|
|
3
72
|
*
|
|
@@ -6,72 +75,7 @@
|
|
|
6
75
|
* @returns {object} JSDoc ESLint rules object.
|
|
7
76
|
*/
|
|
8
77
|
const jsdocRules = (jsdoc = true) =>
|
|
9
|
-
jsdoc
|
|
10
|
-
? {
|
|
11
|
-
// ---- JSDoc Rules for PUBLIC / EXPORTED APIs ----
|
|
12
|
-
'jsdoc/require-jsdoc': [
|
|
13
|
-
'error',
|
|
14
|
-
{
|
|
15
|
-
publicOnly: true,
|
|
16
|
-
require: {
|
|
17
|
-
FunctionDeclaration: true,
|
|
18
|
-
MethodDefinition: true,
|
|
19
|
-
ClassDeclaration: true,
|
|
20
|
-
ArrowFunctionExpression: true,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
|
|
25
|
-
// Descriptions must exist and be meaningful
|
|
26
|
-
'jsdoc/require-description': 'error',
|
|
27
|
-
|
|
28
|
-
// Params must be fully documented
|
|
29
|
-
'jsdoc/require-param': 'error',
|
|
30
|
-
'jsdoc/require-param-name': 'error',
|
|
31
|
-
'jsdoc/require-param-description': 'error',
|
|
32
|
-
'jsdoc/require-param-type': 'error',
|
|
33
|
-
|
|
34
|
-
// Returns must be documented
|
|
35
|
-
'jsdoc/require-returns': 'error',
|
|
36
|
-
'jsdoc/require-returns-description': 'error',
|
|
37
|
-
'jsdoc/require-returns-type': 'error',
|
|
38
|
-
|
|
39
|
-
// Throws must be documented
|
|
40
|
-
'jsdoc/require-throws': 'error',
|
|
41
|
-
|
|
42
|
-
// Strict correctness
|
|
43
|
-
'jsdoc/check-tag-names': 'error',
|
|
44
|
-
'jsdoc/no-undefined-types': ['error', { definedTypes: ['JSX.Element'] }],
|
|
45
|
-
'jsdoc/valid-types': 'error',
|
|
46
|
-
|
|
47
|
-
// Enforce clean structure
|
|
48
|
-
'jsdoc/tag-lines': ['error', 'any', { startLines: 1, endLines: 0, applyToEndTag: true }],
|
|
49
|
-
|
|
50
|
-
'jsdoc/check-alignment': 'error',
|
|
51
|
-
'jsdoc/check-indentation': 'off',
|
|
52
|
-
|
|
53
|
-
// Optional but powerful for large teams
|
|
54
|
-
'jsdoc/sort-tags': [
|
|
55
|
-
'warn',
|
|
56
|
-
{
|
|
57
|
-
tagSequence: [
|
|
58
|
-
{ tags: ['description'] },
|
|
59
|
-
{ tags: ['template'] },
|
|
60
|
-
{ tags: ['param'] },
|
|
61
|
-
{ tags: ['returns'] },
|
|
62
|
-
{ tags: ['example'] },
|
|
63
|
-
],
|
|
64
|
-
},
|
|
65
|
-
],
|
|
66
|
-
|
|
67
|
-
// Avoid useless docs
|
|
68
|
-
'jsdoc/no-types': 'off',
|
|
69
|
-
'jsdoc/informative-docs': 'off',
|
|
70
|
-
|
|
71
|
-
// Enforce property docs in typedef-style (optional)
|
|
72
|
-
'jsdoc/require-property-description': 'warn',
|
|
73
|
-
}
|
|
74
|
-
: {};
|
|
78
|
+
jsdoc ? { ...JSDOC_REQUIRE_RULES, ...JSDOC_CORRECTNESS_RULES, ...JSDOC_STYLE_RULES } : {};
|
|
75
79
|
|
|
76
80
|
/**
|
|
77
81
|
* Creates TypeScript-specific rules.
|
|
@@ -116,7 +120,7 @@ const prettierRules = (prettier = true) => (prettier ? { 'prettier/prettier': 'w
|
|
|
116
120
|
const importOrderRules = (importOrder = true) =>
|
|
117
121
|
importOrder
|
|
118
122
|
? {
|
|
119
|
-
'import/order': [
|
|
123
|
+
'import-x/order': [
|
|
120
124
|
'error',
|
|
121
125
|
{
|
|
122
126
|
groups: ['builtin', 'external', 'internal', ['parent', 'sibling'], 'index', 'object'],
|
|
@@ -1,71 +1,61 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
1
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
2
2
|
|
|
3
3
|
// Test suite for the ESLint rules configuration module.
|
|
4
4
|
describe('eslint/lib/rules.js', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
let commonRules;
|
|
6
|
+
|
|
7
|
+
// Module-level import before tests to reduce duplication.
|
|
8
|
+
beforeAll(async () => {
|
|
8
9
|
const module = await import('./rules.js');
|
|
10
|
+
commonRules = module.commonRules;
|
|
11
|
+
});
|
|
9
12
|
|
|
13
|
+
// Test that the module exports the commonRules function.
|
|
14
|
+
it('should export commonRules function', () => {
|
|
10
15
|
// Verify that commonRules is a function.
|
|
11
|
-
expect(typeof
|
|
16
|
+
expect(typeof commonRules).toBe('function');
|
|
12
17
|
});
|
|
13
18
|
|
|
14
19
|
// Test that commonRules returns an object containing ESLint rules.
|
|
15
|
-
it('should return an object with rules',
|
|
16
|
-
// Dynamically import the rules module to test its function.
|
|
17
|
-
const module = await import('./rules.js');
|
|
18
|
-
|
|
20
|
+
it('should return an object with rules', () => {
|
|
19
21
|
// Call commonRules with no options to get base rules.
|
|
20
|
-
const result =
|
|
22
|
+
const result = commonRules();
|
|
21
23
|
|
|
22
24
|
// Verify that the result is an object (ESLint rules config).
|
|
23
25
|
expect(typeof result).toBe('object');
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
// Test that TypeScript rules are included when typescript option is true.
|
|
27
|
-
it('should include TypeScript rules when typescript is true',
|
|
28
|
-
// Dynamically import the rules module to test its function.
|
|
29
|
-
const module = await import('./rules.js');
|
|
30
|
-
|
|
29
|
+
it('should include TypeScript rules when typescript is true', () => {
|
|
31
30
|
// Call commonRules with typescript option enabled.
|
|
32
|
-
const result =
|
|
31
|
+
const result = commonRules({ typescript: true });
|
|
33
32
|
|
|
34
33
|
// Verify that a TypeScript-specific rule is present in the config.
|
|
35
34
|
expect(result['@typescript-eslint/no-unused-vars']).toBeDefined();
|
|
36
35
|
});
|
|
37
36
|
|
|
38
37
|
// Test that import order rules are included when importOrder option is true.
|
|
39
|
-
it('should include import order rules when importOrder is true',
|
|
40
|
-
// Dynamically import the rules module to test its function.
|
|
41
|
-
const module = await import('./rules.js');
|
|
42
|
-
|
|
38
|
+
it('should include import order rules when importOrder is true', () => {
|
|
43
39
|
// Call commonRules with importOrder option enabled.
|
|
44
|
-
const result =
|
|
40
|
+
const result = commonRules({ importOrder: true });
|
|
45
41
|
|
|
46
|
-
// Verify that the import/order rule is present in the config.
|
|
47
|
-
expect(result['import/order']).toBeDefined();
|
|
42
|
+
// Verify that the import-x/order rule is present in the config.
|
|
43
|
+
expect(result['import-x/order']).toBeDefined();
|
|
48
44
|
});
|
|
49
45
|
|
|
50
46
|
// Test that Prettier rules are included when prettier option is true.
|
|
51
|
-
it('should include Prettier rules when prettier is true',
|
|
52
|
-
// Dynamically import the rules module to test its function.
|
|
53
|
-
const module = await import('./rules.js');
|
|
54
|
-
|
|
47
|
+
it('should include Prettier rules when prettier is true', () => {
|
|
55
48
|
// Call commonRules with prettier option enabled.
|
|
56
|
-
const result =
|
|
49
|
+
const result = commonRules({ prettier: true });
|
|
57
50
|
|
|
58
51
|
// Verify that the prettier/prettier rule is present in the config.
|
|
59
52
|
expect(result['prettier/prettier']).toBeDefined();
|
|
60
53
|
});
|
|
61
54
|
|
|
62
55
|
// Test that JSDoc rules are included when jsdoc option is true.
|
|
63
|
-
it('should include JSDoc rules when jsdoc is true',
|
|
64
|
-
// Dynamically import the rules module to test its function.
|
|
65
|
-
const module = await import('./rules.js');
|
|
66
|
-
|
|
56
|
+
it('should include JSDoc rules when jsdoc is true', () => {
|
|
67
57
|
// Call commonRules with jsdoc option enabled.
|
|
68
|
-
const result =
|
|
58
|
+
const result = commonRules({ jsdoc: true });
|
|
69
59
|
|
|
70
60
|
// Verify that the jsdoc/require-jsdoc rule is present in the config.
|
|
71
61
|
expect(result['jsdoc/require-jsdoc']).toBeDefined();
|
package/src/eslint/lib/setup.js
CHANGED
|
@@ -1,26 +1,5 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
|
|
4
|
-
import { FlatCompat } from '@eslint/eslintrc';
|
|
5
|
-
import js from '@eslint/js';
|
|
6
1
|
import tsParser from '@typescript-eslint/parser';
|
|
7
2
|
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Creates a FlatCompat instance for using flat config format with older
|
|
13
|
-
* ESLint configurations.
|
|
14
|
-
*/
|
|
15
|
-
const compat = new FlatCompat({ baseDirectory: __dirname, recommendedConfig: js.configs.recommended });
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Returns the setup object containing shared utilities for ESLint configs.
|
|
19
|
-
*
|
|
20
|
-
* @returns {{ compat: FlatCompat, tsParser: typeof tsParser, __dirname: string }} Reusable setup utilities for ESLint configurations.
|
|
21
|
-
*/
|
|
22
|
-
export const setup = () => ({ compat, tsParser, __dirname });
|
|
23
|
-
|
|
24
3
|
/**
|
|
25
4
|
* Common parser configuration for TypeScript files.
|
|
26
5
|
*
|
|
@@ -2,15 +2,6 @@ import { describe, it, expect } from 'vitest';
|
|
|
2
2
|
|
|
3
3
|
// Test suite for the ESLint setup utilities module.
|
|
4
4
|
describe('eslint/lib/setup.js', () => {
|
|
5
|
-
// Test that the module exports the setup function.
|
|
6
|
-
it('should export setup function', async () => {
|
|
7
|
-
// Dynamically import the setup module to test its exports.
|
|
8
|
-
const module = await import('./setup.js');
|
|
9
|
-
|
|
10
|
-
// Verify that setup is a function.
|
|
11
|
-
expect(typeof module.setup).toBe('function');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
5
|
// Test that the module exports the commonParser object with a parser property.
|
|
15
6
|
it('should export commonParser object', async () => {
|
|
16
7
|
// Dynamically import the setup module to test its exports.
|
package/src/eslint/next.js
CHANGED
|
@@ -9,9 +9,21 @@
|
|
|
9
9
|
* =====================================================================
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import
|
|
12
|
+
import nextCoreWebVitals from 'eslint-config-next/core-web-vitals';
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
import { buildConfig, files } from './lib/index.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Removes the next/typescript item from the core-web-vitals config array.
|
|
18
|
+
* The \@typescript-eslint plugin is registered on the main config object
|
|
19
|
+
* instead (via build-config.js when typescript: true), which avoids plugin
|
|
20
|
+
* redefinition errors when TypeScript rules are applied in the shared config.
|
|
21
|
+
*
|
|
22
|
+
* @param {import('eslint').Linter.Config[]} configs - Config array to process.
|
|
23
|
+
*
|
|
24
|
+
* @returns {import('eslint').Linter.Config[]} Config array with next/typescript removed.
|
|
25
|
+
*/
|
|
26
|
+
const prepareNextConfig = (configs) => configs.filter((c) => c.name !== 'next/typescript');
|
|
15
27
|
|
|
16
28
|
/**
|
|
17
29
|
* Creates an ESLint configuration object for Next.js projects with TypeScript
|
|
@@ -38,13 +50,8 @@ export const createConfig = (options = {}) => {
|
|
|
38
50
|
const { prettier = true, react = true, a11y = true, importOrder = true, jsdoc = true } = options;
|
|
39
51
|
|
|
40
52
|
return buildConfig({
|
|
41
|
-
compat,
|
|
42
53
|
files: files.withTs,
|
|
43
|
-
builtinPlugins: [
|
|
44
|
-
conditionalPlugins: {
|
|
45
|
-
react: ['plugin:react/recommended', 'plugin:react-hooks/recommended'],
|
|
46
|
-
a11y: 'plugin:jsx-a11y/recommended',
|
|
47
|
-
},
|
|
54
|
+
builtinPlugins: [...prepareNextConfig(nextCoreWebVitals)],
|
|
48
55
|
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
49
56
|
settings: { react: { version: 'detect' } },
|
|
50
57
|
rules: {
|
package/src/eslint/react.js
CHANGED
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
* =====================================================================
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import
|
|
12
|
+
import jsxA11y from 'eslint-plugin-jsx-a11y';
|
|
13
|
+
import reactRecommended from 'eslint-plugin-react/configs/recommended.js';
|
|
14
|
+
import reactHooks from 'eslint-plugin-react-hooks';
|
|
15
|
+
import tsEslint from 'typescript-eslint';
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
import { buildConfig, files } from './lib/index.js';
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
20
|
* Creates an ESLint configuration object for React projects with TypeScript
|
|
@@ -37,14 +40,13 @@ export const createConfig = (options = {}) => {
|
|
|
37
40
|
const { prettier = true, a11y = true, importOrder = true, jsdoc = true } = options;
|
|
38
41
|
|
|
39
42
|
return buildConfig({
|
|
40
|
-
compat,
|
|
41
43
|
files: files.withTs,
|
|
42
44
|
builtinPlugins: [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
{ ...reactRecommended, files: ['**/*.{jsx,tsx}'] },
|
|
46
|
+
{ ...reactHooks.configs.flat.recommended, files: ['**/*.{jsx,tsx}'] },
|
|
47
|
+
...tsEslint.configs.recommended,
|
|
46
48
|
],
|
|
47
|
-
conditionalPlugins: { a11y: '
|
|
49
|
+
conditionalPlugins: { a11y: { ...jsxA11y.flatConfigs.recommended, files: ['**/*.{jsx,tsx}'] } },
|
|
48
50
|
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
49
51
|
settings: { react: { version: 'detect' } },
|
|
50
52
|
rules: { 'react/react-in-jsx-scope': 'off', 'react/no-unknown-property': ['error', { ignore: ['jsx', 'global'] }] },
|
package/src/eslint/typescript.js
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* =====================================================================
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import
|
|
12
|
+
import tsEslint from 'typescript-eslint';
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
import { buildConfig, files } from './lib/index.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Creates an ESLint configuration object for TypeScript projects.
|
|
@@ -35,9 +35,8 @@ export const createConfig = (options = {}) => {
|
|
|
35
35
|
const { prettier = true, importOrder = true, jsdoc = true } = options;
|
|
36
36
|
|
|
37
37
|
return buildConfig({
|
|
38
|
-
compat,
|
|
39
38
|
files: files.withTs,
|
|
40
|
-
builtinPlugins: [
|
|
39
|
+
builtinPlugins: [...tsEslint.configs.recommended],
|
|
41
40
|
typescript: true,
|
|
42
41
|
options: { ...options, prettier, importOrder, jsdoc },
|
|
43
42
|
});
|