@nx/eslint 20.4.0-canary.20250117-cba25da → 20.4.0-canary.20250118-ee135b2

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/eslint",
3
- "version": "20.4.0-canary.20250117-cba25da",
3
+ "version": "20.4.0-canary.20250118-ee135b2",
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,8 +35,8 @@
35
35
  "eslint": "^8.0.0 || ^9.0.0"
36
36
  },
37
37
  "dependencies": {
38
- "@nx/devkit": "20.4.0-canary.20250117-cba25da",
39
- "@nx/js": "20.4.0-canary.20250117-cba25da",
38
+ "@nx/devkit": "20.4.0-canary.20250118-ee135b2",
39
+ "@nx/js": "20.4.0-canary.20250118-ee135b2",
40
40
  "semver": "^7.5.3",
41
41
  "tslib": "^2.3.0",
42
42
  "typescript": "~5.7.2"
@@ -4,7 +4,7 @@ import { ESLint } from 'eslint';
4
4
  * Converts an ESLint JSON config to a flat config.
5
5
  * Deletes the original file along with .eslintignore if it exists.
6
6
  */
7
- export declare function convertEslintJsonToFlatConfig(tree: Tree, root: string, config: ESLint.ConfigData, ignorePaths: string[]): {
7
+ export declare function convertEslintJsonToFlatConfig(tree: Tree, root: string, config: ESLint.ConfigData, ignorePaths: string[], format: 'cjs' | 'mjs'): {
8
8
  content: string;
9
9
  addESLintRC: boolean;
10
10
  addESLintJS: boolean;
@@ -10,7 +10,7 @@ const path_utils_1 = require("../../utils/flat-config/path-utils");
10
10
  * Converts an ESLint JSON config to a flat config.
11
11
  * Deletes the original file along with .eslintignore if it exists.
12
12
  */
13
- function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths) {
13
+ function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths, format) {
14
14
  const importsMap = new Map();
15
15
  const exportElements = [];
16
16
  let isFlatCompatNeeded = false;
@@ -22,7 +22,7 @@ function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths) {
22
22
  ignores: ['**/dist'],
23
23
  }));
24
24
  if (config.extends) {
25
- const extendsResult = addExtends(importsMap, exportElements, config);
25
+ const extendsResult = addExtends(importsMap, exportElements, config, format);
26
26
  isFlatCompatNeeded = extendsResult.isFlatCompatNeeded;
27
27
  isESLintJSNeeded = extendsResult.isESLintJSNeeded;
28
28
  }
@@ -74,7 +74,7 @@ function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths) {
74
74
  override.parser) {
75
75
  isFlatCompatNeeded = true;
76
76
  }
77
- exportElements.push((0, ast_utils_1.generateFlatOverride)(override));
77
+ exportElements.push((0, ast_utils_1.generateFlatOverride)(override, format));
78
78
  });
79
79
  }
80
80
  if (config.ignorePatterns) {
@@ -100,7 +100,7 @@ function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths) {
100
100
  }
101
101
  }
102
102
  // create the node list and print it to new file
103
- const nodeList = (0, ast_utils_1.createNodeList)(importsMap, exportElements);
103
+ const nodeList = (0, ast_utils_1.createNodeList)(importsMap, exportElements, format);
104
104
  let content = (0, ast_utils_1.stringifyNodeList)(nodeList);
105
105
  if (isFlatCompatNeeded) {
106
106
  content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
@@ -112,7 +112,7 @@ function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths) {
112
112
  };
113
113
  }
114
114
  // add parsed extends to export blocks and add import statements
115
- function addExtends(importsMap, configBlocks, config) {
115
+ function addExtends(importsMap, configBlocks, config, format) {
116
116
  let isFlatCompatNeeded = false;
117
117
  let isESLintJSNeeded = false;
118
118
  const extendsConfig = Array.isArray(config.extends)
@@ -126,7 +126,7 @@ function addExtends(importsMap, configBlocks, config) {
126
126
  if (imp.match(/\.eslintrc(.base)?\.json$/)) {
127
127
  const localName = index ? `baseConfig${index}` : 'baseConfig';
128
128
  configBlocks.push((0, ast_utils_1.generateSpreadElement)(localName));
129
- const newImport = imp.replace(/^(.*)\.eslintrc(.base)?\.json$/, '$1eslint$2.config.cjs');
129
+ const newImport = imp.replace(/^(.*)\.eslintrc(.base)?\.json$/, `$1eslint$2.config.${format}`);
130
130
  importsMap.set(newImport, localName);
131
131
  }
132
132
  else {
@@ -14,20 +14,21 @@ async function convertToFlatConfigGenerator(tree, options) {
14
14
  if (eslintFile.endsWith('.js')) {
15
15
  throw new Error('Only json and yaml eslint config files are supported for conversion');
16
16
  }
17
+ options.eslintConfigFormat ??= 'mjs';
17
18
  const eslintIgnoreFiles = new Set(['.eslintignore']);
18
- // convert root eslint config to eslint.config.cjs
19
- convertRootToFlatConfig(tree, eslintFile);
19
+ // convert root eslint config to eslint.config.cjs or eslint.base.config.mjs based on eslintConfigFormat
20
+ convertRootToFlatConfig(tree, eslintFile, options.eslintConfigFormat);
20
21
  // convert project eslint files to eslint.config.cjs
21
22
  const projects = (0, devkit_1.getProjects)(tree);
22
23
  for (const [project, projectConfig] of projects) {
23
- convertProjectToFlatConfig(tree, project, projectConfig, (0, devkit_1.readNxJson)(tree), eslintIgnoreFiles);
24
+ convertProjectToFlatConfig(tree, project, projectConfig, (0, devkit_1.readNxJson)(tree), eslintIgnoreFiles, options.eslintConfigFormat);
24
25
  }
25
26
  // delete all .eslintignore files
26
27
  for (const ignoreFile of eslintIgnoreFiles) {
27
28
  tree.delete(ignoreFile);
28
29
  }
29
30
  // replace references in nx.json
30
- updateNxJsonConfig(tree);
31
+ updateNxJsonConfig(tree, options.eslintConfigFormat);
31
32
  // install missing packages
32
33
  if (!options.skipFormat) {
33
34
  await (0, devkit_1.formatFiles)(tree);
@@ -35,13 +36,13 @@ async function convertToFlatConfigGenerator(tree, options) {
35
36
  return () => (0, devkit_1.installPackagesTask)(tree);
36
37
  }
37
38
  exports.default = convertToFlatConfigGenerator;
38
- function convertRootToFlatConfig(tree, eslintFile) {
39
+ function convertRootToFlatConfig(tree, eslintFile, format) {
39
40
  if (/\.base\.(js|json|yml|yaml)$/.test(eslintFile)) {
40
- convertConfigToFlatConfig(tree, '', eslintFile, 'eslint.base.config.cjs');
41
+ convertConfigToFlatConfig(tree, '', eslintFile, `eslint.base.config.${format}`, format);
41
42
  }
42
- convertConfigToFlatConfig(tree, '', eslintFile.replace('.base.', '.'), 'eslint.config.cjs');
43
+ convertConfigToFlatConfig(tree, '', eslintFile.replace('.base.', '.'), `eslint.config.${format}`, format);
43
44
  }
44
- function convertProjectToFlatConfig(tree, project, projectConfig, nxJson, eslintIgnoreFiles) {
45
+ function convertProjectToFlatConfig(tree, project, projectConfig, nxJson, eslintIgnoreFiles, format) {
45
46
  const eslintFile = (0, eslint_file_1.findEslintFile)(tree, projectConfig.root);
46
47
  if (eslintFile && !eslintFile.endsWith('.js')) {
47
48
  if (projectConfig.targets) {
@@ -67,7 +68,7 @@ function convertProjectToFlatConfig(tree, project, projectConfig, nxJson, eslint
67
68
  ? p === '@nx/eslint/plugin'
68
69
  : p.plugin === '@nx/eslint/plugin');
69
70
  if (nxHasEsLintTargets || nxHasEsLintPlugin || eslintTargets.length > 0) {
70
- convertConfigToFlatConfig(tree, projectConfig.root, eslintFile, 'eslint.config.cjs', ignorePath);
71
+ convertConfigToFlatConfig(tree, projectConfig.root, eslintFile, `eslint.config.${format}`, format, ignorePath);
71
72
  eslintIgnoreFiles.add(`${projectConfig.root}/.eslintignore`);
72
73
  if (ignorePath) {
73
74
  eslintIgnoreFiles.add(ignorePath);
@@ -78,35 +79,35 @@ function convertProjectToFlatConfig(tree, project, projectConfig, nxJson, eslint
78
79
  }
79
80
  // update names of eslint files in nx.json
80
81
  // and remove eslintignore
81
- function updateNxJsonConfig(tree) {
82
+ function updateNxJsonConfig(tree, format) {
82
83
  if (tree.exists('nx.json')) {
83
84
  (0, devkit_1.updateJson)(tree, 'nx.json', (json) => {
84
85
  if (json.targetDefaults?.lint?.inputs) {
85
86
  const inputSet = new Set(json.targetDefaults.lint.inputs);
86
- inputSet.add('{workspaceRoot}/eslint.config.cjs');
87
+ inputSet.add(`{workspaceRoot}/eslint.config.${format}`);
87
88
  json.targetDefaults.lint.inputs = Array.from(inputSet);
88
89
  }
89
90
  if (json.targetDefaults?.['@nx/eslint:lint']?.inputs) {
90
91
  const inputSet = new Set(json.targetDefaults['@nx/eslint:lint'].inputs);
91
- inputSet.add('{workspaceRoot}/eslint.config.cjs');
92
+ inputSet.add(`{workspaceRoot}/eslint.config.${format}`);
92
93
  json.targetDefaults['@nx/eslint:lint'].inputs = Array.from(inputSet);
93
94
  }
94
95
  if (json.namedInputs?.production) {
95
96
  const inputSet = new Set(json.namedInputs.production);
96
- inputSet.add('!{projectRoot}/eslint.config.cjs');
97
+ inputSet.add(`!{projectRoot}/eslint.config.${format}`);
97
98
  json.namedInputs.production = Array.from(inputSet);
98
99
  }
99
100
  return json;
100
101
  });
101
102
  }
102
103
  }
103
- function convertConfigToFlatConfig(tree, root, source, target, ignorePath) {
104
+ function convertConfigToFlatConfig(tree, root, source, target, format, ignorePath) {
104
105
  const ignorePaths = ignorePath
105
106
  ? [ignorePath, `${root}/.eslintignore`]
106
107
  : [`${root}/.eslintignore`];
107
108
  if (source.endsWith('.json')) {
108
109
  const config = (0, devkit_1.readJson)(tree, `${root}/${source}`);
109
- const conversionResult = (0, json_converter_1.convertEslintJsonToFlatConfig)(tree, root, config, ignorePaths);
110
+ const conversionResult = (0, json_converter_1.convertEslintJsonToFlatConfig)(tree, root, config, ignorePaths, format);
110
111
  return processConvertedConfig(tree, root, source, target, conversionResult);
111
112
  }
112
113
  if (source.endsWith('.yaml') || source.endsWith('.yml')) {
@@ -116,7 +117,7 @@ function convertConfigToFlatConfig(tree, root, source, target, ignorePath) {
116
117
  json: true,
117
118
  filename: source,
118
119
  });
119
- const conversionResult = (0, json_converter_1.convertEslintJsonToFlatConfig)(tree, root, config, ignorePaths);
120
+ const conversionResult = (0, json_converter_1.convertEslintJsonToFlatConfig)(tree, root, config, ignorePaths, format);
120
121
  return processConvertedConfig(tree, root, source, target, conversionResult);
121
122
  }
122
123
  }
@@ -1,3 +1,5 @@
1
1
  export interface ConvertToFlatConfigGeneratorSchema {
2
2
  skipFormat?: boolean;
3
+ // Internal option
4
+ eslintConfigFormat?: 'mjs' | 'cjs';
3
5
  }
@@ -34,6 +34,7 @@ function postTargetTransformer(target, tree, projectDetails, inferredTargetConfi
34
34
  '{workspaceRoot}/.eslintrc.json',
35
35
  '{workspaceRoot}/.eslintignore',
36
36
  '{workspaceRoot}/eslint.config.cjs',
37
+ '{workspaceRoot}/eslint.config.mjs',
37
38
  ].includes(input));
38
39
  if (inputs.length === 0) {
39
40
  delete target.inputs;
@@ -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: (format: "cjs" | "mjs", rootProject?: boolean) => string;
@@ -78,8 +78,8 @@ 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 = (format, rootProject) => {
82
+ const nodeList = (0, ast_utils_1.createNodeList)(new Map(), [], format);
83
83
  let content = (0, ast_utils_1.stringifyNodeList)(nodeList);
84
84
  content = (0, ast_utils_1.addImportToFlatConfig)(content, 'nx', '@nx/eslint-plugin');
85
85
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/base'), { insertAtTheEnd: false });
@@ -87,7 +87,7 @@ const getGlobalFlatEslintConfiguration = (rootProject) => {
87
87
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/javascript'));
88
88
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
89
89
  ignores: ['**/dist'],
90
- }));
90
+ }, format));
91
91
  if (!rootProject) {
92
92
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
93
93
  files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
@@ -107,7 +107,7 @@ const getGlobalFlatEslintConfiguration = (rootProject) => {
107
107
  },
108
108
  ],
109
109
  },
110
- }));
110
+ }, format));
111
111
  }
112
112
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
113
113
  files: [
@@ -119,7 +119,7 @@ const getGlobalFlatEslintConfiguration = (rootProject) => {
119
119
  '**/*.mjs',
120
120
  ],
121
121
  rules: {},
122
- }));
122
+ }, format));
123
123
  return content;
124
124
  };
125
125
  exports.getGlobalFlatEslintConfiguration = getGlobalFlatEslintConfiguration;
@@ -1,3 +1,3 @@
1
1
  import { GeneratorCallback, ProjectConfiguration, TargetConfiguration, Tree } from '@nx/devkit';
2
- export declare function migrateConfigToMonorepoStyle(projects: ProjectConfiguration[], tree: Tree, unitTestRunner: string, keepExistingVersions?: boolean): GeneratorCallback;
2
+ export declare function migrateConfigToMonorepoStyle(projects: ProjectConfiguration[], tree: Tree, unitTestRunner: string, eslintConfigFormat: 'mjs' | 'cjs', keepExistingVersions?: boolean): GeneratorCallback;
3
3
  export declare function findLintTarget(project: ProjectConfiguration): TargetConfiguration;
@@ -11,9 +11,19 @@ const versions_1 = require("../../utils/versions");
11
11
  const ast_utils_1 = require("../utils/flat-config/ast-utils");
12
12
  const plugin_1 = require("../utils/plugin");
13
13
  const config_file_1 = require("../../utils/config-file");
14
- function migrateConfigToMonorepoStyle(projects, tree, unitTestRunner, keepExistingVersions) {
14
+ function migrateConfigToMonorepoStyle(projects, tree, unitTestRunner, eslintConfigFormat, keepExistingVersions) {
15
15
  const rootEslintConfig = (0, eslint_file_1.findEslintFile)(tree);
16
16
  let skipCleanup = false;
17
+ if (rootEslintConfig) {
18
+ // We do not want to mix the formats
19
+ const fileExtension = (0, path_1.extname)(rootEslintConfig);
20
+ if (fileExtension === '.mjs' || fileExtension === '.cjs') {
21
+ eslintConfigFormat = fileExtension.slice(1);
22
+ }
23
+ else {
24
+ eslintConfigFormat = (0, eslint_file_1.determineEslintConfigFormat)(tree.read(rootEslintConfig, 'utf-8'));
25
+ }
26
+ }
17
27
  if (rootEslintConfig?.match(/\.base\./) &&
18
28
  !projects.some((p) => p.root === '.')) {
19
29
  // if the migration has been run already, we need to rename the base config
@@ -27,9 +37,9 @@ function migrateConfigToMonorepoStyle(projects, tree, unitTestRunner, keepExisti
27
37
  (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
28
38
  '@eslint/js': versions_1.eslintVersion,
29
39
  }, undefined, keepExistingVersions);
30
- tree.write(tree.exists('eslint.config.cjs')
31
- ? 'eslint.base.config.cjs'
32
- : 'eslint.config.cjs', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)());
40
+ tree.write(tree.exists(`eslint.config.${eslintConfigFormat}`)
41
+ ? `eslint.base.config.${eslintConfigFormat}`
42
+ : `eslint.config.${eslintConfigFormat}`, (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(eslintConfigFormat));
33
43
  }
34
44
  else {
35
45
  const eslintFile = (0, eslint_file_1.findEslintFile)(tree, '.');
@@ -80,7 +90,7 @@ function migrateEslintFile(projectEslintPath, tree) {
80
90
  let config = tree.read(projectEslintPath, 'utf-8');
81
91
  // remove @nx plugin
82
92
  config = (0, ast_utils_1.removePlugin)(config, '@nx', '@nx/eslint-plugin-nx');
83
- // extend eslint.base.config.cjs
93
+ // if base config is cjs, we will need to import it using async import
84
94
  config = (0, ast_utils_1.addImportToFlatConfig)(config, 'baseConfig', `${(0, devkit_1.offsetFromRoot)((0, path_1.dirname)(projectEslintPath))}${baseFile}`);
85
95
  config = (0, ast_utils_1.addBlockToFlatConfigExport)(config, (0, ast_utils_1.generateSpreadElement)('baseConfig'), { insertAtTheEnd: false });
86
96
  // cleanup file extends
@@ -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.cjs');
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,7 +29,7 @@ function addTargetDefaults(tree) {
28
29
  'default',
29
30
  `{workspaceRoot}/.eslintrc.json`,
30
31
  `{workspaceRoot}/.eslintignore`,
31
- `{workspaceRoot}/eslint.config.cjs`,
32
+ `{workspaceRoot}/eslint.config.${format}`,
32
33
  ];
33
34
  (0, devkit_1.updateNxJson)(tree, nxJson);
34
35
  }
@@ -50,8 +51,18 @@ async function initEsLint(tree, options) {
50
51
  const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
51
52
  nxJson.useInferencePlugins !== false;
52
53
  options.addPlugin ??= addPluginDefault;
54
+ options.eslintConfigFormat ??= 'mjs';
53
55
  const hasPlugin = (0, plugin_2.hasEslintPlugin)(tree);
54
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
+ }
55
66
  const graph = await (0, devkit_1.createProjectGraphAsync)();
56
67
  const lintTargetNames = [
57
68
  'lint',
@@ -70,7 +81,7 @@ async function initEsLint(tree, options) {
70
81
  if (rootEslintFile) {
71
82
  return () => { };
72
83
  }
73
- updateProductionFileset(tree);
84
+ updateProductionFileset(tree, options.eslintConfigFormat);
74
85
  updateVsCodeRecommendedExtensions(tree);
75
86
  if (options.addPlugin) {
76
87
  await (0, add_plugin_1.addPlugin)(tree, graph, '@nx/eslint/plugin', plugin_1.createNodesV2, {
@@ -78,7 +89,7 @@ async function initEsLint(tree, options) {
78
89
  }, options.updatePackageScripts);
79
90
  }
80
91
  else {
81
- addTargetDefaults(tree);
92
+ addTargetDefaults(tree, options.eslintConfigFormat);
82
93
  }
83
94
  const tasks = [];
84
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
  */
@@ -18,6 +18,7 @@ function lintProjectGenerator(tree, options) {
18
18
  }
19
19
  async function lintProjectGeneratorInternal(tree, options) {
20
20
  const nxJson = (0, devkit_1.readNxJson)(tree);
21
+ options.eslintConfigFormat ??= 'mjs';
21
22
  const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
22
23
  nxJson.useInferencePlugins !== false;
23
24
  options.addPlugin ??= addPluginDefault;
@@ -25,12 +26,14 @@ async function lintProjectGeneratorInternal(tree, options) {
25
26
  const initTask = await (0, init_1.lintInitGenerator)(tree, {
26
27
  skipPackageJson: options.skipPackageJson,
27
28
  addPlugin: options.addPlugin,
29
+ eslintConfigFormat: options.eslintConfigFormat,
28
30
  });
29
31
  tasks.push(initTask);
30
32
  const rootEsLintTask = (0, setup_root_eslint_1.setupRootEsLint)(tree, {
31
33
  unitTestRunner: options.unitTestRunner,
32
34
  skipPackageJson: options.skipPackageJson,
33
35
  rootProject: options.rootProject,
36
+ eslintConfigFormat: options.eslintConfigFormat,
34
37
  });
35
38
  tasks.push(rootEsLintTask);
36
39
  const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
@@ -84,7 +87,7 @@ async function lintProjectGeneratorInternal(tree, options) {
84
87
  filteredProjects.push(project);
85
88
  }
86
89
  });
87
- const migrateTask = (0, init_migration_1.migrateConfigToMonorepoStyle)(filteredProjects, tree, options.unitTestRunner, options.keepExistingVersions);
90
+ const migrateTask = (0, init_migration_1.migrateConfigToMonorepoStyle)(filteredProjects, tree, options.unitTestRunner, options.eslintConfigFormat, options.keepExistingVersions);
88
91
  tasks.push(migrateTask);
89
92
  }
90
93
  }
@@ -116,6 +119,18 @@ function createEsLintConfiguration(tree, options, projectConfig, setParserOption
116
119
  const pathToRootConfig = extendedRootConfig
117
120
  ? `${(0, devkit_1.offsetFromRoot)(projectConfig.root)}${extendedRootConfig}`
118
121
  : undefined;
122
+ if (extendedRootConfig) {
123
+ // We do not want to mix the formats
124
+ // if the base file extension is `.mjs` we should use `mjs` for the new file
125
+ // or if base the file extension is `.cjs` then the format should be `cjs`
126
+ const fileExtension = (0, path_1.extname)(extendedRootConfig);
127
+ if (fileExtension === '.mjs' || fileExtension === '.cjs') {
128
+ options.eslintConfigFormat = fileExtension.slice(1);
129
+ }
130
+ else {
131
+ options.eslintConfigFormat = (0, eslint_file_1.determineEslintConfigFormat)(tree.read(extendedRootConfig, 'utf-8'));
132
+ }
133
+ }
119
134
  const addDependencyChecks = options.addPackageJsonDependencyChecks ||
120
135
  isBuildableLibraryProject(projectConfig);
121
136
  const overrides = (0, flat_config_1.useFlatConfig)(tree)
@@ -180,11 +195,11 @@ function createEsLintConfiguration(tree, options, projectConfig, setParserOption
180
195
  nodes.push((0, ast_utils_1.generateSpreadElement)('baseConfig'));
181
196
  }
182
197
  overrides.forEach((override) => {
183
- nodes.push((0, ast_utils_1.generateFlatOverride)(override));
198
+ nodes.push((0, ast_utils_1.generateFlatOverride)(override, options.eslintConfigFormat));
184
199
  });
185
- const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes);
200
+ const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes, options.eslintConfigFormat);
186
201
  const content = (0, ast_utils_1.stringifyNodeList)(nodeList);
187
- tree.write((0, path_1.join)(projectConfig.root, `eslint.config.cjs`), content);
202
+ tree.write((0, path_1.join)(projectConfig.root, `eslint.config.${options.eslintConfigFormat}`), content);
188
203
  }
189
204
  else {
190
205
  (0, devkit_1.writeJson)(tree, (0, path_1.join)(projectConfig.root, `.eslintrc.json`), {
@@ -212,8 +227,7 @@ function isBuildableLibraryProject(projectConfig) {
212
227
  */
213
228
  function isMigrationToMonorepoNeeded(tree, graph) {
214
229
  // the base config is already created, migration has been done
215
- if (tree.exists(config_file_1.baseEsLintConfigFile) ||
216
- tree.exists(config_file_1.baseEsLintFlatConfigFile)) {
230
+ if ([config_file_1.baseEsLintConfigFile, ...config_file_1.BASE_ESLINT_CONFIG_FILENAMES].some((f) => tree.exists(f))) {
217
231
  return false;
218
232
  }
219
233
  const nodes = Object.values(graph.nodes);
@@ -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.cjs', (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,6 +3,7 @@ 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;
@@ -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,17 +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");
23
25
  const posix_1 = require("node:path/posix");
24
26
  function findEslintFile(tree, projectRoot) {
25
- if (projectRoot === undefined && tree.exists(config_file_1.baseEsLintConfigFile)) {
26
- return config_file_1.baseEsLintConfigFile;
27
- }
28
- if (projectRoot === undefined && tree.exists(config_file_1.baseEsLintFlatConfigFile)) {
29
- return config_file_1.baseEsLintFlatConfigFile;
30
- }
31
- if (projectRoot === undefined &&
32
- tree.exists(config_file_1.legacyBaseEsLintFlatConfigFile)) {
33
- return config_file_1.legacyBaseEsLintFlatConfigFile;
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
+ }
34
36
  }
35
37
  projectRoot ??= '';
36
38
  for (const file of config_file_1.ESLINT_CONFIG_FILENAMES) {
@@ -47,7 +49,8 @@ function isEslintConfigSupported(tree, projectRoot = '') {
47
49
  }
48
50
  return (eslintFile.endsWith('.json') ||
49
51
  eslintFile.endsWith('.config.js') ||
50
- eslintFile.endsWith('.config.cjs'));
52
+ eslintFile.endsWith('.config.cjs') ||
53
+ eslintFile.endsWith('.config.mjs'));
51
54
  }
52
55
  function updateRelativePathsInConfig(tree, sourcePath, destinationPath) {
53
56
  if (sourcePath === destinationPath ||
@@ -91,6 +94,17 @@ function replaceFlatConfigPaths(config, sourceRoot, offset, destinationRoot, tre
91
94
  `require('${newPath}')` +
92
95
  newConfig.slice(match.index + match[0].length);
93
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
+ }
94
108
  // replace projects
95
109
  const projectRegex = RegExp(/project:\s?\[?['"](.*)['"]\]?/g);
96
110
  while ((match = projectRegex.exec(newConfig)) !== null) {
@@ -116,6 +130,12 @@ function offsetFilePath(projectRoot, pathToFile, offset, tree) {
116
130
  }
117
131
  return (0, devkit_1.joinPathFragments)(offset, projectRoot, pathToFile);
118
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
+ }
119
139
  function addOverrideToLintConfig(tree, root, override, options = {
120
140
  insertAtTheEnd: true,
121
141
  }) {
@@ -123,7 +143,12 @@ function addOverrideToLintConfig(tree, root, override, options = {
123
143
  if ((0, flat_config_1.useFlatConfig)(tree)) {
124
144
  let fileName;
125
145
  if (isBase) {
126
- fileName = (0, devkit_1.joinPathFragments)(root, config_file_1.baseEsLintFlatConfigFile);
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
+ }
127
152
  }
128
153
  else {
129
154
  for (const f of flat_config_1.eslintFlatConfigFilenames) {
@@ -133,8 +158,9 @@ function addOverrideToLintConfig(tree, root, override, options = {
133
158
  }
134
159
  }
135
160
  }
136
- const flatOverride = (0, ast_utils_1.generateFlatOverride)(override);
137
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);
138
164
  // Check if the provided override using legacy eslintrc properties or plugins, if so we need to add compat
139
165
  if ((0, ast_utils_1.overrideNeedsCompat)(override)) {
140
166
  content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
@@ -213,7 +239,12 @@ function lintConfigHasOverride(tree, rootOrFile, lookup, checkBaseConfig = false
213
239
  checkBaseConfig &&
214
240
  findEslintFile(tree, root).includes('.base');
215
241
  if (isBase) {
216
- fileName = (0, devkit_1.joinPathFragments)(root, config_file_1.baseEsLintFlatConfigFile);
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
+ }
217
248
  }
218
249
  if ((0, flat_config_1.useFlatConfig)(tree)) {
219
250
  if (!fileName) {
@@ -242,13 +273,14 @@ function replaceOverridesInLintConfig(tree, root, overrides) {
242
273
  }
243
274
  }
244
275
  let content = tree.read(fileName, 'utf8');
276
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
245
277
  // Check if any of the provided overrides using legacy eslintrc properties or plugins, if so we need to add compat
246
278
  if (overrides.some(ast_utils_1.overrideNeedsCompat)) {
247
279
  content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
248
280
  }
249
281
  content = (0, ast_utils_1.removeOverridesFromLintConfig)(content);
250
282
  overrides.forEach((override) => {
251
- const flatOverride = (0, ast_utils_1.generateFlatOverride)(override);
283
+ const flatOverride = (0, ast_utils_1.generateFlatOverride)(override, format);
252
284
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, flatOverride);
253
285
  });
254
286
  tree.write(fileName, content);
@@ -271,6 +303,14 @@ function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
271
303
  break;
272
304
  }
273
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';
274
314
  let shouldImportEslintCompat = false;
275
315
  // assume eslint version is 9 if not found, as it's what we'd be generating by default
276
316
  const eslintVersion = (0, version_utils_1.getInstalledEslintVersion)(tree) ?? versions_1.eslint9__eslintVersion;