@nx/eslint 20.3.0-canary.20241218-fb40366 → 20.3.0-rc.0

Sign up to get free protection for your applications and to get access to all the features.
package/migrations.json CHANGED
@@ -24,6 +24,11 @@
24
24
  "version": "20.2.0-beta.5",
25
25
  "description": "Update TypeScript ESLint packages to v8.13.0 if they are already on v8",
26
26
  "implementation": "./src/migrations/update-20-2-0/update-typescript-eslint-v8-13-0"
27
+ },
28
+ "add-file-extensions-to-overrides": {
29
+ "version": "20.3.0-beta.1",
30
+ "description": "Update ESLint flat config to include .cjs, .mjs, .cts, and .mts files in overrides (if needed)",
31
+ "implementation": "./src/migrations/update-20-3-0/add-file-extensions-to-overrides"
27
32
  }
28
33
  },
29
34
  "packageJsonUpdates": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/eslint",
3
- "version": "20.3.0-canary.20241218-fb40366",
3
+ "version": "20.3.0-rc.0",
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.3.0-canary.20241218-fb40366",
39
- "@nx/js": "20.3.0-canary.20241218-fb40366",
38
+ "@nx/devkit": "20.3.0-rc.0",
39
+ "@nx/js": "20.3.0-rc.0",
40
40
  "semver": "^7.5.3",
41
41
  "tslib": "^2.3.0",
42
42
  "typescript": "~5.6.2"
@@ -17,6 +17,10 @@ function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths) {
17
17
  let isESLintJSNeeded = false;
18
18
  let combinedConfig = [];
19
19
  let languageOptions = [];
20
+ // exclude dist and eslint config from being linted, which matches the default for new workspaces
21
+ exportElements.push((0, ast_utils_1.generateAst)({
22
+ ignores: ['**/dist'],
23
+ }));
20
24
  if (config.extends) {
21
25
  const extendsResult = addExtends(importsMap, exportElements, config);
22
26
  isFlatCompatNeeded = extendsResult.isFlatCompatNeeded;
@@ -122,7 +126,7 @@ function addExtends(importsMap, configBlocks, config) {
122
126
  if (imp.match(/\.eslintrc(.base)?\.json$/)) {
123
127
  const localName = index ? `baseConfig${index}` : 'baseConfig';
124
128
  configBlocks.push((0, ast_utils_1.generateSpreadElement)(localName));
125
- const newImport = imp.replace(/^(.*)\.eslintrc(.base)?\.json$/, '$1eslint$2.config.js');
129
+ const newImport = imp.replace(/^(.*)\.eslintrc(.base)?\.json$/, '$1eslint$2.config.cjs');
126
130
  importsMap.set(newImport, localName);
127
131
  }
128
132
  else {
@@ -15,9 +15,9 @@ async function convertToFlatConfigGenerator(tree, options) {
15
15
  throw new Error('Only json and yaml eslint config files are supported for conversion');
16
16
  }
17
17
  const eslintIgnoreFiles = new Set(['.eslintignore']);
18
- // convert root eslint config to eslint.config.js
18
+ // convert root eslint config to eslint.config.cjs
19
19
  convertRootToFlatConfig(tree, eslintFile);
20
- // convert project eslint files to eslint.config.js
20
+ // convert project eslint files to eslint.config.cjs
21
21
  const projects = (0, devkit_1.getProjects)(tree);
22
22
  for (const [project, projectConfig] of projects) {
23
23
  convertProjectToFlatConfig(tree, project, projectConfig, (0, devkit_1.readNxJson)(tree), eslintIgnoreFiles);
@@ -37,9 +37,9 @@ async function convertToFlatConfigGenerator(tree, options) {
37
37
  exports.default = convertToFlatConfigGenerator;
38
38
  function convertRootToFlatConfig(tree, eslintFile) {
39
39
  if (/\.base\.(js|json|yml|yaml)$/.test(eslintFile)) {
40
- convertConfigToFlatConfig(tree, '', eslintFile, 'eslint.base.config.js');
40
+ convertConfigToFlatConfig(tree, '', eslintFile, 'eslint.base.config.cjs');
41
41
  }
42
- convertConfigToFlatConfig(tree, '', eslintFile.replace('.base.', '.'), 'eslint.config.js');
42
+ convertConfigToFlatConfig(tree, '', eslintFile.replace('.base.', '.'), 'eslint.config.cjs');
43
43
  }
44
44
  function convertProjectToFlatConfig(tree, project, projectConfig, nxJson, eslintIgnoreFiles) {
45
45
  const eslintFile = (0, eslint_file_1.findEslintFile)(tree, projectConfig.root);
@@ -67,7 +67,7 @@ function convertProjectToFlatConfig(tree, project, projectConfig, nxJson, eslint
67
67
  ? p === '@nx/eslint/plugin'
68
68
  : p.plugin === '@nx/eslint/plugin');
69
69
  if (nxHasEsLintTargets || nxHasEsLintPlugin || eslintTargets.length > 0) {
70
- convertConfigToFlatConfig(tree, projectConfig.root, eslintFile, 'eslint.config.js', ignorePath);
70
+ convertConfigToFlatConfig(tree, projectConfig.root, eslintFile, 'eslint.config.cjs', ignorePath);
71
71
  eslintIgnoreFiles.add(`${projectConfig.root}/.eslintignore`);
72
72
  if (ignorePath) {
73
73
  eslintIgnoreFiles.add(ignorePath);
@@ -83,17 +83,17 @@ function updateNxJsonConfig(tree) {
83
83
  (0, devkit_1.updateJson)(tree, 'nx.json', (json) => {
84
84
  if (json.targetDefaults?.lint?.inputs) {
85
85
  const inputSet = new Set(json.targetDefaults.lint.inputs);
86
- inputSet.add('{workspaceRoot}/eslint.config.js');
86
+ inputSet.add('{workspaceRoot}/eslint.config.cjs');
87
87
  json.targetDefaults.lint.inputs = Array.from(inputSet);
88
88
  }
89
89
  if (json.targetDefaults?.['@nx/eslint:lint']?.inputs) {
90
90
  const inputSet = new Set(json.targetDefaults['@nx/eslint:lint'].inputs);
91
- inputSet.add('{workspaceRoot}/eslint.config.js');
91
+ inputSet.add('{workspaceRoot}/eslint.config.cjs');
92
92
  json.targetDefaults['@nx/eslint:lint'].inputs = Array.from(inputSet);
93
93
  }
94
94
  if (json.namedInputs?.production) {
95
95
  const inputSet = new Set(json.namedInputs.production);
96
- inputSet.add('!{projectRoot}/eslint.config.js');
96
+ inputSet.add('!{projectRoot}/eslint.config.cjs');
97
97
  json.namedInputs.production = Array.from(inputSet);
98
98
  }
99
99
  return json;
@@ -33,7 +33,7 @@ function postTargetTransformer(target, tree, projectDetails, inferredTargetConfi
33
33
  'default',
34
34
  '{workspaceRoot}/.eslintrc.json',
35
35
  '{workspaceRoot}/.eslintignore',
36
- '{workspaceRoot}/eslint.config.js',
36
+ '{workspaceRoot}/eslint.config.cjs',
37
37
  ].includes(input));
38
38
  if (inputs.length === 0) {
39
39
  delete target.inputs;
@@ -85,7 +85,9 @@ const getGlobalFlatEslintConfiguration = (rootProject) => {
85
85
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/base'), { insertAtTheEnd: false });
86
86
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/typescript'));
87
87
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/javascript'));
88
- content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({ ignores: ['**/dist'] }));
88
+ content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
89
+ ignores: ['**/dist'],
90
+ }));
89
91
  if (!rootProject) {
90
92
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
91
93
  files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
@@ -108,7 +110,14 @@ const getGlobalFlatEslintConfiguration = (rootProject) => {
108
110
  }));
109
111
  }
110
112
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
111
- files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
113
+ files: [
114
+ '**/*.ts',
115
+ '**/*.tsx',
116
+ '**/*.js',
117
+ '**/*.jsx',
118
+ '**/*.cjs',
119
+ '**/*.mjs',
120
+ ],
112
121
  rules: {},
113
122
  }));
114
123
  return content;
@@ -27,9 +27,9 @@ function migrateConfigToMonorepoStyle(projects, tree, unitTestRunner, keepExisti
27
27
  (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
28
28
  '@eslint/js': versions_1.eslintVersion,
29
29
  }, undefined, keepExistingVersions);
30
- tree.write(tree.exists('eslint.config.js')
31
- ? 'eslint.base.config.js'
32
- : 'eslint.config.js', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)());
30
+ tree.write(tree.exists('eslint.config.cjs')
31
+ ? 'eslint.base.config.cjs'
32
+ : 'eslint.config.cjs', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)());
33
33
  }
34
34
  else {
35
35
  const eslintFile = (0, eslint_file_1.findEslintFile)(tree, '.');
@@ -80,7 +80,7 @@ function migrateEslintFile(projectEslintPath, tree) {
80
80
  let config = tree.read(projectEslintPath, 'utf-8');
81
81
  // remove @nx plugin
82
82
  config = (0, ast_utils_1.removePlugin)(config, '@nx', '@nx/eslint-plugin-nx');
83
- // extend eslint.base.config.js
83
+ // extend eslint.base.config.cjs
84
84
  config = (0, ast_utils_1.addImportToFlatConfig)(config, 'baseConfig', `${(0, devkit_1.offsetFromRoot)((0, path_1.dirname)(projectEslintPath))}${baseFile}`);
85
85
  config = (0, ast_utils_1.addBlockToFlatConfigExport)(config, (0, ast_utils_1.generateSpreadElement)('baseConfig'), { insertAtTheEnd: false });
86
86
  // cleanup file extends
@@ -13,7 +13,7 @@ function updateProductionFileset(tree) {
13
13
  const productionFileSet = nxJson.namedInputs?.production;
14
14
  if (productionFileSet) {
15
15
  productionFileSet.push('!{projectRoot}/.eslintrc.json');
16
- productionFileSet.push('!{projectRoot}/eslint.config.js');
16
+ productionFileSet.push('!{projectRoot}/eslint.config.cjs');
17
17
  // Dedupe and set
18
18
  nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
19
19
  }
@@ -28,7 +28,7 @@ function addTargetDefaults(tree) {
28
28
  'default',
29
29
  `{workspaceRoot}/.eslintrc.json`,
30
30
  `{workspaceRoot}/.eslintignore`,
31
- `{workspaceRoot}/eslint.config.js`,
31
+ `{workspaceRoot}/eslint.config.cjs`,
32
32
  ];
33
33
  (0, devkit_1.updateNxJson)(tree, nxJson);
34
34
  }
@@ -184,8 +184,7 @@ function createEsLintConfiguration(tree, options, projectConfig, setParserOption
184
184
  });
185
185
  const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes);
186
186
  const content = (0, ast_utils_1.stringifyNodeList)(nodeList);
187
- const ext = extendedRootConfig?.endsWith('.cjs') ? '.cjs' : '.js';
188
- tree.write((0, path_1.join)(projectConfig.root, `eslint.config${ext}`), content);
187
+ tree.write((0, path_1.join)(projectConfig.root, `eslint.config.cjs`), content);
189
188
  }
190
189
  else {
191
190
  (0, devkit_1.writeJson)(tree, (0, path_1.join)(projectConfig.root, `.eslintrc.json`), {
@@ -38,7 +38,7 @@ function setUpLegacyRootEslintRc(tree, options) {
38
38
  : () => { };
39
39
  }
40
40
  function setUpRootFlatConfig(tree, options) {
41
- tree.write('eslint.config.js', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(options.rootProject));
41
+ tree.write('eslint.config.cjs', (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(options.rootProject));
42
42
  return !options.skipPackageJson
43
43
  ? (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
44
44
  '@eslint/js': versions_1.eslint9__eslintVersion,
@@ -28,6 +28,10 @@ function findEslintFile(tree, projectRoot) {
28
28
  if (projectRoot === undefined && tree.exists(config_file_1.baseEsLintFlatConfigFile)) {
29
29
  return config_file_1.baseEsLintFlatConfigFile;
30
30
  }
31
+ if (projectRoot === undefined &&
32
+ tree.exists(config_file_1.legacyBaseEsLintFlatConfigFile)) {
33
+ return config_file_1.legacyBaseEsLintFlatConfigFile;
34
+ }
31
35
  projectRoot ??= '';
32
36
  for (const file of config_file_1.ESLINT_CONFIG_FILENAMES) {
33
37
  if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, file))) {
@@ -117,7 +121,18 @@ function addOverrideToLintConfig(tree, root, override, options = {
117
121
  }) {
118
122
  const isBase = options.checkBaseConfig && findEslintFile(tree, root).includes('.base');
119
123
  if ((0, flat_config_1.useFlatConfig)(tree)) {
120
- const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
124
+ let fileName;
125
+ if (isBase) {
126
+ fileName = (0, devkit_1.joinPathFragments)(root, config_file_1.baseEsLintFlatConfigFile);
127
+ }
128
+ else {
129
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
130
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
131
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
132
+ break;
133
+ }
134
+ }
135
+ }
121
136
  const flatOverride = (0, ast_utils_1.generateFlatOverride)(override);
122
137
  let content = tree.read(fileName, 'utf8');
123
138
  // Check if the provided override using legacy eslintrc properties or plugins, if so we need to add compat
@@ -148,7 +163,14 @@ function updateOverrideInLintConfig(tree, rootOrFile, lookup, update) {
148
163
  root = (0, posix_1.dirname)(rootOrFile);
149
164
  }
150
165
  if ((0, flat_config_1.useFlatConfig)(tree)) {
151
- fileName ??= (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
166
+ if (!fileName) {
167
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
168
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
169
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
170
+ break;
171
+ }
172
+ }
173
+ }
152
174
  let content = tree.read(fileName, 'utf8');
153
175
  content = (0, ast_utils_1.replaceOverride)(content, root, lookup, update);
154
176
  tree.write(fileName, content);
@@ -190,8 +212,18 @@ function lintConfigHasOverride(tree, rootOrFile, lookup, checkBaseConfig = false
190
212
  const isBase = !fileName &&
191
213
  checkBaseConfig &&
192
214
  findEslintFile(tree, root).includes('.base');
215
+ if (isBase) {
216
+ fileName = (0, devkit_1.joinPathFragments)(root, config_file_1.baseEsLintFlatConfigFile);
217
+ }
193
218
  if ((0, flat_config_1.useFlatConfig)(tree)) {
194
- fileName ??= (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintFlatConfigFile : (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
219
+ if (!fileName) {
220
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
221
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
222
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
223
+ break;
224
+ }
225
+ }
226
+ }
195
227
  const content = tree.read(fileName, 'utf8');
196
228
  return (0, ast_utils_1.hasOverride)(content, lookup);
197
229
  }
@@ -202,7 +234,13 @@ function lintConfigHasOverride(tree, rootOrFile, lookup, checkBaseConfig = false
202
234
  }
203
235
  function replaceOverridesInLintConfig(tree, root, overrides) {
204
236
  if ((0, flat_config_1.useFlatConfig)(tree)) {
205
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
237
+ let fileName;
238
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
239
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
240
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
241
+ break;
242
+ }
243
+ }
206
244
  let content = tree.read(fileName, 'utf8');
207
245
  // Check if any of the provided overrides using legacy eslintrc properties or plugins, if so we need to add compat
208
246
  if (overrides.some(ast_utils_1.overrideNeedsCompat)) {
@@ -226,7 +264,13 @@ function replaceOverridesInLintConfig(tree, root, overrides) {
226
264
  function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
227
265
  if ((0, flat_config_1.useFlatConfig)(tree)) {
228
266
  const pluginExtends = [];
229
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
267
+ let fileName;
268
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
269
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
270
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
271
+ break;
272
+ }
273
+ }
230
274
  let shouldImportEslintCompat = false;
231
275
  // assume eslint version is 9 if not found, as it's what we'd be generating by default
232
276
  const eslintVersion = (0, version_utils_1.getInstalledEslintVersion)(tree) ?? versions_1.eslint9__eslintVersion;
@@ -294,7 +338,13 @@ function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
294
338
  function addPredefinedConfigToFlatLintConfig(tree, root, predefinedConfigName, moduleName = 'nx', moduleImportPath = '@nx/eslint-plugin', spread = true, insertAtTheEnd = true) {
295
339
  if (!(0, flat_config_1.useFlatConfig)(tree))
296
340
  throw new Error('Predefined configs can only be used with flat configs');
297
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
341
+ let fileName;
342
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
343
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
344
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
345
+ break;
346
+ }
347
+ }
298
348
  let content = tree.read(fileName, 'utf8');
299
349
  content = (0, ast_utils_1.addImportToFlatConfig)(content, moduleName, moduleImportPath);
300
350
  content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)(predefinedConfigName, moduleName, spread), { insertAtTheEnd });
@@ -303,7 +353,13 @@ function addPredefinedConfigToFlatLintConfig(tree, root, predefinedConfigName, m
303
353
  function addPluginsToLintConfig(tree, root, plugin) {
304
354
  const plugins = Array.isArray(plugin) ? plugin : [plugin];
305
355
  if ((0, flat_config_1.useFlatConfig)(tree)) {
306
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
356
+ let fileName;
357
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
358
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
359
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
360
+ break;
361
+ }
362
+ }
307
363
  let content = tree.read(fileName, 'utf8');
308
364
  const mappedPlugins = [];
309
365
  plugins.forEach((name) => {
@@ -327,7 +383,13 @@ function addPluginsToLintConfig(tree, root, plugin) {
327
383
  }
328
384
  function addIgnoresToLintConfig(tree, root, ignorePatterns) {
329
385
  if ((0, flat_config_1.useFlatConfig)(tree)) {
330
- const fileName = (0, devkit_1.joinPathFragments)(root, (0, flat_config_1.getRootESLintFlatConfigFilename)(tree));
386
+ let fileName;
387
+ for (const f of flat_config_1.eslintFlatConfigFilenames) {
388
+ if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
389
+ fileName = (0, devkit_1.joinPathFragments)(root, f);
390
+ break;
391
+ }
392
+ }
331
393
  const block = (0, ast_utils_1.generateAst)({
332
394
  ignores: ignorePatterns.map((path) => (0, path_utils_1.mapFilePath)(path)),
333
395
  });
@@ -670,10 +670,32 @@ function generateFlatOverride(_override) {
670
670
  });
671
671
  }
672
672
  // At this point we are applying the flat config compat tooling to the override
673
- const { excludedFiles, parser, parserOptions, rules, files, ...rest } = override;
673
+ let { excludedFiles, parser, parserOptions, rules, files, ...rest } = override;
674
674
  const objectLiteralElements = [
675
675
  ts.factory.createSpreadAssignment(ts.factory.createIdentifier('config')),
676
676
  ];
677
+ // If converting the JS rule, then we need to match ESLint default and also include .cjs and .mjs files.
678
+ if ((Array.isArray(rest.extends) &&
679
+ rest.extends.includes('plugin:@nx/javascript')) ||
680
+ rest.extends === 'plugin:@nx/javascript') {
681
+ const newFiles = new Set(files);
682
+ newFiles.add('**/*.js');
683
+ newFiles.add('**/*.jsx');
684
+ newFiles.add('**/*.cjs');
685
+ newFiles.add('**/*.mjs');
686
+ files = Array.from(newFiles);
687
+ }
688
+ // If converting the TS rule, then we need to match ESLint default and also include .cts and .mts files.
689
+ if ((Array.isArray(rest.extends) &&
690
+ rest.extends.includes('plugin:@nx/typescript')) ||
691
+ rest.extends === 'plugin:@nx/typescript') {
692
+ const newFiles = new Set(files);
693
+ newFiles.add('**/*.ts');
694
+ newFiles.add('**/*.tsx');
695
+ newFiles.add('**/*.cts');
696
+ newFiles.add('**/*.mts');
697
+ files = Array.from(newFiles);
698
+ }
677
699
  addTSObjectProperty(objectLiteralElements, 'files', files);
678
700
  addTSObjectProperty(objectLiteralElements, 'excludedFiles', excludedFiles);
679
701
  // Apply rules (and spread ...config.rules into it as the first assignment)
@@ -0,0 +1,2 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ export default function (tree: Tree): Promise<void>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const ts = require("typescript");
5
+ const js_1 = require("@nx/js");
6
+ async function default_1(tree) {
7
+ let rootConfig;
8
+ // NOTE: we don't support generating ESM base config currently so they are not handled.
9
+ for (const candidate of ['eslint.config.js', 'eslint.config.cjs']) {
10
+ if (tree.exists(candidate)) {
11
+ rootConfig = candidate;
12
+ break;
13
+ }
14
+ }
15
+ if (!rootConfig)
16
+ return;
17
+ updateOverrideFileExtensions(tree, rootConfig, 'plugin:@nx/typescript', [`'**/*.ts'`, `'**/*.tsx'`], [`'**/*.cts'`, `'**/*.mts'`]);
18
+ updateOverrideFileExtensions(tree, rootConfig, 'plugin:@nx/javascript', [`'**/*.js'`, `'**/*.jsx'`], [`'**/*.cjs'`, `'**/*.mjs'`]);
19
+ }
20
+ function updateOverrideFileExtensions(tree, configFile, plugin, matchingExts, newExts) {
21
+ const content = tree.read(configFile, 'utf-8');
22
+ const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
23
+ let compatNode;
24
+ const spreadElementNodes = (0, js_1.findNodes)(source, ts.SyntaxKind.SpreadElement);
25
+ for (const a of spreadElementNodes) {
26
+ const assignmentNodes = (0, js_1.findNodes)(a, ts.SyntaxKind.PropertyAssignment);
27
+ if (assignmentNodes.length === 0)
28
+ continue;
29
+ for (const b of assignmentNodes) {
30
+ if (b.name.getText() === 'extends' &&
31
+ b.initializer.getText().includes(plugin)) {
32
+ compatNode = a;
33
+ break;
34
+ }
35
+ }
36
+ }
37
+ if (compatNode) {
38
+ const arrayNodes = (0, js_1.findNodes)(compatNode, ts.SyntaxKind.ArrayLiteralExpression);
39
+ for (const a of arrayNodes) {
40
+ if (matchingExts.every((ext) => a.elements.some((e) => e.getText() === ext))) {
41
+ const exts = new Set(a.elements.map((e) => e.getText()));
42
+ for (const ext of newExts) {
43
+ exts.add(ext);
44
+ }
45
+ (0, js_1.replaceChange)(tree, source, configFile, a.getStart(a.getSourceFile()), `[${Array.from(exts).join(', ')}]`, a.getText()).getText();
46
+ }
47
+ }
48
+ }
49
+ }
@@ -2,7 +2,8 @@ export declare const ESLINT_FLAT_CONFIG_FILENAMES: string[];
2
2
  export declare const ESLINT_OLD_CONFIG_FILENAMES: string[];
3
3
  export declare const ESLINT_CONFIG_FILENAMES: string[];
4
4
  export declare const baseEsLintConfigFile = ".eslintrc.base.json";
5
- export declare const baseEsLintFlatConfigFile = "eslint.base.config.js";
5
+ export declare const baseEsLintFlatConfigFile = "eslint.base.config.cjs";
6
+ export declare const legacyBaseEsLintFlatConfigFile = "eslint.base.config.js";
6
7
  export declare function isFlatConfig(configFilePath: string): boolean;
7
8
  export declare function findFlatConfigFile(directory: string, workspaceRoot: string): string | null;
8
9
  export declare function findOldConfigFile(filePathOrDirectory: string, workspaceRoot: string): string | null;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.baseEsLintFlatConfigFile = exports.baseEsLintConfigFile = exports.ESLINT_CONFIG_FILENAMES = exports.ESLINT_OLD_CONFIG_FILENAMES = exports.ESLINT_FLAT_CONFIG_FILENAMES = void 0;
3
+ exports.legacyBaseEsLintFlatConfigFile = exports.baseEsLintFlatConfigFile = exports.baseEsLintConfigFile = exports.ESLINT_CONFIG_FILENAMES = exports.ESLINT_OLD_CONFIG_FILENAMES = exports.ESLINT_FLAT_CONFIG_FILENAMES = void 0;
4
4
  exports.isFlatConfig = isFlatConfig;
5
5
  exports.findFlatConfigFile = findFlatConfigFile;
6
6
  exports.findOldConfigFile = findOldConfigFile;
@@ -21,7 +21,9 @@ exports.ESLINT_CONFIG_FILENAMES = [
21
21
  ...exports.ESLINT_FLAT_CONFIG_FILENAMES,
22
22
  ];
23
23
  exports.baseEsLintConfigFile = '.eslintrc.base.json';
24
- exports.baseEsLintFlatConfigFile = 'eslint.base.config.js';
24
+ exports.baseEsLintFlatConfigFile = 'eslint.base.config.cjs';
25
+ // Make sure we can handle previous file extension as well for migrations or custom generators.
26
+ exports.legacyBaseEsLintFlatConfigFile = 'eslint.base.config.js';
25
27
  function isFlatConfig(configFilePath) {
26
28
  const configFileName = (0, path_1.basename)(configFilePath);
27
29
  return exports.ESLINT_FLAT_CONFIG_FILENAMES.includes(configFileName);
@@ -6,8 +6,8 @@ exports.useFlatConfig = useFlatConfig;
6
6
  const semver_1 = require("semver");
7
7
  // todo: add support for eslint.config.mjs,
8
8
  exports.eslintFlatConfigFilenames = [
9
- 'eslint.config.js',
10
9
  'eslint.config.cjs',
10
+ 'eslint.config.js',
11
11
  ];
12
12
  function getRootESLintFlatConfigFilename(tree) {
13
13
  for (const file of exports.eslintFlatConfigFilenames) {