@nx/eslint 0.0.0-pr-27404-9b7456c → 0.0.0-pr-27809-4160bae

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/eslint",
3
- "version": "0.0.0-pr-27404-9b7456c",
3
+ "version": "0.0.0-pr-27809-4160bae",
4
4
  "private": false,
5
5
  "description": "The ESLint plugin for Nx contains executors, generators and utilities used for linting JavaScript/TypeScript projects within an Nx workspace.",
6
6
  "repository": {
@@ -35,12 +35,12 @@
35
35
  "eslint": "^8.0.0 || ^9.0.0"
36
36
  },
37
37
  "dependencies": {
38
- "@nx/devkit": "0.0.0-pr-27404-9b7456c",
39
- "@nx/js": "0.0.0-pr-27404-9b7456c",
38
+ "@nx/devkit": "0.0.0-pr-27809-4160bae",
39
+ "@nx/js": "0.0.0-pr-27809-4160bae",
40
40
  "semver": "^7.5.3",
41
41
  "tslib": "^2.3.0",
42
42
  "typescript": "~5.4.2",
43
- "@nx/linter": "0.0.0-pr-27404-9b7456c"
43
+ "@nx/linter": "0.0.0-pr-27809-4160bae"
44
44
  },
45
45
  "peerDependenciesMeta": {
46
46
  "@zkochan/js-yaml": {
@@ -9,9 +9,7 @@ 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)({
13
- useFlatConfigOverrideVal: useFlatConfig,
14
- });
12
+ const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)(useFlatConfig);
15
13
  const eslintOptions = {
16
14
  overrideConfigFile: eslintConfigPath,
17
15
  fix: !!options.fix,
@@ -71,18 +71,6 @@ 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
- }
86
74
  });
87
75
  }
88
76
  if (config.ignorePatterns) {
@@ -108,13 +96,9 @@ function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths) {
108
96
  }
109
97
  }
110
98
  // create the node list and print it to new file
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
- }
99
+ const nodeList = (0, ast_utils_1.createNodeList)(importsMap, exportElements, isFlatCompatNeeded);
116
100
  return {
117
- content,
101
+ content: (0, ast_utils_1.stringifyNodeList)(nodeList),
118
102
  addESLintRC: isFlatCompatNeeded,
119
103
  addESLintJS: isESLintJSNeeded,
120
104
  };
@@ -6,6 +6,7 @@ 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;
9
10
  async function convertToFlatConfigGenerator(tree, options) {
10
11
  const eslintFile = (0, eslint_file_1.findEslintFile)(tree);
11
12
  if (!eslintFile) {
@@ -32,7 +33,9 @@ async function convertToFlatConfigGenerator(tree, options) {
32
33
  if (!options.skipFormat) {
33
34
  await (0, devkit_1.formatFiles)(tree);
34
35
  }
35
- return () => (0, devkit_1.installPackagesTask)(tree);
36
+ if (shouldInstallDeps) {
37
+ return () => (0, devkit_1.installPackagesTask)(tree);
38
+ }
36
39
  }
37
40
  exports.default = convertToFlatConfigGenerator;
38
41
  function convertRootToFlatConfig(tree, eslintFile) {
@@ -125,18 +128,17 @@ function processConvertedConfig(tree, root, source, target, { content, addESLint
125
128
  tree.delete((0, path_1.join)(root, source));
126
129
  // save new
127
130
  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
- };
134
131
  // add missing packages
135
132
  if (addESLintRC) {
136
- devDependencies['@eslint/eslintrc'] = versions_1.eslintrcVersion;
133
+ shouldInstallDeps = true;
134
+ (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
135
+ '@eslint/eslintrc': versions_1.eslintrcVersion,
136
+ });
137
137
  }
138
138
  if (addESLintJS) {
139
- devDependencies['@eslint/js'] = versions_1.eslintVersion;
139
+ shouldInstallDeps = true;
140
+ (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
141
+ '@eslint/js': versions_1.eslintVersion,
142
+ });
140
143
  }
141
- (0, devkit_1.addDependenciesToPackageJson)(tree, {}, devDependencies);
142
144
  }
@@ -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: (rootProject?: boolean) => string;
29
+ export declare const getGlobalFlatEslintConfiguration: (unitTestRunner?: string, rootProject?: boolean) => string;
@@ -78,37 +78,24 @@ const getGlobalEsLintConfiguration = (unitTestRunner, rootProject) => {
78
78
  return config;
79
79
  };
80
80
  exports.getGlobalEsLintConfiguration = getGlobalEsLintConfiguration;
81
- const getGlobalFlatEslintConfiguration = (rootProject) => {
82
- const nodeList = (0, ast_utils_1.createNodeList)(new Map(), []);
81
+ const getGlobalFlatEslintConfiguration = (unitTestRunner, rootProject) => {
82
+ const nodeList = (0, ast_utils_1.createNodeList)(new Map(), [], true);
83
83
  let content = (0, ast_utils_1.stringifyNodeList)(nodeList);
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'));
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
88
  if (!rootProject) {
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
- }));
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));
108
95
  }
109
- content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
110
- files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
111
- rules: {},
96
+ // add ignore for .nx folder
97
+ content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateAst)({
98
+ ignores: ['.nx'],
112
99
  }));
113
100
  return content;
114
101
  };
@@ -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)(unitTestRunner));
33
33
  }
34
34
  else {
35
35
  const eslintFile = (0, eslint_file_1.findEslintFile)(tree, '.');
@@ -91,11 +91,6 @@ 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
- ]);
99
94
  tree.write(projectEslintPath, config);
100
95
  }
101
96
  else {
@@ -114,63 +114,55 @@ 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 = 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) {
117
+ const addDependencyChecks = isBuildableLibraryProject(projectConfig);
118
+ const overrides = [
119
+ {
120
+ files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
121
+ /**
122
+ * NOTE: We no longer set parserOptions.project by default when creating new projects.
123
+ *
124
+ * We have observed that users rarely add rules requiring type-checking to their Nx workspaces, and therefore
125
+ * do not actually need the capabilites which parserOptions.project provides. When specifying parserOptions.project,
126
+ * typescript-eslint needs to create full TypeScript Programs for you. When omitting it, it can perform a simple
127
+ * parse (and AST tranformation) of the source files it encounters during a lint run, which is much faster and much
128
+ * less memory intensive.
129
+ *
130
+ * In the rare case that users attempt to add rules requiring type-checking to their setup later on (and haven't set
131
+ * parserOptions.project), the executor will attempt to look for the particular error typescript-eslint gives you
132
+ * and provide feedback to the user.
133
+ */
134
+ parserOptions: !setParserOptionsProject
135
+ ? undefined
136
+ : {
137
+ project: [`${projectConfig.root}/tsconfig.*?.json`],
138
+ },
139
+ /**
140
+ * Having an empty rules object present makes it more obvious to the user where they would
141
+ * extend things from if they needed to
142
+ */
143
+ rules: {},
144
+ },
145
+ {
146
+ files: ['*.ts', '*.tsx'],
147
+ rules: {},
148
+ },
149
+ {
150
+ files: ['*.js', '*.jsx'],
151
+ rules: {},
152
+ },
153
+ ];
154
+ if (options.addPackageJsonDependencyChecks ||
155
+ isBuildableLibraryProject(projectConfig)) {
159
156
  overrides.push({
160
157
  files: ['*.json'],
161
158
  parser: 'jsonc-eslint-parser',
162
159
  rules: {
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
- ],
160
+ '@nx/dependency-checks': 'error',
170
161
  },
171
162
  });
172
163
  }
173
164
  if ((0, flat_config_1.useFlatConfig)(tree)) {
165
+ const isCompatNeeded = addDependencyChecks;
174
166
  const nodes = [];
175
167
  const importMap = new Map();
176
168
  if (extendedRootConfig) {
@@ -180,7 +172,7 @@ function createEsLintConfiguration(tree, options, projectConfig, setParserOption
180
172
  overrides.forEach((override) => {
181
173
  nodes.push((0, ast_utils_1.generateFlatOverride)(override));
182
174
  });
183
- const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes);
175
+ const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes, isCompatNeeded);
184
176
  const content = (0, ast_utils_1.stringifyNodeList)(nodeList);
185
177
  tree.write((0, path_1.join)(projectConfig.root, 'eslint.config.js'), content);
186
178
  }
@@ -2,7 +2,6 @@
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");
6
5
  const versions_1 = require("../../utils/versions");
7
6
  const global_eslint_config_1 = require("../init/global-eslint-config");
8
7
  const eslint_file_1 = require("../utils/eslint-file");
@@ -11,12 +10,6 @@ function setupRootEsLint(tree, options) {
11
10
  if (rootEslintFile) {
12
11
  return () => { };
13
12
  }
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) {
20
13
  (0, devkit_1.writeJson)(tree, '.eslintrc.json', (0, global_eslint_config_1.getGlobalEsLintConfiguration)(options.unitTestRunner, options.rootProject));
21
14
  if (tree.exists('.eslintignore')) {
22
15
  let content = tree.read('.eslintignore', 'utf-8');
@@ -37,15 +30,3 @@ function setUpLegacyRootEslintRc(tree, options) {
37
30
  })
38
31
  : () => { };
39
32
  }
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,23 +1,16 @@
1
- import { type GeneratorCallback, type Tree } from '@nx/devkit';
1
+ import 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: Partial<Linter.ConfigOverride<Linter.RulesRecord>>, options?: {
6
+ export declare function addOverrideToLintConfig(tree: Tree, root: string, override: 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 | {
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;
13
+ export declare function addExtendsToLintConfig(tree: Tree, root: string, plugin: string | string[]): void;
21
14
  export declare function addPluginsToLintConfig(tree: Tree, root: string, plugin: string | string[]): void;
22
15
  export declare function addIgnoresToLintConfig(tree: Tree, root: string, ignorePatterns: string[]): void;
23
16
  export declare function getPluginImport(pluginName: string): string;
@@ -8,18 +8,14 @@ exports.updateOverrideInLintConfig = updateOverrideInLintConfig;
8
8
  exports.lintConfigHasOverride = lintConfigHasOverride;
9
9
  exports.replaceOverridesInLintConfig = replaceOverridesInLintConfig;
10
10
  exports.addExtendsToLintConfig = addExtendsToLintConfig;
11
- exports.addPredefinedConfigToFlatLintConfig = addPredefinedConfigToFlatLintConfig;
12
11
  exports.addPluginsToLintConfig = addPluginsToLintConfig;
13
12
  exports.addIgnoresToLintConfig = addIgnoresToLintConfig;
14
13
  exports.getPluginImport = getPluginImport;
15
14
  const devkit_1 = require("@nx/devkit");
16
- const semver_1 = require("semver");
17
- const config_file_1 = require("../../utils/config-file");
18
15
  const flat_config_1 = require("../../utils/flat-config");
19
- const version_utils_1 = require("../../utils/version-utils");
20
- const versions_1 = require("../../utils/versions");
21
16
  const ast_utils_1 = require("./flat-config/ast-utils");
22
17
  const path_utils_1 = require("./flat-config/path-utils");
18
+ const config_file_1 = require("../../utils/config-file");
23
19
  function findEslintFile(tree, projectRoot) {
24
20
  if (projectRoot === undefined && tree.exists(config_file_1.baseEsLintConfigFile)) {
25
21
  return config_file_1.baseEsLintConfigFile;
@@ -114,12 +110,12 @@ function addOverrideToLintConfig(tree, root, override, options = {
114
110
  }) {
115
111
  const isBase = options.checkBaseConfig && findEslintFile(tree, root).includes('.base');
116
112
  if ((0, flat_config_1.useFlatConfig)(tree)) {
117
- const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
113
+ const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.flatConfigEslintFilename)(tree));
118
114
  const flatOverride = (0, ast_utils_1.generateFlatOverride)(override);
119
115
  let content = tree.read(fileName, 'utf8');
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);
116
+ // we will be using compat here so we need to make sure it's added
117
+ if (overrideNeedsCompat(override)) {
118
+ content = (0, ast_utils_1.addCompatToFlatConfig)(content);
123
119
  }
124
120
  tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(content, flatOverride, options));
125
121
  }
@@ -137,9 +133,12 @@ function addOverrideToLintConfig(tree, root, override, options = {
137
133
  });
138
134
  }
139
135
  }
136
+ function overrideNeedsCompat(override) {
137
+ return (override.env || override.extends || override.plugins || override.parser);
138
+ }
140
139
  function updateOverrideInLintConfig(tree, root, lookup, update) {
141
140
  if ((0, flat_config_1.useFlatConfig)(tree)) {
142
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
141
+ const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.flatConfigEslintFilename)(tree));
143
142
  let content = tree.read(fileName, 'utf8');
144
143
  content = (0, ast_utils_1.replaceOverride)(content, root, lookup, update);
145
144
  tree.write(fileName, content);
@@ -174,7 +173,7 @@ function lintConfigHasOverride(tree, root, lookup, checkBaseConfig = false) {
174
173
  }
175
174
  const isBase = checkBaseConfig && findEslintFile(tree, root).includes('.base');
176
175
  if ((0, flat_config_1.useFlatConfig)(tree)) {
177
- const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
176
+ const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.flatConfigEslintFilename)(tree));
178
177
  const content = tree.read(fileName, 'utf8');
179
178
  return (0, ast_utils_1.hasOverride)(content, lookup);
180
179
  }
@@ -185,11 +184,11 @@ function lintConfigHasOverride(tree, root, lookup, checkBaseConfig = false) {
185
184
  }
186
185
  function replaceOverridesInLintConfig(tree, root, overrides) {
187
186
  if ((0, flat_config_1.useFlatConfig)(tree)) {
188
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
187
+ const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.flatConfigEslintFilename)(tree));
189
188
  let content = tree.read(fileName, 'utf8');
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);
189
+ // we will be using compat here so we need to make sure it's added
190
+ if (overrides.some(overrideNeedsCompat)) {
191
+ content = (0, ast_utils_1.addCompatToFlatConfig)(content);
193
192
  }
194
193
  content = (0, ast_utils_1.removeOverridesFromLintConfig)(content);
195
194
  overrides.forEach((override) => {
@@ -206,62 +205,18 @@ function replaceOverridesInLintConfig(tree, root, overrides) {
206
205
  });
207
206
  }
208
207
  }
209
- function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
208
+ function addExtendsToLintConfig(tree, root, plugin) {
209
+ const plugins = Array.isArray(plugin) ? plugin : [plugin];
210
210
  if ((0, flat_config_1.useFlatConfig)(tree)) {
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
- }
211
+ const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.flatConfigEslintFilename)(tree));
212
+ const pluginExtends = (0, ast_utils_1.generatePluginExtendsElement)(plugins);
245
213
  let content = tree.read(fileName, 'utf8');
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 () => { };
214
+ content = (0, ast_utils_1.addCompatToFlatConfig)(content);
215
+ tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(content, pluginExtends, {
216
+ insertAtTheEnd: false,
217
+ }));
262
218
  }
263
219
  else {
264
- const plugins = (Array.isArray(plugin) ? plugin : [plugin]).map((p) => typeof p === 'string' ? p : p.name);
265
220
  const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
266
221
  (0, devkit_1.updateJson)(tree, fileName, (json) => {
267
222
  json.extends ??= [];
@@ -271,22 +226,12 @@ function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
271
226
  ];
272
227
  return json;
273
228
  });
274
- return () => { };
275
229
  }
276
230
  }
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
- }
286
231
  function addPluginsToLintConfig(tree, root, plugin) {
287
232
  const plugins = Array.isArray(plugin) ? plugin : [plugin];
288
233
  if ((0, flat_config_1.useFlatConfig)(tree)) {
289
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
234
+ const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.flatConfigEslintFilename)(tree));
290
235
  let content = tree.read(fileName, 'utf8');
291
236
  const mappedPlugins = [];
292
237
  plugins.forEach((name) => {
@@ -310,7 +255,7 @@ function addPluginsToLintConfig(tree, root, plugin) {
310
255
  }
311
256
  function addIgnoresToLintConfig(tree, root, ignorePatterns) {
312
257
  if ((0, flat_config_1.useFlatConfig)(tree)) {
313
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
258
+ const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.flatConfigEslintFilename)(tree));
314
259
  const block = (0, ast_utils_1.generateAst)({
315
260
  ignores: ignorePatterns.map((path) => (0, path_utils_1.mapFilePath)(path)),
316
261
  });
@@ -8,15 +8,11 @@ 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: Partial<Linter.ConfigOverride<Linter.RulesRecord>>) => Partial<Linter.ConfigOverride<Linter.RulesRecord>>): string;
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
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;
20
16
  /**
21
17
  * Injects new ts.expression to the end of the module.exports array.
22
18
  */
@@ -26,7 +22,6 @@ export declare function addBlockToFlatConfigExport(content: string, config: ts.E
26
22
  }): string;
27
23
  export declare function removePlugin(content: string, pluginName: string, pluginImport: string): string;
28
24
  export declare function removeCompatExtends(content: string, compatExtends: string[]): string;
29
- export declare function removePredefinedConfigs(content: string, moduleImport: string, moduleVariable: string, configs: string[]): string;
30
25
  /**
31
26
  * Add plugins block to the top of the export blocks
32
27
  */
@@ -38,15 +33,14 @@ export declare function addPluginsToExportsBlock(content: string, plugins: {
38
33
  /**
39
34
  * Adds compat if missing to flat config
40
35
  */
41
- export declare function addFlatCompatToFlatConfig(content: string): string;
36
+ export declare function addCompatToFlatConfig(content: string): string;
42
37
  /**
43
38
  * Generate node list representing the imports and the exports blocks
44
39
  * Optionally add flat compat initialization
45
40
  */
46
- export declare function createNodeList(importsMap: Map<string, string>, exportElements: ts.Expression[]): ts.NodeArray<ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile>;
41
+ export declare function createNodeList(importsMap: Map<string, string>, exportElements: ts.Expression[], isFlatCompatNeeded: boolean): ts.NodeArray<ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile>;
47
42
  export declare function generateSpreadElement(name: string): ts.SpreadElement;
48
43
  export declare function generatePluginExtendsElement(plugins: string[]): ts.SpreadElement;
49
- export declare function generatePluginExtendsElementWithCompatFixup(plugin: string): ts.SpreadElement;
50
44
  /**
51
45
  * Stringifies TS nodes to file content string
52
46
  */
@@ -55,20 +49,12 @@ export declare function stringifyNodeList(nodes: ts.NodeArray<ts.VariableStateme
55
49
  * generates AST require statement
56
50
  */
57
51
  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
- };
61
52
  /**
62
- * Generates an AST object or spread element representing a modern flat config entry,
63
- * based on a given legacy eslintrc JSON override object
53
+ * Generates AST object or spread element based on JSON override object
64
54
  */
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>>;
55
+ export declare function generateFlatOverride(override: Linter.ConfigOverride<Linter.RulesRecord>): ts.ObjectLiteralExpression | ts.SpreadElement;
56
+ export declare function mapFilePaths(override: Linter.ConfigOverride<Linter.RulesRecord>): void;
68
57
  /**
69
58
  * Generates an AST from a JSON-type input
70
59
  */
71
- export declare function generateAst<T>(input: unknown, propertyAssignmentReplacer?: {
72
- keyToMatch: RegExp | string;
73
- replacer: (propertyAssignment: ts.PropertyAssignment, propertyName: string) => ts.PropertyAssignment;
74
- }): T;
60
+ export declare function generateAst<T>(input: unknown): T;
@@ -4,22 +4,17 @@ exports.removeOverridesFromLintConfig = removeOverridesFromLintConfig;
4
4
  exports.hasOverride = hasOverride;
5
5
  exports.replaceOverride = replaceOverride;
6
6
  exports.addImportToFlatConfig = addImportToFlatConfig;
7
- exports.removeImportFromFlatConfig = removeImportFromFlatConfig;
8
7
  exports.addBlockToFlatConfigExport = addBlockToFlatConfigExport;
9
8
  exports.removePlugin = removePlugin;
10
9
  exports.removeCompatExtends = removeCompatExtends;
11
- exports.removePredefinedConfigs = removePredefinedConfigs;
12
10
  exports.addPluginsToExportsBlock = addPluginsToExportsBlock;
13
- exports.addFlatCompatToFlatConfig = addFlatCompatToFlatConfig;
11
+ exports.addCompatToFlatConfig = addCompatToFlatConfig;
14
12
  exports.createNodeList = createNodeList;
15
13
  exports.generateSpreadElement = generateSpreadElement;
16
14
  exports.generatePluginExtendsElement = generatePluginExtendsElement;
17
- exports.generatePluginExtendsElementWithCompatFixup = generatePluginExtendsElementWithCompatFixup;
18
15
  exports.stringifyNodeList = stringifyNodeList;
19
16
  exports.generateRequire = generateRequire;
20
- exports.overrideNeedsCompat = overrideNeedsCompat;
21
17
  exports.generateFlatOverride = generateFlatOverride;
22
- exports.generateFlatPredefinedConfig = generateFlatPredefinedConfig;
23
18
  exports.mapFilePaths = mapFilePaths;
24
19
  exports.generateAst = generateAst;
25
20
  const devkit_1 = require("@nx/devkit");
@@ -91,7 +86,10 @@ function hasOverride(content, lookup) {
91
86
  // strip any spread elements
92
87
  objSource = fullNodeText.replace(SPREAD_ELEMENTS_REGEXP, '');
93
88
  }
94
- const data = parseTextToJson(objSource);
89
+ const data = (0, devkit_1.parseJson)(objSource
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": '));
95
93
  if (lookup(data)) {
96
94
  return true;
97
95
  }
@@ -103,9 +101,7 @@ function parseTextToJson(text) {
103
101
  return (0, devkit_1.parseJson)(text
104
102
  // ensure property names have double quotes so that JSON.parse works
105
103
  .replace(/'/g, '"')
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"'));
104
+ .replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": '));
109
105
  }
110
106
  /**
111
107
  * Finds an override matching the lookup function and applies the update function to it
@@ -142,18 +138,13 @@ function replaceOverride(content, root, lookup, update) {
142
138
  start,
143
139
  length: end - start,
144
140
  });
145
- let updatedData = update(data);
141
+ const updatedData = update(data);
146
142
  if (updatedData) {
147
- updatedData = mapFilePaths(updatedData);
143
+ mapFilePaths(updatedData);
148
144
  changes.push({
149
145
  type: devkit_1.ChangeType.Insert,
150
146
  index: start,
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
147
+ text: JSON.stringify(updatedData, null, 2).slice(2, -2), // remove curly braces and start/end line breaks since we are injecting just properties
157
148
  });
158
149
  }
159
150
  }
@@ -235,32 +226,6 @@ function addImportToFlatConfig(content, variable, imp) {
235
226
  },
236
227
  ]);
237
228
  }
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
- }
264
229
  /**
265
230
  * Injects new ts.expression to the end of the module.exports array.
266
231
  */
@@ -277,11 +242,6 @@ function addBlockToFlatConfigExport(content, config, options = {
277
242
  return node.expression.right.elements;
278
243
  }
279
244
  });
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;
285
245
  const insert = printer.printNode(ts.EmitHint.Expression, config, source);
286
246
  if (options.insertAtTheEnd) {
287
247
  const index = exportsArray.length > 0
@@ -419,7 +379,7 @@ function removePlugin(content, pluginName, pluginImport) {
419
379
  function removeCompatExtends(content, compatExtends) {
420
380
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
421
381
  const changes = [];
422
- findAllBlocks(source)?.forEach((node) => {
382
+ findAllBlocks(source).forEach((node) => {
423
383
  if (ts.isSpreadElement(node) &&
424
384
  ts.isCallExpression(node.expression) &&
425
385
  ts.isArrowFunction(node.expression.arguments[0]) &&
@@ -444,45 +404,13 @@ function removeCompatExtends(content, compatExtends) {
444
404
  type: devkit_1.ChangeType.Insert,
445
405
  index: node.pos,
446
406
  text: '\n' +
447
- body.replace(new RegExp('[ \t]s*...' + paramName + '(\\.rules)?[ \t]*,?\\s*', 'g'), ''),
407
+ body.replace(new RegExp('[ \t]s*...' + paramName + '[ \t]*,?\\s*', 'g'), ''),
448
408
  });
449
409
  }
450
410
  }
451
411
  });
452
412
  return (0, devkit_1.applyChangesToString)(content, changes);
453
413
  }
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
- }
486
414
  /**
487
415
  * Add plugins block to the top of the export blocks
488
416
  */
@@ -499,7 +427,7 @@ function addPluginsToExportsBlock(content, plugins) {
499
427
  /**
500
428
  * Adds compat if missing to flat config
501
429
  */
502
- function addFlatCompatToFlatConfig(content) {
430
+ function addCompatToFlatConfig(content) {
503
431
  let result = content;
504
432
  result = addImportToFlatConfig(result, 'js', '@eslint/js');
505
433
  if (result.includes('const compat = new FlatCompat')) {
@@ -511,21 +439,28 @@ function addFlatCompatToFlatConfig(content) {
511
439
  {
512
440
  type: devkit_1.ChangeType.Insert,
513
441
  index: index - 1,
514
- text: `
515
- const compat = new FlatCompat({
516
- baseDirectory: __dirname,
517
- recommendedConfig: js.configs.recommended,
518
- });
519
- `,
442
+ text: `${DEFAULT_FLAT_CONFIG}\n`,
520
443
  },
521
444
  ]);
522
445
  }
446
+ const DEFAULT_FLAT_CONFIG = `
447
+ const compat = new FlatCompat({
448
+ baseDirectory: __dirname,
449
+ recommendedConfig: js.configs.recommended,
450
+ });
451
+ `;
523
452
  /**
524
453
  * Generate node list representing the imports and the exports blocks
525
454
  * Optionally add flat compat initialization
526
455
  */
527
- function createNodeList(importsMap, exportElements) {
456
+ function createNodeList(importsMap, exportElements, isFlatCompatNeeded) {
528
457
  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
+ }
529
464
  // generateRequire(varName, imp, ts.factory);
530
465
  Array.from(importsMap.entries()).forEach(([imp, varName]) => {
531
466
  importsList.push(generateRequire(varName, imp));
@@ -533,7 +468,7 @@ function createNodeList(importsMap, exportElements) {
533
468
  return ts.factory.createNodeArray([
534
469
  // add plugin imports
535
470
  ...importsList,
536
- ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.JS),
471
+ ts.createSourceFile('', isFlatCompatNeeded ? DEFAULT_FLAT_CONFIG : '', ts.ScriptTarget.Latest, false, ts.ScriptKind.JS),
537
472
  // creates:
538
473
  // module.exports = [ ... ];
539
474
  ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('module'), ts.factory.createIdentifier('exports')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createArrayLiteralExpression(exportElements, true))),
@@ -545,11 +480,6 @@ function generateSpreadElement(name) {
545
480
  function generatePluginExtendsElement(plugins) {
546
481
  return ts.factory.createSpreadElement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('compat'), ts.factory.createIdentifier('extends')), undefined, plugins.map((plugin) => ts.factory.createStringLiteral(plugin))));
547
482
  }
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
- }
553
483
  /**
554
484
  * Stringifies TS nodes to file content string
555
485
  */
@@ -572,95 +502,21 @@ function generateRequire(variableName, imp) {
572
502
  ], ts.NodeFlags.Const));
573
503
  }
574
504
  /**
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
505
+ * Generates AST object or spread element based on JSON override object
598
506
  */
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;
507
+ function generateFlatOverride(override) {
508
+ mapFilePaths(override);
509
+ if (!override.env &&
510
+ !override.extends &&
511
+ !override.plugins &&
512
+ !override.parser) {
513
+ if (override.parserOptions) {
514
+ const { parserOptions, ...rest } = override;
515
+ return generateAst({ ...rest, languageOptions: { parserOptions } });
613
516
  }
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
- });
517
+ return generateAst(override);
661
518
  }
662
- // At this point we are applying the flat config compat tooling to the override
663
- const { excludedFiles, parser, parserOptions, rules, files, ...rest } = override;
519
+ const { files, excludedFiles, rules, parserOptions, ...rest } = override;
664
520
  const objectLiteralElements = [
665
521
  ts.factory.createSpreadAssignment(ts.factory.createIdentifier('config')),
666
522
  ];
@@ -687,14 +543,7 @@ function generateFlatOverride(_override) {
687
543
  ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createParenthesizedExpression(ts.factory.createObjectLiteralExpression(objectLiteralElements, true))),
688
544
  ]));
689
545
  }
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
- };
546
+ function mapFilePaths(override) {
698
547
  if (override.files) {
699
548
  override.files = Array.isArray(override.files)
700
549
  ? override.files
@@ -707,7 +556,6 @@ function mapFilePaths(_override) {
707
556
  : [override.excludedFiles];
708
557
  override.excludedFiles = override.excludedFiles.map((file) => (0, path_utils_1.mapFilePath)(file));
709
558
  }
710
- return override;
711
559
  }
712
560
  function addTSObjectProperty(elements, key, value) {
713
561
  if (value) {
@@ -717,16 +565,18 @@ function addTSObjectProperty(elements, key, value) {
717
565
  /**
718
566
  * Generates an AST from a JSON-type input
719
567
  */
720
- function generateAst(input, propertyAssignmentReplacer) {
568
+ function generateAst(input) {
721
569
  if (Array.isArray(input)) {
722
- return ts.factory.createArrayLiteralExpression(input.map((item) => generateAst(item, propertyAssignmentReplacer)), input.length > 1 // multiline only if more than one item
570
+ return ts.factory.createArrayLiteralExpression(input.map((item) => generateAst(item)), input.length > 1 // multiline only if more than one item
723
571
  );
724
572
  }
725
573
  if (input === null) {
726
574
  return ts.factory.createNull();
727
575
  }
728
576
  if (typeof input === 'object') {
729
- return ts.factory.createObjectLiteralExpression(generatePropertyAssignmentsFromObjectEntries(input, propertyAssignmentReplacer), Object.keys(input).length > 1 // multiline only if more than one property
577
+ return ts.factory.createObjectLiteralExpression(Object.entries(input)
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
730
580
  );
731
581
  }
732
582
  if (typeof input === 'string') {
@@ -741,20 +591,6 @@ function generateAst(input, propertyAssignmentReplacer) {
741
591
  // since we are parsing JSON, this should never happen
742
592
  throw new Error(`Unknown type: ${typeof input} `);
743
593
  }
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
- }
758
594
  function isValidKey(key) {
759
595
  return /^[a-zA-Z0-9_]+$/.test(key);
760
596
  }
@@ -47,9 +47,7 @@ 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)({
51
- useFlatConfigOverrideVal: (0, config_file_1.isFlatConfig)(configFilePath),
52
- });
50
+ const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)((0, config_file_1.isFlatConfig)(configFilePath));
53
51
  const eslintVersion = ESLint.version;
54
52
  const projects = {};
55
53
  await Promise.all(dedupedProjectRoots.map(async (childProjectRoot, index) => {
@@ -98,9 +96,7 @@ const internalCreateNodes = async (configFilePath, options, context, projectsCac
98
96
  };
99
97
  const internalCreateNodesV2 = async (configFilePath, options, context, eslintConfigFiles, projectRootsByEslintRoots, lintableFilesPerProjectRoot, projectsCache) => {
100
98
  const configDir = (0, posix_1.dirname)(configFilePath);
101
- const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)({
102
- useFlatConfigOverrideVal: (0, config_file_1.isFlatConfig)(configFilePath),
103
- });
99
+ const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)((0, config_file_1.isFlatConfig)(configFilePath));
104
100
  const eslintVersion = ESLint.version;
105
101
  const projects = {};
106
102
  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 getRootESLintFlatConfigFilename(tree: Tree): string;
4
- export declare function useFlatConfig(tree?: Tree): boolean;
3
+ export declare function flatConfigEslintFilename(tree: Tree): string;
4
+ export declare function useFlatConfig(tree: Tree): boolean;
@@ -1,47 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.eslintFlatConfigFilenames = void 0;
4
- exports.getRootESLintFlatConfigFilename = getRootESLintFlatConfigFilename;
4
+ exports.flatConfigEslintFilename = flatConfigEslintFilename;
5
5
  exports.useFlatConfig = useFlatConfig;
6
- const semver_1 = require("semver");
7
6
  // todo: add support for eslint.config.mjs,
8
7
  exports.eslintFlatConfigFilenames = [
9
8
  'eslint.config.js',
10
9
  'eslint.config.cjs',
11
10
  ];
12
- function getRootESLintFlatConfigFilename(tree) {
11
+ function flatConfigEslintFilename(tree) {
13
12
  for (const file of exports.eslintFlatConfigFilenames) {
14
13
  if (tree.exists(file)) {
15
14
  return file;
16
15
  }
17
16
  }
18
- throw new Error('Could not find root flat config file');
17
+ throw new Error('Could not find flat config file');
19
18
  }
20
19
  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
37
20
  try {
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');
21
+ return !!flatConfigEslintFilename(tree);
42
22
  }
43
23
  catch {
44
- // Default to assuming flat config in case ESLint is not yet installed
45
- return true;
24
+ return false;
46
25
  }
47
26
  }
@@ -1,4 +1,2 @@
1
1
  import type { ESLint } from 'eslint';
2
- export declare function resolveESLintClass(opts?: {
3
- useFlatConfigOverrideVal: boolean;
4
- }): Promise<typeof ESLint>;
2
+ export declare function resolveESLintClass(useFlatConfig?: boolean): Promise<typeof ESLint>;
@@ -1,19 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveESLintClass = resolveESLintClass;
4
- const flat_config_1 = require("../utils/flat-config");
5
- async function resolveESLintClass(opts) {
4
+ async function resolveESLintClass(useFlatConfig = false) {
6
5
  try {
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;
6
+ // In eslint 8.57.0 (the final v8 version), a dedicated API was added for resolving the correct ESLint class.
7
+ const eslint = await Promise.resolve().then(() => require('eslint'));
8
+ if (typeof eslint.loadESLint === 'function') {
9
+ return await eslint.loadESLint({ useFlatConfig });
10
+ }
11
+ // If that API is not available (an older version of v8), we need to use the old way of resolving the ESLint class.
12
+ if (!useFlatConfig) {
13
+ return eslint.ESLint;
14
+ }
15
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
16
+ const { FlatESLint } = require('eslint/use-at-your-own-risk');
17
+ return FlatESLint;
15
18
  }
16
19
  catch {
17
- throw new Error('Unable to find `eslint`. Ensure a valid `eslint` version is installed.');
20
+ throw new Error('Unable to find ESLint. Ensure ESLint is installed.');
18
21
  }
19
22
  }
@@ -3,6 +3,3 @@ 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";
@@ -1,12 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.eslintCompat = exports.eslint9__eslintVersion = exports.eslint9__typescriptESLintVersion = exports.typescriptESLintVersion = exports.eslintConfigPrettierVersion = exports.eslintrcVersion = exports.eslintVersion = exports.nxVersion = void 0;
3
+ 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';
@@ -1,2 +0,0 @@
1
- import { type Tree } from '@nx/devkit';
2
- export declare function getInstalledEslintVersion(tree?: Tree): string | null;
@@ -1,31 +0,0 @@
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
- }