@nx/eslint 19.8.0-canary.20240912-b6140d4 → 19.8.0-canary.20240913-5bbaffb
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/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.8.0-canary.
|
3
|
+
"version": "19.8.0-canary.20240913-5bbaffb",
|
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.8.0-canary.
|
39
|
-
"@nx/js": "19.8.0-canary.
|
38
|
+
"@nx/devkit": "19.8.0-canary.20240913-5bbaffb",
|
39
|
+
"@nx/js": "19.8.0-canary.20240913-5bbaffb",
|
40
40
|
"semver": "^7.5.3",
|
41
41
|
"tslib": "^2.3.0",
|
42
42
|
"typescript": "~5.4.2",
|
43
|
-
"@nx/linter": "19.8.0-canary.
|
43
|
+
"@nx/linter": "19.8.0-canary.20240913-5bbaffb"
|
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
|
}
|
@@ -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';
|