@nx/eslint 17.0.0-beta.8
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/LICENSE +22 -0
- package/README.md +61 -0
- package/executors.json +10 -0
- package/generators.json +22 -0
- package/index.d.ts +4 -0
- package/index.js +14 -0
- package/migrations.json +79 -0
- package/package.json +51 -0
- package/src/executors/lint/hasher.d.ts +9 -0
- package/src/executors/lint/hasher.js +43 -0
- package/src/executors/lint/lint.impl.d.ts +5 -0
- package/src/executors/lint/lint.impl.js +140 -0
- package/src/executors/lint/schema.d.ts +39 -0
- package/src/executors/lint/schema.json +144 -0
- package/src/executors/lint/utility/eslint-utils.d.ts +6 -0
- package/src/executors/lint/utility/eslint-utils.js +71 -0
- package/src/generators/convert-to-flat-config/converters/json-converter.d.ts +6 -0
- package/src/generators/convert-to-flat-config/converters/json-converter.js +180 -0
- package/src/generators/convert-to-flat-config/generator.d.ts +4 -0
- package/src/generators/convert-to-flat-config/generator.js +83 -0
- package/src/generators/convert-to-flat-config/schema.d.ts +3 -0
- package/src/generators/convert-to-flat-config/schema.json +17 -0
- package/src/generators/init/global-eslint-config.d.ts +29 -0
- package/src/generators/init/global-eslint-config.js +98 -0
- package/src/generators/init/init-migration.d.ts +3 -0
- package/src/generators/init/init-migration.js +103 -0
- package/src/generators/init/init.d.ts +9 -0
- package/src/generators/init/init.js +65 -0
- package/src/generators/lint-project/lint-project.d.ts +16 -0
- package/src/generators/lint-project/lint-project.js +191 -0
- package/src/generators/utils/eslint-file.d.ts +19 -0
- package/src/generators/utils/eslint-file.js +269 -0
- package/src/generators/utils/eslint-targets.d.ts +2 -0
- package/src/generators/utils/eslint-targets.js +18 -0
- package/src/generators/utils/flat-config/ast-utils.d.ts +61 -0
- package/src/generators/utils/flat-config/ast-utils.js +581 -0
- package/src/generators/utils/flat-config/path-utils.d.ts +2 -0
- package/src/generators/utils/flat-config/path-utils.js +31 -0
- package/src/generators/utils/linter.d.ts +4 -0
- package/src/generators/utils/linter.js +8 -0
- package/src/generators/workspace-rule/files/__name__.spec.ts__tmpl__ +11 -0
- package/src/generators/workspace-rule/files/__name__.ts__tmpl__ +37 -0
- package/src/generators/workspace-rule/schema.json +26 -0
- package/src/generators/workspace-rule/workspace-rule.d.ts +6 -0
- package/src/generators/workspace-rule/workspace-rule.js +73 -0
- package/src/generators/workspace-rules-project/files/index.ts__tmpl__ +27 -0
- package/src/generators/workspace-rules-project/files/tsconfig.json__tmpl__ +13 -0
- package/src/generators/workspace-rules-project/files/tsconfig.lint.json__tmpl__ +9 -0
- package/src/generators/workspace-rules-project/schema.json +23 -0
- package/src/generators/workspace-rules-project/workspace-rules-project.d.ts +7 -0
- package/src/generators/workspace-rules-project/workspace-rules-project.js +80 -0
- package/src/migrations/update-15-0-0/add-eslint-inputs.d.ts +2 -0
- package/src/migrations/update-15-0-0/add-eslint-inputs.js +27 -0
- package/src/migrations/update-15-7-1/add-eslint-ignore.d.ts +2 -0
- package/src/migrations/update-15-7-1/add-eslint-ignore.js +36 -0
- package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.d.ts +2 -0
- package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.js +9 -0
- package/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.d.ts +2 -0
- package/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.js +44 -0
- package/src/migrations/update-17-0-0-rename-to-eslint/update-17-0-0-rename-to-eslint.d.ts +2 -0
- package/src/migrations/update-17-0-0-rename-to-eslint/update-17-0-0-rename-to-eslint.js +45 -0
- package/src/utils/flat-config.d.ts +2 -0
- package/src/utils/flat-config.js +7 -0
- package/src/utils/rules-requiring-type-checking.d.ts +3 -0
- package/src/utils/rules-requiring-type-checking.js +84 -0
- package/src/utils/versions.d.ts +5 -0
- package/src/utils/versions.js +8 -0
- package/src/utils/workspace-lint-rules.d.ts +1 -0
- package/src/utils/workspace-lint-rules.js +5 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveAndInstantiateESLint = void 0;
|
|
4
|
+
async function resolveESLintClass(useFlatConfig = false) {
|
|
5
|
+
try {
|
|
6
|
+
if (!useFlatConfig) {
|
|
7
|
+
return (await Promise.resolve().then(() => require('eslint'))).ESLint;
|
|
8
|
+
}
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
10
|
+
const { FlatESLint } = require('eslint/use-at-your-own-risk');
|
|
11
|
+
return FlatESLint;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new Error('Unable to find ESLint. Ensure ESLint is installed.');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function resolveAndInstantiateESLint(eslintConfigPath, options, useFlatConfig = false) {
|
|
18
|
+
if (useFlatConfig &&
|
|
19
|
+
eslintConfigPath &&
|
|
20
|
+
!eslintConfigPath?.endsWith('eslint.config.js')) {
|
|
21
|
+
throw new Error('When using the new Flat Config with ESLint, all configs must be named eslint.config.js and .eslintrc files may not be used. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
|
|
22
|
+
}
|
|
23
|
+
const ESLint = await resolveESLintClass(useFlatConfig);
|
|
24
|
+
const eslintOptions = {
|
|
25
|
+
overrideConfigFile: eslintConfigPath,
|
|
26
|
+
fix: !!options.fix,
|
|
27
|
+
cache: !!options.cache,
|
|
28
|
+
cacheLocation: options.cacheLocation || undefined,
|
|
29
|
+
cacheStrategy: options.cacheStrategy || undefined,
|
|
30
|
+
/**
|
|
31
|
+
* Default is `true` and if not overridden the eslint.lintFiles() method will throw an error
|
|
32
|
+
* when no target files are found.
|
|
33
|
+
*
|
|
34
|
+
* We don't want ESLint to throw an error if a user has only just created
|
|
35
|
+
* a project and therefore doesn't necessarily have matching files, for example.
|
|
36
|
+
*
|
|
37
|
+
* Also, the angular generator creates a lint pattern for `html` files, but there may
|
|
38
|
+
* not be any html files in the project, so keeping it true would break linting every time
|
|
39
|
+
*/
|
|
40
|
+
errorOnUnmatchedPattern: false,
|
|
41
|
+
reportUnusedDisableDirectives: options.reportUnusedDisableDirectives || undefined,
|
|
42
|
+
};
|
|
43
|
+
if (useFlatConfig) {
|
|
44
|
+
if (typeof options.useEslintrc !== 'undefined') {
|
|
45
|
+
throw new Error('For Flat Config, the `useEslintrc` option is not applicable. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
|
|
46
|
+
}
|
|
47
|
+
if (options.resolvePluginsRelativeTo !== undefined) {
|
|
48
|
+
throw new Error('For Flat Config, ESLint removed `resolvePluginsRelativeTo` and so it is not supported as an option. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
|
|
49
|
+
}
|
|
50
|
+
if (options.ignorePath !== undefined) {
|
|
51
|
+
throw new Error('For Flat Config, ESLint removed `ignorePath` and so it is not supported as an option. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
eslintOptions.rulePaths = options.rulesdir || [];
|
|
56
|
+
eslintOptions.resolvePluginsRelativeTo =
|
|
57
|
+
options.resolvePluginsRelativeTo || undefined;
|
|
58
|
+
eslintOptions.ignorePath = options.ignorePath || undefined;
|
|
59
|
+
/**
|
|
60
|
+
* If "noEslintrc" is set to `true` (and therefore here "useEslintrc" will be `false`), then ESLint will not
|
|
61
|
+
* merge the provided config with others it finds automatically.
|
|
62
|
+
*/
|
|
63
|
+
eslintOptions.useEslintrc = !options.noEslintrc;
|
|
64
|
+
}
|
|
65
|
+
const eslint = new ESLint(eslintOptions);
|
|
66
|
+
return {
|
|
67
|
+
ESLint,
|
|
68
|
+
eslint,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
exports.resolveAndInstantiateESLint = resolveAndInstantiateESLint;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Tree } from '@nx/devkit';
|
|
2
|
+
/**
|
|
3
|
+
* Converts an ESLint JSON config to a flat config.
|
|
4
|
+
* Deletes the original file along with .eslintignore if it exists.
|
|
5
|
+
*/
|
|
6
|
+
export declare function convertEslintJsonToFlatConfig(tree: Tree, root: string, sourceFile: string, destinationFile: string): void;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertEslintJsonToFlatConfig = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const ts = require("typescript");
|
|
7
|
+
const versions_1 = require("../../../utils/versions");
|
|
8
|
+
const ast_utils_1 = require("../../utils/flat-config/ast-utils");
|
|
9
|
+
const eslint_file_1 = require("../../utils/eslint-file");
|
|
10
|
+
/**
|
|
11
|
+
* Converts an ESLint JSON config to a flat config.
|
|
12
|
+
* Deletes the original file along with .eslintignore if it exists.
|
|
13
|
+
*/
|
|
14
|
+
function convertEslintJsonToFlatConfig(tree, root, sourceFile, destinationFile) {
|
|
15
|
+
const importsMap = new Map();
|
|
16
|
+
const exportElements = [];
|
|
17
|
+
let isFlatCompatNeeded = false;
|
|
18
|
+
let combinedConfig = [];
|
|
19
|
+
let languageOptions = [];
|
|
20
|
+
// read original config
|
|
21
|
+
const config = (0, devkit_1.readJson)(tree, `${root}/${sourceFile}`);
|
|
22
|
+
if (config.extends) {
|
|
23
|
+
isFlatCompatNeeded = addExtends(importsMap, exportElements, config, tree);
|
|
24
|
+
}
|
|
25
|
+
if (config.plugins) {
|
|
26
|
+
addPlugins(importsMap, exportElements, config);
|
|
27
|
+
}
|
|
28
|
+
if (config.parser) {
|
|
29
|
+
const imp = config.parser;
|
|
30
|
+
const parserName = (0, devkit_1.names)(imp).propertyName;
|
|
31
|
+
importsMap.set(imp, parserName);
|
|
32
|
+
languageOptions.push(ts.factory.createPropertyAssignment('parser', ts.factory.createIdentifier(parserName)));
|
|
33
|
+
}
|
|
34
|
+
if (config.parserOptions) {
|
|
35
|
+
languageOptions.push(ts.factory.createPropertyAssignment('parserOptions', (0, ast_utils_1.generateAst)(config.parserOptions)));
|
|
36
|
+
}
|
|
37
|
+
if (config.globals || config.env) {
|
|
38
|
+
if (config.env) {
|
|
39
|
+
importsMap.set('globals', 'globals');
|
|
40
|
+
}
|
|
41
|
+
languageOptions.push(ts.factory.createPropertyAssignment('globals', ts.factory.createObjectLiteralExpression([
|
|
42
|
+
...Object.keys(config.env || {}).map((env) => ts.factory.createSpreadAssignment(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('globals'), ts.factory.createIdentifier(env)))),
|
|
43
|
+
...Object.keys(config.globals || {}).map((key) => ts.factory.createPropertyAssignment(key, (0, ast_utils_1.generateAst)(config.globals[key]))),
|
|
44
|
+
])));
|
|
45
|
+
}
|
|
46
|
+
if (config.settings) {
|
|
47
|
+
combinedConfig.push(ts.factory.createPropertyAssignment('settings', (0, ast_utils_1.generateAst)(config.settings)));
|
|
48
|
+
}
|
|
49
|
+
if (config.noInlineConfig !== undefined ||
|
|
50
|
+
config.reportUnusedDisableDirectives !== undefined) {
|
|
51
|
+
combinedConfig.push(ts.factory.createPropertyAssignment('linterOptions', (0, ast_utils_1.generateAst)({
|
|
52
|
+
noInlineConfig: config.noInlineConfig,
|
|
53
|
+
reportUnusedDisableDirectives: config.reportUnusedDisableDirectives,
|
|
54
|
+
})));
|
|
55
|
+
}
|
|
56
|
+
if (languageOptions.length > 0) {
|
|
57
|
+
combinedConfig.push(ts.factory.createPropertyAssignment('languageOptions', ts.factory.createObjectLiteralExpression(languageOptions, languageOptions.length > 1)));
|
|
58
|
+
}
|
|
59
|
+
if (combinedConfig.length > 0) {
|
|
60
|
+
exportElements.push(ts.factory.createObjectLiteralExpression(combinedConfig, combinedConfig.length > 1));
|
|
61
|
+
}
|
|
62
|
+
if (config.rules) {
|
|
63
|
+
exportElements.push((0, ast_utils_1.generateAst)({ rules: config.rules }));
|
|
64
|
+
}
|
|
65
|
+
if (config.overrides) {
|
|
66
|
+
config.overrides.forEach((override) => {
|
|
67
|
+
if (override.env ||
|
|
68
|
+
override.extends ||
|
|
69
|
+
override.plugins ||
|
|
70
|
+
override.parser) {
|
|
71
|
+
isFlatCompatNeeded = true;
|
|
72
|
+
}
|
|
73
|
+
exportElements.push((0, ast_utils_1.generateFlatOverride)(override, root));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (config.ignorePatterns) {
|
|
77
|
+
const patterns = (Array.isArray(config.ignorePatterns)
|
|
78
|
+
? config.ignorePatterns
|
|
79
|
+
: [config.ignorePatterns]).filter((pattern) => !['**/*', '!**/*', 'node_modules'].includes(pattern)); // these are useless in a flat config
|
|
80
|
+
if (patterns.length > 0) {
|
|
81
|
+
exportElements.push((0, ast_utils_1.generateAst)({
|
|
82
|
+
ignores: patterns.map((path) => (0, ast_utils_1.mapFilePath)(path, root)),
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (tree.exists(`${root}/.eslintignore`)) {
|
|
87
|
+
const patterns = tree
|
|
88
|
+
.read(`${root}/.eslintignore`, 'utf-8')
|
|
89
|
+
.split('\n')
|
|
90
|
+
.filter((line) => line.length > 0 && line !== 'node_modules')
|
|
91
|
+
.map((path) => (0, ast_utils_1.mapFilePath)(path, root));
|
|
92
|
+
if (patterns.length > 0) {
|
|
93
|
+
exportElements.push((0, ast_utils_1.generateAst)({ ignores: patterns }));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
tree.delete((0, path_1.join)(root, sourceFile));
|
|
97
|
+
tree.delete((0, path_1.join)(root, '.eslintignore'));
|
|
98
|
+
// create the node list and print it to new file
|
|
99
|
+
const nodeList = (0, ast_utils_1.createNodeList)(importsMap, exportElements, isFlatCompatNeeded);
|
|
100
|
+
const content = (0, ast_utils_1.stringifyNodeList)(nodeList, root, destinationFile);
|
|
101
|
+
tree.write((0, path_1.join)(root, destinationFile), content);
|
|
102
|
+
if (isFlatCompatNeeded) {
|
|
103
|
+
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
104
|
+
'@eslint/eslintrc': versions_1.eslintrcVersion,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.convertEslintJsonToFlatConfig = convertEslintJsonToFlatConfig;
|
|
109
|
+
// add parsed extends to export blocks and add import statements
|
|
110
|
+
function addExtends(importsMap, configBlocks, config, tree) {
|
|
111
|
+
let isFlatCompatNeeded = false;
|
|
112
|
+
const extendsConfig = Array.isArray(config.extends)
|
|
113
|
+
? config.extends
|
|
114
|
+
: [config.extends];
|
|
115
|
+
const eslintrcConfigs = [];
|
|
116
|
+
// add base extends
|
|
117
|
+
extendsConfig
|
|
118
|
+
.filter((imp) => imp.match(/^\.?(\.\/)/))
|
|
119
|
+
.forEach((imp, index) => {
|
|
120
|
+
if (imp.match(/\.eslintrc(.base)?\.json$/)) {
|
|
121
|
+
const localName = index ? `baseConfig${index}` : 'baseConfig';
|
|
122
|
+
configBlocks.push((0, ast_utils_1.generateSpreadElement)(localName));
|
|
123
|
+
const newImport = imp.replace(/^(.*)\.eslintrc(.base)?\.json$/, '$1eslint$2.config.js');
|
|
124
|
+
importsMap.set(newImport, localName);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
eslintrcConfigs.push(imp);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
// add plugin extends
|
|
131
|
+
const pluginExtends = extendsConfig.filter((imp) => !imp.match(/^\.?(\.\/)/));
|
|
132
|
+
if (pluginExtends.length) {
|
|
133
|
+
const eslintPluginExtends = pluginExtends.filter((imp) => imp.startsWith('eslint:'));
|
|
134
|
+
pluginExtends.forEach((imp) => {
|
|
135
|
+
if (!imp.startsWith('eslint:')) {
|
|
136
|
+
eslintrcConfigs.push(imp);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
if (eslintPluginExtends.length) {
|
|
140
|
+
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
141
|
+
'@eslint/js': versions_1.eslintVersion,
|
|
142
|
+
});
|
|
143
|
+
importsMap.set('@eslint/js', 'js');
|
|
144
|
+
eslintPluginExtends.forEach((plugin) => {
|
|
145
|
+
configBlocks.push(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('js'), ts.factory.createIdentifier('configs')), ts.factory.createIdentifier(plugin.slice(7)) // strip 'eslint:' prefix
|
|
146
|
+
));
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (eslintrcConfigs.length) {
|
|
151
|
+
isFlatCompatNeeded = true;
|
|
152
|
+
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
153
|
+
'@eslint/js': versions_1.eslintVersion,
|
|
154
|
+
});
|
|
155
|
+
configBlocks.push((0, ast_utils_1.generatePluginExtendsElement)(eslintrcConfigs));
|
|
156
|
+
}
|
|
157
|
+
return isFlatCompatNeeded;
|
|
158
|
+
}
|
|
159
|
+
function addPlugins(importsMap, configBlocks, config) {
|
|
160
|
+
const mappedPlugins = [];
|
|
161
|
+
config.plugins.forEach((name) => {
|
|
162
|
+
const imp = (0, eslint_file_1.getPluginImport)(name);
|
|
163
|
+
const varName = (0, devkit_1.names)(imp).propertyName;
|
|
164
|
+
mappedPlugins.push({ name, varName, imp });
|
|
165
|
+
});
|
|
166
|
+
mappedPlugins.forEach(({ varName, imp }) => {
|
|
167
|
+
importsMap.set(imp, varName);
|
|
168
|
+
});
|
|
169
|
+
const pluginsAst = ts.factory.createObjectLiteralExpression([
|
|
170
|
+
ts.factory.createPropertyAssignment('plugins', ts.factory.createObjectLiteralExpression(mappedPlugins.map(({ name, varName }) => {
|
|
171
|
+
return ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(name), ts.factory.createIdentifier(varName));
|
|
172
|
+
}), mappedPlugins.length > 1)),
|
|
173
|
+
...(config.processor
|
|
174
|
+
? [
|
|
175
|
+
ts.factory.createPropertyAssignment('processor', ts.factory.createStringLiteral(config.processor)),
|
|
176
|
+
]
|
|
177
|
+
: []),
|
|
178
|
+
], false);
|
|
179
|
+
configBlocks.push(pluginsAst);
|
|
180
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Tree } from '@nx/devkit';
|
|
2
|
+
import { ConvertToFlatConfigGeneratorSchema } from './schema';
|
|
3
|
+
export declare function convertToFlatConfigGenerator(tree: Tree, options: ConvertToFlatConfigGeneratorSchema): Promise<void>;
|
|
4
|
+
export default convertToFlatConfigGenerator;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertToFlatConfigGenerator = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const eslint_file_1 = require("../utils/eslint-file");
|
|
6
|
+
const json_converter_1 = require("./converters/json-converter");
|
|
7
|
+
async function convertToFlatConfigGenerator(tree, options) {
|
|
8
|
+
const eslintFile = (0, eslint_file_1.findEslintFile)(tree);
|
|
9
|
+
if (!eslintFile) {
|
|
10
|
+
throw new Error('Could not find root eslint file');
|
|
11
|
+
}
|
|
12
|
+
if (!eslintFile.endsWith('.json')) {
|
|
13
|
+
throw new Error('Only json eslint config files are supported for conversion');
|
|
14
|
+
}
|
|
15
|
+
// rename root eslint config to eslint.config.js
|
|
16
|
+
convertRootToFlatConfig(tree, eslintFile);
|
|
17
|
+
// rename and map files
|
|
18
|
+
const projects = (0, devkit_1.getProjects)(tree);
|
|
19
|
+
for (const [project, projectConfig] of projects) {
|
|
20
|
+
convertProjectToFlatConfig(tree, project, projectConfig, (0, devkit_1.readNxJson)(tree));
|
|
21
|
+
}
|
|
22
|
+
// replace references in nx.json
|
|
23
|
+
updateNxJsonConfig(tree);
|
|
24
|
+
// install missing packages
|
|
25
|
+
if (!options.skipFormat) {
|
|
26
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.convertToFlatConfigGenerator = convertToFlatConfigGenerator;
|
|
30
|
+
exports.default = convertToFlatConfigGenerator;
|
|
31
|
+
function convertRootToFlatConfig(tree, eslintFile) {
|
|
32
|
+
if (eslintFile.endsWith('.base.json')) {
|
|
33
|
+
convertConfigToFlatConfig(tree, '', '.eslintrc.base.json', 'eslint.base.config.js');
|
|
34
|
+
}
|
|
35
|
+
convertConfigToFlatConfig(tree, '', '.eslintrc.json', 'eslint.config.js');
|
|
36
|
+
}
|
|
37
|
+
function convertProjectToFlatConfig(tree, project, projectConfig, nxJson) {
|
|
38
|
+
if (tree.exists(`${projectConfig.root}/.eslintrc.json`)) {
|
|
39
|
+
if (projectConfig.targets) {
|
|
40
|
+
const eslintTargets = Object.keys(projectConfig.targets || {}).filter((t) => projectConfig.targets[t].executor === '@nx/eslint:lint');
|
|
41
|
+
for (const target of eslintTargets) {
|
|
42
|
+
// remove any obsolete `eslintConfig` options pointing to the old config file
|
|
43
|
+
if (projectConfig.targets[target].options?.eslintConfig) {
|
|
44
|
+
delete projectConfig.targets[target].options.eslintConfig;
|
|
45
|
+
}
|
|
46
|
+
(0, devkit_1.updateProjectConfiguration)(tree, project, projectConfig);
|
|
47
|
+
}
|
|
48
|
+
const nxHasLintTargets = Object.keys(nxJson.targetDefaults || {}).some((t) => (t === '@nx/eslint:lint' ||
|
|
49
|
+
nxJson.targetDefaults[t].executor === '@nx/eslint:lint') &&
|
|
50
|
+
projectConfig.targets?.[t]);
|
|
51
|
+
if (nxHasLintTargets || eslintTargets.length > 0) {
|
|
52
|
+
convertConfigToFlatConfig(tree, projectConfig.root, '.eslintrc.json', 'eslint.config.js');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// update names of eslint files in nx.json
|
|
58
|
+
// and remove eslintignore
|
|
59
|
+
function updateNxJsonConfig(tree) {
|
|
60
|
+
if (tree.exists('nx.json')) {
|
|
61
|
+
(0, devkit_1.updateJson)(tree, 'nx.json', (json) => {
|
|
62
|
+
if (json.targetDefaults?.lint?.inputs) {
|
|
63
|
+
const inputSet = new Set(json.targetDefaults.lint.inputs);
|
|
64
|
+
inputSet.add('{workspaceRoot}/eslint.config.js');
|
|
65
|
+
json.targetDefaults.lint.inputs = Array.from(inputSet);
|
|
66
|
+
}
|
|
67
|
+
if (json.targetDefaults?.['@nx/eslint:lint']?.inputs) {
|
|
68
|
+
const inputSet = new Set(json.targetDefaults['@nx/eslint:lint'].inputs);
|
|
69
|
+
inputSet.add('{workspaceRoot}/eslint.config.js');
|
|
70
|
+
json.targetDefaults['@nx/eslint:lint'].inputs = Array.from(inputSet);
|
|
71
|
+
}
|
|
72
|
+
if (json.namedInputs?.production) {
|
|
73
|
+
const inputSet = new Set(json.namedInputs.production);
|
|
74
|
+
inputSet.add('!{projectRoot}/eslint.config.js');
|
|
75
|
+
json.namedInputs.production = Array.from(inputSet);
|
|
76
|
+
}
|
|
77
|
+
return json;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function convertConfigToFlatConfig(tree, root, source, target) {
|
|
82
|
+
(0, json_converter_1.convertEslintJsonToFlatConfig)(tree, root, source, target);
|
|
83
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "ConvertToFlatConfig",
|
|
4
|
+
"cli": "nx",
|
|
5
|
+
"description": "Convert an Nx workspace's ESLint configs to use Flat Config.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"skipFormat": {
|
|
9
|
+
"type": "boolean",
|
|
10
|
+
"description": "Skip formatting files.",
|
|
11
|
+
"default": false,
|
|
12
|
+
"x-priority": "internal"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"additionalProperties": false,
|
|
16
|
+
"required": []
|
|
17
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Linter } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* This configuration is intended to apply to all TypeScript source files.
|
|
4
|
+
* See the eslint-plugin package for what is in the referenced shareable config.
|
|
5
|
+
*/
|
|
6
|
+
export declare const typeScriptOverride: {
|
|
7
|
+
files: string[];
|
|
8
|
+
extends: string[];
|
|
9
|
+
/**
|
|
10
|
+
* Having an empty rules object present makes it more obvious to the user where they would
|
|
11
|
+
* extend things from if they needed to
|
|
12
|
+
*/
|
|
13
|
+
rules: {};
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* This configuration is intended to apply to all JavaScript source files.
|
|
17
|
+
* See the eslint-plugin package for what is in the referenced shareable config.
|
|
18
|
+
*/
|
|
19
|
+
export declare const javaScriptOverride: {
|
|
20
|
+
files: string[];
|
|
21
|
+
extends: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Having an empty rules object present makes it more obvious to the user where they would
|
|
24
|
+
* extend things from if they needed to
|
|
25
|
+
*/
|
|
26
|
+
rules: {};
|
|
27
|
+
};
|
|
28
|
+
export declare const getGlobalEsLintConfiguration: (unitTestRunner?: string, rootProject?: boolean) => Linter.Config;
|
|
29
|
+
export declare const getGlobalFlatEslintConfiguration: (unitTestRunner?: string, rootProject?: boolean) => string;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getGlobalFlatEslintConfiguration = exports.getGlobalEsLintConfiguration = exports.javaScriptOverride = exports.typeScriptOverride = void 0;
|
|
4
|
+
const ast_utils_1 = require("../utils/flat-config/ast-utils");
|
|
5
|
+
/**
|
|
6
|
+
* This configuration is intended to apply to all TypeScript source files.
|
|
7
|
+
* See the eslint-plugin package for what is in the referenced shareable config.
|
|
8
|
+
*/
|
|
9
|
+
exports.typeScriptOverride = {
|
|
10
|
+
files: ['*.ts', '*.tsx'],
|
|
11
|
+
extends: ['plugin:@nx/typescript'],
|
|
12
|
+
/**
|
|
13
|
+
* Having an empty rules object present makes it more obvious to the user where they would
|
|
14
|
+
* extend things from if they needed to
|
|
15
|
+
*/
|
|
16
|
+
rules: {},
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* This configuration is intended to apply to all JavaScript source files.
|
|
20
|
+
* See the eslint-plugin package for what is in the referenced shareable config.
|
|
21
|
+
*/
|
|
22
|
+
exports.javaScriptOverride = {
|
|
23
|
+
files: ['*.js', '*.jsx'],
|
|
24
|
+
extends: ['plugin:@nx/javascript'],
|
|
25
|
+
/**
|
|
26
|
+
* Having an empty rules object present makes it more obvious to the user where they would
|
|
27
|
+
* extend things from if they needed to
|
|
28
|
+
*/
|
|
29
|
+
rules: {},
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* This configuration is intended to apply to all "source code" (but not
|
|
33
|
+
* markup like HTML, or other custom file types like GraphQL)
|
|
34
|
+
*/
|
|
35
|
+
const moduleBoundariesOverride = {
|
|
36
|
+
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
|
37
|
+
rules: {
|
|
38
|
+
'@nx/enforce-module-boundaries': [
|
|
39
|
+
'error',
|
|
40
|
+
{
|
|
41
|
+
enforceBuildableLibDependency: true,
|
|
42
|
+
allow: [],
|
|
43
|
+
depConstraints: [{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] }],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* This configuration is intended to apply to all "source code" (but not
|
|
50
|
+
* markup like HTML, or other custom file types like GraphQL)
|
|
51
|
+
*/
|
|
52
|
+
const jestOverride = {
|
|
53
|
+
files: ['*.spec.ts', '*.spec.tsx', '*.spec.js', '*.spec.jsx'],
|
|
54
|
+
env: {
|
|
55
|
+
jest: true,
|
|
56
|
+
},
|
|
57
|
+
rules: {},
|
|
58
|
+
};
|
|
59
|
+
const getGlobalEsLintConfiguration = (unitTestRunner, rootProject) => {
|
|
60
|
+
const config = {
|
|
61
|
+
root: true,
|
|
62
|
+
ignorePatterns: rootProject ? ['!**/*'] : ['**/*'],
|
|
63
|
+
plugins: ['@nx'],
|
|
64
|
+
/**
|
|
65
|
+
* We leverage ESLint's "overrides" capability so that we can set up a root config which will support
|
|
66
|
+
* all permutations of Nx workspaces across all frameworks, libraries and tools.
|
|
67
|
+
*
|
|
68
|
+
* The key point is that we need entirely different ESLint config to apply to different types of files,
|
|
69
|
+
* but we still want to share common config where possible.
|
|
70
|
+
*/
|
|
71
|
+
overrides: [
|
|
72
|
+
...(rootProject ? [] : [moduleBoundariesOverride]),
|
|
73
|
+
exports.typeScriptOverride,
|
|
74
|
+
exports.javaScriptOverride,
|
|
75
|
+
...(unitTestRunner === 'jest' ? [jestOverride] : []),
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
return config;
|
|
79
|
+
};
|
|
80
|
+
exports.getGlobalEsLintConfiguration = getGlobalEsLintConfiguration;
|
|
81
|
+
const getGlobalFlatEslintConfiguration = (unitTestRunner, rootProject) => {
|
|
82
|
+
const nodeList = (0, ast_utils_1.createNodeList)(new Map(), [], true);
|
|
83
|
+
let content = (0, ast_utils_1.stringifyNodeList)(nodeList, '', 'eslint.config.js');
|
|
84
|
+
content = (0, ast_utils_1.addImportToFlatConfig)(content, 'nxPlugin', '@nx/eslint-plugin');
|
|
85
|
+
content = (0, ast_utils_1.addPluginsToExportsBlock)(content, [
|
|
86
|
+
{ name: '@nx', varName: 'nxPlugin', imp: '@nx/eslint-plugin' },
|
|
87
|
+
]);
|
|
88
|
+
if (!rootProject) {
|
|
89
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(moduleBoundariesOverride, ''));
|
|
90
|
+
}
|
|
91
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(exports.typeScriptOverride, ''));
|
|
92
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(exports.javaScriptOverride, ''));
|
|
93
|
+
if (unitTestRunner === 'jest') {
|
|
94
|
+
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(jestOverride, ''));
|
|
95
|
+
}
|
|
96
|
+
return content;
|
|
97
|
+
};
|
|
98
|
+
exports.getGlobalFlatEslintConfiguration = getGlobalFlatEslintConfiguration;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ProjectConfiguration, TargetConfiguration, Tree } from '@nx/devkit';
|
|
2
|
+
export declare function migrateConfigToMonorepoStyle(projects: ProjectConfiguration[], tree: Tree, unitTestRunner: string): void;
|
|
3
|
+
export declare function findLintTarget(project: ProjectConfiguration): TargetConfiguration;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findLintTarget = exports.migrateConfigToMonorepoStyle = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const eslint_file_1 = require("../utils/eslint-file");
|
|
7
|
+
const global_eslint_config_1 = require("./global-eslint-config");
|
|
8
|
+
const flat_config_1 = require("../../utils/flat-config");
|
|
9
|
+
const versions_1 = require("../../utils/versions");
|
|
10
|
+
const ast_utils_1 = require("../utils/flat-config/ast-utils");
|
|
11
|
+
function migrateConfigToMonorepoStyle(projects, tree, unitTestRunner) {
|
|
12
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
13
|
+
// we need this for the compat
|
|
14
|
+
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
15
|
+
'@eslint/js': versions_1.eslintVersion,
|
|
16
|
+
});
|
|
17
|
+
tree.write('eslint.base.config.js', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(unitTestRunner));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
(0, devkit_1.writeJson)(tree, '.eslintrc.base.json', (0, global_eslint_config_1.getGlobalEsLintConfiguration)(unitTestRunner));
|
|
21
|
+
}
|
|
22
|
+
// update extens in all projects' eslint configs
|
|
23
|
+
projects.forEach((project) => {
|
|
24
|
+
const lintTarget = findLintTarget(project);
|
|
25
|
+
if (lintTarget) {
|
|
26
|
+
const eslintFile = lintTarget.options.eslintConfig || (0, eslint_file_1.findEslintFile)(tree, project.root);
|
|
27
|
+
if (eslintFile) {
|
|
28
|
+
const projectEslintPath = (0, devkit_1.joinPathFragments)(project.root, eslintFile);
|
|
29
|
+
migrateEslintFile(projectEslintPath, tree);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
exports.migrateConfigToMonorepoStyle = migrateConfigToMonorepoStyle;
|
|
35
|
+
function findLintTarget(project) {
|
|
36
|
+
return Object.values(project.targets ?? {}).find((target) => target.executor === '@nx/eslint:lint' ||
|
|
37
|
+
target.executor === '@nx/linter:eslint' ||
|
|
38
|
+
target.executor === '@nrwl/linter:eslint');
|
|
39
|
+
}
|
|
40
|
+
exports.findLintTarget = findLintTarget;
|
|
41
|
+
function migrateEslintFile(projectEslintPath, tree) {
|
|
42
|
+
if ((0, eslint_file_1.isEslintConfigSupported)(tree)) {
|
|
43
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
44
|
+
let config = tree.read(projectEslintPath, 'utf-8');
|
|
45
|
+
// remove @nx plugin
|
|
46
|
+
config = (0, ast_utils_1.removePlugin)(config, '@nx', '@nx/eslint-plugin-nx');
|
|
47
|
+
// extend eslint.base.config.js
|
|
48
|
+
config = (0, ast_utils_1.addImportToFlatConfig)(config, 'baseConfig', `${(0, devkit_1.offsetFromRoot)((0, path_1.dirname)(projectEslintPath))}eslint.base.config.js`);
|
|
49
|
+
config = (0, ast_utils_1.addBlockToFlatConfigExport)(config, (0, ast_utils_1.generateSpreadElement)('baseConfig'), { insertAtTheEnd: false });
|
|
50
|
+
// cleanup file extends
|
|
51
|
+
config = (0, ast_utils_1.removeCompatExtends)(config, [
|
|
52
|
+
'plugin:@nx/typescript',
|
|
53
|
+
'plugin:@nx/javascript',
|
|
54
|
+
'plugin:@nrwl/typescript',
|
|
55
|
+
'plugin:@nrwl/javascript',
|
|
56
|
+
]);
|
|
57
|
+
console.warn('Flat eslint config is not supported yet for migration');
|
|
58
|
+
tree.write(projectEslintPath, config);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
(0, devkit_1.updateJson)(tree, projectEslintPath, (json) => {
|
|
62
|
+
// we have a new root now
|
|
63
|
+
delete json.root;
|
|
64
|
+
// remove nrwl/nx plugins
|
|
65
|
+
if (json.plugins) {
|
|
66
|
+
json.plugins = json.plugins.filter((p) => p !== '@nx' && p !== '@nrwl/nx');
|
|
67
|
+
if (json.plugins.length === 0) {
|
|
68
|
+
delete json.plugins;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// add extends
|
|
72
|
+
json.extends = json.extends || [];
|
|
73
|
+
const pathToRootConfig = `${(0, devkit_1.offsetFromRoot)((0, path_1.dirname)(projectEslintPath))}.eslintrc.base.json`;
|
|
74
|
+
if (json.extends.indexOf(pathToRootConfig) === -1) {
|
|
75
|
+
json.extends.push(pathToRootConfig);
|
|
76
|
+
}
|
|
77
|
+
// cleanup overrides
|
|
78
|
+
if (json.overrides) {
|
|
79
|
+
json.overrides.forEach((override) => {
|
|
80
|
+
if (override.extends) {
|
|
81
|
+
override.extends = override.extends.filter((ext) => ext !== 'plugin:@nx/typescript' &&
|
|
82
|
+
ext !== 'plugin:@nrwl/nx/typescript' &&
|
|
83
|
+
ext !== 'plugin:@nx/javascript' &&
|
|
84
|
+
ext !== 'plugin:@nrwl/nx/javascript');
|
|
85
|
+
if (override.extends.length === 0) {
|
|
86
|
+
delete override.extends;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return json;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (projectEslintPath.endsWith('.yml') ||
|
|
97
|
+
projectEslintPath.endsWith('.yaml')) {
|
|
98
|
+
console.warn('YAML eslint config is not supported yet for migration');
|
|
99
|
+
}
|
|
100
|
+
if (projectEslintPath.endsWith('.js') || projectEslintPath.endsWith('.cjs')) {
|
|
101
|
+
console.warn('JS eslint config is not supported yet for migration');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GeneratorCallback, Tree } from '@nx/devkit';
|
|
2
|
+
import { Linter } from '../utils/linter';
|
|
3
|
+
export interface LinterInitOptions {
|
|
4
|
+
linter?: Linter;
|
|
5
|
+
unitTestRunner?: string;
|
|
6
|
+
skipPackageJson?: boolean;
|
|
7
|
+
rootProject?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function lintInitGenerator(tree: Tree, options: LinterInitOptions): GeneratorCallback;
|