@nx/eslint 19.7.4 → 19.8.0-beta.1
Sign up to get free protection for your applications and to get access to all the features.
- package/package.json +5 -5
- 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/convert-to-inferred/convert-to-inferred.js +1 -1
- package/src/generators/init/global-eslint-config.d.ts +1 -1
- package/src/generators/init/global-eslint-config.js +28 -15
- package/src/generators/init/init-migration.js +6 -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 +21 -7
- package/src/generators/utils/flat-config/ast-utils.js +209 -45
- package/src/plugins/plugin.js +6 -2
- 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": "19.
|
3
|
+
"version": "19.8.0-beta.1",
|
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": "19.
|
39
|
-
"@nx/js": "19.
|
38
|
+
"@nx/devkit": "19.8.0-beta.1",
|
39
|
+
"@nx/js": "19.8.0-beta.1",
|
40
40
|
"semver": "^7.5.3",
|
41
41
|
"tslib": "^2.3.0",
|
42
42
|
"typescript": "~5.4.2",
|
43
|
-
"@nx/linter": "19.
|
43
|
+
"@nx/linter": "19.8.0-beta.1"
|
44
44
|
},
|
45
45
|
"peerDependenciesMeta": {
|
46
46
|
"@zkochan/js-yaml": {
|
@@ -52,4 +52,4 @@
|
|
52
52
|
},
|
53
53
|
"type": "commonjs",
|
54
54
|
"types": "./index.d.ts"
|
55
|
-
}
|
55
|
+
}
|
@@ -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
|
}
|
@@ -20,7 +20,7 @@ async function convertToInferred(tree, options) {
|
|
20
20
|
},
|
21
21
|
], options.project);
|
22
22
|
if (migratedProjects.size === 0) {
|
23
|
-
throw new
|
23
|
+
throw new executor_to_plugin_migrator_1.NoTargetsToMigrateError();
|
24
24
|
}
|
25
25
|
if (!options.skipFormat) {
|
26
26
|
await (0, devkit_1.formatFiles)(tree);
|
@@ -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,37 @@ 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
|
-
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
89
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
|
90
|
+
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
91
|
+
rules: {
|
92
|
+
'@nx/enforce-module-boundaries': [
|
93
|
+
'error',
|
94
|
+
{
|
95
|
+
enforceBuildableLibDependency: true,
|
96
|
+
allow: [
|
97
|
+
// This allows a root project to be present without causing lint errors
|
98
|
+
// since all projects will depend on this base file.
|
99
|
+
'^.*/eslint(\\.base)?\\.config\\.[cm]?js$',
|
100
|
+
],
|
101
|
+
depConstraints: [
|
102
|
+
{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] },
|
103
|
+
],
|
104
|
+
},
|
105
|
+
],
|
106
|
+
},
|
107
|
+
}));
|
95
108
|
}
|
96
|
-
|
97
|
-
|
98
|
-
|
109
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
|
110
|
+
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
111
|
+
rules: {},
|
99
112
|
}));
|
100
113
|
return content;
|
101
114
|
};
|
@@ -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, '.');
|
@@ -91,6 +91,11 @@ function migrateEslintFile(projectEslintPath, tree) {
|
|
91
91
|
'plugin:@nrwl/typescript',
|
92
92
|
'plugin:@nrwl/javascript',
|
93
93
|
]);
|
94
|
+
config = (0, ast_utils_1.removePredefinedConfigs)(config, '@nx/eslint-plugin', 'nx', [
|
95
|
+
'flat/base',
|
96
|
+
'flat/typescript',
|
97
|
+
'flat/javascript',
|
98
|
+
]);
|
94
99
|
tree.write(projectEslintPath, config);
|
95
100
|
}
|
96
101
|
else {
|
@@ -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: ['{projectRoot}/eslint.config.{js,cjs,mjs}'],
|
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,11 +8,15 @@ 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
|
*/
|
15
15
|
export declare function addImportToFlatConfig(content: string, variable: string | string[], imp: string): string;
|
16
|
+
/**
|
17
|
+
* Remove an import from flat config
|
18
|
+
*/
|
19
|
+
export declare function removeImportFromFlatConfig(content: string, variable: string, imp: string): string;
|
16
20
|
/**
|
17
21
|
* Injects new ts.expression to the end of the module.exports array.
|
18
22
|
*/
|
@@ -22,6 +26,7 @@ export declare function addBlockToFlatConfigExport(content: string, config: ts.E
|
|
22
26
|
}): string;
|
23
27
|
export declare function removePlugin(content: string, pluginName: string, pluginImport: string): string;
|
24
28
|
export declare function removeCompatExtends(content: string, compatExtends: string[]): string;
|
29
|
+
export declare function removePredefinedConfigs(content: string, moduleImport: string, moduleVariable: string, configs: string[]): string;
|
25
30
|
/**
|
26
31
|
* Add plugins block to the top of the export blocks
|
27
32
|
*/
|
@@ -33,14 +38,15 @@ export declare function addPluginsToExportsBlock(content: string, plugins: {
|
|
33
38
|
/**
|
34
39
|
* Adds compat if missing to flat config
|
35
40
|
*/
|
36
|
-
export declare function
|
41
|
+
export declare function addFlatCompatToFlatConfig(content: string): string;
|
37
42
|
/**
|
38
43
|
* Generate node list representing the imports and the exports blocks
|
39
44
|
* Optionally add flat compat initialization
|
40
45
|
*/
|
41
|
-
export declare function createNodeList(importsMap: Map<string, string>, exportElements: ts.Expression[]
|
46
|
+
export declare function createNodeList(importsMap: Map<string, string>, exportElements: ts.Expression[]): ts.NodeArray<ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile>;
|
42
47
|
export declare function generateSpreadElement(name: string): ts.SpreadElement;
|
43
48
|
export declare function generatePluginExtendsElement(plugins: string[]): ts.SpreadElement;
|
49
|
+
export declare function generatePluginExtendsElementWithCompatFixup(plugin: string): ts.SpreadElement;
|
44
50
|
/**
|
45
51
|
* Stringifies TS nodes to file content string
|
46
52
|
*/
|
@@ -49,12 +55,20 @@ export declare function stringifyNodeList(nodes: ts.NodeArray<ts.VariableStateme
|
|
49
55
|
* generates AST require statement
|
50
56
|
*/
|
51
57
|
export declare function generateRequire(variableName: string | ts.ObjectBindingPattern, imp: string): ts.VariableStatement;
|
58
|
+
export declare function overrideNeedsCompat(override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>): string | string[] | {
|
59
|
+
[name: string]: boolean;
|
60
|
+
};
|
52
61
|
/**
|
53
|
-
* Generates AST object or spread element
|
62
|
+
* Generates an AST object or spread element representing a modern flat config entry,
|
63
|
+
* based on a given legacy eslintrc JSON override object
|
54
64
|
*/
|
55
|
-
export declare function generateFlatOverride(
|
56
|
-
export declare function
|
65
|
+
export declare function generateFlatOverride(_override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>): ts.ObjectLiteralExpression | ts.SpreadElement;
|
66
|
+
export declare function generateFlatPredefinedConfig(predefinedConfigName: string, moduleName?: string, spread?: boolean): ts.ObjectLiteralExpression | ts.SpreadElement | ts.ElementAccessExpression;
|
67
|
+
export declare function mapFilePaths(_override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>): Partial<Linter.ConfigOverride<Linter.RulesRecord>>;
|
57
68
|
/**
|
58
69
|
* Generates an AST from a JSON-type input
|
59
70
|
*/
|
60
|
-
export declare function generateAst<T>(input: unknown
|
71
|
+
export declare function generateAst<T>(input: unknown, propertyAssignmentReplacer?: {
|
72
|
+
keyToMatch: RegExp | string;
|
73
|
+
replacer: (propertyAssignment: ts.PropertyAssignment, propertyName: string) => ts.PropertyAssignment;
|
74
|
+
}): T;
|
@@ -4,17 +4,22 @@ exports.removeOverridesFromLintConfig = removeOverridesFromLintConfig;
|
|
4
4
|
exports.hasOverride = hasOverride;
|
5
5
|
exports.replaceOverride = replaceOverride;
|
6
6
|
exports.addImportToFlatConfig = addImportToFlatConfig;
|
7
|
+
exports.removeImportFromFlatConfig = removeImportFromFlatConfig;
|
7
8
|
exports.addBlockToFlatConfigExport = addBlockToFlatConfigExport;
|
8
9
|
exports.removePlugin = removePlugin;
|
9
10
|
exports.removeCompatExtends = removeCompatExtends;
|
11
|
+
exports.removePredefinedConfigs = removePredefinedConfigs;
|
10
12
|
exports.addPluginsToExportsBlock = addPluginsToExportsBlock;
|
11
|
-
exports.
|
13
|
+
exports.addFlatCompatToFlatConfig = addFlatCompatToFlatConfig;
|
12
14
|
exports.createNodeList = createNodeList;
|
13
15
|
exports.generateSpreadElement = generateSpreadElement;
|
14
16
|
exports.generatePluginExtendsElement = generatePluginExtendsElement;
|
17
|
+
exports.generatePluginExtendsElementWithCompatFixup = generatePluginExtendsElementWithCompatFixup;
|
15
18
|
exports.stringifyNodeList = stringifyNodeList;
|
16
19
|
exports.generateRequire = generateRequire;
|
20
|
+
exports.overrideNeedsCompat = overrideNeedsCompat;
|
17
21
|
exports.generateFlatOverride = generateFlatOverride;
|
22
|
+
exports.generateFlatPredefinedConfig = generateFlatPredefinedConfig;
|
18
23
|
exports.mapFilePaths = mapFilePaths;
|
19
24
|
exports.generateAst = generateAst;
|
20
25
|
const devkit_1 = require("@nx/devkit");
|
@@ -86,10 +91,7 @@ function hasOverride(content, lookup) {
|
|
86
91
|
// strip any spread elements
|
87
92
|
objSource = fullNodeText.replace(SPREAD_ELEMENTS_REGEXP, '');
|
88
93
|
}
|
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": '));
|
94
|
+
const data = parseTextToJson(objSource);
|
93
95
|
if (lookup(data)) {
|
94
96
|
return true;
|
95
97
|
}
|
@@ -101,7 +103,9 @@ function parseTextToJson(text) {
|
|
101
103
|
return (0, devkit_1.parseJson)(text
|
102
104
|
// ensure property names have double quotes so that JSON.parse works
|
103
105
|
.replace(/'/g, '"')
|
104
|
-
.replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": ')
|
106
|
+
.replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": ')
|
107
|
+
// stringify any require calls to avoid JSON parsing errors, turn them into just the string value being required
|
108
|
+
.replace(/require\(['"]([^'"]+)['"]\)/g, '"$1"'));
|
105
109
|
}
|
106
110
|
/**
|
107
111
|
* Finds an override matching the lookup function and applies the update function to it
|
@@ -138,13 +142,18 @@ function replaceOverride(content, root, lookup, update) {
|
|
138
142
|
start,
|
139
143
|
length: end - start,
|
140
144
|
});
|
141
|
-
|
145
|
+
let updatedData = update(data);
|
142
146
|
if (updatedData) {
|
143
|
-
mapFilePaths(updatedData);
|
147
|
+
updatedData = mapFilePaths(updatedData);
|
144
148
|
changes.push({
|
145
149
|
type: devkit_1.ChangeType.Insert,
|
146
150
|
index: start,
|
147
|
-
text: JSON.stringify(updatedData, null, 2)
|
151
|
+
text: JSON.stringify(updatedData, null, 2)
|
152
|
+
// restore any parser require calls that were stripped during JSON parsing
|
153
|
+
.replace(/"parser": "([^"]+)"/g, (_, parser) => {
|
154
|
+
return `"parser": require('${parser}')`;
|
155
|
+
})
|
156
|
+
.slice(2, -2), // remove curly braces and start/end line breaks since we are injecting just properties
|
148
157
|
});
|
149
158
|
}
|
150
159
|
}
|
@@ -226,6 +235,32 @@ function addImportToFlatConfig(content, variable, imp) {
|
|
226
235
|
},
|
227
236
|
]);
|
228
237
|
}
|
238
|
+
/**
|
239
|
+
* Remove an import from flat config
|
240
|
+
*/
|
241
|
+
function removeImportFromFlatConfig(content, variable, imp) {
|
242
|
+
const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
243
|
+
const changes = [];
|
244
|
+
ts.forEachChild(source, (node) => {
|
245
|
+
// we can only combine object binding patterns
|
246
|
+
if (ts.isVariableStatement(node) &&
|
247
|
+
ts.isVariableDeclaration(node.declarationList.declarations[0]) &&
|
248
|
+
ts.isIdentifier(node.declarationList.declarations[0].name) &&
|
249
|
+
node.declarationList.declarations[0].name.getText() === variable &&
|
250
|
+
ts.isCallExpression(node.declarationList.declarations[0].initializer) &&
|
251
|
+
node.declarationList.declarations[0].initializer.expression.getText() ===
|
252
|
+
'require' &&
|
253
|
+
ts.isStringLiteral(node.declarationList.declarations[0].initializer.arguments[0]) &&
|
254
|
+
node.declarationList.declarations[0].initializer.arguments[0].text === imp) {
|
255
|
+
changes.push({
|
256
|
+
type: devkit_1.ChangeType.Delete,
|
257
|
+
start: node.pos,
|
258
|
+
length: node.end - node.pos,
|
259
|
+
});
|
260
|
+
}
|
261
|
+
});
|
262
|
+
return (0, devkit_1.applyChangesToString)(content, changes);
|
263
|
+
}
|
229
264
|
/**
|
230
265
|
* Injects new ts.expression to the end of the module.exports array.
|
231
266
|
*/
|
@@ -242,6 +277,11 @@ function addBlockToFlatConfigExport(content, config, options = {
|
|
242
277
|
return node.expression.right.elements;
|
243
278
|
}
|
244
279
|
});
|
280
|
+
// The config is not in the format that we generate with, skip update.
|
281
|
+
// This could happen during `init-migration` when extracting config from the base, but
|
282
|
+
// base config was not generated by Nx.
|
283
|
+
if (!exportsArray)
|
284
|
+
return content;
|
245
285
|
const insert = printer.printNode(ts.EmitHint.Expression, config, source);
|
246
286
|
if (options.insertAtTheEnd) {
|
247
287
|
const index = exportsArray.length > 0
|
@@ -379,7 +419,7 @@ function removePlugin(content, pluginName, pluginImport) {
|
|
379
419
|
function removeCompatExtends(content, compatExtends) {
|
380
420
|
const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
381
421
|
const changes = [];
|
382
|
-
findAllBlocks(source)
|
422
|
+
findAllBlocks(source)?.forEach((node) => {
|
383
423
|
if (ts.isSpreadElement(node) &&
|
384
424
|
ts.isCallExpression(node.expression) &&
|
385
425
|
ts.isArrowFunction(node.expression.arguments[0]) &&
|
@@ -404,13 +444,45 @@ function removeCompatExtends(content, compatExtends) {
|
|
404
444
|
type: devkit_1.ChangeType.Insert,
|
405
445
|
index: node.pos,
|
406
446
|
text: '\n' +
|
407
|
-
body.replace(new RegExp('[ \t]s*...' + paramName + '[ \t]*,?\\s*', 'g'), ''),
|
447
|
+
body.replace(new RegExp('[ \t]s*...' + paramName + '(\\.rules)?[ \t]*,?\\s*', 'g'), ''),
|
408
448
|
});
|
409
449
|
}
|
410
450
|
}
|
411
451
|
});
|
412
452
|
return (0, devkit_1.applyChangesToString)(content, changes);
|
413
453
|
}
|
454
|
+
function removePredefinedConfigs(content, moduleImport, moduleVariable, configs) {
|
455
|
+
const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
456
|
+
const changes = [];
|
457
|
+
let removeImport = true;
|
458
|
+
findAllBlocks(source)?.forEach((node) => {
|
459
|
+
if (ts.isSpreadElement(node) &&
|
460
|
+
ts.isElementAccessExpression(node.expression) &&
|
461
|
+
ts.isPropertyAccessExpression(node.expression.expression) &&
|
462
|
+
ts.isIdentifier(node.expression.expression.expression) &&
|
463
|
+
node.expression.expression.expression.getText() === moduleVariable &&
|
464
|
+
ts.isStringLiteral(node.expression.argumentExpression)) {
|
465
|
+
const config = node.expression.argumentExpression.getText();
|
466
|
+
// Check the text without quotes
|
467
|
+
if (configs.includes(config.substring(1, config.length - 1))) {
|
468
|
+
changes.push({
|
469
|
+
type: devkit_1.ChangeType.Delete,
|
470
|
+
start: node.pos,
|
471
|
+
length: node.end - node.pos + 1, // trailing comma
|
472
|
+
});
|
473
|
+
}
|
474
|
+
else {
|
475
|
+
// If there is still a config used, do not remove import
|
476
|
+
removeImport = false;
|
477
|
+
}
|
478
|
+
}
|
479
|
+
});
|
480
|
+
let updated = (0, devkit_1.applyChangesToString)(content, changes);
|
481
|
+
if (removeImport) {
|
482
|
+
updated = removeImportFromFlatConfig(updated, moduleVariable, moduleImport);
|
483
|
+
}
|
484
|
+
return updated;
|
485
|
+
}
|
414
486
|
/**
|
415
487
|
* Add plugins block to the top of the export blocks
|
416
488
|
*/
|
@@ -427,7 +499,7 @@ function addPluginsToExportsBlock(content, plugins) {
|
|
427
499
|
/**
|
428
500
|
* Adds compat if missing to flat config
|
429
501
|
*/
|
430
|
-
function
|
502
|
+
function addFlatCompatToFlatConfig(content) {
|
431
503
|
let result = content;
|
432
504
|
result = addImportToFlatConfig(result, 'js', '@eslint/js');
|
433
505
|
if (result.includes('const compat = new FlatCompat')) {
|
@@ -439,28 +511,21 @@ function addCompatToFlatConfig(content) {
|
|
439
511
|
{
|
440
512
|
type: devkit_1.ChangeType.Insert,
|
441
513
|
index: index - 1,
|
442
|
-
text:
|
514
|
+
text: `
|
515
|
+
const compat = new FlatCompat({
|
516
|
+
baseDirectory: __dirname,
|
517
|
+
recommendedConfig: js.configs.recommended,
|
518
|
+
});
|
519
|
+
`,
|
443
520
|
},
|
444
521
|
]);
|
445
522
|
}
|
446
|
-
const DEFAULT_FLAT_CONFIG = `
|
447
|
-
const compat = new FlatCompat({
|
448
|
-
baseDirectory: __dirname,
|
449
|
-
recommendedConfig: js.configs.recommended,
|
450
|
-
});
|
451
|
-
`;
|
452
523
|
/**
|
453
524
|
* Generate node list representing the imports and the exports blocks
|
454
525
|
* Optionally add flat compat initialization
|
455
526
|
*/
|
456
|
-
function createNodeList(importsMap, exportElements
|
527
|
+
function createNodeList(importsMap, exportElements) {
|
457
528
|
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
529
|
// generateRequire(varName, imp, ts.factory);
|
465
530
|
Array.from(importsMap.entries()).forEach(([imp, varName]) => {
|
466
531
|
importsList.push(generateRequire(varName, imp));
|
@@ -468,7 +533,7 @@ function createNodeList(importsMap, exportElements, isFlatCompatNeeded) {
|
|
468
533
|
return ts.factory.createNodeArray([
|
469
534
|
// add plugin imports
|
470
535
|
...importsList,
|
471
|
-
ts.createSourceFile('',
|
536
|
+
ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.JS),
|
472
537
|
// creates:
|
473
538
|
// module.exports = [ ... ];
|
474
539
|
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 +545,11 @@ function generateSpreadElement(name) {
|
|
480
545
|
function generatePluginExtendsElement(plugins) {
|
481
546
|
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
547
|
}
|
548
|
+
function generatePluginExtendsElementWithCompatFixup(plugin) {
|
549
|
+
return ts.factory.createSpreadElement(ts.factory.createCallExpression(ts.factory.createIdentifier('fixupConfigRules'), undefined, [
|
550
|
+
ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('compat'), ts.factory.createIdentifier('extends')), undefined, [ts.factory.createStringLiteral(plugin)]),
|
551
|
+
]));
|
552
|
+
}
|
483
553
|
/**
|
484
554
|
* Stringifies TS nodes to file content string
|
485
555
|
*/
|
@@ -502,21 +572,95 @@ function generateRequire(variableName, imp) {
|
|
502
572
|
], ts.NodeFlags.Const));
|
503
573
|
}
|
504
574
|
/**
|
505
|
-
*
|
575
|
+
* FROM: https://github.com/eslint/rewrite/blob/e2a7ec809db20e638abbad250d105ddbde88a8d5/packages/migrate-config/src/migrate-config.js#L222
|
576
|
+
*
|
577
|
+
* Converts a glob pattern to a format that can be used in a flat config.
|
578
|
+
* @param {string} pattern The glob pattern to convert.
|
579
|
+
* @returns {string} The converted glob pattern.
|
580
|
+
*/
|
581
|
+
function convertGlobPattern(pattern) {
|
582
|
+
const isNegated = pattern.startsWith('!');
|
583
|
+
const patternToTest = isNegated ? pattern.slice(1) : pattern;
|
584
|
+
// if the pattern is already in the correct format, return it
|
585
|
+
if (patternToTest === '**' || patternToTest.includes('/')) {
|
586
|
+
return pattern;
|
587
|
+
}
|
588
|
+
return `${isNegated ? '!' : ''}**/${patternToTest}`;
|
589
|
+
}
|
590
|
+
// FROM: https://github.com/eslint/rewrite/blob/e2a7ec809db20e638abbad250d105ddbde88a8d5/packages/migrate-config/src/migrate-config.js#L38
|
591
|
+
const keysToCopy = ['settings', 'rules', 'processor'];
|
592
|
+
function overrideNeedsCompat(override) {
|
593
|
+
return override.env || override.extends || override.plugins;
|
594
|
+
}
|
595
|
+
/**
|
596
|
+
* Generates an AST object or spread element representing a modern flat config entry,
|
597
|
+
* based on a given legacy eslintrc JSON override object
|
506
598
|
*/
|
507
|
-
function generateFlatOverride(
|
508
|
-
mapFilePaths(
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
if (
|
514
|
-
|
515
|
-
|
599
|
+
function generateFlatOverride(_override) {
|
600
|
+
const override = mapFilePaths(_override);
|
601
|
+
// We do not need the compat tooling for this override
|
602
|
+
if (!overrideNeedsCompat(override)) {
|
603
|
+
// Ensure files is an array
|
604
|
+
let files = override.files;
|
605
|
+
if (typeof files === 'string') {
|
606
|
+
files = [files];
|
607
|
+
}
|
608
|
+
const flatConfigOverride = {
|
609
|
+
files,
|
610
|
+
};
|
611
|
+
if (override.rules) {
|
612
|
+
flatConfigOverride.rules = override.rules;
|
516
613
|
}
|
517
|
-
|
614
|
+
// Copy over everything that stays the same
|
615
|
+
keysToCopy.forEach((key) => {
|
616
|
+
if (override[key]) {
|
617
|
+
flatConfigOverride[key] = override[key];
|
618
|
+
}
|
619
|
+
});
|
620
|
+
if (override.parser || override.parserOptions) {
|
621
|
+
const languageOptions = {};
|
622
|
+
if (override.parser) {
|
623
|
+
languageOptions['parser'] = override.parser;
|
624
|
+
}
|
625
|
+
if (override.parserOptions) {
|
626
|
+
languageOptions['parserOptions'] = override.parserOptions;
|
627
|
+
}
|
628
|
+
if (Object.keys(languageOptions).length) {
|
629
|
+
flatConfigOverride.languageOptions = languageOptions;
|
630
|
+
}
|
631
|
+
}
|
632
|
+
if (override['languageOptions']) {
|
633
|
+
flatConfigOverride.languageOptions = override['languageOptions'];
|
634
|
+
}
|
635
|
+
if (override.excludedFiles) {
|
636
|
+
flatConfigOverride.ignores = (Array.isArray(override.excludedFiles)
|
637
|
+
? override.excludedFiles
|
638
|
+
: [override.excludedFiles]).map((p) => convertGlobPattern(p));
|
639
|
+
}
|
640
|
+
return generateAst(flatConfigOverride, {
|
641
|
+
keyToMatch: /^(parser|rules)$/,
|
642
|
+
replacer: (propertyAssignment, propertyName) => {
|
643
|
+
if (propertyName === 'rules') {
|
644
|
+
// Add comment that user can override rules if there are no overrides.
|
645
|
+
if (ts.isObjectLiteralExpression(propertyAssignment.initializer) &&
|
646
|
+
propertyAssignment.initializer.properties.length === 0) {
|
647
|
+
return ts.addSyntheticLeadingComment(ts.factory.createPropertyAssignment(propertyAssignment.name, ts.factory.createObjectLiteralExpression([])), ts.SyntaxKind.SingleLineCommentTrivia, ' Override or add rules here');
|
648
|
+
}
|
649
|
+
return propertyAssignment;
|
650
|
+
}
|
651
|
+
else {
|
652
|
+
// Change parser to require statement.
|
653
|
+
return ts.factory.createPropertyAssignment('parser', ts.factory.createCallExpression(ts.factory.createIdentifier('require'), undefined, [
|
654
|
+
ts.factory.createStringLiteral(override['languageOptions']?.['parserOptions']?.parser ??
|
655
|
+
override['languageOptions']?.parser ??
|
656
|
+
override.parser),
|
657
|
+
]));
|
658
|
+
}
|
659
|
+
},
|
660
|
+
});
|
518
661
|
}
|
519
|
-
|
662
|
+
// At this point we are applying the flat config compat tooling to the override
|
663
|
+
const { excludedFiles, parser, parserOptions, rules, files, ...rest } = override;
|
520
664
|
const objectLiteralElements = [
|
521
665
|
ts.factory.createSpreadAssignment(ts.factory.createIdentifier('config')),
|
522
666
|
];
|
@@ -543,7 +687,14 @@ function generateFlatOverride(override) {
|
|
543
687
|
], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createParenthesizedExpression(ts.factory.createObjectLiteralExpression(objectLiteralElements, true))),
|
544
688
|
]));
|
545
689
|
}
|
546
|
-
function
|
690
|
+
function generateFlatPredefinedConfig(predefinedConfigName, moduleName = 'nx', spread = true) {
|
691
|
+
const node = ts.factory.createElementAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(moduleName), ts.factory.createIdentifier('configs')), ts.factory.createStringLiteral(predefinedConfigName));
|
692
|
+
return spread ? ts.factory.createSpreadElement(node) : node;
|
693
|
+
}
|
694
|
+
function mapFilePaths(_override) {
|
695
|
+
const override = {
|
696
|
+
..._override,
|
697
|
+
};
|
547
698
|
if (override.files) {
|
548
699
|
override.files = Array.isArray(override.files)
|
549
700
|
? override.files
|
@@ -556,6 +707,7 @@ function mapFilePaths(override) {
|
|
556
707
|
: [override.excludedFiles];
|
557
708
|
override.excludedFiles = override.excludedFiles.map((file) => (0, path_utils_1.mapFilePath)(file));
|
558
709
|
}
|
710
|
+
return override;
|
559
711
|
}
|
560
712
|
function addTSObjectProperty(elements, key, value) {
|
561
713
|
if (value) {
|
@@ -565,18 +717,16 @@ function addTSObjectProperty(elements, key, value) {
|
|
565
717
|
/**
|
566
718
|
* Generates an AST from a JSON-type input
|
567
719
|
*/
|
568
|
-
function generateAst(input) {
|
720
|
+
function generateAst(input, propertyAssignmentReplacer) {
|
569
721
|
if (Array.isArray(input)) {
|
570
|
-
return ts.factory.createArrayLiteralExpression(input.map((item) => generateAst(item)), input.length > 1 // multiline only if more than one item
|
722
|
+
return ts.factory.createArrayLiteralExpression(input.map((item) => generateAst(item, propertyAssignmentReplacer)), input.length > 1 // multiline only if more than one item
|
571
723
|
);
|
572
724
|
}
|
573
725
|
if (input === null) {
|
574
726
|
return ts.factory.createNull();
|
575
727
|
}
|
576
728
|
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
|
729
|
+
return ts.factory.createObjectLiteralExpression(generatePropertyAssignmentsFromObjectEntries(input, propertyAssignmentReplacer), Object.keys(input).length > 1 // multiline only if more than one property
|
580
730
|
);
|
581
731
|
}
|
582
732
|
if (typeof input === 'string') {
|
@@ -591,6 +741,20 @@ function generateAst(input) {
|
|
591
741
|
// since we are parsing JSON, this should never happen
|
592
742
|
throw new Error(`Unknown type: ${typeof input} `);
|
593
743
|
}
|
744
|
+
function generatePropertyAssignmentsFromObjectEntries(input, propertyAssignmentReplacer) {
|
745
|
+
return Object.entries(input)
|
746
|
+
.filter(([_, value]) => value !== undefined)
|
747
|
+
.map(([key, value]) => {
|
748
|
+
const original = ts.factory.createPropertyAssignment(isValidKey(key) ? key : ts.factory.createStringLiteral(key), generateAst(value, propertyAssignmentReplacer));
|
749
|
+
if (propertyAssignmentReplacer &&
|
750
|
+
(typeof propertyAssignmentReplacer.keyToMatch === 'string'
|
751
|
+
? key === propertyAssignmentReplacer.keyToMatch
|
752
|
+
: propertyAssignmentReplacer.keyToMatch.test(key))) {
|
753
|
+
return propertyAssignmentReplacer.replacer(original, key);
|
754
|
+
}
|
755
|
+
return original;
|
756
|
+
});
|
757
|
+
}
|
594
758
|
function isValidKey(key) {
|
595
759
|
return /^[a-zA-Z0-9_]+$/.test(key);
|
596
760
|
}
|
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) => {
|
@@ -96,7 +98,9 @@ const internalCreateNodes = async (configFilePath, options, context, projectsCac
|
|
96
98
|
};
|
97
99
|
const internalCreateNodesV2 = async (configFilePath, options, context, eslintConfigFiles, projectRootsByEslintRoots, lintableFilesPerProjectRoot, projectsCache) => {
|
98
100
|
const configDir = (0, posix_1.dirname)(configFilePath);
|
99
|
-
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
|
+
});
|
100
104
|
const eslintVersion = ESLint.version;
|
101
105
|
const projects = {};
|
102
106
|
await Promise.all(projectRootsByEslintRoots.get(configDir).map(async (projectRoot) => {
|
@@ -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';
|