@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,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lintInitGenerator = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const versions_1 = require("../../utils/versions");
|
|
6
|
+
const eslint_file_1 = require("../utils/eslint-file");
|
|
7
|
+
const global_eslint_config_1 = require("./global-eslint-config");
|
|
8
|
+
function addTargetDefaults(tree) {
|
|
9
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
10
|
+
const productionFileSet = nxJson.namedInputs?.production;
|
|
11
|
+
if (productionFileSet) {
|
|
12
|
+
// Remove .eslintrc.json
|
|
13
|
+
productionFileSet.push('!{projectRoot}/.eslintrc.json');
|
|
14
|
+
productionFileSet.push('!{projectRoot}/eslint.config.js');
|
|
15
|
+
// Dedupe and set
|
|
16
|
+
nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
|
|
17
|
+
}
|
|
18
|
+
nxJson.targetDefaults ??= {};
|
|
19
|
+
nxJson.targetDefaults.lint ??= {};
|
|
20
|
+
nxJson.targetDefaults.lint.inputs ??= [
|
|
21
|
+
'default',
|
|
22
|
+
`{workspaceRoot}/.eslintrc.json`,
|
|
23
|
+
`{workspaceRoot}/.eslintignore`,
|
|
24
|
+
`{workspaceRoot}/eslint.config.js`,
|
|
25
|
+
];
|
|
26
|
+
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Initializes ESLint configuration in a workspace and adds necessary dependencies.
|
|
30
|
+
*/
|
|
31
|
+
function initEsLint(tree, options) {
|
|
32
|
+
if ((0, eslint_file_1.findEslintFile)(tree)) {
|
|
33
|
+
return () => { };
|
|
34
|
+
}
|
|
35
|
+
if (!options.skipPackageJson) {
|
|
36
|
+
(0, devkit_1.removeDependenciesFromPackageJson)(tree, ['@nx/eslint'], []);
|
|
37
|
+
}
|
|
38
|
+
(0, devkit_1.writeJson)(tree, '.eslintrc.json', (0, global_eslint_config_1.getGlobalEsLintConfiguration)(options.unitTestRunner, options.rootProject));
|
|
39
|
+
tree.write('.eslintignore', 'node_modules\n');
|
|
40
|
+
addTargetDefaults(tree);
|
|
41
|
+
if (tree.exists('.vscode/extensions.json')) {
|
|
42
|
+
(0, devkit_1.updateJson)(tree, '.vscode/extensions.json', (json) => {
|
|
43
|
+
json.recommendations ||= [];
|
|
44
|
+
const extension = 'dbaeumer.vscode-eslint';
|
|
45
|
+
if (!json.recommendations.includes(extension)) {
|
|
46
|
+
json.recommendations.push(extension);
|
|
47
|
+
}
|
|
48
|
+
return json;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return !options.skipPackageJson
|
|
52
|
+
? (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
53
|
+
'@nx/eslint': versions_1.nxVersion,
|
|
54
|
+
'@nx/eslint-plugin': versions_1.nxVersion,
|
|
55
|
+
'@typescript-eslint/parser': versions_1.typescriptESLintVersion,
|
|
56
|
+
'@typescript-eslint/eslint-plugin': versions_1.typescriptESLintVersion,
|
|
57
|
+
eslint: versions_1.eslintVersion,
|
|
58
|
+
'eslint-config-prettier': versions_1.eslintConfigPrettierVersion,
|
|
59
|
+
})
|
|
60
|
+
: () => { };
|
|
61
|
+
}
|
|
62
|
+
function lintInitGenerator(tree, options) {
|
|
63
|
+
return initEsLint(tree, options);
|
|
64
|
+
}
|
|
65
|
+
exports.lintInitGenerator = lintInitGenerator;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Tree } from '@nx/devkit';
|
|
2
|
+
import { Linter as LinterEnum } from '../utils/linter';
|
|
3
|
+
interface LintProjectOptions {
|
|
4
|
+
project: string;
|
|
5
|
+
linter?: LinterEnum;
|
|
6
|
+
eslintFilePatterns?: string[];
|
|
7
|
+
tsConfigPaths?: string[];
|
|
8
|
+
skipFormat: boolean;
|
|
9
|
+
setParserOptionsProject?: boolean;
|
|
10
|
+
skipPackageJson?: boolean;
|
|
11
|
+
unitTestRunner?: string;
|
|
12
|
+
rootProject?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function mapLintPattern(projectRoot: string, extension: string, rootProject?: boolean): string;
|
|
15
|
+
export declare function lintProjectGenerator(tree: Tree, options: LintProjectOptions): Promise<import("@nx/devkit").GeneratorCallback>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lintProjectGenerator = exports.mapLintPattern = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const eslint_file_1 = require("../utils/eslint-file");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const init_1 = require("../init/init");
|
|
8
|
+
const init_migration_1 = require("../init/init-migration");
|
|
9
|
+
const project_configuration_1 = require("nx/src/generators/utils/project-configuration");
|
|
10
|
+
const flat_config_1 = require("../../utils/flat-config");
|
|
11
|
+
const ast_utils_1 = require("../utils/flat-config/ast-utils");
|
|
12
|
+
function mapLintPattern(projectRoot, extension, rootProject) {
|
|
13
|
+
if (rootProject && (projectRoot === '.' || projectRoot === '')) {
|
|
14
|
+
return `${projectRoot}/src/**/*.${extension}`;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return `${projectRoot}/**/*.${extension}`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.mapLintPattern = mapLintPattern;
|
|
21
|
+
async function lintProjectGenerator(tree, options) {
|
|
22
|
+
const installTask = (0, init_1.lintInitGenerator)(tree, {
|
|
23
|
+
linter: options.linter,
|
|
24
|
+
unitTestRunner: options.unitTestRunner,
|
|
25
|
+
skipPackageJson: options.skipPackageJson,
|
|
26
|
+
rootProject: options.rootProject,
|
|
27
|
+
});
|
|
28
|
+
const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
|
|
29
|
+
const lintFilePatterns = options.eslintFilePatterns ?? [];
|
|
30
|
+
if (isBuildableLibraryProject(projectConfig)) {
|
|
31
|
+
lintFilePatterns.push(`${projectConfig.root}/package.json`);
|
|
32
|
+
}
|
|
33
|
+
projectConfig.targets['lint'] = {
|
|
34
|
+
executor: '@nx/eslint:lint',
|
|
35
|
+
outputs: ['{options.outputFile}'],
|
|
36
|
+
options: {
|
|
37
|
+
lintFilePatterns: lintFilePatterns,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
// we are adding new project which is not the root project or
|
|
41
|
+
// companion e2e app so we should check if migration to
|
|
42
|
+
// monorepo style is needed
|
|
43
|
+
if (!options.rootProject) {
|
|
44
|
+
const projects = {};
|
|
45
|
+
(0, project_configuration_1.getProjects)(tree).forEach((v, k) => (projects[k] = v));
|
|
46
|
+
if (isMigrationToMonorepoNeeded(projects, tree)) {
|
|
47
|
+
// we only migrate project configurations that have been created
|
|
48
|
+
const filteredProjects = [];
|
|
49
|
+
Object.entries(projects).forEach(([name, project]) => {
|
|
50
|
+
if (name !== options.project) {
|
|
51
|
+
filteredProjects.push(project);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
(0, init_migration_1.migrateConfigToMonorepoStyle)(filteredProjects, tree, options.unitTestRunner);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// our root `.eslintrc` is already the project config, so we should not override it
|
|
58
|
+
// additionally, the companion e2e app would have `rootProject: true`
|
|
59
|
+
// so we need to check for the root path as well
|
|
60
|
+
if (!options.rootProject || projectConfig.root !== '.') {
|
|
61
|
+
createEsLintConfiguration(tree, projectConfig, options.setParserOptionsProject);
|
|
62
|
+
}
|
|
63
|
+
// Buildable libs need source analysis enabled for linting `package.json`.
|
|
64
|
+
if (isBuildableLibraryProject(projectConfig) &&
|
|
65
|
+
!isJsAnalyzeSourceFilesEnabled(tree)) {
|
|
66
|
+
(0, devkit_1.updateJson)(tree, 'nx.json', (json) => {
|
|
67
|
+
json.pluginsConfig ??= {};
|
|
68
|
+
json.pluginsConfig['@nx/js'] ??= {};
|
|
69
|
+
json.pluginsConfig['@nx/js'].analyzeSourceFiles = true;
|
|
70
|
+
return json;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
(0, devkit_1.updateProjectConfiguration)(tree, options.project, projectConfig);
|
|
74
|
+
if (!options.skipFormat) {
|
|
75
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
76
|
+
}
|
|
77
|
+
return installTask;
|
|
78
|
+
}
|
|
79
|
+
exports.lintProjectGenerator = lintProjectGenerator;
|
|
80
|
+
function createEsLintConfiguration(tree, projectConfig, setParserOptionsProject) {
|
|
81
|
+
const eslintConfig = (0, eslint_file_1.findEslintFile)(tree);
|
|
82
|
+
const pathToRootConfig = eslintConfig
|
|
83
|
+
? `${(0, devkit_1.offsetFromRoot)(projectConfig.root)}${eslintConfig}`
|
|
84
|
+
: undefined;
|
|
85
|
+
const addDependencyChecks = isBuildableLibraryProject(projectConfig);
|
|
86
|
+
const overrides = [
|
|
87
|
+
{
|
|
88
|
+
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
|
89
|
+
/**
|
|
90
|
+
* NOTE: We no longer set parserOptions.project by default when creating new projects.
|
|
91
|
+
*
|
|
92
|
+
* We have observed that users rarely add rules requiring type-checking to their Nx workspaces, and therefore
|
|
93
|
+
* do not actually need the capabilites which parserOptions.project provides. When specifying parserOptions.project,
|
|
94
|
+
* typescript-eslint needs to create full TypeScript Programs for you. When omitting it, it can perform a simple
|
|
95
|
+
* parse (and AST tranformation) of the source files it encounters during a lint run, which is much faster and much
|
|
96
|
+
* less memory intensive.
|
|
97
|
+
*
|
|
98
|
+
* In the rare case that users attempt to add rules requiring type-checking to their setup later on (and haven't set
|
|
99
|
+
* parserOptions.project), the executor will attempt to look for the particular error typescript-eslint gives you
|
|
100
|
+
* and provide feedback to the user.
|
|
101
|
+
*/
|
|
102
|
+
parserOptions: !setParserOptionsProject
|
|
103
|
+
? undefined
|
|
104
|
+
: {
|
|
105
|
+
project: [`${projectConfig.root}/tsconfig.*?.json`],
|
|
106
|
+
},
|
|
107
|
+
/**
|
|
108
|
+
* Having an empty rules object present makes it more obvious to the user where they would
|
|
109
|
+
* extend things from if they needed to
|
|
110
|
+
*/
|
|
111
|
+
rules: {},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
files: ['*.ts', '*.tsx'],
|
|
115
|
+
rules: {},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
files: ['*.js', '*.jsx'],
|
|
119
|
+
rules: {},
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
if (isBuildableLibraryProject(projectConfig)) {
|
|
123
|
+
overrides.push({
|
|
124
|
+
files: ['*.json'],
|
|
125
|
+
parser: 'jsonc-eslint-parser',
|
|
126
|
+
rules: {
|
|
127
|
+
'@nx/dependency-checks': 'error',
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
132
|
+
const isCompatNeeded = addDependencyChecks;
|
|
133
|
+
const nodes = [];
|
|
134
|
+
const importMap = new Map();
|
|
135
|
+
if (eslintConfig) {
|
|
136
|
+
importMap.set(pathToRootConfig, 'baseConfig');
|
|
137
|
+
nodes.push((0, ast_utils_1.generateSpreadElement)('baseConfig'));
|
|
138
|
+
}
|
|
139
|
+
overrides.forEach((override) => {
|
|
140
|
+
nodes.push((0, ast_utils_1.generateFlatOverride)(override, projectConfig.root));
|
|
141
|
+
});
|
|
142
|
+
const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes, isCompatNeeded);
|
|
143
|
+
const content = (0, ast_utils_1.stringifyNodeList)(nodeList, projectConfig.root, 'eslint.config.js');
|
|
144
|
+
tree.write((0, path_1.join)(projectConfig.root, 'eslint.config.js'), content);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
(0, devkit_1.writeJson)(tree, (0, path_1.join)(projectConfig.root, `.eslintrc.json`), {
|
|
148
|
+
extends: eslintConfig ? [pathToRootConfig] : undefined,
|
|
149
|
+
// Include project files to be linted since the global one excludes all files.
|
|
150
|
+
ignorePatterns: ['!**/*'],
|
|
151
|
+
overrides,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function isJsAnalyzeSourceFilesEnabled(tree) {
|
|
156
|
+
const nxJson = (0, devkit_1.readJson)(tree, 'nx.json');
|
|
157
|
+
const jsPluginConfig = nxJson.pluginsConfig?.['@nx/js'];
|
|
158
|
+
return (jsPluginConfig?.analyzeSourceFiles ??
|
|
159
|
+
nxJson.extends !== 'nx/presets/npm.json');
|
|
160
|
+
}
|
|
161
|
+
function isBuildableLibraryProject(projectConfig) {
|
|
162
|
+
return (projectConfig.projectType === 'library' &&
|
|
163
|
+
projectConfig.targets?.build &&
|
|
164
|
+
!!projectConfig.targets.build);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Detect based on the state of lint target configuration of the root project
|
|
168
|
+
* if we should migrate eslint configs to monorepo style
|
|
169
|
+
*/
|
|
170
|
+
function isMigrationToMonorepoNeeded(projects, tree) {
|
|
171
|
+
// the base config is already created, migration has been done
|
|
172
|
+
if (tree.exists(eslint_file_1.baseEsLintConfigFile) ||
|
|
173
|
+
tree.exists(eslint_file_1.baseEsLintFlatConfigFile)) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const configs = Object.values(projects);
|
|
177
|
+
if (configs.length === 1) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
// get root project
|
|
181
|
+
const rootProject = configs.find((p) => p.root === '.');
|
|
182
|
+
if (!rootProject || !rootProject.targets) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
// find if root project has lint target
|
|
186
|
+
const lintTarget = (0, init_migration_1.findLintTarget)(rootProject);
|
|
187
|
+
if (!lintTarget) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Tree } from '@nx/devkit';
|
|
2
|
+
import { Linter } from 'eslint';
|
|
3
|
+
export declare const eslintConfigFileWhitelist: string[];
|
|
4
|
+
export declare const baseEsLintConfigFile = ".eslintrc.base.json";
|
|
5
|
+
export declare const baseEsLintFlatConfigFile = "eslint.base.config.js";
|
|
6
|
+
export declare function findEslintFile(tree: Tree, projectRoot?: string): string | null;
|
|
7
|
+
export declare function isEslintConfigSupported(tree: Tree, projectRoot?: string): boolean;
|
|
8
|
+
export declare function updateRelativePathsInConfig(tree: Tree, sourcePath: string, destinationPath: string): void;
|
|
9
|
+
export declare function addOverrideToLintConfig(tree: Tree, root: string, override: Linter.ConfigOverride<Linter.RulesRecord>, options?: {
|
|
10
|
+
insertAtTheEnd?: boolean;
|
|
11
|
+
checkBaseConfig?: boolean;
|
|
12
|
+
}): void;
|
|
13
|
+
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;
|
|
14
|
+
export declare function lintConfigHasOverride(tree: Tree, root: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, checkBaseConfig?: boolean): boolean;
|
|
15
|
+
export declare function replaceOverridesInLintConfig(tree: Tree, root: string, overrides: Linter.ConfigOverride<Linter.RulesRecord>[]): void;
|
|
16
|
+
export declare function addExtendsToLintConfig(tree: Tree, root: string, plugin: string | string[]): void;
|
|
17
|
+
export declare function addPluginsToLintConfig(tree: Tree, root: string, plugin: string | string[]): void;
|
|
18
|
+
export declare function addIgnoresToLintConfig(tree: Tree, root: string, ignorePatterns: string[]): void;
|
|
19
|
+
export declare function getPluginImport(pluginName: string): string;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPluginImport = exports.addIgnoresToLintConfig = exports.addPluginsToLintConfig = exports.addExtendsToLintConfig = exports.replaceOverridesInLintConfig = exports.lintConfigHasOverride = exports.updateOverrideInLintConfig = exports.addOverrideToLintConfig = exports.updateRelativePathsInConfig = exports.isEslintConfigSupported = exports.findEslintFile = exports.baseEsLintFlatConfigFile = exports.baseEsLintConfigFile = exports.eslintConfigFileWhitelist = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const flat_config_1 = require("../../utils/flat-config");
|
|
6
|
+
const ast_utils_1 = require("./flat-config/ast-utils");
|
|
7
|
+
exports.eslintConfigFileWhitelist = [
|
|
8
|
+
'.eslintrc',
|
|
9
|
+
'.eslintrc.js',
|
|
10
|
+
'.eslintrc.cjs',
|
|
11
|
+
'.eslintrc.yaml',
|
|
12
|
+
'.eslintrc.yml',
|
|
13
|
+
'.eslintrc.json',
|
|
14
|
+
'eslint.config.js',
|
|
15
|
+
];
|
|
16
|
+
exports.baseEsLintConfigFile = '.eslintrc.base.json';
|
|
17
|
+
exports.baseEsLintFlatConfigFile = 'eslint.base.config.js';
|
|
18
|
+
function findEslintFile(tree, projectRoot = '') {
|
|
19
|
+
if (projectRoot === '' && tree.exists(exports.baseEsLintConfigFile)) {
|
|
20
|
+
return exports.baseEsLintConfigFile;
|
|
21
|
+
}
|
|
22
|
+
if (projectRoot === '' && tree.exists(exports.baseEsLintFlatConfigFile)) {
|
|
23
|
+
return exports.baseEsLintFlatConfigFile;
|
|
24
|
+
}
|
|
25
|
+
for (const file of exports.eslintConfigFileWhitelist) {
|
|
26
|
+
if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, file))) {
|
|
27
|
+
return file;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
exports.findEslintFile = findEslintFile;
|
|
33
|
+
function isEslintConfigSupported(tree, projectRoot = '') {
|
|
34
|
+
const eslintFile = findEslintFile(tree, projectRoot);
|
|
35
|
+
if (!eslintFile) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return eslintFile.endsWith('.json') || eslintFile.endsWith('.config.js');
|
|
39
|
+
}
|
|
40
|
+
exports.isEslintConfigSupported = isEslintConfigSupported;
|
|
41
|
+
function updateRelativePathsInConfig(tree, sourcePath, destinationPath) {
|
|
42
|
+
if (sourcePath === destinationPath ||
|
|
43
|
+
!isEslintConfigSupported(tree, destinationPath)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const configPath = (0, devkit_1.joinPathFragments)(destinationPath, findEslintFile(tree, destinationPath));
|
|
47
|
+
const offset = (0, devkit_1.offsetFromRoot)(destinationPath);
|
|
48
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
49
|
+
const config = tree.read(configPath, 'utf-8');
|
|
50
|
+
tree.write(configPath, replaceFlatConfigPaths(config, sourcePath, offset, destinationPath));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
(0, devkit_1.updateJson)(tree, configPath, (json) => {
|
|
54
|
+
if (typeof json.extends === 'string') {
|
|
55
|
+
json.extends = offsetFilePath(sourcePath, json.extends, offset);
|
|
56
|
+
}
|
|
57
|
+
else if (json.extends) {
|
|
58
|
+
json.extends = json.extends.map((extend) => offsetFilePath(sourcePath, extend, offset));
|
|
59
|
+
}
|
|
60
|
+
json.overrides?.forEach((o) => {
|
|
61
|
+
if (o.parserOptions?.project) {
|
|
62
|
+
o.parserOptions.project = Array.isArray(o.parserOptions.project)
|
|
63
|
+
? o.parserOptions.project.map((p) => p.replace(sourcePath, destinationPath))
|
|
64
|
+
: o.parserOptions.project.replace(sourcePath, destinationPath);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return json;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.updateRelativePathsInConfig = updateRelativePathsInConfig;
|
|
72
|
+
function replaceFlatConfigPaths(config, sourceRoot, offset, destinationRoot) {
|
|
73
|
+
let match;
|
|
74
|
+
let newConfig = config;
|
|
75
|
+
// replace requires
|
|
76
|
+
const requireRegex = RegExp(/require\(['"](.*)['"]\)/g);
|
|
77
|
+
while ((match = requireRegex.exec(newConfig)) !== null) {
|
|
78
|
+
const newPath = offsetFilePath(sourceRoot, match[1], offset);
|
|
79
|
+
newConfig =
|
|
80
|
+
newConfig.slice(0, match.index) +
|
|
81
|
+
`require('${newPath}')` +
|
|
82
|
+
newConfig.slice(match.index + match[0].length);
|
|
83
|
+
}
|
|
84
|
+
// replace projects
|
|
85
|
+
const projectRegex = RegExp(/project:\s?\[?['"](.*)['"]\]?/g);
|
|
86
|
+
while ((match = projectRegex.exec(newConfig)) !== null) {
|
|
87
|
+
const newProjectDef = match[0].replaceAll(sourceRoot, destinationRoot);
|
|
88
|
+
newConfig =
|
|
89
|
+
newConfig.slice(0, match.index) +
|
|
90
|
+
newProjectDef +
|
|
91
|
+
newConfig.slice(match.index + match[0].length);
|
|
92
|
+
}
|
|
93
|
+
return newConfig;
|
|
94
|
+
}
|
|
95
|
+
function offsetFilePath(projectRoot, pathToFile, offset) {
|
|
96
|
+
if (!pathToFile.startsWith('..')) {
|
|
97
|
+
// not a relative path
|
|
98
|
+
return pathToFile;
|
|
99
|
+
}
|
|
100
|
+
return (0, devkit_1.joinPathFragments)(offset, projectRoot, pathToFile);
|
|
101
|
+
}
|
|
102
|
+
function addOverrideToLintConfig(tree, root, override, options = {
|
|
103
|
+
insertAtTheEnd: true,
|
|
104
|
+
}) {
|
|
105
|
+
const isBase = options.checkBaseConfig && findEslintFile(tree, root).includes('.base');
|
|
106
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
107
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? exports.baseEsLintFlatConfigFile : 'eslint.config.js');
|
|
108
|
+
const flatOverride = (0, ast_utils_1.generateFlatOverride)(override, root);
|
|
109
|
+
let content = tree.read(fileName, 'utf8');
|
|
110
|
+
// we will be using compat here so we need to make sure it's added
|
|
111
|
+
if (overrideNeedsCompat(override)) {
|
|
112
|
+
content = (0, ast_utils_1.addCompatToFlatConfig)(content);
|
|
113
|
+
}
|
|
114
|
+
tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(content, flatOverride, options));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? exports.baseEsLintConfigFile : '.eslintrc.json');
|
|
118
|
+
(0, devkit_1.updateJson)(tree, fileName, (json) => {
|
|
119
|
+
json.overrides ?? [];
|
|
120
|
+
if (options.insertAtTheEnd) {
|
|
121
|
+
json.overrides.push(override);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
json.overrides.unshift(override);
|
|
125
|
+
}
|
|
126
|
+
return json;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.addOverrideToLintConfig = addOverrideToLintConfig;
|
|
131
|
+
function overrideNeedsCompat(override) {
|
|
132
|
+
return (!override.env && !override.extends && !override.plugins && !override.parser);
|
|
133
|
+
}
|
|
134
|
+
function updateOverrideInLintConfig(tree, root, lookup, update) {
|
|
135
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
136
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, 'eslint.config.js');
|
|
137
|
+
let content = tree.read(fileName, 'utf8');
|
|
138
|
+
content = (0, ast_utils_1.replaceOverride)(content, root, lookup, update);
|
|
139
|
+
tree.write(fileName, content);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
|
|
143
|
+
(0, devkit_1.updateJson)(tree, fileName, (json) => {
|
|
144
|
+
const index = json.overrides.findIndex(lookup);
|
|
145
|
+
if (index !== -1) {
|
|
146
|
+
json.overrides[index] = update(json.overrides[index]);
|
|
147
|
+
}
|
|
148
|
+
return json;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.updateOverrideInLintConfig = updateOverrideInLintConfig;
|
|
153
|
+
function lintConfigHasOverride(tree, root, lookup, checkBaseConfig = false) {
|
|
154
|
+
if (!isEslintConfigSupported(tree, root)) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const isBase = checkBaseConfig && findEslintFile(tree, root).includes('.base');
|
|
158
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
159
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? exports.baseEsLintFlatConfigFile : 'eslint.config.js');
|
|
160
|
+
const content = tree.read(fileName, 'utf8');
|
|
161
|
+
return (0, ast_utils_1.hasOverride)(content, lookup);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? exports.baseEsLintConfigFile : '.eslintrc.json');
|
|
165
|
+
return (0, devkit_1.readJson)(tree, fileName).overrides?.some(lookup) || false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.lintConfigHasOverride = lintConfigHasOverride;
|
|
169
|
+
function replaceOverridesInLintConfig(tree, root, overrides) {
|
|
170
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
171
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, 'eslint.config.js');
|
|
172
|
+
let content = tree.read(fileName, 'utf8');
|
|
173
|
+
// we will be using compat here so we need to make sure it's added
|
|
174
|
+
if (overrides.some(overrideNeedsCompat)) {
|
|
175
|
+
content = (0, ast_utils_1.addCompatToFlatConfig)(content);
|
|
176
|
+
}
|
|
177
|
+
content = (0, ast_utils_1.removeOverridesFromLintConfig)(content);
|
|
178
|
+
overrides.forEach((override) => {
|
|
179
|
+
const flatOverride = (0, ast_utils_1.generateFlatOverride)(override, root);
|
|
180
|
+
(0, ast_utils_1.addBlockToFlatConfigExport)(content, flatOverride);
|
|
181
|
+
});
|
|
182
|
+
tree.write(fileName, content);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
|
|
186
|
+
(0, devkit_1.updateJson)(tree, fileName, (json) => {
|
|
187
|
+
json.overrides = overrides;
|
|
188
|
+
return json;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.replaceOverridesInLintConfig = replaceOverridesInLintConfig;
|
|
193
|
+
function addExtendsToLintConfig(tree, root, plugin) {
|
|
194
|
+
const plugins = Array.isArray(plugin) ? plugin : [plugin];
|
|
195
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
196
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, 'eslint.config.js');
|
|
197
|
+
const pluginExtends = (0, ast_utils_1.generatePluginExtendsElement)(plugins);
|
|
198
|
+
tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(tree.read(fileName, 'utf8'), pluginExtends));
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
|
|
202
|
+
(0, devkit_1.updateJson)(tree, fileName, (json) => {
|
|
203
|
+
json.extends = [...plugins, ...(json.extends ?? [])];
|
|
204
|
+
return json;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
exports.addExtendsToLintConfig = addExtendsToLintConfig;
|
|
209
|
+
function addPluginsToLintConfig(tree, root, plugin) {
|
|
210
|
+
const plugins = Array.isArray(plugin) ? plugin : [plugin];
|
|
211
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
212
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, 'eslint.config.js');
|
|
213
|
+
let content = tree.read(fileName, 'utf8');
|
|
214
|
+
const mappedPlugins = [];
|
|
215
|
+
plugins.forEach((name) => {
|
|
216
|
+
const imp = getPluginImport(name);
|
|
217
|
+
const varName = (0, devkit_1.names)(imp).propertyName;
|
|
218
|
+
mappedPlugins.push({ name, varName, imp });
|
|
219
|
+
});
|
|
220
|
+
mappedPlugins.forEach(({ varName, imp }) => {
|
|
221
|
+
content = (0, ast_utils_1.addImportToFlatConfig)(content, varName, imp);
|
|
222
|
+
});
|
|
223
|
+
content = (0, ast_utils_1.addPluginsToExportsBlock)(content, mappedPlugins);
|
|
224
|
+
tree.write(fileName, content);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
|
|
228
|
+
(0, devkit_1.updateJson)(tree, fileName, (json) => {
|
|
229
|
+
json.plugins = [...plugins, ...(json.plugins ?? [])];
|
|
230
|
+
return json;
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
exports.addPluginsToLintConfig = addPluginsToLintConfig;
|
|
235
|
+
function addIgnoresToLintConfig(tree, root, ignorePatterns) {
|
|
236
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
|
237
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, 'eslint.config.js');
|
|
238
|
+
const block = (0, ast_utils_1.generateAst)({
|
|
239
|
+
ignores: ignorePatterns.map((path) => (0, ast_utils_1.mapFilePath)(path, root)),
|
|
240
|
+
});
|
|
241
|
+
tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(tree.read(fileName, 'utf8'), block));
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
|
|
245
|
+
(0, devkit_1.updateJson)(tree, fileName, (json) => {
|
|
246
|
+
const ignoreSet = new Set([
|
|
247
|
+
...(json.ignorePatterns ?? []),
|
|
248
|
+
...ignorePatterns,
|
|
249
|
+
]);
|
|
250
|
+
json.ignorePatterns = Array.from(ignoreSet);
|
|
251
|
+
return json;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
exports.addIgnoresToLintConfig = addIgnoresToLintConfig;
|
|
256
|
+
function getPluginImport(pluginName) {
|
|
257
|
+
if (pluginName.includes('eslint-plugin-')) {
|
|
258
|
+
return pluginName;
|
|
259
|
+
}
|
|
260
|
+
if (!pluginName.startsWith('@')) {
|
|
261
|
+
return `eslint-plugin-${pluginName}`;
|
|
262
|
+
}
|
|
263
|
+
if (!pluginName.includes('/')) {
|
|
264
|
+
return `${pluginName}/eslint-plugin`;
|
|
265
|
+
}
|
|
266
|
+
const [scope, name] = pluginName.split('/');
|
|
267
|
+
return `${scope}/eslint-plugin-${name}`;
|
|
268
|
+
}
|
|
269
|
+
exports.getPluginImport = getPluginImport;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getEslintTargets = void 0;
|
|
4
|
+
const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
|
|
5
|
+
function getEslintTargets(tree) {
|
|
6
|
+
const eslintTargetNames = new Set();
|
|
7
|
+
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nx/eslint:lint', (_, __, target) => {
|
|
8
|
+
eslintTargetNames.add(target);
|
|
9
|
+
});
|
|
10
|
+
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nx/linter:eslint', (_, __, target) => {
|
|
11
|
+
eslintTargetNames.add(target);
|
|
12
|
+
});
|
|
13
|
+
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nrwl/linter:eslint', (_, __, target) => {
|
|
14
|
+
eslintTargetNames.add(target);
|
|
15
|
+
});
|
|
16
|
+
return eslintTargetNames;
|
|
17
|
+
}
|
|
18
|
+
exports.getEslintTargets = getEslintTargets;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Linter } from 'eslint';
|
|
2
|
+
import * as ts from 'typescript';
|
|
3
|
+
/**
|
|
4
|
+
* Remove all overrides from the config file
|
|
5
|
+
*/
|
|
6
|
+
export declare function removeOverridesFromLintConfig(content: string): string;
|
|
7
|
+
export declare function hasOverride(content: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Finds an override matching the lookup function and applies the update function to it
|
|
10
|
+
*/
|
|
11
|
+
export declare function replaceOverride(content: string, root: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, update: (override: Linter.ConfigOverride<Linter.RulesRecord>) => Linter.ConfigOverride<Linter.RulesRecord>): string;
|
|
12
|
+
/**
|
|
13
|
+
* Adding require statement to the top of the file
|
|
14
|
+
*/
|
|
15
|
+
export declare function addImportToFlatConfig(content: string, variable: string | string[], imp: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Injects new ts.expression to the end of the module.exports array.
|
|
18
|
+
*/
|
|
19
|
+
export declare function addBlockToFlatConfigExport(content: string, config: ts.Expression | ts.SpreadElement, options?: {
|
|
20
|
+
insertAtTheEnd?: boolean;
|
|
21
|
+
checkBaseConfig?: boolean;
|
|
22
|
+
}): string;
|
|
23
|
+
export declare function removePlugin(content: string, pluginName: string, pluginImport: string): string;
|
|
24
|
+
export declare function removeCompatExtends(content: string, compatExtends: string[]): string;
|
|
25
|
+
/**
|
|
26
|
+
* Add plugins block to the top of the export blocks
|
|
27
|
+
*/
|
|
28
|
+
export declare function addPluginsToExportsBlock(content: string, plugins: {
|
|
29
|
+
name: string;
|
|
30
|
+
varName: string;
|
|
31
|
+
imp: string;
|
|
32
|
+
}[]): string;
|
|
33
|
+
/**
|
|
34
|
+
* Adds compat if missing to flat config
|
|
35
|
+
*/
|
|
36
|
+
export declare function addCompatToFlatConfig(content: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Generate node list representing the imports and the exports blocks
|
|
39
|
+
* Optionally add flat compat initialization
|
|
40
|
+
*/
|
|
41
|
+
export declare function createNodeList(importsMap: Map<string, string>, exportElements: ts.Expression[], isFlatCompatNeeded: boolean): ts.NodeArray<ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile>;
|
|
42
|
+
export declare function generateSpreadElement(name: string): ts.SpreadElement;
|
|
43
|
+
export declare function generatePluginExtendsElement(plugins: string[]): ts.SpreadElement;
|
|
44
|
+
/**
|
|
45
|
+
* Stringifies TS nodes to file content string
|
|
46
|
+
*/
|
|
47
|
+
export declare function stringifyNodeList(nodes: ts.NodeArray<ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile>, root: string, fileName: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* generates AST require statement
|
|
50
|
+
*/
|
|
51
|
+
export declare function generateRequire(variableName: string | ts.ObjectBindingPattern, imp: string): ts.VariableStatement;
|
|
52
|
+
/**
|
|
53
|
+
* Generates AST object or spread element based on JSON override object
|
|
54
|
+
*/
|
|
55
|
+
export declare function generateFlatOverride(override: Linter.ConfigOverride<Linter.RulesRecord>, root: string): ts.ObjectLiteralExpression | ts.SpreadElement;
|
|
56
|
+
export declare function mapFilePaths(override: Linter.ConfigOverride<Linter.RulesRecord>, root: string): void;
|
|
57
|
+
export declare function mapFilePath(filePath: string, root: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Generates an AST from a JSON-type input
|
|
60
|
+
*/
|
|
61
|
+
export declare function generateAst<T>(input: unknown): T;
|