@nx/eslint 0.0.0-pr-27786-e5e2109 → 0.0.0-pr-27404-f7ba497
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/package.json +4 -4
- package/src/executors/lint/utility/eslint-utils.js +3 -1
- package/src/generators/convert-to-flat-config/converters/json-converter.js +18 -2
- package/src/generators/convert-to-flat-config/generator.js +10 -12
- package/src/generators/init/global-eslint-config.d.ts +1 -1
- package/src/generators/init/global-eslint-config.js +9 -14
- package/src/generators/init/init-migration.js +1 -1
- package/src/generators/lint-project/lint-project.js +50 -42
- package/src/generators/lint-project/setup-root-eslint.js +19 -0
- package/src/generators/utils/eslint-file.d.ts +10 -3
- package/src/generators/utils/eslint-file.js +79 -24
- package/src/generators/utils/flat-config/ast-utils.d.ts +16 -7
- package/src/generators/utils/flat-config/ast-utils.js +143 -44
- package/src/plugins/plugin.js +12 -12
- package/src/utils/flat-config.d.ts +2 -2
- package/src/utils/flat-config.js +26 -5
- package/src/utils/resolve-eslint-class.d.ts +3 -1
- package/src/utils/resolve-eslint-class.js +11 -14
- package/src/utils/version-utils.d.ts +2 -0
- package/src/utils/version-utils.js +31 -0
- package/src/utils/versions.d.ts +3 -0
- package/src/utils/versions.js +5 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/eslint",
|
|
3
|
-
"version": "0.0.0-pr-
|
|
3
|
+
"version": "0.0.0-pr-27404-f7ba497",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The ESLint plugin for Nx contains executors, generators and utilities used for linting JavaScript/TypeScript projects within an Nx workspace.",
|
|
6
6
|
"repository": {
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"eslint": "^8.0.0 || ^9.0.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@nx/devkit": "0.0.0-pr-
|
|
39
|
-
"@nx/js": "0.0.0-pr-
|
|
38
|
+
"@nx/devkit": "0.0.0-pr-27404-f7ba497",
|
|
39
|
+
"@nx/js": "0.0.0-pr-27404-f7ba497",
|
|
40
40
|
"semver": "^7.5.3",
|
|
41
41
|
"tslib": "^2.3.0",
|
|
42
42
|
"typescript": "~5.4.2",
|
|
43
|
-
"@nx/linter": "0.0.0-pr-
|
|
43
|
+
"@nx/linter": "0.0.0-pr-27404-f7ba497"
|
|
44
44
|
},
|
|
45
45
|
"peerDependenciesMeta": {
|
|
46
46
|
"@zkochan/js-yaml": {
|
|
@@ -9,7 +9,9 @@ async function resolveAndInstantiateESLint(eslintConfigPath, options, useFlatCon
|
|
|
9
9
|
// todo: add support for eslint.config.mjs,
|
|
10
10
|
'When using the new Flat Config with ESLint, all configs must be named eslint.config.js or eslint.config.cjs and .eslintrc files may not be used. See https://eslint.org/docs/latest/use/configure/configuration-files');
|
|
11
11
|
}
|
|
12
|
-
const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)(
|
|
12
|
+
const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)({
|
|
13
|
+
useFlatConfigOverrideVal: useFlatConfig,
|
|
14
|
+
});
|
|
13
15
|
const eslintOptions = {
|
|
14
16
|
overrideConfigFile: eslintConfigPath,
|
|
15
17
|
fix: !!options.fix,
|
|
@@ -71,6 +71,18 @@ function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths) {
|
|
|
71
71
|
isFlatCompatNeeded = true;
|
|
72
72
|
}
|
|
73
73
|
exportElements.push((0, ast_utils_1.generateFlatOverride)(override));
|
|
74
|
+
// eslint-plugin-import cannot be used with ESLint v9 yet
|
|
75
|
+
// TODO(jack): Once v9 support is released, remove this block.
|
|
76
|
+
// See: https://github.com/import-js/eslint-plugin-import/pull/2996
|
|
77
|
+
if (override.extends === 'plugin:@nx/react') {
|
|
78
|
+
exportElements.push((0, ast_utils_1.generateFlatOverride)({
|
|
79
|
+
rules: {
|
|
80
|
+
'import/first': 'off',
|
|
81
|
+
'import/no-amd': 'off',
|
|
82
|
+
'import/no-webpack-loader-syntax': 'off',
|
|
83
|
+
},
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
74
86
|
});
|
|
75
87
|
}
|
|
76
88
|
if (config.ignorePatterns) {
|
|
@@ -96,9 +108,13 @@ function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths) {
|
|
|
96
108
|
}
|
|
97
109
|
}
|
|
98
110
|
// create the node list and print it to new file
|
|
99
|
-
const nodeList = (0, ast_utils_1.createNodeList)(importsMap, exportElements
|
|
111
|
+
const nodeList = (0, ast_utils_1.createNodeList)(importsMap, exportElements);
|
|
112
|
+
let content = (0, ast_utils_1.stringifyNodeList)(nodeList);
|
|
113
|
+
if (isFlatCompatNeeded) {
|
|
114
|
+
content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
|
|
115
|
+
}
|
|
100
116
|
return {
|
|
101
|
-
content
|
|
117
|
+
content,
|
|
102
118
|
addESLintRC: isFlatCompatNeeded,
|
|
103
119
|
addESLintJS: isESLintJSNeeded,
|
|
104
120
|
};
|
|
@@ -6,7 +6,6 @@ const eslint_file_1 = require("../utils/eslint-file");
|
|
|
6
6
|
const path_1 = require("path");
|
|
7
7
|
const versions_1 = require("../../utils/versions");
|
|
8
8
|
const json_converter_1 = require("./converters/json-converter");
|
|
9
|
-
let shouldInstallDeps = false;
|
|
10
9
|
async function convertToFlatConfigGenerator(tree, options) {
|
|
11
10
|
const eslintFile = (0, eslint_file_1.findEslintFile)(tree);
|
|
12
11
|
if (!eslintFile) {
|
|
@@ -33,9 +32,7 @@ async function convertToFlatConfigGenerator(tree, options) {
|
|
|
33
32
|
if (!options.skipFormat) {
|
|
34
33
|
await (0, devkit_1.formatFiles)(tree);
|
|
35
34
|
}
|
|
36
|
-
|
|
37
|
-
return () => (0, devkit_1.installPackagesTask)(tree);
|
|
38
|
-
}
|
|
35
|
+
return () => (0, devkit_1.installPackagesTask)(tree);
|
|
39
36
|
}
|
|
40
37
|
exports.default = convertToFlatConfigGenerator;
|
|
41
38
|
function convertRootToFlatConfig(tree, eslintFile) {
|
|
@@ -128,17 +125,18 @@ function processConvertedConfig(tree, root, source, target, { content, addESLint
|
|
|
128
125
|
tree.delete((0, path_1.join)(root, source));
|
|
129
126
|
// save new
|
|
130
127
|
tree.write((0, path_1.join)(root, target), content);
|
|
128
|
+
// These dependencies are required for flat configs that are generated by subsequent app/lib generators.
|
|
129
|
+
const devDependencies = {
|
|
130
|
+
eslint: versions_1.eslint9__eslintVersion,
|
|
131
|
+
'eslint-config-prettier': versions_1.eslintConfigPrettierVersion,
|
|
132
|
+
'typescript-eslint': versions_1.eslint9__typescriptESLintVersion,
|
|
133
|
+
};
|
|
131
134
|
// add missing packages
|
|
132
135
|
if (addESLintRC) {
|
|
133
|
-
|
|
134
|
-
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
135
|
-
'@eslint/eslintrc': versions_1.eslintrcVersion,
|
|
136
|
-
});
|
|
136
|
+
devDependencies['@eslint/eslintrc'] = versions_1.eslintrcVersion;
|
|
137
137
|
}
|
|
138
138
|
if (addESLintJS) {
|
|
139
|
-
|
|
140
|
-
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
141
|
-
'@eslint/js': versions_1.eslintVersion,
|
|
142
|
-
});
|
|
139
|
+
devDependencies['@eslint/js'] = versions_1.eslintVersion;
|
|
143
140
|
}
|
|
141
|
+
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, devDependencies);
|
|
144
142
|
}
|
|
@@ -26,4 +26,4 @@ export declare const javaScriptOverride: {
|
|
|
26
26
|
rules: {};
|
|
27
27
|
};
|
|
28
28
|
export declare const getGlobalEsLintConfiguration: (unitTestRunner?: string, rootProject?: boolean) => Linter.Config;
|
|
29
|
-
export declare const getGlobalFlatEslintConfiguration: (
|
|
29
|
+
export declare const getGlobalFlatEslintConfiguration: (rootProject?: boolean) => string;
|
|
@@ -78,24 +78,19 @@ const getGlobalEsLintConfiguration = (unitTestRunner, rootProject) => {
|
|
|
78
78
|
return config;
|
|
79
79
|
};
|
|
80
80
|
exports.getGlobalEsLintConfiguration = getGlobalEsLintConfiguration;
|
|
81
|
-
const getGlobalFlatEslintConfiguration = (
|
|
82
|
-
const nodeList = (0, ast_utils_1.createNodeList)(new Map(), []
|
|
81
|
+
const getGlobalFlatEslintConfiguration = (rootProject) => {
|
|
82
|
+
const nodeList = (0, ast_utils_1.createNodeList)(new Map(), []);
|
|
83
83
|
let content = (0, ast_utils_1.stringifyNodeList)(nodeList);
|
|
84
|
-
content = (0, ast_utils_1.addImportToFlatConfig)(content, '
|
|
85
|
-
content = (0, ast_utils_1.
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
content = (0, ast_utils_1.addImportToFlatConfig)(content, 'nx', '@nx/eslint-plugin');
|
|
85
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/base'), { insertAtTheEnd: false });
|
|
86
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/typescript'));
|
|
87
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/javascript'));
|
|
88
88
|
if (!rootProject) {
|
|
89
89
|
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(moduleBoundariesOverride));
|
|
90
90
|
}
|
|
91
|
-
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(jestOverride));
|
|
95
|
-
}
|
|
96
|
-
// add ignore for .nx folder
|
|
97
|
-
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateAst)({
|
|
98
|
-
ignores: ['.nx'],
|
|
91
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
|
|
92
|
+
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
|
93
|
+
rules: {},
|
|
99
94
|
}));
|
|
100
95
|
return content;
|
|
101
96
|
};
|
|
@@ -29,7 +29,7 @@ function migrateConfigToMonorepoStyle(projects, tree, unitTestRunner, keepExisti
|
|
|
29
29
|
}, undefined, keepExistingVersions);
|
|
30
30
|
tree.write(tree.exists('eslint.config.js')
|
|
31
31
|
? 'eslint.base.config.js'
|
|
32
|
-
: 'eslint.config.js', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(
|
|
32
|
+
: 'eslint.config.js', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)());
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
35
|
const eslintFile = (0, eslint_file_1.findEslintFile)(tree, '.');
|
|
@@ -114,55 +114,63 @@ function createEsLintConfiguration(tree, options, projectConfig, setParserOption
|
|
|
114
114
|
const pathToRootConfig = extendedRootConfig
|
|
115
115
|
? `${(0, devkit_1.offsetFromRoot)(projectConfig.root)}${extendedRootConfig}`
|
|
116
116
|
: undefined;
|
|
117
|
-
const addDependencyChecks =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
117
|
+
const addDependencyChecks = options.addPackageJsonDependencyChecks ||
|
|
118
|
+
isBuildableLibraryProject(projectConfig);
|
|
119
|
+
const overrides = (0, flat_config_1.useFlatConfig)(tree)
|
|
120
|
+
? // For flat configs, we don't need to generate different overrides for each file. Users should add their own overrides as needed.
|
|
121
|
+
[]
|
|
122
|
+
: [
|
|
123
|
+
{
|
|
124
|
+
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
|
125
|
+
/**
|
|
126
|
+
* NOTE: We no longer set parserOptions.project by default when creating new projects.
|
|
127
|
+
*
|
|
128
|
+
* We have observed that users rarely add rules requiring type-checking to their Nx workspaces, and therefore
|
|
129
|
+
* do not actually need the capabilites which parserOptions.project provides. When specifying parserOptions.project,
|
|
130
|
+
* typescript-eslint needs to create full TypeScript Programs for you. When omitting it, it can perform a simple
|
|
131
|
+
* parse (and AST tranformation) of the source files it encounters during a lint run, which is much faster and much
|
|
132
|
+
* less memory intensive.
|
|
133
|
+
*
|
|
134
|
+
* In the rare case that users attempt to add rules requiring type-checking to their setup later on (and haven't set
|
|
135
|
+
* parserOptions.project), the executor will attempt to look for the particular error typescript-eslint gives you
|
|
136
|
+
* and provide feedback to the user.
|
|
137
|
+
*/
|
|
138
|
+
parserOptions: !setParserOptionsProject
|
|
139
|
+
? undefined
|
|
140
|
+
: {
|
|
141
|
+
project: [`${projectConfig.root}/tsconfig.*?.json`],
|
|
142
|
+
},
|
|
143
|
+
/**
|
|
144
|
+
* Having an empty rules object present makes it more obvious to the user where they would
|
|
145
|
+
* extend things from if they needed to
|
|
146
|
+
*/
|
|
147
|
+
rules: {},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
files: ['*.ts', '*.tsx'],
|
|
151
|
+
rules: {},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
files: ['*.js', '*.jsx'],
|
|
155
|
+
rules: {},
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
if (addDependencyChecks) {
|
|
156
159
|
overrides.push({
|
|
157
160
|
files: ['*.json'],
|
|
158
161
|
parser: 'jsonc-eslint-parser',
|
|
159
162
|
rules: {
|
|
160
|
-
'@nx/dependency-checks':
|
|
163
|
+
'@nx/dependency-checks': [
|
|
164
|
+
'error',
|
|
165
|
+
{
|
|
166
|
+
// With flat configs, we don't want to include imports in the eslint js/cjs/mjs files to be checked
|
|
167
|
+
ignoredFiles: ['**/*/*eslint*'],
|
|
168
|
+
},
|
|
169
|
+
],
|
|
161
170
|
},
|
|
162
171
|
});
|
|
163
172
|
}
|
|
164
173
|
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
165
|
-
const isCompatNeeded = addDependencyChecks;
|
|
166
174
|
const nodes = [];
|
|
167
175
|
const importMap = new Map();
|
|
168
176
|
if (extendedRootConfig) {
|
|
@@ -172,7 +180,7 @@ function createEsLintConfiguration(tree, options, projectConfig, setParserOption
|
|
|
172
180
|
overrides.forEach((override) => {
|
|
173
181
|
nodes.push((0, ast_utils_1.generateFlatOverride)(override));
|
|
174
182
|
});
|
|
175
|
-
const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes
|
|
183
|
+
const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes);
|
|
176
184
|
const content = (0, ast_utils_1.stringifyNodeList)(nodeList);
|
|
177
185
|
tree.write((0, path_1.join)(projectConfig.root, 'eslint.config.js'), content);
|
|
178
186
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.setupRootEsLint = setupRootEsLint;
|
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const flat_config_1 = require("../../utils/flat-config");
|
|
5
6
|
const versions_1 = require("../../utils/versions");
|
|
6
7
|
const global_eslint_config_1 = require("../init/global-eslint-config");
|
|
7
8
|
const eslint_file_1 = require("../utils/eslint-file");
|
|
@@ -10,6 +11,12 @@ function setupRootEsLint(tree, options) {
|
|
|
10
11
|
if (rootEslintFile) {
|
|
11
12
|
return () => { };
|
|
12
13
|
}
|
|
14
|
+
if (!(0, flat_config_1.useFlatConfig)(tree)) {
|
|
15
|
+
return setUpLegacyRootEslintRc(tree, options);
|
|
16
|
+
}
|
|
17
|
+
return setUpRootFlatConfig(tree, options);
|
|
18
|
+
}
|
|
19
|
+
function setUpLegacyRootEslintRc(tree, options) {
|
|
13
20
|
(0, devkit_1.writeJson)(tree, '.eslintrc.json', (0, global_eslint_config_1.getGlobalEsLintConfiguration)(options.unitTestRunner, options.rootProject));
|
|
14
21
|
if (tree.exists('.eslintignore')) {
|
|
15
22
|
let content = tree.read('.eslintignore', 'utf-8');
|
|
@@ -30,3 +37,15 @@ function setupRootEsLint(tree, options) {
|
|
|
30
37
|
})
|
|
31
38
|
: () => { };
|
|
32
39
|
}
|
|
40
|
+
function setUpRootFlatConfig(tree, options) {
|
|
41
|
+
tree.write('eslint.config.js', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(options.rootProject));
|
|
42
|
+
return !options.skipPackageJson
|
|
43
|
+
? (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
44
|
+
'@eslint/js': versions_1.eslint9__eslintVersion,
|
|
45
|
+
'@nx/eslint-plugin': versions_1.nxVersion,
|
|
46
|
+
eslint: versions_1.eslint9__eslintVersion,
|
|
47
|
+
'eslint-config-prettier': versions_1.eslintConfigPrettierVersion,
|
|
48
|
+
'typescript-eslint': versions_1.eslint9__typescriptESLintVersion,
|
|
49
|
+
})
|
|
50
|
+
: () => { };
|
|
51
|
+
}
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type GeneratorCallback, type Tree } from '@nx/devkit';
|
|
2
2
|
import type { Linter } from 'eslint';
|
|
3
3
|
export declare function findEslintFile(tree: Tree, projectRoot?: string): string | null;
|
|
4
4
|
export declare function isEslintConfigSupported(tree: Tree, projectRoot?: string): boolean;
|
|
5
5
|
export declare function updateRelativePathsInConfig(tree: Tree, sourcePath: string, destinationPath: string): void;
|
|
6
|
-
export declare function addOverrideToLintConfig(tree: Tree, root: string, override: Linter.ConfigOverride<Linter.RulesRecord
|
|
6
|
+
export declare function addOverrideToLintConfig(tree: Tree, root: string, override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>, options?: {
|
|
7
7
|
insertAtTheEnd?: boolean;
|
|
8
8
|
checkBaseConfig?: boolean;
|
|
9
9
|
}): void;
|
|
10
10
|
export declare function updateOverrideInLintConfig(tree: Tree, root: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, update: (override: Linter.ConfigOverride<Linter.RulesRecord>) => Linter.ConfigOverride<Linter.RulesRecord>): void;
|
|
11
11
|
export declare function lintConfigHasOverride(tree: Tree, root: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, checkBaseConfig?: boolean): boolean;
|
|
12
12
|
export declare function replaceOverridesInLintConfig(tree: Tree, root: string, overrides: Linter.ConfigOverride<Linter.RulesRecord>[]): void;
|
|
13
|
-
export declare function addExtendsToLintConfig(tree: Tree, root: string, plugin: string |
|
|
13
|
+
export declare function addExtendsToLintConfig(tree: Tree, root: string, plugin: string | {
|
|
14
|
+
name: string;
|
|
15
|
+
needCompatFixup: boolean;
|
|
16
|
+
} | Array<string | {
|
|
17
|
+
name: string;
|
|
18
|
+
needCompatFixup: boolean;
|
|
19
|
+
}>, insertAtTheEnd?: boolean): GeneratorCallback;
|
|
20
|
+
export declare function addPredefinedConfigToFlatLintConfig(tree: Tree, root: string, predefinedConfigName: string, moduleName?: string, moduleImportPath?: string, spread?: boolean, insertAtTheEnd?: boolean): void;
|
|
14
21
|
export declare function addPluginsToLintConfig(tree: Tree, root: string, plugin: string | string[]): void;
|
|
15
22
|
export declare function addIgnoresToLintConfig(tree: Tree, root: string, ignorePatterns: string[]): void;
|
|
16
23
|
export declare function getPluginImport(pluginName: string): string;
|
|
@@ -8,14 +8,18 @@ exports.updateOverrideInLintConfig = updateOverrideInLintConfig;
|
|
|
8
8
|
exports.lintConfigHasOverride = lintConfigHasOverride;
|
|
9
9
|
exports.replaceOverridesInLintConfig = replaceOverridesInLintConfig;
|
|
10
10
|
exports.addExtendsToLintConfig = addExtendsToLintConfig;
|
|
11
|
+
exports.addPredefinedConfigToFlatLintConfig = addPredefinedConfigToFlatLintConfig;
|
|
11
12
|
exports.addPluginsToLintConfig = addPluginsToLintConfig;
|
|
12
13
|
exports.addIgnoresToLintConfig = addIgnoresToLintConfig;
|
|
13
14
|
exports.getPluginImport = getPluginImport;
|
|
14
15
|
const devkit_1 = require("@nx/devkit");
|
|
16
|
+
const semver_1 = require("semver");
|
|
17
|
+
const config_file_1 = require("../../utils/config-file");
|
|
15
18
|
const flat_config_1 = require("../../utils/flat-config");
|
|
19
|
+
const version_utils_1 = require("../../utils/version-utils");
|
|
20
|
+
const versions_1 = require("../../utils/versions");
|
|
16
21
|
const ast_utils_1 = require("./flat-config/ast-utils");
|
|
17
22
|
const path_utils_1 = require("./flat-config/path-utils");
|
|
18
|
-
const config_file_1 = require("../../utils/config-file");
|
|
19
23
|
function findEslintFile(tree, projectRoot) {
|
|
20
24
|
if (projectRoot === undefined && tree.exists(config_file_1.baseEsLintConfigFile)) {
|
|
21
25
|
return config_file_1.baseEsLintConfigFile;
|
|
@@ -110,12 +114,12 @@ function addOverrideToLintConfig(tree, root, override, options = {
|
|
|
110
114
|
}) {
|
|
111
115
|
const isBase = options.checkBaseConfig && findEslintFile(tree, root).includes('.base');
|
|
112
116
|
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
113
|
-
const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.
|
|
117
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
|
|
114
118
|
const flatOverride = (0, ast_utils_1.generateFlatOverride)(override);
|
|
115
119
|
let content = tree.read(fileName, 'utf8');
|
|
116
|
-
//
|
|
117
|
-
if (overrideNeedsCompat(override)) {
|
|
118
|
-
content = (0, ast_utils_1.
|
|
120
|
+
// Check if the provided override using legacy eslintrc properties or plugins, if so we need to add compat
|
|
121
|
+
if ((0, ast_utils_1.overrideNeedsCompat)(override)) {
|
|
122
|
+
content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
|
|
119
123
|
}
|
|
120
124
|
tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(content, flatOverride, options));
|
|
121
125
|
}
|
|
@@ -133,12 +137,9 @@ function addOverrideToLintConfig(tree, root, override, options = {
|
|
|
133
137
|
});
|
|
134
138
|
}
|
|
135
139
|
}
|
|
136
|
-
function overrideNeedsCompat(override) {
|
|
137
|
-
return (override.env || override.extends || override.plugins || override.parser);
|
|
138
|
-
}
|
|
139
140
|
function updateOverrideInLintConfig(tree, root, lookup, update) {
|
|
140
141
|
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
141
|
-
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.
|
|
142
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
|
|
142
143
|
let content = tree.read(fileName, 'utf8');
|
|
143
144
|
content = (0, ast_utils_1.replaceOverride)(content, root, lookup, update);
|
|
144
145
|
tree.write(fileName, content);
|
|
@@ -173,7 +174,7 @@ function lintConfigHasOverride(tree, root, lookup, checkBaseConfig = false) {
|
|
|
173
174
|
}
|
|
174
175
|
const isBase = checkBaseConfig && findEslintFile(tree, root).includes('.base');
|
|
175
176
|
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
176
|
-
const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.
|
|
177
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
|
|
177
178
|
const content = tree.read(fileName, 'utf8');
|
|
178
179
|
return (0, ast_utils_1.hasOverride)(content, lookup);
|
|
179
180
|
}
|
|
@@ -184,11 +185,11 @@ function lintConfigHasOverride(tree, root, lookup, checkBaseConfig = false) {
|
|
|
184
185
|
}
|
|
185
186
|
function replaceOverridesInLintConfig(tree, root, overrides) {
|
|
186
187
|
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
187
|
-
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.
|
|
188
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
|
|
188
189
|
let content = tree.read(fileName, 'utf8');
|
|
189
|
-
//
|
|
190
|
-
if (overrides.some(overrideNeedsCompat)) {
|
|
191
|
-
content = (0, ast_utils_1.
|
|
190
|
+
// Check if any of the provided overrides using legacy eslintrc properties or plugins, if so we need to add compat
|
|
191
|
+
if (overrides.some(ast_utils_1.overrideNeedsCompat)) {
|
|
192
|
+
content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
|
|
192
193
|
}
|
|
193
194
|
content = (0, ast_utils_1.removeOverridesFromLintConfig)(content);
|
|
194
195
|
overrides.forEach((override) => {
|
|
@@ -205,18 +206,62 @@ function replaceOverridesInLintConfig(tree, root, overrides) {
|
|
|
205
206
|
});
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
|
-
function addExtendsToLintConfig(tree, root, plugin) {
|
|
209
|
-
const plugins = Array.isArray(plugin) ? plugin : [plugin];
|
|
209
|
+
function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
|
|
210
210
|
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
211
|
-
const
|
|
212
|
-
const
|
|
211
|
+
const pluginExtends = [];
|
|
212
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
|
|
213
|
+
let shouldImportEslintCompat = false;
|
|
214
|
+
// assume eslint version is 9 if not found, as it's what we'd be generating by default
|
|
215
|
+
const eslintVersion = (0, version_utils_1.getInstalledEslintVersion)(tree) ?? versions_1.eslint9__eslintVersion;
|
|
216
|
+
if ((0, semver_1.gte)(eslintVersion, '9.0.0')) {
|
|
217
|
+
// eslint v9 requires the incompatible plugins to be wrapped with a helper from @eslint/compat
|
|
218
|
+
const plugins = (Array.isArray(plugin) ? plugin : [plugin]).map((p) => typeof p === 'string' ? { name: p, needCompatFixup: false } : p);
|
|
219
|
+
let compatiblePluginsBatch = [];
|
|
220
|
+
plugins.forEach(({ name, needCompatFixup }) => {
|
|
221
|
+
if (needCompatFixup) {
|
|
222
|
+
if (compatiblePluginsBatch.length > 0) {
|
|
223
|
+
// flush the current batch of compatible plugins and reset it
|
|
224
|
+
pluginExtends.push((0, ast_utils_1.generatePluginExtendsElement)(compatiblePluginsBatch));
|
|
225
|
+
compatiblePluginsBatch = [];
|
|
226
|
+
}
|
|
227
|
+
// generate the extends for the incompatible plugin
|
|
228
|
+
pluginExtends.push((0, ast_utils_1.generatePluginExtendsElementWithCompatFixup)(name));
|
|
229
|
+
shouldImportEslintCompat = true;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// add the compatible plugin to the current batch
|
|
233
|
+
compatiblePluginsBatch.push(name);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
if (compatiblePluginsBatch.length > 0) {
|
|
237
|
+
// flush the batch of compatible plugins
|
|
238
|
+
pluginExtends.push((0, ast_utils_1.generatePluginExtendsElement)(compatiblePluginsBatch));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
const plugins = (Array.isArray(plugin) ? plugin : [plugin]).map((p) => typeof p === 'string' ? p : p.name);
|
|
243
|
+
pluginExtends.push((0, ast_utils_1.generatePluginExtendsElement)(plugins));
|
|
244
|
+
}
|
|
213
245
|
let content = tree.read(fileName, 'utf8');
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
246
|
+
if (shouldImportEslintCompat) {
|
|
247
|
+
content = (0, ast_utils_1.addImportToFlatConfig)(content, ['fixupConfigRules'], '@eslint/compat');
|
|
248
|
+
}
|
|
249
|
+
content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
|
|
250
|
+
// reverse the order to ensure they are added in the correct order at the
|
|
251
|
+
// start of the `extends` array
|
|
252
|
+
for (const pluginExtend of pluginExtends.reverse()) {
|
|
253
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, pluginExtend, {
|
|
254
|
+
insertAtTheEnd,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
tree.write(fileName, content);
|
|
258
|
+
if (shouldImportEslintCompat) {
|
|
259
|
+
return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { '@eslint/compat': versions_1.eslintCompat }, undefined, true);
|
|
260
|
+
}
|
|
261
|
+
return () => { };
|
|
218
262
|
}
|
|
219
263
|
else {
|
|
264
|
+
const plugins = (Array.isArray(plugin) ? plugin : [plugin]).map((p) => typeof p === 'string' ? p : p.name);
|
|
220
265
|
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
|
|
221
266
|
(0, devkit_1.updateJson)(tree, fileName, (json) => {
|
|
222
267
|
json.extends ??= [];
|
|
@@ -226,12 +271,22 @@ function addExtendsToLintConfig(tree, root, plugin) {
|
|
|
226
271
|
];
|
|
227
272
|
return json;
|
|
228
273
|
});
|
|
274
|
+
return () => { };
|
|
229
275
|
}
|
|
230
276
|
}
|
|
277
|
+
function addPredefinedConfigToFlatLintConfig(tree, root, predefinedConfigName, moduleName = 'nx', moduleImportPath = '@nx/eslint-plugin', spread = true, insertAtTheEnd = true) {
|
|
278
|
+
if (!(0, flat_config_1.useFlatConfig)(tree))
|
|
279
|
+
throw new Error('Predefined configs can only be used with flat configs');
|
|
280
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
|
|
281
|
+
let content = tree.read(fileName, 'utf8');
|
|
282
|
+
content = (0, ast_utils_1.addImportToFlatConfig)(content, moduleName, moduleImportPath);
|
|
283
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)(predefinedConfigName, moduleName, spread), { insertAtTheEnd });
|
|
284
|
+
tree.write(fileName, content);
|
|
285
|
+
}
|
|
231
286
|
function addPluginsToLintConfig(tree, root, plugin) {
|
|
232
287
|
const plugins = Array.isArray(plugin) ? plugin : [plugin];
|
|
233
288
|
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
234
|
-
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.
|
|
289
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
|
|
235
290
|
let content = tree.read(fileName, 'utf8');
|
|
236
291
|
const mappedPlugins = [];
|
|
237
292
|
plugins.forEach((name) => {
|
|
@@ -255,7 +310,7 @@ function addPluginsToLintConfig(tree, root, plugin) {
|
|
|
255
310
|
}
|
|
256
311
|
function addIgnoresToLintConfig(tree, root, ignorePatterns) {
|
|
257
312
|
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
258
|
-
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.
|
|
313
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
|
|
259
314
|
const block = (0, ast_utils_1.generateAst)({
|
|
260
315
|
ignores: ignorePatterns.map((path) => (0, path_utils_1.mapFilePath)(path)),
|
|
261
316
|
});
|
|
@@ -8,7 +8,7 @@ export declare function hasOverride(content: string, lookup: (override: Linter.C
|
|
|
8
8
|
/**
|
|
9
9
|
* Finds an override matching the lookup function and applies the update function to it
|
|
10
10
|
*/
|
|
11
|
-
export declare function replaceOverride(content: string, root: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, update?: (override: Linter.ConfigOverride<Linter.RulesRecord
|
|
11
|
+
export declare function replaceOverride(content: string, root: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, update?: (override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>) => Partial<Linter.ConfigOverride<Linter.RulesRecord>>): string;
|
|
12
12
|
/**
|
|
13
13
|
* Adding require statement to the top of the file
|
|
14
14
|
*/
|
|
@@ -33,14 +33,15 @@ export declare function addPluginsToExportsBlock(content: string, plugins: {
|
|
|
33
33
|
/**
|
|
34
34
|
* Adds compat if missing to flat config
|
|
35
35
|
*/
|
|
36
|
-
export declare function
|
|
36
|
+
export declare function addFlatCompatToFlatConfig(content: string): string;
|
|
37
37
|
/**
|
|
38
38
|
* Generate node list representing the imports and the exports blocks
|
|
39
39
|
* Optionally add flat compat initialization
|
|
40
40
|
*/
|
|
41
|
-
export declare function createNodeList(importsMap: Map<string, string>, exportElements: ts.Expression[]
|
|
41
|
+
export declare function createNodeList(importsMap: Map<string, string>, exportElements: ts.Expression[]): ts.NodeArray<ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile>;
|
|
42
42
|
export declare function generateSpreadElement(name: string): ts.SpreadElement;
|
|
43
43
|
export declare function generatePluginExtendsElement(plugins: string[]): ts.SpreadElement;
|
|
44
|
+
export declare function generatePluginExtendsElementWithCompatFixup(plugin: string): ts.SpreadElement;
|
|
44
45
|
/**
|
|
45
46
|
* Stringifies TS nodes to file content string
|
|
46
47
|
*/
|
|
@@ -49,12 +50,20 @@ export declare function stringifyNodeList(nodes: ts.NodeArray<ts.VariableStateme
|
|
|
49
50
|
* generates AST require statement
|
|
50
51
|
*/
|
|
51
52
|
export declare function generateRequire(variableName: string | ts.ObjectBindingPattern, imp: string): ts.VariableStatement;
|
|
53
|
+
export declare function overrideNeedsCompat(override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>): string | string[] | {
|
|
54
|
+
[name: string]: boolean;
|
|
55
|
+
};
|
|
52
56
|
/**
|
|
53
|
-
* Generates AST object or spread element
|
|
57
|
+
* Generates an AST object or spread element representing a modern flat config entry,
|
|
58
|
+
* based on a given legacy eslintrc JSON override object
|
|
54
59
|
*/
|
|
55
|
-
export declare function generateFlatOverride(
|
|
56
|
-
export declare function
|
|
60
|
+
export declare function generateFlatOverride(_override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>): ts.ObjectLiteralExpression | ts.SpreadElement;
|
|
61
|
+
export declare function generateFlatPredefinedConfig(predefinedConfigName: string, moduleName?: string, spread?: boolean): ts.ObjectLiteralExpression | ts.SpreadElement | ts.ElementAccessExpression;
|
|
62
|
+
export declare function mapFilePaths(_override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>): Partial<Linter.ConfigOverride<Linter.RulesRecord>>;
|
|
57
63
|
/**
|
|
58
64
|
* Generates an AST from a JSON-type input
|
|
59
65
|
*/
|
|
60
|
-
export declare function generateAst<T>(input: unknown
|
|
66
|
+
export declare function generateAst<T>(input: unknown, propertyAssignmentReplacer?: {
|
|
67
|
+
keyToMatch: RegExp | string;
|
|
68
|
+
replacer: (propertyAssignment: ts.PropertyAssignment, propertyName: string) => ts.PropertyAssignment;
|
|
69
|
+
}): T;
|
|
@@ -8,13 +8,16 @@ exports.addBlockToFlatConfigExport = addBlockToFlatConfigExport;
|
|
|
8
8
|
exports.removePlugin = removePlugin;
|
|
9
9
|
exports.removeCompatExtends = removeCompatExtends;
|
|
10
10
|
exports.addPluginsToExportsBlock = addPluginsToExportsBlock;
|
|
11
|
-
exports.
|
|
11
|
+
exports.addFlatCompatToFlatConfig = addFlatCompatToFlatConfig;
|
|
12
12
|
exports.createNodeList = createNodeList;
|
|
13
13
|
exports.generateSpreadElement = generateSpreadElement;
|
|
14
14
|
exports.generatePluginExtendsElement = generatePluginExtendsElement;
|
|
15
|
+
exports.generatePluginExtendsElementWithCompatFixup = generatePluginExtendsElementWithCompatFixup;
|
|
15
16
|
exports.stringifyNodeList = stringifyNodeList;
|
|
16
17
|
exports.generateRequire = generateRequire;
|
|
18
|
+
exports.overrideNeedsCompat = overrideNeedsCompat;
|
|
17
19
|
exports.generateFlatOverride = generateFlatOverride;
|
|
20
|
+
exports.generateFlatPredefinedConfig = generateFlatPredefinedConfig;
|
|
18
21
|
exports.mapFilePaths = mapFilePaths;
|
|
19
22
|
exports.generateAst = generateAst;
|
|
20
23
|
const devkit_1 = require("@nx/devkit");
|
|
@@ -86,10 +89,7 @@ function hasOverride(content, lookup) {
|
|
|
86
89
|
// strip any spread elements
|
|
87
90
|
objSource = fullNodeText.replace(SPREAD_ELEMENTS_REGEXP, '');
|
|
88
91
|
}
|
|
89
|
-
const data = (
|
|
90
|
-
// ensure property names have double quotes so that JSON.parse works
|
|
91
|
-
.replace(/'/g, '"')
|
|
92
|
-
.replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": '));
|
|
92
|
+
const data = parseTextToJson(objSource);
|
|
93
93
|
if (lookup(data)) {
|
|
94
94
|
return true;
|
|
95
95
|
}
|
|
@@ -101,7 +101,9 @@ function parseTextToJson(text) {
|
|
|
101
101
|
return (0, devkit_1.parseJson)(text
|
|
102
102
|
// ensure property names have double quotes so that JSON.parse works
|
|
103
103
|
.replace(/'/g, '"')
|
|
104
|
-
.replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": ')
|
|
104
|
+
.replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": ')
|
|
105
|
+
// stringify any require calls to avoid JSON parsing errors, turn them into just the string value being required
|
|
106
|
+
.replace(/require\(['"]([^'"]+)['"]\)/g, '"$1"'));
|
|
105
107
|
}
|
|
106
108
|
/**
|
|
107
109
|
* Finds an override matching the lookup function and applies the update function to it
|
|
@@ -138,13 +140,18 @@ function replaceOverride(content, root, lookup, update) {
|
|
|
138
140
|
start,
|
|
139
141
|
length: end - start,
|
|
140
142
|
});
|
|
141
|
-
|
|
143
|
+
let updatedData = update(data);
|
|
142
144
|
if (updatedData) {
|
|
143
|
-
mapFilePaths(updatedData);
|
|
145
|
+
updatedData = mapFilePaths(updatedData);
|
|
144
146
|
changes.push({
|
|
145
147
|
type: devkit_1.ChangeType.Insert,
|
|
146
148
|
index: start,
|
|
147
|
-
text: JSON.stringify(updatedData, null, 2)
|
|
149
|
+
text: JSON.stringify(updatedData, null, 2)
|
|
150
|
+
// restore any parser require calls that were stripped during JSON parsing
|
|
151
|
+
.replace(/"parser": "([^"]+)"/g, (_, parser) => {
|
|
152
|
+
return `"parser": require('${parser}')`;
|
|
153
|
+
})
|
|
154
|
+
.slice(2, -2), // remove curly braces and start/end line breaks since we are injecting just properties
|
|
148
155
|
});
|
|
149
156
|
}
|
|
150
157
|
}
|
|
@@ -404,7 +411,7 @@ function removeCompatExtends(content, compatExtends) {
|
|
|
404
411
|
type: devkit_1.ChangeType.Insert,
|
|
405
412
|
index: node.pos,
|
|
406
413
|
text: '\n' +
|
|
407
|
-
body.replace(new RegExp('[ \t]s*...' + paramName + '[ \t]*,?\\s*', 'g'), ''),
|
|
414
|
+
body.replace(new RegExp('[ \t]s*...' + paramName + '(\\.rules)?[ \t]*,?\\s*', 'g'), ''),
|
|
408
415
|
});
|
|
409
416
|
}
|
|
410
417
|
}
|
|
@@ -427,7 +434,7 @@ function addPluginsToExportsBlock(content, plugins) {
|
|
|
427
434
|
/**
|
|
428
435
|
* Adds compat if missing to flat config
|
|
429
436
|
*/
|
|
430
|
-
function
|
|
437
|
+
function addFlatCompatToFlatConfig(content) {
|
|
431
438
|
let result = content;
|
|
432
439
|
result = addImportToFlatConfig(result, 'js', '@eslint/js');
|
|
433
440
|
if (result.includes('const compat = new FlatCompat')) {
|
|
@@ -439,28 +446,21 @@ function addCompatToFlatConfig(content) {
|
|
|
439
446
|
{
|
|
440
447
|
type: devkit_1.ChangeType.Insert,
|
|
441
448
|
index: index - 1,
|
|
442
|
-
text:
|
|
449
|
+
text: `
|
|
450
|
+
const compat = new FlatCompat({
|
|
451
|
+
baseDirectory: __dirname,
|
|
452
|
+
recommendedConfig: js.configs.recommended,
|
|
453
|
+
});
|
|
454
|
+
`,
|
|
443
455
|
},
|
|
444
456
|
]);
|
|
445
457
|
}
|
|
446
|
-
const DEFAULT_FLAT_CONFIG = `
|
|
447
|
-
const compat = new FlatCompat({
|
|
448
|
-
baseDirectory: __dirname,
|
|
449
|
-
recommendedConfig: js.configs.recommended,
|
|
450
|
-
});
|
|
451
|
-
`;
|
|
452
458
|
/**
|
|
453
459
|
* Generate node list representing the imports and the exports blocks
|
|
454
460
|
* Optionally add flat compat initialization
|
|
455
461
|
*/
|
|
456
|
-
function createNodeList(importsMap, exportElements
|
|
462
|
+
function createNodeList(importsMap, exportElements) {
|
|
457
463
|
const importsList = [];
|
|
458
|
-
if (isFlatCompatNeeded) {
|
|
459
|
-
importsMap.set('@eslint/js', 'js');
|
|
460
|
-
importsList.push(generateRequire(ts.factory.createObjectBindingPattern([
|
|
461
|
-
ts.factory.createBindingElement(undefined, undefined, 'FlatCompat'),
|
|
462
|
-
]), '@eslint/eslintrc'));
|
|
463
|
-
}
|
|
464
464
|
// generateRequire(varName, imp, ts.factory);
|
|
465
465
|
Array.from(importsMap.entries()).forEach(([imp, varName]) => {
|
|
466
466
|
importsList.push(generateRequire(varName, imp));
|
|
@@ -468,7 +468,7 @@ function createNodeList(importsMap, exportElements, isFlatCompatNeeded) {
|
|
|
468
468
|
return ts.factory.createNodeArray([
|
|
469
469
|
// add plugin imports
|
|
470
470
|
...importsList,
|
|
471
|
-
ts.createSourceFile('',
|
|
471
|
+
ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.JS),
|
|
472
472
|
// creates:
|
|
473
473
|
// module.exports = [ ... ];
|
|
474
474
|
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('module'), ts.factory.createIdentifier('exports')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createArrayLiteralExpression(exportElements, true))),
|
|
@@ -480,6 +480,11 @@ function generateSpreadElement(name) {
|
|
|
480
480
|
function generatePluginExtendsElement(plugins) {
|
|
481
481
|
return ts.factory.createSpreadElement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('compat'), ts.factory.createIdentifier('extends')), undefined, plugins.map((plugin) => ts.factory.createStringLiteral(plugin))));
|
|
482
482
|
}
|
|
483
|
+
function generatePluginExtendsElementWithCompatFixup(plugin) {
|
|
484
|
+
return ts.factory.createSpreadElement(ts.factory.createCallExpression(ts.factory.createIdentifier('fixupConfigRules'), undefined, [
|
|
485
|
+
ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('compat'), ts.factory.createIdentifier('extends')), undefined, [ts.factory.createStringLiteral(plugin)]),
|
|
486
|
+
]));
|
|
487
|
+
}
|
|
483
488
|
/**
|
|
484
489
|
* Stringifies TS nodes to file content string
|
|
485
490
|
*/
|
|
@@ -502,21 +507,95 @@ function generateRequire(variableName, imp) {
|
|
|
502
507
|
], ts.NodeFlags.Const));
|
|
503
508
|
}
|
|
504
509
|
/**
|
|
505
|
-
*
|
|
510
|
+
* FROM: https://github.com/eslint/rewrite/blob/e2a7ec809db20e638abbad250d105ddbde88a8d5/packages/migrate-config/src/migrate-config.js#L222
|
|
511
|
+
*
|
|
512
|
+
* Converts a glob pattern to a format that can be used in a flat config.
|
|
513
|
+
* @param {string} pattern The glob pattern to convert.
|
|
514
|
+
* @returns {string} The converted glob pattern.
|
|
506
515
|
*/
|
|
507
|
-
function
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
+
function convertGlobPattern(pattern) {
|
|
517
|
+
const isNegated = pattern.startsWith('!');
|
|
518
|
+
const patternToTest = isNegated ? pattern.slice(1) : pattern;
|
|
519
|
+
// if the pattern is already in the correct format, return it
|
|
520
|
+
if (patternToTest === '**' || patternToTest.includes('/')) {
|
|
521
|
+
return pattern;
|
|
522
|
+
}
|
|
523
|
+
return `${isNegated ? '!' : ''}**/${patternToTest}`;
|
|
524
|
+
}
|
|
525
|
+
// FROM: https://github.com/eslint/rewrite/blob/e2a7ec809db20e638abbad250d105ddbde88a8d5/packages/migrate-config/src/migrate-config.js#L38
|
|
526
|
+
const keysToCopy = ['settings', 'rules', 'processor'];
|
|
527
|
+
function overrideNeedsCompat(override) {
|
|
528
|
+
return override.env || override.extends || override.plugins;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Generates an AST object or spread element representing a modern flat config entry,
|
|
532
|
+
* based on a given legacy eslintrc JSON override object
|
|
533
|
+
*/
|
|
534
|
+
function generateFlatOverride(_override) {
|
|
535
|
+
const override = mapFilePaths(_override);
|
|
536
|
+
// We do not need the compat tooling for this override
|
|
537
|
+
if (!overrideNeedsCompat(override)) {
|
|
538
|
+
// Ensure files is an array
|
|
539
|
+
let files = override.files;
|
|
540
|
+
if (typeof files === 'string') {
|
|
541
|
+
files = [files];
|
|
542
|
+
}
|
|
543
|
+
const flatConfigOverride = {
|
|
544
|
+
files,
|
|
545
|
+
};
|
|
546
|
+
if (override.rules) {
|
|
547
|
+
flatConfigOverride.rules = override.rules;
|
|
548
|
+
}
|
|
549
|
+
// Copy over everything that stays the same
|
|
550
|
+
keysToCopy.forEach((key) => {
|
|
551
|
+
if (override[key]) {
|
|
552
|
+
flatConfigOverride[key] = override[key];
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
if (override.parser || override.parserOptions) {
|
|
556
|
+
const languageOptions = {};
|
|
557
|
+
if (override.parser) {
|
|
558
|
+
languageOptions['parser'] = override.parser;
|
|
559
|
+
}
|
|
560
|
+
if (override.parserOptions) {
|
|
561
|
+
languageOptions['parserOptions'] = override.parserOptions;
|
|
562
|
+
}
|
|
563
|
+
if (Object.keys(languageOptions).length) {
|
|
564
|
+
flatConfigOverride.languageOptions = languageOptions;
|
|
565
|
+
}
|
|
516
566
|
}
|
|
517
|
-
|
|
567
|
+
if (override['languageOptions']) {
|
|
568
|
+
flatConfigOverride.languageOptions = override['languageOptions'];
|
|
569
|
+
}
|
|
570
|
+
if (override.excludedFiles) {
|
|
571
|
+
flatConfigOverride.ignores = (Array.isArray(override.excludedFiles)
|
|
572
|
+
? override.excludedFiles
|
|
573
|
+
: [override.excludedFiles]).map((p) => convertGlobPattern(p));
|
|
574
|
+
}
|
|
575
|
+
return generateAst(flatConfigOverride, {
|
|
576
|
+
keyToMatch: /^(parser|rules)$/,
|
|
577
|
+
replacer: (propertyAssignment, propertyName) => {
|
|
578
|
+
if (propertyName === 'rules') {
|
|
579
|
+
// Add comment that user can override rules if there are no overrides.
|
|
580
|
+
if (ts.isObjectLiteralExpression(propertyAssignment.initializer) &&
|
|
581
|
+
propertyAssignment.initializer.properties.length === 0) {
|
|
582
|
+
return ts.addSyntheticLeadingComment(ts.factory.createPropertyAssignment(propertyAssignment.name, ts.factory.createObjectLiteralExpression([])), ts.SyntaxKind.SingleLineCommentTrivia, ' Override or add rules here');
|
|
583
|
+
}
|
|
584
|
+
return propertyAssignment;
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
// Change parser to require statement.
|
|
588
|
+
return ts.factory.createPropertyAssignment('parser', ts.factory.createCallExpression(ts.factory.createIdentifier('require'), undefined, [
|
|
589
|
+
ts.factory.createStringLiteral(override['languageOptions']?.['parserOptions']?.parser ??
|
|
590
|
+
override['languageOptions']?.parser ??
|
|
591
|
+
override.parser),
|
|
592
|
+
]));
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
});
|
|
518
596
|
}
|
|
519
|
-
|
|
597
|
+
// At this point we are applying the flat config compat tooling to the override
|
|
598
|
+
const { excludedFiles, parser, parserOptions, rules, files, ...rest } = override;
|
|
520
599
|
const objectLiteralElements = [
|
|
521
600
|
ts.factory.createSpreadAssignment(ts.factory.createIdentifier('config')),
|
|
522
601
|
];
|
|
@@ -543,7 +622,14 @@ function generateFlatOverride(override) {
|
|
|
543
622
|
], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createParenthesizedExpression(ts.factory.createObjectLiteralExpression(objectLiteralElements, true))),
|
|
544
623
|
]));
|
|
545
624
|
}
|
|
546
|
-
function
|
|
625
|
+
function generateFlatPredefinedConfig(predefinedConfigName, moduleName = 'nx', spread = true) {
|
|
626
|
+
const node = ts.factory.createElementAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(moduleName), ts.factory.createIdentifier('configs')), ts.factory.createStringLiteral(predefinedConfigName));
|
|
627
|
+
return spread ? ts.factory.createSpreadElement(node) : node;
|
|
628
|
+
}
|
|
629
|
+
function mapFilePaths(_override) {
|
|
630
|
+
const override = {
|
|
631
|
+
..._override,
|
|
632
|
+
};
|
|
547
633
|
if (override.files) {
|
|
548
634
|
override.files = Array.isArray(override.files)
|
|
549
635
|
? override.files
|
|
@@ -556,6 +642,7 @@ function mapFilePaths(override) {
|
|
|
556
642
|
: [override.excludedFiles];
|
|
557
643
|
override.excludedFiles = override.excludedFiles.map((file) => (0, path_utils_1.mapFilePath)(file));
|
|
558
644
|
}
|
|
645
|
+
return override;
|
|
559
646
|
}
|
|
560
647
|
function addTSObjectProperty(elements, key, value) {
|
|
561
648
|
if (value) {
|
|
@@ -565,18 +652,16 @@ function addTSObjectProperty(elements, key, value) {
|
|
|
565
652
|
/**
|
|
566
653
|
* Generates an AST from a JSON-type input
|
|
567
654
|
*/
|
|
568
|
-
function generateAst(input) {
|
|
655
|
+
function generateAst(input, propertyAssignmentReplacer) {
|
|
569
656
|
if (Array.isArray(input)) {
|
|
570
|
-
return ts.factory.createArrayLiteralExpression(input.map((item) => generateAst(item)), input.length > 1 // multiline only if more than one item
|
|
657
|
+
return ts.factory.createArrayLiteralExpression(input.map((item) => generateAst(item, propertyAssignmentReplacer)), input.length > 1 // multiline only if more than one item
|
|
571
658
|
);
|
|
572
659
|
}
|
|
573
660
|
if (input === null) {
|
|
574
661
|
return ts.factory.createNull();
|
|
575
662
|
}
|
|
576
663
|
if (typeof input === 'object') {
|
|
577
|
-
return ts.factory.createObjectLiteralExpression(Object.
|
|
578
|
-
.filter(([_, value]) => value !== undefined)
|
|
579
|
-
.map(([key, value]) => ts.factory.createPropertyAssignment(isValidKey(key) ? key : ts.factory.createStringLiteral(key), generateAst(value))), Object.keys(input).length > 1 // multiline only if more than one property
|
|
664
|
+
return ts.factory.createObjectLiteralExpression(generatePropertyAssignmentsFromObjectEntries(input, propertyAssignmentReplacer), Object.keys(input).length > 1 // multiline only if more than one property
|
|
580
665
|
);
|
|
581
666
|
}
|
|
582
667
|
if (typeof input === 'string') {
|
|
@@ -591,6 +676,20 @@ function generateAst(input) {
|
|
|
591
676
|
// since we are parsing JSON, this should never happen
|
|
592
677
|
throw new Error(`Unknown type: ${typeof input} `);
|
|
593
678
|
}
|
|
679
|
+
function generatePropertyAssignmentsFromObjectEntries(input, propertyAssignmentReplacer) {
|
|
680
|
+
return Object.entries(input)
|
|
681
|
+
.filter(([_, value]) => value !== undefined)
|
|
682
|
+
.map(([key, value]) => {
|
|
683
|
+
const original = ts.factory.createPropertyAssignment(isValidKey(key) ? key : ts.factory.createStringLiteral(key), generateAst(value, propertyAssignmentReplacer));
|
|
684
|
+
if (propertyAssignmentReplacer &&
|
|
685
|
+
(typeof propertyAssignmentReplacer.keyToMatch === 'string'
|
|
686
|
+
? key === propertyAssignmentReplacer.keyToMatch
|
|
687
|
+
: propertyAssignmentReplacer.keyToMatch.test(key))) {
|
|
688
|
+
return propertyAssignmentReplacer.replacer(original, key);
|
|
689
|
+
}
|
|
690
|
+
return original;
|
|
691
|
+
});
|
|
692
|
+
}
|
|
594
693
|
function isValidKey(key) {
|
|
595
694
|
return /^[a-zA-Z0-9_]+$/.test(key);
|
|
596
695
|
}
|
package/src/plugins/plugin.js
CHANGED
|
@@ -47,7 +47,9 @@ const internalCreateNodes = async (configFilePath, options, context, projectsCac
|
|
|
47
47
|
// dedupe and sort project roots by depth for more efficient traversal
|
|
48
48
|
const dedupedProjectRoots = Array.from(new Set(projectFiles.map((f) => (0, posix_1.dirname)(f)))).sort((a, b) => (a !== b && isSubDir(a, b) ? -1 : 1));
|
|
49
49
|
const excludePatterns = dedupedProjectRoots.map((root) => `${root}/**/*`);
|
|
50
|
-
const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)(
|
|
50
|
+
const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)({
|
|
51
|
+
useFlatConfigOverrideVal: (0, config_file_1.isFlatConfig)(configFilePath),
|
|
52
|
+
});
|
|
51
53
|
const eslintVersion = ESLint.version;
|
|
52
54
|
const projects = {};
|
|
53
55
|
await Promise.all(dedupedProjectRoots.map(async (childProjectRoot, index) => {
|
|
@@ -94,10 +96,11 @@ const internalCreateNodes = async (configFilePath, options, context, projectsCac
|
|
|
94
96
|
projects,
|
|
95
97
|
};
|
|
96
98
|
};
|
|
97
|
-
|
|
98
|
-
const internalCreateNodesV2 = async (configFilePath, options, context, eslintConfigFiles, allProjectRoots, projectRootsByEslintRoots, lintableFilesPerProjectRoot, projectsCache) => {
|
|
99
|
+
const internalCreateNodesV2 = async (configFilePath, options, context, eslintConfigFiles, projectRootsByEslintRoots, lintableFilesPerProjectRoot, projectsCache) => {
|
|
99
100
|
const configDir = (0, posix_1.dirname)(configFilePath);
|
|
100
|
-
const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)(
|
|
101
|
+
const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)({
|
|
102
|
+
useFlatConfigOverrideVal: (0, config_file_1.isFlatConfig)(configFilePath),
|
|
103
|
+
});
|
|
101
104
|
const eslintVersion = ESLint.version;
|
|
102
105
|
const projects = {};
|
|
103
106
|
await Promise.all(projectRootsByEslintRoots.get(configDir).map(async (projectRoot) => {
|
|
@@ -112,11 +115,6 @@ const internalCreateNodesV2 = async (configFilePath, options, context, eslintCon
|
|
|
112
115
|
Object.assign(projects, projectsCache[hash]);
|
|
113
116
|
return;
|
|
114
117
|
}
|
|
115
|
-
if (!lintableFilesPerProjectRoot.size) {
|
|
116
|
-
collectingLintableFilesPromise ??= collectLintableFilesByProjectRoot(lintableFilesPerProjectRoot, allProjectRoots, options, context);
|
|
117
|
-
await collectingLintableFilesPromise;
|
|
118
|
-
collectingLintableFilesPromise = null;
|
|
119
|
-
}
|
|
120
118
|
const eslint = new ESLint({
|
|
121
119
|
cwd: (0, posix_1.join)(context.workspaceRoot, projectRoot),
|
|
122
120
|
});
|
|
@@ -155,9 +153,9 @@ exports.createNodesV2 = [
|
|
|
155
153
|
const cachePath = (0, posix_1.join)(cache_directory_1.workspaceDataDirectory, `eslint-${optionsHash}.hash`);
|
|
156
154
|
const targetsCache = readTargetsCache(cachePath);
|
|
157
155
|
const { eslintConfigFiles, projectRoots, projectRootsByEslintRoots } = splitConfigFiles(configFiles);
|
|
158
|
-
const lintableFilesPerProjectRoot =
|
|
156
|
+
const lintableFilesPerProjectRoot = await collectLintableFilesByProjectRoot(projectRoots, options, context);
|
|
159
157
|
try {
|
|
160
|
-
return await (0, devkit_1.createNodesFromFiles)((configFile, options, context) => internalCreateNodesV2(configFile, options, context, eslintConfigFiles,
|
|
158
|
+
return await (0, devkit_1.createNodesFromFiles)((configFile, options, context) => internalCreateNodesV2(configFile, options, context, eslintConfigFiles, projectRootsByEslintRoots, lintableFilesPerProjectRoot, targetsCache), eslintConfigFiles, options, context);
|
|
161
159
|
}
|
|
162
160
|
finally {
|
|
163
161
|
writeTargetsToCache(cachePath, targetsCache);
|
|
@@ -203,7 +201,8 @@ function groupProjectRootsByEslintRoots(eslintConfigFiles, projectRoots) {
|
|
|
203
201
|
}
|
|
204
202
|
return projectRootsByEslintRoots;
|
|
205
203
|
}
|
|
206
|
-
async function collectLintableFilesByProjectRoot(
|
|
204
|
+
async function collectLintableFilesByProjectRoot(projectRoots, options, context) {
|
|
205
|
+
const lintableFilesPerProjectRoot = new Map();
|
|
207
206
|
const lintableFiles = await (0, workspace_context_1.globWithWorkspaceContext)(context.workspaceRoot, [
|
|
208
207
|
`**/*.{${options.extensions.join(',')}}`,
|
|
209
208
|
]);
|
|
@@ -216,6 +215,7 @@ async function collectLintableFilesByProjectRoot(lintableFilesPerProjectRoot, pr
|
|
|
216
215
|
lintableFilesPerProjectRoot.get(projectRoot).push(file);
|
|
217
216
|
}
|
|
218
217
|
}
|
|
218
|
+
return lintableFilesPerProjectRoot;
|
|
219
219
|
}
|
|
220
220
|
function getRootForDirectory(directory, roots) {
|
|
221
221
|
let currentPath = (0, posix_1.normalize)(directory);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Tree } from '@nx/devkit';
|
|
2
2
|
export declare const eslintFlatConfigFilenames: string[];
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function useFlatConfig(tree
|
|
3
|
+
export declare function getRootESLintFlatConfigFilename(tree: Tree): string;
|
|
4
|
+
export declare function useFlatConfig(tree?: Tree): boolean;
|
package/src/utils/flat-config.js
CHANGED
|
@@ -1,26 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.eslintFlatConfigFilenames = void 0;
|
|
4
|
-
exports.
|
|
4
|
+
exports.getRootESLintFlatConfigFilename = getRootESLintFlatConfigFilename;
|
|
5
5
|
exports.useFlatConfig = useFlatConfig;
|
|
6
|
+
const semver_1 = require("semver");
|
|
6
7
|
// todo: add support for eslint.config.mjs,
|
|
7
8
|
exports.eslintFlatConfigFilenames = [
|
|
8
9
|
'eslint.config.js',
|
|
9
10
|
'eslint.config.cjs',
|
|
10
11
|
];
|
|
11
|
-
function
|
|
12
|
+
function getRootESLintFlatConfigFilename(tree) {
|
|
12
13
|
for (const file of exports.eslintFlatConfigFilenames) {
|
|
13
14
|
if (tree.exists(file)) {
|
|
14
15
|
return file;
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
|
-
throw new Error('Could not find flat config file');
|
|
18
|
+
throw new Error('Could not find root flat config file');
|
|
18
19
|
}
|
|
19
20
|
function useFlatConfig(tree) {
|
|
21
|
+
// Prioritize taking ESLint's own environment variable into account when determining if we should use flat config
|
|
22
|
+
// If it is not defined, then default to true.
|
|
23
|
+
if (process.env.ESLINT_USE_FLAT_CONFIG === 'true') {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
else if (process.env.ESLINT_USE_FLAT_CONFIG === 'false') {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
// If we find an existing flat config file in the root of the provided tree, we should use flat config
|
|
30
|
+
if (tree) {
|
|
31
|
+
const hasRootFlatConfig = exports.eslintFlatConfigFilenames.some((filename) => tree.exists(filename));
|
|
32
|
+
if (hasRootFlatConfig) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Otherwise fallback to checking the installed eslint version
|
|
20
37
|
try {
|
|
21
|
-
|
|
38
|
+
const { ESLint } = require('eslint');
|
|
39
|
+
// Default to any v8 version to compare against in this case as it implies a much older version of ESLint was found (and gte() requires a valid version)
|
|
40
|
+
const eslintVersion = ESLint.version || '8.0.0';
|
|
41
|
+
return (0, semver_1.gte)(eslintVersion, '9.0.0');
|
|
22
42
|
}
|
|
23
43
|
catch {
|
|
24
|
-
|
|
44
|
+
// Default to assuming flat config in case ESLint is not yet installed
|
|
45
|
+
return true;
|
|
25
46
|
}
|
|
26
47
|
}
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveESLintClass = resolveESLintClass;
|
|
4
|
-
|
|
4
|
+
const flat_config_1 = require("../utils/flat-config");
|
|
5
|
+
async function resolveESLintClass(opts) {
|
|
5
6
|
try {
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16
|
-
const { FlatESLint } = require('eslint/use-at-your-own-risk');
|
|
17
|
-
return FlatESLint;
|
|
7
|
+
// Explicitly use the FlatESLint and LegacyESLint classes here because the ESLint class points at a different one based on ESLint v8 vs ESLint v9
|
|
8
|
+
// But the decision on which one to use is not just based on the major version of ESLint.
|
|
9
|
+
// @ts-expect-error The may be wrong based on our installed eslint version
|
|
10
|
+
const { LegacyESLint, FlatESLint } = await Promise.resolve().then(() => require('eslint/use-at-your-own-risk'));
|
|
11
|
+
const shouldESLintUseFlatConfig = typeof opts?.useFlatConfigOverrideVal === 'boolean'
|
|
12
|
+
? opts.useFlatConfigOverrideVal
|
|
13
|
+
: (0, flat_config_1.useFlatConfig)();
|
|
14
|
+
return shouldESLintUseFlatConfig ? FlatESLint : LegacyESLint;
|
|
18
15
|
}
|
|
19
16
|
catch {
|
|
20
|
-
throw new Error('Unable to find
|
|
17
|
+
throw new Error('Unable to find `eslint`. Ensure a valid `eslint` version is installed.');
|
|
21
18
|
}
|
|
22
19
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getInstalledEslintVersion = getInstalledEslintVersion;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const semver_1 = require("@nx/devkit/src/utils/semver");
|
|
6
|
+
const devkit_internals_1 = require("nx/src/devkit-internals");
|
|
7
|
+
function getInstalledEslintVersion(tree) {
|
|
8
|
+
try {
|
|
9
|
+
const eslintPackageJson = (0, devkit_internals_1.readModulePackageJson)('eslint').packageJson;
|
|
10
|
+
return eslintPackageJson.version;
|
|
11
|
+
}
|
|
12
|
+
catch { }
|
|
13
|
+
// eslint is not installed on disk, it could be in the package.json
|
|
14
|
+
// but waiting to be installed
|
|
15
|
+
const rootPackageJson = tree
|
|
16
|
+
? (0, devkit_1.readJson)(tree, 'package.json')
|
|
17
|
+
: (0, devkit_1.readJsonFile)('package.json');
|
|
18
|
+
const eslintVersionInRootPackageJson = rootPackageJson.devDependencies?.['eslint'] ??
|
|
19
|
+
rootPackageJson.dependencies?.['eslint'];
|
|
20
|
+
if (!eslintVersionInRootPackageJson) {
|
|
21
|
+
// eslint is not installed
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
// try to parse and return the version
|
|
26
|
+
return (0, semver_1.checkAndCleanWithSemver)('eslint', eslintVersionInRootPackageJson);
|
|
27
|
+
}
|
|
28
|
+
catch { }
|
|
29
|
+
// we could not resolve the version
|
|
30
|
+
return null;
|
|
31
|
+
}
|
package/src/utils/versions.d.ts
CHANGED
|
@@ -3,3 +3,6 @@ export declare const eslintVersion = "~8.57.0";
|
|
|
3
3
|
export declare const eslintrcVersion = "^2.1.1";
|
|
4
4
|
export declare const eslintConfigPrettierVersion = "^9.0.0";
|
|
5
5
|
export declare const typescriptESLintVersion = "^7.16.0";
|
|
6
|
+
export declare const eslint9__typescriptESLintVersion = "^8.0.0";
|
|
7
|
+
export declare const eslint9__eslintVersion = "^9.8.0";
|
|
8
|
+
export declare const eslintCompat = "^1.1.1";
|
package/src/utils/versions.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.typescriptESLintVersion = exports.eslintConfigPrettierVersion = exports.eslintrcVersion = exports.eslintVersion = exports.nxVersion = void 0;
|
|
3
|
+
exports.eslintCompat = exports.eslint9__eslintVersion = exports.eslint9__typescriptESLintVersion = exports.typescriptESLintVersion = exports.eslintConfigPrettierVersion = exports.eslintrcVersion = exports.eslintVersion = exports.nxVersion = void 0;
|
|
4
4
|
exports.nxVersion = require('../../package.json').version;
|
|
5
5
|
exports.eslintVersion = '~8.57.0';
|
|
6
6
|
exports.eslintrcVersion = '^2.1.1';
|
|
7
7
|
exports.eslintConfigPrettierVersion = '^9.0.0';
|
|
8
8
|
exports.typescriptESLintVersion = '^7.16.0';
|
|
9
|
+
// Updated linting stack for ESLint v9, typescript-eslint v8
|
|
10
|
+
exports.eslint9__typescriptESLintVersion = '^8.0.0';
|
|
11
|
+
exports.eslint9__eslintVersion = '^9.8.0';
|
|
12
|
+
exports.eslintCompat = '^1.1.1';
|