@vijayhardaha/dev-config 2.2.0 → 2.3.0
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 +39 -0
- package/package.json +17 -20
- package/src/__tests__/config-constants.test.js +111 -0
- package/src/{index.test.js → __tests__/index.test.js} +1 -1
- package/src/commitlint/{index.test.js → __tests__/index.test.js} +5 -5
- package/src/commitlint/index.js +3 -1
- package/src/config-constants.js +172 -0
- package/src/eslint/{index.test.js → __tests__/index.test.js} +3 -3
- package/src/eslint/{next.test.js → __tests__/next.test.js} +3 -3
- package/src/eslint/{react.test.js → __tests__/react.test.js} +3 -3
- package/src/eslint/{typescript.test.js → __tests__/typescript.test.js} +3 -3
- package/src/eslint/lib/{build-config.test.js → __tests__/build-config.test.js} +11 -11
- package/src/eslint/lib/{files.test.js → __tests__/files.test.js} +2 -2
- package/src/eslint/lib/{ignores.test.js → __tests__/ignores.test.js} +3 -3
- package/src/eslint/lib/{index.test.js → __tests__/index.test.js} +2 -2
- package/src/eslint/lib/{language-options.test.js → __tests__/language-options.test.js} +2 -2
- package/src/eslint/lib/{rules.test.js → __tests__/rules.test.js} +1 -1
- package/src/eslint/lib/{setup.test.js → __tests__/setup.test.js} +1 -1
- package/src/eslint/lib/build-config.js +11 -110
- package/src/eslint/lib/files.js +3 -1
- package/src/eslint/lib/plugin-helpers/__tests__/enabled-plugins.test.js +49 -0
- package/src/eslint/lib/plugin-helpers/__tests__/fixup.test.js +48 -0
- package/src/eslint/lib/plugin-helpers/__tests__/flatten.test.js +51 -0
- package/src/eslint/lib/plugin-helpers/__tests__/parser.test.js +78 -0
- package/src/eslint/lib/plugin-helpers/__tests__/strip.test.js +70 -0
- package/src/eslint/lib/plugin-helpers/enabled-plugins.js +26 -0
- package/src/eslint/lib/plugin-helpers/fixup.js +34 -0
- package/src/eslint/lib/plugin-helpers/flatten.js +31 -0
- package/src/eslint/lib/plugin-helpers/index.js +18 -0
- package/src/eslint/lib/plugin-helpers/parser.js +33 -0
- package/src/eslint/lib/plugin-helpers/strip.js +45 -0
- package/src/gulp-smacss/{index.test.js → __tests__/index.test.js} +2 -2
- package/src/gulp-smacss/index.js +5 -0
- package/src/jsconfig/{index.test.js → __tests__/index.test.js} +3 -3
- package/src/lib/__tests__/config-utils.test.js +145 -0
- package/src/lib/__tests__/validators.test.js +229 -0
- package/src/lib/config-utils.js +187 -0
- package/src/lib/validators.js +225 -0
- package/src/next-sitemap/{index.test.js → __tests__/index.test.js} +31 -5
- package/src/next-sitemap/index.js +22 -8
- package/src/prettier/{index.test.js → __tests__/index.test.js} +4 -4
- package/src/prettier/index.js +11 -47
- package/src/stylelint/{index.test.js → __tests__/index.test.js} +5 -5
- package/src/stylelint/index.js +4 -2
- package/src/tsconfig/{index.test.js → __tests__/index.test.js} +5 -5
|
@@ -5,7 +5,7 @@ describe('eslint/lib/ignores.js', () => {
|
|
|
5
5
|
// Test that the module exports the globalIgnores function.
|
|
6
6
|
it('should export globalIgnores function', async () => {
|
|
7
7
|
// Dynamically import the ignores module to test its exports.
|
|
8
|
-
const module = await import('
|
|
8
|
+
const module = await import('../ignores.js');
|
|
9
9
|
|
|
10
10
|
// Verify that globalIgnores is a function.
|
|
11
11
|
expect(typeof module.globalIgnores).toBe('function');
|
|
@@ -14,7 +14,7 @@ describe('eslint/lib/ignores.js', () => {
|
|
|
14
14
|
// Test that globalIgnores returns an array (ESLint's expected format for ignores).
|
|
15
15
|
it('should return an array of config objects', async () => {
|
|
16
16
|
// Dynamically import the ignores module to test its function.
|
|
17
|
-
const module = await import('
|
|
17
|
+
const module = await import('../ignores.js');
|
|
18
18
|
|
|
19
19
|
// Call globalIgnores with no parameters to get default ignores.
|
|
20
20
|
const result = module.globalIgnores();
|
|
@@ -26,7 +26,7 @@ describe('eslint/lib/ignores.js', () => {
|
|
|
26
26
|
// Test that globalIgnores properly merges user-provided ignores with defaults.
|
|
27
27
|
it('should merge user ignores with default ignores', async () => {
|
|
28
28
|
// Dynamically import the ignores module to test its function.
|
|
29
|
-
const module = await import('
|
|
29
|
+
const module = await import('../ignores.js');
|
|
30
30
|
|
|
31
31
|
// Call globalIgnores with a custom ignore pattern.
|
|
32
32
|
const result = module.globalIgnores(['**/custom-ignore/']);
|
|
@@ -5,7 +5,7 @@ describe('eslint/lib/index.js', () => {
|
|
|
5
5
|
// Test that the module exports a files object with TypeScript and non-TypeScript file arrays.
|
|
6
6
|
it('should export files object', async () => {
|
|
7
7
|
// Dynamically import the index module to test its exports.
|
|
8
|
-
const module = await import('
|
|
8
|
+
const module = await import('../index.js');
|
|
9
9
|
|
|
10
10
|
// Verify that files is an object.
|
|
11
11
|
expect(typeof module.files).toBe('object');
|
|
@@ -20,7 +20,7 @@ describe('eslint/lib/index.js', () => {
|
|
|
20
20
|
// Test that the module exports the buildConfig function.
|
|
21
21
|
it('should export buildConfig function', async () => {
|
|
22
22
|
// Dynamically import the index module to test its exports.
|
|
23
|
-
const module = await import('
|
|
23
|
+
const module = await import('../index.js');
|
|
24
24
|
|
|
25
25
|
// Verify that buildConfig is a function.
|
|
26
26
|
expect(typeof module.buildConfig).toBe('function');
|
|
@@ -5,7 +5,7 @@ describe('eslint/lib/language-options.js', () => {
|
|
|
5
5
|
// Test that the module exports the commonLanguageOptions object.
|
|
6
6
|
it('should export commonLanguageOptions object', async () => {
|
|
7
7
|
// Dynamically import the language-options module to test its exports.
|
|
8
|
-
const module = await import('
|
|
8
|
+
const module = await import('../language-options.js');
|
|
9
9
|
|
|
10
10
|
// Verify that commonLanguageOptions is an object.
|
|
11
11
|
expect(typeof module.commonLanguageOptions).toBe('object');
|
|
@@ -14,7 +14,7 @@ describe('eslint/lib/language-options.js', () => {
|
|
|
14
14
|
// Test that the exported language options have the correct properties.
|
|
15
15
|
it('should have correct language options properties', async () => {
|
|
16
16
|
// Dynamically import the language-options module to test its exports.
|
|
17
|
-
const module = await import('
|
|
17
|
+
const module = await import('../language-options.js');
|
|
18
18
|
|
|
19
19
|
// Verify that ecmaVersion is set to 'latest' (ESLint's latest ECMAScript version).
|
|
20
20
|
expect(module.commonLanguageOptions.ecmaVersion).toBe('latest');
|
|
@@ -6,7 +6,7 @@ describe('eslint/lib/rules.js', () => {
|
|
|
6
6
|
|
|
7
7
|
// Module-level import before tests to reduce duplication.
|
|
8
8
|
beforeAll(async () => {
|
|
9
|
-
const module = await import('
|
|
9
|
+
const module = await import('../rules.js');
|
|
10
10
|
commonRules = module.commonRules;
|
|
11
11
|
});
|
|
12
12
|
|
|
@@ -5,7 +5,7 @@ describe('eslint/lib/setup.js', () => {
|
|
|
5
5
|
// Test that the module exports the commonParser object with a parser property.
|
|
6
6
|
it('should export commonParser object', async () => {
|
|
7
7
|
// Dynamically import the setup module to test its exports.
|
|
8
|
-
const module = await import('
|
|
8
|
+
const module = await import('../setup.js');
|
|
9
9
|
|
|
10
10
|
// Verify that commonParser is an object.
|
|
11
11
|
expect(typeof module.commonParser).toBe('object');
|
|
@@ -9,6 +9,7 @@ import prettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
|
9
9
|
|
|
10
10
|
import { globalIgnores } from './ignores.js';
|
|
11
11
|
import { commonLanguageOptions } from './language-options.js';
|
|
12
|
+
import { getEnabledPlugins, flattenPlugins, fixupPlugins, stripPlugins, stripParser } from './plugin-helpers/index.js';
|
|
12
13
|
import { commonRules } from './rules.js';
|
|
13
14
|
import { commonParser } from './setup.js';
|
|
14
15
|
|
|
@@ -17,122 +18,22 @@ try {
|
|
|
17
18
|
createTypeScriptImportResolver = (await import('eslint-import-resolver-typescript')).createTypeScriptImportResolver;
|
|
18
19
|
} catch {
|
|
19
20
|
createTypeScriptImportResolver = null;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
* @returns {object[]} Flat config objects for enabled plugins.
|
|
29
|
-
*/
|
|
30
|
-
const getEnabledPlugins = (conditionalPlugins, options) =>
|
|
31
|
-
Object.entries(conditionalPlugins)
|
|
32
|
-
.filter(([key]) => options[key])
|
|
33
|
-
.flatMap(([, value]) => (Array.isArray(value) ? value : [value]));
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Flattens a mixed list of config arrays and objects.
|
|
37
|
-
*
|
|
38
|
-
* @param {(Array|object)[]} plugins - Mixed list of flat config arrays and objects.
|
|
39
|
-
*
|
|
40
|
-
* @returns {object[]} Flattened config array.
|
|
41
|
-
*/
|
|
42
|
-
const flattenPlugins = (plugins) => {
|
|
43
|
-
const result = [];
|
|
44
|
-
|
|
45
|
-
for (const plugin of plugins) {
|
|
46
|
-
if (Array.isArray(plugin)) {
|
|
47
|
-
result.push(...plugin);
|
|
48
|
-
} else if (typeof plugin === 'object' && plugin !== null) {
|
|
49
|
-
result.push(plugin);
|
|
50
|
-
}
|
|
21
|
+
// Debug logging for optional dependency failure
|
|
22
|
+
if (process.env.DEBUG?.includes('eslint') || process.env.DEBUG === '*') {
|
|
23
|
+
console.debug(
|
|
24
|
+
'[ESLint Config] Optional dependency "eslint-import-resolver-typescript" not found. '
|
|
25
|
+
+ 'TypeScript import resolution will be disabled. '
|
|
26
|
+
+ 'Install it with: npm install --save-dev eslint-import-resolver-typescript'
|
|
27
|
+
);
|
|
51
28
|
}
|
|
52
|
-
|
|
53
|
-
return result;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Wraps plugin rules with fixupPluginRules for backward compatibility.
|
|
58
|
-
*
|
|
59
|
-
* @param {object[]} flatConfigs - Flat config array.
|
|
60
|
-
*
|
|
61
|
-
* @returns {object[]} Config array with wrapped plugin rules.
|
|
62
|
-
*/
|
|
63
|
-
const fixPlugins = (flatConfigs) =>
|
|
64
|
-
flatConfigs.map((config) => {
|
|
65
|
-
if (!config.plugins) return config;
|
|
66
|
-
|
|
67
|
-
const fixed = { ...config, plugins: {} };
|
|
68
|
-
|
|
69
|
-
for (const [name, plugin] of Object.entries(config.plugins)) {
|
|
70
|
-
fixed.plugins[name] = fixupPluginRules(plugin);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return fixed;
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Removes centrally-registered plugins from individual configs to prevent
|
|
78
|
-
* "Cannot redefine plugin" errors.
|
|
79
|
-
*
|
|
80
|
-
* @param {object[]} flatConfigs - Flat config array.
|
|
81
|
-
* @param {string[]} pluginNames - Plugin names to strip from configs.
|
|
82
|
-
*
|
|
83
|
-
* @returns {object[]} Config array with specified plugins removed.
|
|
84
|
-
*/
|
|
85
|
-
const stripPlugins = (flatConfigs, pluginNames) => {
|
|
86
|
-
if (pluginNames.length === 0) return flatConfigs;
|
|
87
|
-
|
|
88
|
-
const skip = new Set(pluginNames);
|
|
89
|
-
|
|
90
|
-
return flatConfigs.map((config) => {
|
|
91
|
-
if (!config.plugins) return config;
|
|
92
|
-
|
|
93
|
-
const plugins = { ...config.plugins };
|
|
94
|
-
|
|
95
|
-
for (const name of skip) {
|
|
96
|
-
delete plugins[name];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (Object.keys(plugins).length === 0) {
|
|
100
|
-
const rest = { ...config };
|
|
101
|
-
delete rest.plugins;
|
|
102
|
-
|
|
103
|
-
return rest;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return { ...config, plugins };
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Removes the parser from individual configs when the main config provides one.
|
|
112
|
-
* Prevents parser conflicts (e.g., eslint-config-next/parser vs `@typescript-eslint/parser`).
|
|
113
|
-
*
|
|
114
|
-
* @param {object[]} flatConfigs - Flat config array.
|
|
115
|
-
*
|
|
116
|
-
* @returns {object[]} Config array with parsers removed.
|
|
117
|
-
*/
|
|
118
|
-
const stripParser = (flatConfigs) =>
|
|
119
|
-
flatConfigs.map((config) => {
|
|
120
|
-
if (!config.languageOptions?.parser) return config;
|
|
121
|
-
|
|
122
|
-
const languageOptions = { ...config.languageOptions };
|
|
123
|
-
delete languageOptions.parser;
|
|
124
|
-
|
|
125
|
-
return Object.keys(languageOptions).length > 0
|
|
126
|
-
? { ...config, languageOptions }
|
|
127
|
-
: { ...config, languageOptions: undefined };
|
|
128
|
-
});
|
|
29
|
+
}
|
|
129
30
|
|
|
130
31
|
/**
|
|
131
32
|
* Merges user-provided global ignores with common defaults.
|
|
132
33
|
*
|
|
133
34
|
* @param {string[]|undefined} userGlobalIgnores - User-provided ignore patterns.
|
|
134
35
|
*
|
|
135
|
-
* @returns {
|
|
36
|
+
* @returns {import('eslint').Linter.Config[]} ESLint global ignores configuration.
|
|
136
37
|
*/
|
|
137
38
|
const mergeGlobalIgnores = (userGlobalIgnores) =>
|
|
138
39
|
Array.isArray(userGlobalIgnores) ? globalIgnores(userGlobalIgnores) : globalIgnores();
|
|
@@ -240,7 +141,7 @@ export const buildConfig = ({
|
|
|
240
141
|
].filter(Boolean);
|
|
241
142
|
|
|
242
143
|
const flatConfigs = flattenPlugins(mergedPlugins);
|
|
243
|
-
const wrappedConfigs =
|
|
144
|
+
const wrappedConfigs = fixupPlugins(flatConfigs);
|
|
244
145
|
const strippedConfigs = stripPlugins(wrappedConfigs, Object.keys(centralPlugins));
|
|
245
146
|
const parsedConfigs = typescript ? stripParser(strippedConfigs) : strippedConfigs;
|
|
246
147
|
const mergedGlobalIgnores = mergeGlobalIgnores(opts.globalIgnores);
|
package/src/eslint/lib/files.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { ESLINT } from '../../config-constants.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* File patterns for ESLint configuration.
|
|
3
5
|
*
|
|
4
6
|
* @type {{ withTs: string[], withoutTs: string[] }}
|
|
5
7
|
*/
|
|
6
|
-
export const files =
|
|
8
|
+
export const files = ESLINT.FILE_PATTERNS;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { getEnabledPlugins } from '../enabled-plugins.js';
|
|
4
|
+
|
|
5
|
+
describe('getEnabledPlugins', () => {
|
|
6
|
+
it('should filter plugins by enabled options', () => {
|
|
7
|
+
const conditionalPlugins = { typescript: { name: 'typescript' }, react: { name: 'react' }, a11y: { name: 'a11y' } };
|
|
8
|
+
|
|
9
|
+
const options = { typescript: true, react: false, a11y: true };
|
|
10
|
+
const result = getEnabledPlugins(conditionalPlugins, options);
|
|
11
|
+
|
|
12
|
+
expect(result).toHaveLength(2);
|
|
13
|
+
expect(result).toContainEqual({ name: 'typescript' });
|
|
14
|
+
expect(result).toContainEqual({ name: 'a11y' });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should handle array values', () => {
|
|
18
|
+
const conditionalPlugins = { react: [{ name: 'react' }, { name: 'react-hooks' }] };
|
|
19
|
+
|
|
20
|
+
const options = { react: true };
|
|
21
|
+
const result = getEnabledPlugins(conditionalPlugins, options);
|
|
22
|
+
|
|
23
|
+
expect(result).toHaveLength(2);
|
|
24
|
+
expect(result[0]).toEqual({ name: 'react' });
|
|
25
|
+
expect(result[1]).toEqual({ name: 'react-hooks' });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should return empty array when no options enabled', () => {
|
|
29
|
+
const conditionalPlugins = { typescript: { name: 'typescript' }, react: { name: 'react' } };
|
|
30
|
+
|
|
31
|
+
const options = { typescript: false, react: false };
|
|
32
|
+
const result = getEnabledPlugins(conditionalPlugins, options);
|
|
33
|
+
|
|
34
|
+
expect(result).toEqual([]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should handle mixed single and array values', () => {
|
|
38
|
+
const conditionalPlugins = {
|
|
39
|
+
typescript: { name: 'typescript' },
|
|
40
|
+
react: [{ name: 'react' }, { name: 'react-hooks' }],
|
|
41
|
+
a11y: { name: 'a11y' },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const options = { typescript: true, react: true, a11y: false };
|
|
45
|
+
const result = getEnabledPlugins(conditionalPlugins, options);
|
|
46
|
+
|
|
47
|
+
expect(result).toHaveLength(3);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { fixupPlugins } from '../fixup.js';
|
|
4
|
+
|
|
5
|
+
// Mock @eslint/compat
|
|
6
|
+
vi.mock('@eslint/compat', () => ({ fixupPluginRules: (plugin) => ({ ...plugin, _fixed: true }) }));
|
|
7
|
+
|
|
8
|
+
describe('fixupPlugins', () => {
|
|
9
|
+
it('should wrap plugin rules with fixupPluginRules', () => {
|
|
10
|
+
const flatConfigs = [{ plugins: { 'test-plugin': { name: 'test' } } }];
|
|
11
|
+
|
|
12
|
+
const result = fixupPlugins(flatConfigs);
|
|
13
|
+
|
|
14
|
+
expect(result[0].plugins['test-plugin']._fixed).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should handle multiple plugins', () => {
|
|
18
|
+
const flatConfigs = [{ plugins: { 'plugin-1': { name: 'one' }, 'plugin-2': { name: 'two' } } }];
|
|
19
|
+
|
|
20
|
+
const result = fixupPlugins(flatConfigs);
|
|
21
|
+
|
|
22
|
+
expect(result[0].plugins['plugin-1']._fixed).toBe(true);
|
|
23
|
+
expect(result[0].plugins['plugin-2']._fixed).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should skip configs without plugins', () => {
|
|
27
|
+
const flatConfigs = [{ rules: { 'rule-1': 'error' } }, { plugins: { 'test-plugin': { name: 'test' } } }];
|
|
28
|
+
|
|
29
|
+
const result = fixupPlugins(flatConfigs);
|
|
30
|
+
|
|
31
|
+
expect(result[0]).toEqual({ rules: { 'rule-1': 'error' } });
|
|
32
|
+
expect(result[1].plugins['test-plugin']._fixed).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should not mutate original configs', () => {
|
|
36
|
+
const flatConfigs = [{ plugins: { 'test-plugin': { name: 'test' } } }];
|
|
37
|
+
|
|
38
|
+
const original = JSON.parse(JSON.stringify(flatConfigs));
|
|
39
|
+
fixupPlugins(flatConfigs);
|
|
40
|
+
|
|
41
|
+
expect(flatConfigs).toEqual(original);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle empty configs array', () => {
|
|
45
|
+
const result = fixupPlugins([]);
|
|
46
|
+
expect(result).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { flattenPlugins } from '../flatten.js';
|
|
4
|
+
|
|
5
|
+
describe('flattenPlugins', () => {
|
|
6
|
+
it('should flatten mixed arrays and objects', () => {
|
|
7
|
+
const plugins = [
|
|
8
|
+
{ rules: { 'rule-1': 'error' } },
|
|
9
|
+
[{ rules: { 'rule-2': 'error' } }],
|
|
10
|
+
{ rules: { 'rule-3': 'error' } },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const result = flattenPlugins(plugins);
|
|
14
|
+
|
|
15
|
+
expect(result).toHaveLength(3);
|
|
16
|
+
expect(result[0]).toEqual({ rules: { 'rule-1': 'error' } });
|
|
17
|
+
expect(result[1]).toEqual({ rules: { 'rule-2': 'error' } });
|
|
18
|
+
expect(result[2]).toEqual({ rules: { 'rule-3': 'error' } });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should handle nested arrays', () => {
|
|
22
|
+
const plugins = [[{ config: 'a' }, { config: 'b' }], [{ config: 'c' }]];
|
|
23
|
+
|
|
24
|
+
const result = flattenPlugins(plugins);
|
|
25
|
+
|
|
26
|
+
expect(result).toHaveLength(3);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should return empty array for empty input', () => {
|
|
30
|
+
const result = flattenPlugins([]);
|
|
31
|
+
expect(result).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should filter out null and undefined values', () => {
|
|
35
|
+
const plugins = [{ config: 'a' }, null, { config: 'b' }, undefined];
|
|
36
|
+
|
|
37
|
+
const result = flattenPlugins(plugins);
|
|
38
|
+
|
|
39
|
+
expect(result).toHaveLength(2);
|
|
40
|
+
expect(result).toContainEqual({ config: 'a' });
|
|
41
|
+
expect(result).toContainEqual({ config: 'b' });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle boolean and false values', () => {
|
|
45
|
+
const plugins = [{ config: 'a' }, false, { config: 'b' }];
|
|
46
|
+
|
|
47
|
+
const result = flattenPlugins(plugins);
|
|
48
|
+
|
|
49
|
+
expect(result).toHaveLength(2);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { stripParser } from '../parser.js';
|
|
4
|
+
|
|
5
|
+
describe('stripParser', () => {
|
|
6
|
+
it('should remove parser from languageOptions', () => {
|
|
7
|
+
const flatConfigs = [{ languageOptions: { parser: 'typescript-parser', sourceType: 'module' } }];
|
|
8
|
+
|
|
9
|
+
const result = stripParser(flatConfigs);
|
|
10
|
+
|
|
11
|
+
expect(result[0].languageOptions).toEqual({ sourceType: 'module' });
|
|
12
|
+
expect(result[0].languageOptions.parser).toBeUndefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should remove languageOptions if only parser present', () => {
|
|
16
|
+
const flatConfigs = [{ languageOptions: { parser: 'typescript-parser' } }];
|
|
17
|
+
|
|
18
|
+
const result = stripParser(flatConfigs);
|
|
19
|
+
|
|
20
|
+
expect(result[0].languageOptions).toBeUndefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should skip configs without parser', () => {
|
|
24
|
+
const flatConfigs = [{ languageOptions: { sourceType: 'module' } }];
|
|
25
|
+
|
|
26
|
+
const result = stripParser(flatConfigs);
|
|
27
|
+
|
|
28
|
+
expect(result[0]).toEqual({ languageOptions: { sourceType: 'module' } });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should skip configs without languageOptions', () => {
|
|
32
|
+
const flatConfigs = [{ rules: { 'rule-1': 'error' } }];
|
|
33
|
+
|
|
34
|
+
const result = stripParser(flatConfigs);
|
|
35
|
+
|
|
36
|
+
expect(result[0]).toEqual({ rules: { 'rule-1': 'error' } });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should not mutate original configs', () => {
|
|
40
|
+
const flatConfigs = [{ languageOptions: { parser: 'typescript-parser', sourceType: 'module' } }];
|
|
41
|
+
|
|
42
|
+
const original = JSON.parse(JSON.stringify(flatConfigs));
|
|
43
|
+
stripParser(flatConfigs);
|
|
44
|
+
|
|
45
|
+
expect(flatConfigs).toEqual(original);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should handle multiple configs with different states', () => {
|
|
49
|
+
const flatConfigs = [
|
|
50
|
+
{ languageOptions: { parser: 'typescript-parser' } },
|
|
51
|
+
{ languageOptions: { sourceType: 'module' } },
|
|
52
|
+
{ rules: { 'rule-1': 'error' } },
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const result = stripParser(flatConfigs);
|
|
56
|
+
|
|
57
|
+
expect(result[0].languageOptions).toBeUndefined();
|
|
58
|
+
expect(result[1].languageOptions).toEqual({ sourceType: 'module' });
|
|
59
|
+
expect(result[2]).toEqual({ rules: { 'rule-1': 'error' } });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should handle multiple languageOptions properties', () => {
|
|
63
|
+
const flatConfigs = [
|
|
64
|
+
{
|
|
65
|
+
languageOptions: {
|
|
66
|
+
parser: 'typescript-parser',
|
|
67
|
+
sourceType: 'module',
|
|
68
|
+
ecmaVersion: 2021,
|
|
69
|
+
globals: { global: true },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const result = stripParser(flatConfigs);
|
|
75
|
+
|
|
76
|
+
expect(result[0].languageOptions).toEqual({ sourceType: 'module', ecmaVersion: 2021, globals: { global: true } });
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { stripPlugins } from '../strip.js';
|
|
4
|
+
|
|
5
|
+
describe('stripPlugins', () => {
|
|
6
|
+
it('should remove specified plugins', () => {
|
|
7
|
+
const flatConfigs = [{ plugins: { prettier: { name: 'prettier' }, eslint: { name: 'eslint' } } }];
|
|
8
|
+
|
|
9
|
+
const result = stripPlugins(flatConfigs, ['prettier']);
|
|
10
|
+
|
|
11
|
+
expect(result[0].plugins).toEqual({ eslint: { name: 'eslint' } });
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should remove multiple plugins', () => {
|
|
15
|
+
const flatConfigs = [
|
|
16
|
+
{ plugins: { prettier: { name: 'prettier' }, eslint: { name: 'eslint' }, jsdoc: { name: 'jsdoc' } } },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const result = stripPlugins(flatConfigs, ['prettier', 'eslint']);
|
|
20
|
+
|
|
21
|
+
expect(result[0].plugins).toEqual({ jsdoc: { name: 'jsdoc' } });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should remove plugins object if all plugins are stripped', () => {
|
|
25
|
+
const flatConfigs = [{ plugins: { prettier: { name: 'prettier' } }, rules: { 'rule-1': 'error' } }];
|
|
26
|
+
|
|
27
|
+
const result = stripPlugins(flatConfigs, ['prettier']);
|
|
28
|
+
|
|
29
|
+
expect(result[0].plugins).toBeUndefined();
|
|
30
|
+
expect(result[0].rules).toEqual({ 'rule-1': 'error' });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should return same array if no plugins to strip', () => {
|
|
34
|
+
const flatConfigs = [{ plugins: { prettier: { name: 'prettier' } } }];
|
|
35
|
+
|
|
36
|
+
const result = stripPlugins(flatConfigs, []);
|
|
37
|
+
|
|
38
|
+
expect(result).toBe(flatConfigs);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should skip configs without plugins', () => {
|
|
42
|
+
const flatConfigs = [{ rules: { 'rule-1': 'error' } }, { plugins: { prettier: { name: 'prettier' } } }];
|
|
43
|
+
|
|
44
|
+
const result = stripPlugins(flatConfigs, ['prettier']);
|
|
45
|
+
|
|
46
|
+
expect(result[0]).toEqual({ rules: { 'rule-1': 'error' } });
|
|
47
|
+
expect(result[1].plugins).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should not mutate original configs', () => {
|
|
51
|
+
const flatConfigs = [{ plugins: { prettier: { name: 'prettier' }, eslint: { name: 'eslint' } } }];
|
|
52
|
+
|
|
53
|
+
const original = JSON.parse(JSON.stringify(flatConfigs));
|
|
54
|
+
stripPlugins(flatConfigs, ['prettier']);
|
|
55
|
+
|
|
56
|
+
expect(flatConfigs).toEqual(original);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should handle multiple configs', () => {
|
|
60
|
+
const flatConfigs = [
|
|
61
|
+
{ plugins: { prettier: { name: 'prettier' } } },
|
|
62
|
+
{ plugins: { prettier: { name: 'prettier' }, eslint: { name: 'eslint' } } },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const result = stripPlugins(flatConfigs, ['prettier']);
|
|
66
|
+
|
|
67
|
+
expect(result[0].plugins).toBeUndefined();
|
|
68
|
+
expect(result[1].plugins).toEqual({ eslint: { name: 'eslint' } });
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filters conditional plugins based on user options.
|
|
3
|
+
*
|
|
4
|
+
* This helper enables/disables plugins based on corresponding options,
|
|
5
|
+
* supporting both single values and array configurations.
|
|
6
|
+
*
|
|
7
|
+
* @param {object} conditionalPlugins - Plugin map keyed by option name.
|
|
8
|
+
* @param {object} options - User-provided options.
|
|
9
|
+
*
|
|
10
|
+
* @returns {Array<object>} Flat config objects for enabled plugins.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const plugins = getEnabledPlugins(
|
|
14
|
+
* {
|
|
15
|
+
* typescript: tsPlugin,
|
|
16
|
+
* react: [reactPlugin, reactHooksPlugin]
|
|
17
|
+
* },
|
|
18
|
+
* { typescript: true, react: false }
|
|
19
|
+
* );
|
|
20
|
+
* // Returns: [tsPlugin]
|
|
21
|
+
*/
|
|
22
|
+
export function getEnabledPlugins(conditionalPlugins, options) {
|
|
23
|
+
return Object.entries(conditionalPlugins)
|
|
24
|
+
.filter(([key]) => options[key])
|
|
25
|
+
.flatMap(([, value]) => (Array.isArray(value) ? value : [value]));
|
|
26
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { fixupPluginRules } from '@eslint/compat';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wraps plugin rules with fixupPluginRules for backward compatibility.
|
|
5
|
+
*
|
|
6
|
+
* Ensures that plugins from older ESLint versions are properly wrapped
|
|
7
|
+
* to work with ESLint 10+ flat config format.
|
|
8
|
+
*
|
|
9
|
+
* @param {Array<object>} flatConfigs - Flat config array.
|
|
10
|
+
*
|
|
11
|
+
* @returns {Array<object>} Config array with wrapped plugin rules.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const wrapped = fixupPlugins([
|
|
15
|
+
* {
|
|
16
|
+
* plugins: {
|
|
17
|
+
* 'legacy-plugin': legacyPluginInstance
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ]);
|
|
21
|
+
*/
|
|
22
|
+
export function fixupPlugins(flatConfigs) {
|
|
23
|
+
return flatConfigs.map((config) => {
|
|
24
|
+
if (!config.plugins) return config;
|
|
25
|
+
|
|
26
|
+
const fixed = { ...config, plugins: {} };
|
|
27
|
+
|
|
28
|
+
for (const [name, plugin] of Object.entries(config.plugins)) {
|
|
29
|
+
fixed.plugins[name] = fixupPluginRules(plugin);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return fixed;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flattens a mixed list of config arrays and objects into a single array.
|
|
3
|
+
*
|
|
4
|
+
* Handles nested arrays from plugin configurations that may contain
|
|
5
|
+
* both direct config objects and arrays of configs.
|
|
6
|
+
*
|
|
7
|
+
* @param {Array<Array | object>} plugins - Mixed list of flat config arrays and objects.
|
|
8
|
+
*
|
|
9
|
+
* @returns {Array<object>} Flattened config array.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const flattened = flattenPlugins([
|
|
13
|
+
* { rules: { 'rule-1': 'error' } },
|
|
14
|
+
* [{ rules: { 'rule-2': 'error' } }],
|
|
15
|
+
* [{ rules: { 'rule-3': 'error' } }, { rules: { 'rule-4': 'error' } }]
|
|
16
|
+
* ]);
|
|
17
|
+
* // Returns: [{ rules: {...} }, { rules: {...} }, { rules: {...} }, { rules: {...} }]
|
|
18
|
+
*/
|
|
19
|
+
export function flattenPlugins(plugins) {
|
|
20
|
+
const result = [];
|
|
21
|
+
|
|
22
|
+
for (const plugin of plugins) {
|
|
23
|
+
if (Array.isArray(plugin)) {
|
|
24
|
+
result.push(...plugin);
|
|
25
|
+
} else if (typeof plugin === 'object' && plugin !== null) {
|
|
26
|
+
result.push(plugin);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint Plugin Helpers
|
|
3
|
+
*
|
|
4
|
+
* Collection of utility functions for manipulating and processing ESLint
|
|
5
|
+
* flat config plugins. Each function handles a specific transformation:
|
|
6
|
+
*
|
|
7
|
+
* - getEnabledPlugins: Filter plugins by user options
|
|
8
|
+
* - flattenPlugins: Flatten nested arrays and objects
|
|
9
|
+
* - fixupPlugins: Wrap plugins for backward compatibility
|
|
10
|
+
* - stripPlugins: Remove central plugins from nested configs
|
|
11
|
+
* - stripParser: Remove parsers to prevent conflicts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export { getEnabledPlugins } from './enabled-plugins.js';
|
|
15
|
+
export { flattenPlugins } from './flatten.js';
|
|
16
|
+
export { fixupPlugins } from './fixup.js';
|
|
17
|
+
export { stripPlugins } from './strip.js';
|
|
18
|
+
export { stripParser } from './parser.js';
|