@nx/eslint 0.0.0-pr-31222-862e973 → 0.0.0-pr-31313-387cdca

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.
Files changed (55) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -1
  3. package/migrations.json +69 -112
  4. package/package.json +6 -7
  5. package/src/executors/lint/utility/eslint-utils.js +6 -0
  6. package/src/generators/convert-to-flat-config/converters/json-converter.d.ts +1 -1
  7. package/src/generators/convert-to-flat-config/converters/json-converter.js +10 -18
  8. package/src/generators/convert-to-flat-config/generator.js +18 -17
  9. package/src/generators/convert-to-flat-config/schema.d.ts +2 -0
  10. package/src/generators/convert-to-inferred/convert-to-inferred.js +2 -1
  11. package/src/generators/init/global-eslint-config.d.ts +1 -1
  12. package/src/generators/init/global-eslint-config.js +17 -6
  13. package/src/generators/init/init-migration.d.ts +1 -1
  14. package/src/generators/init/init-migration.js +18 -13
  15. package/src/generators/init/init.d.ts +1 -0
  16. package/src/generators/init/init.js +31 -6
  17. package/src/generators/lint-project/lint-project.d.ts +1 -0
  18. package/src/generators/lint-project/lint-project.js +37 -15
  19. package/src/generators/lint-project/setup-root-eslint.d.ts +1 -0
  20. package/src/generators/lint-project/setup-root-eslint.js +2 -1
  21. package/src/generators/utils/eslint-file.d.ts +3 -2
  22. package/src/generators/utils/eslint-file.js +160 -28
  23. package/src/generators/utils/flat-config/ast-utils.d.ts +12 -4
  24. package/src/generators/utils/flat-config/ast-utils.js +412 -63
  25. package/src/generators/utils/linter.d.ts +3 -0
  26. package/src/generators/utils/linter.js +2 -2
  27. package/src/generators/workspace-rule/files/__name__.spec.ts__tmpl__ +11 -2
  28. package/src/generators/workspace-rule/workspace-rule.d.ts +2 -2
  29. package/src/generators/workspace-rule/workspace-rule.js +11 -3
  30. package/src/generators/workspace-rules-project/workspace-rules-project.js +4 -1
  31. package/src/migrations/update-20-2-0/update-typescript-eslint-v8-13-0.d.ts +2 -0
  32. package/src/migrations/update-20-2-0/update-typescript-eslint-v8-13-0.js +23 -0
  33. package/src/migrations/update-20-3-0/add-file-extensions-to-overrides.d.ts +2 -0
  34. package/src/migrations/update-20-3-0/add-file-extensions-to-overrides.js +49 -0
  35. package/src/plugins/plugin.js +21 -10
  36. package/src/utils/config-file.d.ts +3 -1
  37. package/src/utils/config-file.js +5 -2
  38. package/src/utils/flat-config.d.ts +1 -0
  39. package/src/utils/flat-config.js +9 -3
  40. package/src/utils/version-utils.d.ts +1 -0
  41. package/src/utils/version-utils.js +13 -9
  42. package/src/utils/versions.d.ts +3 -2
  43. package/src/utils/versions.js +4 -3
  44. package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.d.ts +0 -2
  45. package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.js +0 -9
  46. package/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.d.ts +0 -2
  47. package/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.js +0 -44
  48. package/src/migrations/update-17-0-0-rename-to-eslint/update-17-0-0-rename-to-eslint.d.ts +0 -2
  49. package/src/migrations/update-17-0-0-rename-to-eslint/update-17-0-0-rename-to-eslint.js +0 -47
  50. package/src/migrations/update-17-1-0/update-typescript-eslint.d.ts +0 -2
  51. package/src/migrations/update-17-1-0/update-typescript-eslint.js +0 -74
  52. package/src/migrations/update-17-2-0/simplify-eslint-patterns.d.ts +0 -2
  53. package/src/migrations/update-17-2-0/simplify-eslint-patterns.js +0 -46
  54. package/src/migrations/update-17-2-9/move-options-to-target-defaults.d.ts +0 -2
  55. package/src/migrations/update-17-2-9/move-options-to-target-defaults.js +0 -107
@@ -4,6 +4,7 @@ export interface LinterInitOptions {
4
4
  keepExistingVersions?: boolean;
5
5
  updatePackageScripts?: boolean;
6
6
  addPlugin?: boolean;
7
+ eslintConfigFormat?: 'mjs' | 'cjs';
7
8
  }
8
9
  export declare function initEsLint(tree: Tree, options: LinterInitOptions): Promise<GeneratorCallback>;
9
10
  export declare function lintInitGenerator(tree: Tree, options: LinterInitOptions): Promise<GeneratorCallback>;
@@ -8,18 +8,19 @@ const versions_1 = require("../../utils/versions");
8
8
  const eslint_file_1 = require("../utils/eslint-file");
9
9
  const plugin_1 = require("../../plugins/plugin");
10
10
  const plugin_2 = require("../utils/plugin");
11
- function updateProductionFileset(tree) {
11
+ const path_1 = require("path");
12
+ function updateProductionFileset(tree, format = 'mjs') {
12
13
  const nxJson = (0, devkit_1.readNxJson)(tree);
13
14
  const productionFileSet = nxJson.namedInputs?.production;
14
15
  if (productionFileSet) {
15
16
  productionFileSet.push('!{projectRoot}/.eslintrc.json');
16
- productionFileSet.push('!{projectRoot}/eslint.config.js');
17
+ productionFileSet.push(`!{projectRoot}/eslint.config.${format}`);
17
18
  // Dedupe and set
18
19
  nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
19
20
  }
20
21
  (0, devkit_1.updateNxJson)(tree, nxJson);
21
22
  }
22
- function addTargetDefaults(tree) {
23
+ function addTargetDefaults(tree, format) {
23
24
  const nxJson = (0, devkit_1.readNxJson)(tree);
24
25
  nxJson.targetDefaults ??= {};
25
26
  nxJson.targetDefaults['@nx/eslint:lint'] ??= {};
@@ -28,17 +29,40 @@ function addTargetDefaults(tree) {
28
29
  'default',
29
30
  `{workspaceRoot}/.eslintrc.json`,
30
31
  `{workspaceRoot}/.eslintignore`,
31
- `{workspaceRoot}/eslint.config.js`,
32
+ `{workspaceRoot}/eslint.config.${format}`,
32
33
  ];
33
34
  (0, devkit_1.updateNxJson)(tree, nxJson);
34
35
  }
36
+ function updateVsCodeRecommendedExtensions(host) {
37
+ if (!host.exists('.vscode/extensions.json')) {
38
+ return;
39
+ }
40
+ (0, devkit_1.updateJson)(host, '.vscode/extensions.json', (json) => {
41
+ json.recommendations = json.recommendations || [];
42
+ const extension = 'dbaeumer.vscode-eslint';
43
+ if (!json.recommendations.includes(extension)) {
44
+ json.recommendations.push(extension);
45
+ }
46
+ return json;
47
+ });
48
+ }
35
49
  async function initEsLint(tree, options) {
36
50
  const nxJson = (0, devkit_1.readNxJson)(tree);
37
51
  const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
38
52
  nxJson.useInferencePlugins !== false;
39
53
  options.addPlugin ??= addPluginDefault;
54
+ options.eslintConfigFormat ??= 'mjs';
40
55
  const hasPlugin = (0, plugin_2.hasEslintPlugin)(tree);
41
56
  const rootEslintFile = (0, eslint_file_1.findEslintFile)(tree);
57
+ if (rootEslintFile) {
58
+ const fileExtension = (0, path_1.extname)(rootEslintFile);
59
+ if (fileExtension === '.mjs' || fileExtension === '.cjs') {
60
+ options.eslintConfigFormat = fileExtension.slice(1);
61
+ }
62
+ else {
63
+ options.eslintConfigFormat = (0, eslint_file_1.determineEslintConfigFormat)(tree.read(rootEslintFile, 'utf-8'));
64
+ }
65
+ }
42
66
  const graph = await (0, devkit_1.createProjectGraphAsync)();
43
67
  const lintTargetNames = [
44
68
  'lint',
@@ -57,14 +81,15 @@ async function initEsLint(tree, options) {
57
81
  if (rootEslintFile) {
58
82
  return () => { };
59
83
  }
60
- updateProductionFileset(tree);
84
+ updateProductionFileset(tree, options.eslintConfigFormat);
85
+ updateVsCodeRecommendedExtensions(tree);
61
86
  if (options.addPlugin) {
62
87
  await (0, add_plugin_1.addPlugin)(tree, graph, '@nx/eslint/plugin', plugin_1.createNodesV2, {
63
88
  targetName: lintTargetNames,
64
89
  }, options.updatePackageScripts);
65
90
  }
66
91
  else {
67
- addTargetDefaults(tree);
92
+ addTargetDefaults(tree, options.eslintConfigFormat);
68
93
  }
69
94
  const tasks = [];
70
95
  if (!options.skipPackageJson) {
@@ -12,6 +12,7 @@ interface LintProjectOptions {
12
12
  rootProject?: boolean;
13
13
  keepExistingVersions?: boolean;
14
14
  addPlugin?: boolean;
15
+ eslintConfigFormat?: 'mjs' | 'cjs';
15
16
  /**
16
17
  * @internal
17
18
  */
@@ -12,12 +12,15 @@ const flat_config_1 = require("../../utils/flat-config");
12
12
  const ast_utils_1 = require("../utils/flat-config/ast-utils");
13
13
  const config_file_1 = require("../../utils/config-file");
14
14
  const plugin_1 = require("../utils/plugin");
15
+ const versions_1 = require("../../utils/versions");
15
16
  const setup_root_eslint_1 = require("./setup-root-eslint");
17
+ const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup");
16
18
  function lintProjectGenerator(tree, options) {
17
19
  return lintProjectGeneratorInternal(tree, { addPlugin: false, ...options });
18
20
  }
19
21
  async function lintProjectGeneratorInternal(tree, options) {
20
22
  const nxJson = (0, devkit_1.readNxJson)(tree);
23
+ options.eslintConfigFormat ??= 'mjs';
21
24
  const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
22
25
  nxJson.useInferencePlugins !== false;
23
26
  options.addPlugin ??= addPluginDefault;
@@ -25,12 +28,14 @@ async function lintProjectGeneratorInternal(tree, options) {
25
28
  const initTask = await (0, init_1.lintInitGenerator)(tree, {
26
29
  skipPackageJson: options.skipPackageJson,
27
30
  addPlugin: options.addPlugin,
31
+ eslintConfigFormat: options.eslintConfigFormat,
28
32
  });
29
33
  tasks.push(initTask);
30
34
  const rootEsLintTask = (0, setup_root_eslint_1.setupRootEsLint)(tree, {
31
35
  unitTestRunner: options.unitTestRunner,
32
36
  skipPackageJson: options.skipPackageJson,
33
37
  rootProject: options.rootProject,
38
+ eslintConfigFormat: options.eslintConfigFormat,
34
39
  });
35
40
  tasks.push(rootEsLintTask);
36
41
  const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
@@ -41,7 +46,7 @@ async function lintProjectGeneratorInternal(tree, options) {
41
46
  if (lintFilePatterns &&
42
47
  lintFilePatterns.length &&
43
48
  !lintFilePatterns.includes('{projectRoot}') &&
44
- isBuildableLibraryProject(projectConfig)) {
49
+ isBuildableLibraryProject(tree, projectConfig)) {
45
50
  lintFilePatterns.push(`{projectRoot}/package.json`);
46
51
  }
47
52
  const hasPlugin = (0, plugin_1.hasEslintPlugin)(tree);
@@ -49,6 +54,7 @@ async function lintProjectGeneratorInternal(tree, options) {
49
54
  if (lintFilePatterns &&
50
55
  lintFilePatterns.length &&
51
56
  lintFilePatterns.some((p) => !['./src', '{projectRoot}', projectConfig.root].includes(p))) {
57
+ projectConfig.targets ??= {};
52
58
  projectConfig.targets['lint'] = {
53
59
  command: `eslint ${lintFilePatterns
54
60
  .join(' ')
@@ -57,6 +63,7 @@ async function lintProjectGeneratorInternal(tree, options) {
57
63
  }
58
64
  }
59
65
  else {
66
+ projectConfig.targets ??= {};
60
67
  projectConfig.targets['lint'] = {
61
68
  executor: '@nx/eslint:lint',
62
69
  };
@@ -82,7 +89,7 @@ async function lintProjectGeneratorInternal(tree, options) {
82
89
  filteredProjects.push(project);
83
90
  }
84
91
  });
85
- const migrateTask = (0, init_migration_1.migrateConfigToMonorepoStyle)(filteredProjects, tree, options.unitTestRunner, options.keepExistingVersions);
92
+ const migrateTask = (0, init_migration_1.migrateConfigToMonorepoStyle)(filteredProjects, tree, options.unitTestRunner, options.eslintConfigFormat, options.keepExistingVersions);
86
93
  tasks.push(migrateTask);
87
94
  }
88
95
  }
@@ -90,10 +97,15 @@ async function lintProjectGeneratorInternal(tree, options) {
90
97
  // additionally, the companion e2e app would have `rootProject: true`
91
98
  // so we need to check for the root path as well
92
99
  if (!options.rootProject || projectConfig.root !== '.') {
93
- createEsLintConfiguration(tree, options, projectConfig, options.setParserOptionsProject, options.rootProject);
100
+ const addDependencyChecks = options.addPackageJsonDependencyChecks ||
101
+ isBuildableLibraryProject(tree, projectConfig);
102
+ createEsLintConfiguration(tree, options, projectConfig, options.setParserOptionsProject, options.rootProject, addDependencyChecks);
103
+ if (addDependencyChecks) {
104
+ tasks.push((0, devkit_1.addDependenciesToPackageJson)(tree, {}, { 'jsonc-eslint-parser': versions_1.jsoncEslintParserVersion }, undefined, true));
105
+ }
94
106
  }
95
107
  // Buildable libs need source analysis enabled for linting `package.json`.
96
- if (isBuildableLibraryProject(projectConfig) &&
108
+ if (isBuildableLibraryProject(tree, projectConfig) &&
97
109
  !isJsAnalyzeSourceFilesEnabled(tree)) {
98
110
  (0, devkit_1.updateJson)(tree, 'nx.json', (json) => {
99
111
  json.pluginsConfig ??= {};
@@ -108,14 +120,24 @@ async function lintProjectGeneratorInternal(tree, options) {
108
120
  }
109
121
  return (0, devkit_1.runTasksInSerial)(...tasks);
110
122
  }
111
- function createEsLintConfiguration(tree, options, projectConfig, setParserOptionsProject, rootProject) {
123
+ function createEsLintConfiguration(tree, options, projectConfig, setParserOptionsProject, rootProject, addDependencyChecks) {
112
124
  // we are only extending root for non-standalone projects or their complementary e2e apps
113
125
  const extendedRootConfig = rootProject ? undefined : (0, eslint_file_1.findEslintFile)(tree);
114
126
  const pathToRootConfig = extendedRootConfig
115
127
  ? `${(0, devkit_1.offsetFromRoot)(projectConfig.root)}${extendedRootConfig}`
116
128
  : undefined;
117
- const addDependencyChecks = options.addPackageJsonDependencyChecks ||
118
- isBuildableLibraryProject(projectConfig);
129
+ if (extendedRootConfig) {
130
+ // We do not want to mix the formats
131
+ // if the base file extension is `.mjs` we should use `mjs` for the new file
132
+ // or if base the file extension is `.cjs` then the format should be `cjs`
133
+ const fileExtension = (0, path_1.extname)(extendedRootConfig);
134
+ if (fileExtension === '.mjs' || fileExtension === '.cjs') {
135
+ options.eslintConfigFormat = fileExtension.slice(1);
136
+ }
137
+ else {
138
+ options.eslintConfigFormat = (0, eslint_file_1.determineEslintConfigFormat)(tree.read(extendedRootConfig, 'utf-8'));
139
+ }
140
+ }
119
141
  const overrides = (0, flat_config_1.useFlatConfig)(tree)
120
142
  ? // For flat configs, we don't need to generate different overrides for each file. Users should add their own overrides as needed.
121
143
  []
@@ -178,11 +200,11 @@ function createEsLintConfiguration(tree, options, projectConfig, setParserOption
178
200
  nodes.push((0, ast_utils_1.generateSpreadElement)('baseConfig'));
179
201
  }
180
202
  overrides.forEach((override) => {
181
- nodes.push((0, ast_utils_1.generateFlatOverride)(override));
203
+ nodes.push((0, ast_utils_1.generateFlatOverride)(override, options.eslintConfigFormat));
182
204
  });
183
- const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes);
205
+ const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes, options.eslintConfigFormat);
184
206
  const content = (0, ast_utils_1.stringifyNodeList)(nodeList);
185
- tree.write((0, path_1.join)(projectConfig.root, 'eslint.config.js'), content);
207
+ tree.write((0, path_1.join)(projectConfig.root, `eslint.config.${options.eslintConfigFormat}`), content);
186
208
  }
187
209
  else {
188
210
  (0, devkit_1.writeJson)(tree, (0, path_1.join)(projectConfig.root, `.eslintrc.json`), {
@@ -199,8 +221,9 @@ function isJsAnalyzeSourceFilesEnabled(tree) {
199
221
  return (jsPluginConfig?.analyzeSourceFiles ??
200
222
  nxJson.extends !== 'nx/presets/npm.json');
201
223
  }
202
- function isBuildableLibraryProject(projectConfig) {
203
- return (projectConfig.projectType === 'library' &&
224
+ function isBuildableLibraryProject(tree, projectConfig) {
225
+ return ((0, ts_solution_setup_1.getProjectType)(tree, projectConfig.root, projectConfig.projectType) ===
226
+ 'library' &&
204
227
  projectConfig.targets?.build &&
205
228
  !!projectConfig.targets.build);
206
229
  }
@@ -210,8 +233,7 @@ function isBuildableLibraryProject(projectConfig) {
210
233
  */
211
234
  function isMigrationToMonorepoNeeded(tree, graph) {
212
235
  // the base config is already created, migration has been done
213
- if (tree.exists(config_file_1.baseEsLintConfigFile) ||
214
- tree.exists(config_file_1.baseEsLintFlatConfigFile)) {
236
+ if ([config_file_1.baseEsLintConfigFile, ...config_file_1.BASE_ESLINT_CONFIG_FILENAMES].some((f) => tree.exists(f))) {
215
237
  return false;
216
238
  }
217
239
  const nodes = Object.values(graph.nodes);
@@ -221,7 +243,7 @@ function isMigrationToMonorepoNeeded(tree, graph) {
221
243
  return false;
222
244
  }
223
245
  for (const targetConfig of Object.values(rootProject.data.targets ?? {})) {
224
- if (['@nx/eslint:lint', '@nrwl/linter:eslint', '@nx/linter:eslint'].includes(targetConfig.executor) ||
246
+ if (['@nx/eslint:lint', '@nx/linter:eslint'].includes(targetConfig.executor) ||
225
247
  (targetConfig.executor === 'nx:run-commands' &&
226
248
  targetConfig.options?.command &&
227
249
  targetConfig.options?.command.startsWith('eslint '))) {
@@ -3,5 +3,6 @@ export type SetupRootEsLintOptions = {
3
3
  unitTestRunner?: string;
4
4
  skipPackageJson?: boolean;
5
5
  rootProject?: boolean;
6
+ eslintConfigFormat?: 'mjs' | 'cjs';
6
7
  };
7
8
  export declare function setupRootEsLint(tree: Tree, options: SetupRootEsLintOptions): GeneratorCallback;
@@ -11,6 +11,7 @@ function setupRootEsLint(tree, options) {
11
11
  if (rootEslintFile) {
12
12
  return () => { };
13
13
  }
14
+ options.eslintConfigFormat ??= 'mjs';
14
15
  if (!(0, flat_config_1.useFlatConfig)(tree)) {
15
16
  return setUpLegacyRootEslintRc(tree, options);
16
17
  }
@@ -38,7 +39,7 @@ function setUpLegacyRootEslintRc(tree, options) {
38
39
  : () => { };
39
40
  }
40
41
  function setUpRootFlatConfig(tree, options) {
41
- tree.write('eslint.config.js', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(options.rootProject));
42
+ tree.write(`eslint.config.${options.eslintConfigFormat}`, (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(options.eslintConfigFormat, options.rootProject));
42
43
  return !options.skipPackageJson
43
44
  ? (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
44
45
  '@eslint/js': versions_1.eslint9__eslintVersion,
@@ -3,12 +3,13 @@ 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 determineEslintConfigFormat(content: string): 'mjs' | 'cjs';
6
7
  export declare function addOverrideToLintConfig(tree: Tree, root: string, override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>, options?: {
7
8
  insertAtTheEnd?: boolean;
8
9
  checkBaseConfig?: boolean;
9
10
  }): void;
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
- export declare function lintConfigHasOverride(tree: Tree, root: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, checkBaseConfig?: boolean): boolean;
11
+ export declare function updateOverrideInLintConfig(tree: Tree, rootOrFile: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, update: (override: Linter.ConfigOverride<Linter.RulesRecord>) => Linter.ConfigOverride<Linter.RulesRecord>): void;
12
+ export declare function lintConfigHasOverride(tree: Tree, rootOrFile: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, checkBaseConfig?: boolean): boolean;
12
13
  export declare function replaceOverridesInLintConfig(tree: Tree, root: string, overrides: Linter.ConfigOverride<Linter.RulesRecord>[]): void;
13
14
  export declare function addExtendsToLintConfig(tree: Tree, root: string, plugin: string | {
14
15
  name: string;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findEslintFile = findEslintFile;
4
4
  exports.isEslintConfigSupported = isEslintConfigSupported;
5
5
  exports.updateRelativePathsInConfig = updateRelativePathsInConfig;
6
+ exports.determineEslintConfigFormat = determineEslintConfigFormat;
6
7
  exports.addOverrideToLintConfig = addOverrideToLintConfig;
7
8
  exports.updateOverrideInLintConfig = updateOverrideInLintConfig;
8
9
  exports.lintConfigHasOverride = lintConfigHasOverride;
@@ -20,12 +21,18 @@ const version_utils_1 = require("../../utils/version-utils");
20
21
  const versions_1 = require("../../utils/versions");
21
22
  const ast_utils_1 = require("./flat-config/ast-utils");
22
23
  const path_utils_1 = require("./flat-config/path-utils");
24
+ const ts = require("typescript");
25
+ const posix_1 = require("node:path/posix");
23
26
  function findEslintFile(tree, projectRoot) {
24
- if (projectRoot === undefined && tree.exists(config_file_1.baseEsLintConfigFile)) {
25
- return config_file_1.baseEsLintConfigFile;
26
- }
27
- if (projectRoot === undefined && tree.exists(config_file_1.baseEsLintFlatConfigFile)) {
28
- return config_file_1.baseEsLintFlatConfigFile;
27
+ if (projectRoot === undefined) {
28
+ for (const file of [
29
+ config_file_1.baseEsLintConfigFile,
30
+ ...config_file_1.BASE_ESLINT_CONFIG_FILENAMES,
31
+ ]) {
32
+ if (tree.exists(file)) {
33
+ return file;
34
+ }
35
+ }
29
36
  }
30
37
  projectRoot ??= '';
31
38
  for (const file of config_file_1.ESLINT_CONFIG_FILENAMES) {
@@ -40,7 +47,10 @@ function isEslintConfigSupported(tree, projectRoot = '') {
40
47
  if (!eslintFile) {
41
48
  return false;
42
49
  }
43
- return eslintFile.endsWith('.json') || eslintFile.endsWith('.config.js');
50
+ return (eslintFile.endsWith('.json') ||
51
+ eslintFile.endsWith('.config.js') ||
52
+ eslintFile.endsWith('.config.cjs') ||
53
+ eslintFile.endsWith('.config.mjs'));
44
54
  }
45
55
  function updateRelativePathsInConfig(tree, sourcePath, destinationPath) {
46
56
  if (sourcePath === destinationPath ||
@@ -84,6 +94,17 @@ function replaceFlatConfigPaths(config, sourceRoot, offset, destinationRoot, tre
84
94
  `require('${newPath}')` +
85
95
  newConfig.slice(match.index + match[0].length);
86
96
  }
97
+ // Handle import statements
98
+ const importRegex = RegExp(/import\s+.*?\s+from\s+['"](.*)['"]/g);
99
+ while ((match = importRegex.exec(newConfig)) !== null) {
100
+ const oldPath = match[1];
101
+ const newPath = offsetFilePath(sourceRoot, oldPath, offset, tree);
102
+ // Replace the old path with the updated path
103
+ newConfig =
104
+ newConfig.slice(0, match.index + match[0].indexOf(oldPath)) +
105
+ newPath +
106
+ newConfig.slice(match.index + match[0].indexOf(oldPath) + oldPath.length);
107
+ }
87
108
  // replace projects
88
109
  const projectRegex = RegExp(/project:\s?\[?['"](.*)['"]\]?/g);
89
110
  while ((match = projectRegex.exec(newConfig)) !== null) {
@@ -109,14 +130,37 @@ function offsetFilePath(projectRoot, pathToFile, offset, tree) {
109
130
  }
110
131
  return (0, devkit_1.joinPathFragments)(offset, projectRoot, pathToFile);
111
132
  }
133
+ function determineEslintConfigFormat(content) {
134
+ const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true);
135
+ // Check if there's an `export default` in the AST
136
+ const hasExportDefault = sourceFile.statements.some((statement) => ts.isExportAssignment(statement) && !statement.isExportEquals);
137
+ return hasExportDefault ? 'mjs' : 'cjs';
138
+ }
112
139
  function addOverrideToLintConfig(tree, root, override, options = {
113
140
  insertAtTheEnd: true,
114
141
  }) {
115
142
  const isBase = options.checkBaseConfig && findEslintFile(tree, root).includes('.base');
116
143
  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));
118
- const flatOverride = (0, ast_utils_1.generateFlatOverride)(override);
144
+ let fileName;
145
+ if (isBase) {
146
+ for (const file of config_file_1.BASE_ESLINT_CONFIG_FILENAMES) {
147
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, file))) {
148
+ fileName = (0, devkit_1.joinPathFragments)(root, file);
149
+ break;
150
+ }
151
+ }
152
+ }
153
+ else {
154
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
155
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
156
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
157
+ break;
158
+ }
159
+ }
160
+ }
119
161
  let content = tree.read(fileName, 'utf8');
162
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
163
+ const flatOverride = (0, ast_utils_1.generateFlatOverride)(override, format);
120
164
  // Check if the provided override using legacy eslintrc properties or plugins, if so we need to add compat
121
165
  if ((0, ast_utils_1.overrideNeedsCompat)(override)) {
122
166
  content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
@@ -137,15 +181,28 @@ function addOverrideToLintConfig(tree, root, override, options = {
137
181
  });
138
182
  }
139
183
  }
140
- function updateOverrideInLintConfig(tree, root, lookup, update) {
184
+ function updateOverrideInLintConfig(tree, rootOrFile, lookup, update) {
185
+ let fileName;
186
+ let root = rootOrFile;
187
+ if (tree.exists(rootOrFile) && tree.isFile(rootOrFile)) {
188
+ fileName = rootOrFile;
189
+ root = (0, posix_1.dirname)(rootOrFile);
190
+ }
141
191
  if ((0, flat_config_1.useFlatConfig)(tree)) {
142
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
192
+ if (!fileName) {
193
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
194
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
195
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
196
+ break;
197
+ }
198
+ }
199
+ }
143
200
  let content = tree.read(fileName, 'utf8');
144
201
  content = (0, ast_utils_1.replaceOverride)(content, root, lookup, update);
145
202
  tree.write(fileName, content);
146
203
  }
147
204
  else {
148
- const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
205
+ fileName ??= (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
149
206
  if (!tree.exists(fileName)) {
150
207
  return;
151
208
  }
@@ -168,32 +225,62 @@ function updateOverrideInLintConfig(tree, root, lookup, update) {
168
225
  });
169
226
  }
170
227
  }
171
- function lintConfigHasOverride(tree, root, lookup, checkBaseConfig = false) {
172
- if (!isEslintConfigSupported(tree, root)) {
228
+ function lintConfigHasOverride(tree, rootOrFile, lookup, checkBaseConfig = false) {
229
+ let fileName;
230
+ let root = rootOrFile;
231
+ if (tree.exists(rootOrFile) && tree.isFile(rootOrFile)) {
232
+ fileName = rootOrFile;
233
+ root = (0, posix_1.dirname)(rootOrFile);
234
+ }
235
+ if (!fileName && !isEslintConfigSupported(tree, root)) {
173
236
  return false;
174
237
  }
175
- const isBase = checkBaseConfig && findEslintFile(tree, root).includes('.base');
238
+ const isBase = !fileName &&
239
+ checkBaseConfig &&
240
+ findEslintFile(tree, root).includes('.base');
241
+ if (isBase) {
242
+ for (const file of config_file_1.BASE_ESLINT_CONFIG_FILENAMES) {
243
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, file))) {
244
+ fileName = (0, devkit_1.joinPathFragments)(root, file);
245
+ break;
246
+ }
247
+ }
248
+ }
176
249
  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));
250
+ if (!fileName) {
251
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
252
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
253
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
254
+ break;
255
+ }
256
+ }
257
+ }
178
258
  const content = tree.read(fileName, 'utf8');
179
259
  return (0, ast_utils_1.hasOverride)(content, lookup);
180
260
  }
181
261
  else {
182
- const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintConfigFile : '.eslintrc.json');
262
+ fileName ??= (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintConfigFile : '.eslintrc.json');
183
263
  return (0, devkit_1.readJson)(tree, fileName).overrides?.some(lookup) || false;
184
264
  }
185
265
  }
186
266
  function replaceOverridesInLintConfig(tree, root, overrides) {
187
267
  if ((0, flat_config_1.useFlatConfig)(tree)) {
188
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
268
+ let fileName;
269
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
270
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
271
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
272
+ break;
273
+ }
274
+ }
189
275
  let content = tree.read(fileName, 'utf8');
276
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
190
277
  // Check if any of the provided overrides using legacy eslintrc properties or plugins, if so we need to add compat
191
278
  if (overrides.some(ast_utils_1.overrideNeedsCompat)) {
192
279
  content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
193
280
  }
194
281
  content = (0, ast_utils_1.removeOverridesFromLintConfig)(content);
195
282
  overrides.forEach((override) => {
196
- const flatOverride = (0, ast_utils_1.generateFlatOverride)(override);
283
+ const flatOverride = (0, ast_utils_1.generateFlatOverride)(override, format);
197
284
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, flatOverride);
198
285
  });
199
286
  tree.write(fileName, content);
@@ -209,7 +296,21 @@ function replaceOverridesInLintConfig(tree, root, overrides) {
209
296
  function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
210
297
  if ((0, flat_config_1.useFlatConfig)(tree)) {
211
298
  const pluginExtends = [];
212
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
299
+ let fileName;
300
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
301
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
302
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
303
+ break;
304
+ }
305
+ }
306
+ // Check the file extension to determine the format of the config if it is .js we look for the export
307
+ const eslintConfigFormat = fileName.endsWith('.mjs')
308
+ ? 'mjs'
309
+ : fileName.endsWith('.cjs')
310
+ ? 'cjs'
311
+ : tree.read(fileName, 'utf-8').includes('module.exports')
312
+ ? 'cjs'
313
+ : 'mjs';
213
314
  let shouldImportEslintCompat = false;
214
315
  // assume eslint version is 9 if not found, as it's what we'd be generating by default
215
316
  const eslintVersion = (0, version_utils_1.getInstalledEslintVersion)(tree) ?? versions_1.eslint9__eslintVersion;
@@ -256,9 +357,9 @@ function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
256
357
  }
257
358
  tree.write(fileName, content);
258
359
  if (shouldImportEslintCompat) {
259
- return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { '@eslint/compat': versions_1.eslintCompat }, undefined, true);
360
+ return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { '@eslint/compat': versions_1.eslintCompat, '@eslint/eslintrc': versions_1.eslintrcVersion }, undefined, true);
260
361
  }
261
- return () => { };
362
+ return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { '@eslint/eslintrc': versions_1.eslintrcVersion }, undefined, true);
262
363
  }
263
364
  else {
264
365
  const plugins = (Array.isArray(plugin) ? plugin : [plugin]).map((p) => typeof p === 'string' ? p : p.name);
@@ -277,7 +378,13 @@ function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
277
378
  function addPredefinedConfigToFlatLintConfig(tree, root, predefinedConfigName, moduleName = 'nx', moduleImportPath = '@nx/eslint-plugin', spread = true, insertAtTheEnd = true) {
278
379
  if (!(0, flat_config_1.useFlatConfig)(tree))
279
380
  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));
381
+ let fileName;
382
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
383
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
384
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
385
+ break;
386
+ }
387
+ }
281
388
  let content = tree.read(fileName, 'utf8');
282
389
  content = (0, ast_utils_1.addImportToFlatConfig)(content, moduleName, moduleImportPath);
283
390
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)(predefinedConfigName, moduleName, spread), { insertAtTheEnd });
@@ -286,7 +393,13 @@ function addPredefinedConfigToFlatLintConfig(tree, root, predefinedConfigName, m
286
393
  function addPluginsToLintConfig(tree, root, plugin) {
287
394
  const plugins = Array.isArray(plugin) ? plugin : [plugin];
288
395
  if ((0, flat_config_1.useFlatConfig)(tree)) {
289
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
396
+ let fileName;
397
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
398
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
399
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
400
+ break;
401
+ }
402
+ }
290
403
  let content = tree.read(fileName, 'utf8');
291
404
  const mappedPlugins = [];
292
405
  plugins.forEach((name) => {
@@ -310,14 +423,33 @@ function addPluginsToLintConfig(tree, root, plugin) {
310
423
  }
311
424
  function addIgnoresToLintConfig(tree, root, ignorePatterns) {
312
425
  if ((0, flat_config_1.useFlatConfig)(tree)) {
313
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
314
- const block = (0, ast_utils_1.generateAst)({
315
- ignores: ignorePatterns.map((path) => (0, path_utils_1.mapFilePath)(path)),
316
- });
317
- tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(tree.read(fileName, 'utf8'), block));
426
+ let fileName;
427
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
428
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
429
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
430
+ break;
431
+ }
432
+ }
433
+ if (!fileName) {
434
+ return;
435
+ }
436
+ let content = tree.read(fileName, 'utf8');
437
+ if ((0, ast_utils_1.hasFlatConfigIgnoresBlock)(content)) {
438
+ content = (0, ast_utils_1.addPatternsToFlatConfigIgnoresBlock)(content, ignorePatterns);
439
+ tree.write(fileName, content);
440
+ }
441
+ else {
442
+ const block = (0, ast_utils_1.generateAst)({
443
+ ignores: ignorePatterns.map((path) => (0, path_utils_1.mapFilePath)(path)),
444
+ });
445
+ tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(content, block));
446
+ }
318
447
  }
319
448
  else {
320
449
  const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
450
+ if (!tree.exists(fileName)) {
451
+ return;
452
+ }
321
453
  (0, devkit_1.updateJson)(tree, fileName, (json) => {
322
454
  const ignoreSet = new Set([
323
455
  ...(json.ignorePatterns ?? []),