@nx/eslint 0.0.0-pr-27404-00130f5 → 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-00130f5",
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-00130f5",
39
- "@nx/js": "0.0.0-pr-27404-00130f5",
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-00130f5"
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,19 +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
89
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(moduleBoundariesOverride));
90
90
  }
91
- content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
92
- files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
93
- rules: {},
91
+ content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(exports.typeScriptOverride));
92
+ content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(exports.javaScriptOverride));
93
+ if (unitTestRunner === 'jest') {
94
+ content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)(jestOverride));
95
+ }
96
+ // add ignore for .nx folder
97
+ content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateAst)({
98
+ ignores: ['.nx'],
94
99
  }));
95
100
  return content;
96
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: ['**/*/*eslint*'],
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
  */
@@ -439,45 +404,13 @@ function removeCompatExtends(content, compatExtends) {
439
404
  type: devkit_1.ChangeType.Insert,
440
405
  index: node.pos,
441
406
  text: '\n' +
442
- body.replace(new RegExp('[ \t]s*...' + paramName + '(\\.rules)?[ \t]*,?\\s*', 'g'), ''),
407
+ body.replace(new RegExp('[ \t]s*...' + paramName + '[ \t]*,?\\s*', 'g'), ''),
443
408
  });
444
409
  }
445
410
  }
446
411
  });
447
412
  return (0, devkit_1.applyChangesToString)(content, changes);
448
413
  }
449
- function removePredefinedConfigs(content, moduleImport, moduleVariable, configs) {
450
- const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
451
- const changes = [];
452
- let removeImport = true;
453
- findAllBlocks(source).forEach((node) => {
454
- if (ts.isSpreadElement(node) &&
455
- ts.isElementAccessExpression(node.expression) &&
456
- ts.isPropertyAccessExpression(node.expression.expression) &&
457
- ts.isIdentifier(node.expression.expression.expression) &&
458
- node.expression.expression.expression.getText() === moduleVariable &&
459
- ts.isStringLiteral(node.expression.argumentExpression)) {
460
- const config = node.expression.argumentExpression.getText();
461
- // Check the text without quotes
462
- if (configs.includes(config.substring(1, config.length - 1))) {
463
- changes.push({
464
- type: devkit_1.ChangeType.Delete,
465
- start: node.pos,
466
- length: node.end - node.pos + 1, // trailing comma
467
- });
468
- }
469
- else {
470
- // If there is still a config used, do not remove import
471
- removeImport = false;
472
- }
473
- }
474
- });
475
- let updated = (0, devkit_1.applyChangesToString)(content, changes);
476
- if (removeImport) {
477
- updated = removeImportFromFlatConfig(updated, moduleVariable, moduleImport);
478
- }
479
- return updated;
480
- }
481
414
  /**
482
415
  * Add plugins block to the top of the export blocks
483
416
  */
@@ -494,7 +427,7 @@ function addPluginsToExportsBlock(content, plugins) {
494
427
  /**
495
428
  * Adds compat if missing to flat config
496
429
  */
497
- function addFlatCompatToFlatConfig(content) {
430
+ function addCompatToFlatConfig(content) {
498
431
  let result = content;
499
432
  result = addImportToFlatConfig(result, 'js', '@eslint/js');
500
433
  if (result.includes('const compat = new FlatCompat')) {
@@ -506,21 +439,28 @@ function addFlatCompatToFlatConfig(content) {
506
439
  {
507
440
  type: devkit_1.ChangeType.Insert,
508
441
  index: index - 1,
509
- text: `
510
- const compat = new FlatCompat({
511
- baseDirectory: __dirname,
512
- recommendedConfig: js.configs.recommended,
513
- });
514
- `,
442
+ text: `${DEFAULT_FLAT_CONFIG}\n`,
515
443
  },
516
444
  ]);
517
445
  }
446
+ const DEFAULT_FLAT_CONFIG = `
447
+ const compat = new FlatCompat({
448
+ baseDirectory: __dirname,
449
+ recommendedConfig: js.configs.recommended,
450
+ });
451
+ `;
518
452
  /**
519
453
  * Generate node list representing the imports and the exports blocks
520
454
  * Optionally add flat compat initialization
521
455
  */
522
- function createNodeList(importsMap, exportElements) {
456
+ function createNodeList(importsMap, exportElements, isFlatCompatNeeded) {
523
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
+ }
524
464
  // generateRequire(varName, imp, ts.factory);
525
465
  Array.from(importsMap.entries()).forEach(([imp, varName]) => {
526
466
  importsList.push(generateRequire(varName, imp));
@@ -528,7 +468,7 @@ function createNodeList(importsMap, exportElements) {
528
468
  return ts.factory.createNodeArray([
529
469
  // add plugin imports
530
470
  ...importsList,
531
- ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.JS),
471
+ ts.createSourceFile('', isFlatCompatNeeded ? DEFAULT_FLAT_CONFIG : '', ts.ScriptTarget.Latest, false, ts.ScriptKind.JS),
532
472
  // creates:
533
473
  // module.exports = [ ... ];
534
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))),
@@ -540,11 +480,6 @@ function generateSpreadElement(name) {
540
480
  function generatePluginExtendsElement(plugins) {
541
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))));
542
482
  }
543
- function generatePluginExtendsElementWithCompatFixup(plugin) {
544
- return ts.factory.createSpreadElement(ts.factory.createCallExpression(ts.factory.createIdentifier('fixupConfigRules'), undefined, [
545
- ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('compat'), ts.factory.createIdentifier('extends')), undefined, [ts.factory.createStringLiteral(plugin)]),
546
- ]));
547
- }
548
483
  /**
549
484
  * Stringifies TS nodes to file content string
550
485
  */
@@ -567,95 +502,21 @@ function generateRequire(variableName, imp) {
567
502
  ], ts.NodeFlags.Const));
568
503
  }
569
504
  /**
570
- * FROM: https://github.com/eslint/rewrite/blob/e2a7ec809db20e638abbad250d105ddbde88a8d5/packages/migrate-config/src/migrate-config.js#L222
571
- *
572
- * Converts a glob pattern to a format that can be used in a flat config.
573
- * @param {string} pattern The glob pattern to convert.
574
- * @returns {string} The converted glob pattern.
575
- */
576
- function convertGlobPattern(pattern) {
577
- const isNegated = pattern.startsWith('!');
578
- const patternToTest = isNegated ? pattern.slice(1) : pattern;
579
- // if the pattern is already in the correct format, return it
580
- if (patternToTest === '**' || patternToTest.includes('/')) {
581
- return pattern;
582
- }
583
- return `${isNegated ? '!' : ''}**/${patternToTest}`;
584
- }
585
- // FROM: https://github.com/eslint/rewrite/blob/e2a7ec809db20e638abbad250d105ddbde88a8d5/packages/migrate-config/src/migrate-config.js#L38
586
- const keysToCopy = ['settings', 'rules', 'processor'];
587
- function overrideNeedsCompat(override) {
588
- return override.env || override.extends || override.plugins;
589
- }
590
- /**
591
- * Generates an AST object or spread element representing a modern flat config entry,
592
- * based on a given legacy eslintrc JSON override object
505
+ * Generates AST object or spread element based on JSON override object
593
506
  */
594
- function generateFlatOverride(_override) {
595
- const override = mapFilePaths(_override);
596
- // We do not need the compat tooling for this override
597
- if (!overrideNeedsCompat(override)) {
598
- // Ensure files is an array
599
- let files = override.files;
600
- if (typeof files === 'string') {
601
- files = [files];
602
- }
603
- const flatConfigOverride = {
604
- files,
605
- };
606
- if (override.rules) {
607
- 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 } });
608
516
  }
609
- // Copy over everything that stays the same
610
- keysToCopy.forEach((key) => {
611
- if (override[key]) {
612
- flatConfigOverride[key] = override[key];
613
- }
614
- });
615
- if (override.parser || override.parserOptions) {
616
- const languageOptions = {};
617
- if (override.parser) {
618
- languageOptions['parser'] = override.parser;
619
- }
620
- if (override.parserOptions) {
621
- languageOptions['parserOptions'] = override.parserOptions;
622
- }
623
- if (Object.keys(languageOptions).length) {
624
- flatConfigOverride.languageOptions = languageOptions;
625
- }
626
- }
627
- if (override['languageOptions']) {
628
- flatConfigOverride.languageOptions = override['languageOptions'];
629
- }
630
- if (override.excludedFiles) {
631
- flatConfigOverride.ignores = (Array.isArray(override.excludedFiles)
632
- ? override.excludedFiles
633
- : [override.excludedFiles]).map((p) => convertGlobPattern(p));
634
- }
635
- return generateAst(flatConfigOverride, {
636
- keyToMatch: /^(parser|rules)$/,
637
- replacer: (propertyAssignment, propertyName) => {
638
- if (propertyName === 'rules') {
639
- // Add comment that user can override rules if there are no overrides.
640
- if (ts.isObjectLiteralExpression(propertyAssignment.initializer) &&
641
- propertyAssignment.initializer.properties.length === 0) {
642
- return ts.addSyntheticLeadingComment(ts.factory.createPropertyAssignment(propertyAssignment.name, ts.factory.createObjectLiteralExpression([])), ts.SyntaxKind.SingleLineCommentTrivia, ' Override or add rules here');
643
- }
644
- return propertyAssignment;
645
- }
646
- else {
647
- // Change parser to require statement.
648
- return ts.factory.createPropertyAssignment('parser', ts.factory.createCallExpression(ts.factory.createIdentifier('require'), undefined, [
649
- ts.factory.createStringLiteral(override['languageOptions']?.['parserOptions']?.parser ??
650
- override['languageOptions']?.parser ??
651
- override.parser),
652
- ]));
653
- }
654
- },
655
- });
517
+ return generateAst(override);
656
518
  }
657
- // At this point we are applying the flat config compat tooling to the override
658
- const { excludedFiles, parser, parserOptions, rules, files, ...rest } = override;
519
+ const { files, excludedFiles, rules, parserOptions, ...rest } = override;
659
520
  const objectLiteralElements = [
660
521
  ts.factory.createSpreadAssignment(ts.factory.createIdentifier('config')),
661
522
  ];
@@ -682,14 +543,7 @@ function generateFlatOverride(_override) {
682
543
  ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createParenthesizedExpression(ts.factory.createObjectLiteralExpression(objectLiteralElements, true))),
683
544
  ]));
684
545
  }
685
- function generateFlatPredefinedConfig(predefinedConfigName, moduleName = 'nx', spread = true) {
686
- const node = ts.factory.createElementAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(moduleName), ts.factory.createIdentifier('configs')), ts.factory.createStringLiteral(predefinedConfigName));
687
- return spread ? ts.factory.createSpreadElement(node) : node;
688
- }
689
- function mapFilePaths(_override) {
690
- const override = {
691
- ..._override,
692
- };
546
+ function mapFilePaths(override) {
693
547
  if (override.files) {
694
548
  override.files = Array.isArray(override.files)
695
549
  ? override.files
@@ -702,7 +556,6 @@ function mapFilePaths(_override) {
702
556
  : [override.excludedFiles];
703
557
  override.excludedFiles = override.excludedFiles.map((file) => (0, path_utils_1.mapFilePath)(file));
704
558
  }
705
- return override;
706
559
  }
707
560
  function addTSObjectProperty(elements, key, value) {
708
561
  if (value) {
@@ -712,16 +565,18 @@ function addTSObjectProperty(elements, key, value) {
712
565
  /**
713
566
  * Generates an AST from a JSON-type input
714
567
  */
715
- function generateAst(input, propertyAssignmentReplacer) {
568
+ function generateAst(input) {
716
569
  if (Array.isArray(input)) {
717
- 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
718
571
  );
719
572
  }
720
573
  if (input === null) {
721
574
  return ts.factory.createNull();
722
575
  }
723
576
  if (typeof input === 'object') {
724
- 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
725
580
  );
726
581
  }
727
582
  if (typeof input === 'string') {
@@ -736,20 +591,6 @@ function generateAst(input, propertyAssignmentReplacer) {
736
591
  // since we are parsing JSON, this should never happen
737
592
  throw new Error(`Unknown type: ${typeof input} `);
738
593
  }
739
- function generatePropertyAssignmentsFromObjectEntries(input, propertyAssignmentReplacer) {
740
- return Object.entries(input)
741
- .filter(([_, value]) => value !== undefined)
742
- .map(([key, value]) => {
743
- const original = ts.factory.createPropertyAssignment(isValidKey(key) ? key : ts.factory.createStringLiteral(key), generateAst(value, propertyAssignmentReplacer));
744
- if (propertyAssignmentReplacer &&
745
- (typeof propertyAssignmentReplacer.keyToMatch === 'string'
746
- ? key === propertyAssignmentReplacer.keyToMatch
747
- : propertyAssignmentReplacer.keyToMatch.test(key))) {
748
- return propertyAssignmentReplacer.replacer(original, key);
749
- }
750
- return original;
751
- });
752
- }
753
594
  function isValidKey(key) {
754
595
  return /^[a-zA-Z0-9_]+$/.test(key);
755
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
- }