@vijayhardaha/dev-config 2.2.1 → 2.3.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 -0
- package/package.json +7 -10
- 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} +10 -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 +3 -110
- package/src/eslint/lib/files.js +3 -1
- package/src/eslint/lib/ignores.js +1 -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} +6 -6
- 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/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
|
|
|
@@ -27,120 +28,12 @@ try {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
/**
|
|
31
|
-
* Filters conditional plugins based on user options.
|
|
32
|
-
*
|
|
33
|
-
* @param {object} conditionalPlugins - Plugin map keyed by option name.
|
|
34
|
-
* @param {object} options - User-provided options.
|
|
35
|
-
*
|
|
36
|
-
* @returns {object[]} Flat config objects for enabled plugins.
|
|
37
|
-
*/
|
|
38
|
-
const getEnabledPlugins = (conditionalPlugins, options) =>
|
|
39
|
-
Object.entries(conditionalPlugins)
|
|
40
|
-
.filter(([key]) => options[key])
|
|
41
|
-
.flatMap(([, value]) => (Array.isArray(value) ? value : [value]));
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Flattens a mixed list of config arrays and objects.
|
|
45
|
-
*
|
|
46
|
-
* @param {(Array|object)[]} plugins - Mixed list of flat config arrays and objects.
|
|
47
|
-
*
|
|
48
|
-
* @returns {object[]} Flattened config array.
|
|
49
|
-
*/
|
|
50
|
-
const flattenPlugins = (plugins) => {
|
|
51
|
-
const result = [];
|
|
52
|
-
|
|
53
|
-
for (const plugin of plugins) {
|
|
54
|
-
if (Array.isArray(plugin)) {
|
|
55
|
-
result.push(...plugin);
|
|
56
|
-
} else if (typeof plugin === 'object' && plugin !== null) {
|
|
57
|
-
result.push(plugin);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return result;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Wraps plugin rules with fixupPluginRules for backward compatibility.
|
|
66
|
-
*
|
|
67
|
-
* @param {object[]} flatConfigs - Flat config array.
|
|
68
|
-
*
|
|
69
|
-
* @returns {object[]} Config array with wrapped plugin rules.
|
|
70
|
-
*/
|
|
71
|
-
const fixPlugins = (flatConfigs) =>
|
|
72
|
-
flatConfigs.map((config) => {
|
|
73
|
-
if (!config.plugins) return config;
|
|
74
|
-
|
|
75
|
-
const fixed = { ...config, plugins: {} };
|
|
76
|
-
|
|
77
|
-
for (const [name, plugin] of Object.entries(config.plugins)) {
|
|
78
|
-
fixed.plugins[name] = fixupPluginRules(plugin);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return fixed;
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Removes centrally-registered plugins from individual configs to prevent
|
|
86
|
-
* "Cannot redefine plugin" errors.
|
|
87
|
-
*
|
|
88
|
-
* @param {object[]} flatConfigs - Flat config array.
|
|
89
|
-
* @param {string[]} pluginNames - Plugin names to strip from configs.
|
|
90
|
-
*
|
|
91
|
-
* @returns {object[]} Config array with specified plugins removed.
|
|
92
|
-
*/
|
|
93
|
-
const stripPlugins = (flatConfigs, pluginNames) => {
|
|
94
|
-
if (pluginNames.length === 0) return flatConfigs;
|
|
95
|
-
|
|
96
|
-
const skip = new Set(pluginNames);
|
|
97
|
-
|
|
98
|
-
return flatConfigs.map((config) => {
|
|
99
|
-
if (!config.plugins) return config;
|
|
100
|
-
|
|
101
|
-
const plugins = { ...config.plugins };
|
|
102
|
-
|
|
103
|
-
for (const name of skip) {
|
|
104
|
-
delete plugins[name];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (Object.keys(plugins).length === 0) {
|
|
108
|
-
const rest = { ...config };
|
|
109
|
-
delete rest.plugins;
|
|
110
|
-
|
|
111
|
-
return rest;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return { ...config, plugins };
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Removes the parser from individual configs when the main config provides one.
|
|
120
|
-
* Prevents parser conflicts (e.g., eslint-config-next/parser vs `@typescript-eslint/parser`).
|
|
121
|
-
*
|
|
122
|
-
* @param {object[]} flatConfigs - Flat config array.
|
|
123
|
-
*
|
|
124
|
-
* @returns {object[]} Config array with parsers removed.
|
|
125
|
-
*/
|
|
126
|
-
const stripParser = (flatConfigs) =>
|
|
127
|
-
flatConfigs.map((config) => {
|
|
128
|
-
if (!config.languageOptions?.parser) return config;
|
|
129
|
-
|
|
130
|
-
const languageOptions = { ...config.languageOptions };
|
|
131
|
-
delete languageOptions.parser;
|
|
132
|
-
|
|
133
|
-
return Object.keys(languageOptions).length > 0
|
|
134
|
-
? { ...config, languageOptions }
|
|
135
|
-
: { ...config, languageOptions: undefined };
|
|
136
|
-
});
|
|
137
|
-
|
|
138
31
|
/**
|
|
139
32
|
* Merges user-provided global ignores with common defaults.
|
|
140
33
|
*
|
|
141
34
|
* @param {string[]|undefined} userGlobalIgnores - User-provided ignore patterns.
|
|
142
35
|
*
|
|
143
|
-
* @returns {
|
|
36
|
+
* @returns {import('eslint').Linter.Config[]} ESLint global ignores configuration.
|
|
144
37
|
*/
|
|
145
38
|
const mergeGlobalIgnores = (userGlobalIgnores) =>
|
|
146
39
|
Array.isArray(userGlobalIgnores) ? globalIgnores(userGlobalIgnores) : globalIgnores();
|
|
@@ -248,7 +141,7 @@ export const buildConfig = ({
|
|
|
248
141
|
].filter(Boolean);
|
|
249
142
|
|
|
250
143
|
const flatConfigs = flattenPlugins(mergedPlugins);
|
|
251
|
-
const wrappedConfigs =
|
|
144
|
+
const wrappedConfigs = fixupPlugins(flatConfigs);
|
|
252
145
|
const strippedConfigs = stripPlugins(wrappedConfigs, Object.keys(centralPlugins));
|
|
253
146
|
const parsedConfigs = typescript ? stripParser(strippedConfigs) : strippedConfigs;
|
|
254
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;
|
|
@@ -37,7 +37,7 @@ const testIgnores = ['**/coverage/', '**/test-results/', '**/.playwright-report/
|
|
|
37
37
|
/**
|
|
38
38
|
* Cache and temporary file ignore patterns.
|
|
39
39
|
*/
|
|
40
|
-
const cacheIgnores = ['**/.cache/', '**/*.log', '**/.DS_Store', '**/Thumbs.db', '**/*.tmp'];
|
|
40
|
+
const cacheIgnores = ['**/.cache/', '**/supabase/.temp/**', '**/*.log', '**/.DS_Store', '**/Thumbs.db', '**/*.tmp'];
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Environment and config file ignore 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';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes the parser from individual configs when main config provides one.
|
|
3
|
+
*
|
|
4
|
+
* Prevents parser conflicts (e.g., eslint-config-next/parser vs \@typescript-eslint/parser).
|
|
5
|
+
* Cleans up empty languageOptions objects after parser removal.
|
|
6
|
+
*
|
|
7
|
+
* @param {Array<object>} flatConfigs - Flat config array.
|
|
8
|
+
*
|
|
9
|
+
* @returns {Array<object>} Config array with parsers removed.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const stripped = stripParser([
|
|
13
|
+
* {
|
|
14
|
+
* languageOptions: {
|
|
15
|
+
* parser: typescriptParser,
|
|
16
|
+
* sourceType: 'module'
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* ]);
|
|
20
|
+
* // Returns: [{ languageOptions: { sourceType: 'module' } }]
|
|
21
|
+
*/
|
|
22
|
+
export function stripParser(flatConfigs) {
|
|
23
|
+
return flatConfigs.map((config) => {
|
|
24
|
+
if (!config.languageOptions?.parser) return config;
|
|
25
|
+
|
|
26
|
+
const languageOptions = { ...config.languageOptions };
|
|
27
|
+
delete languageOptions.parser;
|
|
28
|
+
|
|
29
|
+
return Object.keys(languageOptions).length > 0
|
|
30
|
+
? { ...config, languageOptions }
|
|
31
|
+
: { ...config, languageOptions: undefined };
|
|
32
|
+
});
|
|
33
|
+
}
|