@nx/eslint 20.4.0-beta.0 → 20.4.0-beta.2

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 (29) hide show
  1. package/migrations.json +40 -0
  2. package/package.json +4 -4
  3. package/src/generators/convert-to-flat-config/converters/json-converter.d.ts +1 -1
  4. package/src/generators/convert-to-flat-config/converters/json-converter.js +6 -6
  5. package/src/generators/convert-to-flat-config/generator.js +17 -16
  6. package/src/generators/convert-to-flat-config/schema.d.ts +2 -0
  7. package/src/generators/convert-to-inferred/convert-to-inferred.js +1 -0
  8. package/src/generators/init/global-eslint-config.d.ts +1 -1
  9. package/src/generators/init/global-eslint-config.js +5 -5
  10. package/src/generators/init/init-migration.d.ts +1 -1
  11. package/src/generators/init/init-migration.js +15 -5
  12. package/src/generators/init/init.d.ts +1 -0
  13. package/src/generators/init/init.js +17 -6
  14. package/src/generators/lint-project/lint-project.d.ts +1 -0
  15. package/src/generators/lint-project/lint-project.js +27 -11
  16. package/src/generators/lint-project/setup-root-eslint.d.ts +1 -0
  17. package/src/generators/lint-project/setup-root-eslint.js +2 -1
  18. package/src/generators/utils/eslint-file.d.ts +1 -0
  19. package/src/generators/utils/eslint-file.js +54 -14
  20. package/src/generators/utils/flat-config/ast-utils.d.ts +10 -4
  21. package/src/generators/utils/flat-config/ast-utils.js +328 -59
  22. package/src/generators/workspace-rules-project/workspace-rules-project.js +2 -1
  23. package/src/plugins/plugin.js +1 -1
  24. package/src/utils/config-file.d.ts +2 -1
  25. package/src/utils/config-file.js +3 -2
  26. package/src/utils/flat-config.d.ts +1 -0
  27. package/src/utils/flat-config.js +8 -2
  28. package/src/utils/versions.d.ts +1 -1
  29. package/src/utils/versions.js +1 -1
@@ -17,6 +17,7 @@ exports.generatePluginExtendsElement = generatePluginExtendsElement;
17
17
  exports.generatePluginExtendsElementWithCompatFixup = generatePluginExtendsElementWithCompatFixup;
18
18
  exports.stringifyNodeList = stringifyNodeList;
19
19
  exports.generateRequire = generateRequire;
20
+ exports.generateESMImport = generateESMImport;
20
21
  exports.overrideNeedsCompat = overrideNeedsCompat;
21
22
  exports.generateFlatOverride = generateFlatOverride;
22
23
  exports.generateFlatPredefinedConfig = generateFlatPredefinedConfig;
@@ -35,7 +36,8 @@ const SPREAD_ELEMENTS_REGEXP = /\s*\.\.\.[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)*,?\n?/g;
35
36
  */
36
37
  function removeOverridesFromLintConfig(content) {
37
38
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
38
- const exportsArray = findAllBlocks(source);
39
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
40
+ const exportsArray = format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
39
41
  if (!exportsArray) {
40
42
  return content;
41
43
  }
@@ -52,7 +54,16 @@ function removeOverridesFromLintConfig(content) {
52
54
  });
53
55
  return (0, devkit_1.applyChangesToString)(content, changes);
54
56
  }
55
- function findAllBlocks(source) {
57
+ // TODO Change name
58
+ function findExportDefault(source) {
59
+ return ts.forEachChild(source, function analyze(node) {
60
+ if (ts.isExportAssignment(node) &&
61
+ ts.isArrayLiteralExpression(node.expression)) {
62
+ return node.expression.elements;
63
+ }
64
+ });
65
+ }
66
+ function findModuleExports(source) {
56
67
  return ts.forEachChild(source, function analyze(node) {
57
68
  if (ts.isExpressionStatement(node) &&
58
69
  ts.isBinaryExpression(node.expression) &&
@@ -74,7 +85,8 @@ function isOverride(node) {
74
85
  }
75
86
  function hasOverride(content, lookup) {
76
87
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
77
- const exportsArray = findAllBlocks(source);
88
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
89
+ const exportsArray = format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
78
90
  if (!exportsArray) {
79
91
  return false;
80
92
  }
@@ -105,14 +117,16 @@ function parseTextToJson(text) {
105
117
  .replace(/'/g, '"')
106
118
  .replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": ')
107
119
  // stringify any require calls to avoid JSON parsing errors, turn them into just the string value being required
108
- .replace(/require\(['"]([^'"]+)['"]\)/g, '"$1"'));
120
+ .replace(/require\(['"]([^'"]+)['"]\)/g, '"$1"')
121
+ .replace(/\(?await import\(['"]([^'"]+)['"]\)\)?/g, '"$1"'));
109
122
  }
110
123
  /**
111
124
  * Finds an override matching the lookup function and applies the update function to it
112
125
  */
113
126
  function replaceOverride(content, root, lookup, update) {
114
127
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
115
- const exportsArray = findAllBlocks(source);
128
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
129
+ const exportsArray = format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
116
130
  if (!exportsArray) {
117
131
  return content;
118
132
  }
@@ -145,19 +159,17 @@ function replaceOverride(content, root, lookup, update) {
145
159
  let updatedData = update(data);
146
160
  if (updatedData) {
147
161
  updatedData = mapFilePaths(updatedData);
162
+ const parserReplacement = format === 'mjs'
163
+ ? (parser) => `(await import('${parser}'))`
164
+ : (parser) => `require('${parser}')`;
148
165
  changes.push({
149
166
  type: devkit_1.ChangeType.Insert,
150
167
  index: start,
151
- // NOTE: Indentation added to format without formatting tools like Prettier.
152
168
  text: ' ' +
153
169
  JSON.stringify(updatedData, null, 2)
154
- // restore any parser require calls that were stripped during JSON parsing
155
- .replace(/"parser": "([^"]+)"/g, (_, parser) => {
156
- return `"parser": require('${parser}')`;
157
- })
158
- .slice(2, -2) // remove curly braces and start/end line breaks since we are injecting just properties
159
- // Append indentation so file is formatted without Prettier
160
- .replaceAll(/\n/g, '\n '),
170
+ .replace(/"parser": "([^"]+)"/g, (_, parser) => `"parser": ${parserReplacement(parser)}`)
171
+ .slice(2, -2) // Remove curly braces and start/end line breaks
172
+ .replaceAll(/\n/g, '\n '), // Maintain indentation
161
173
  });
162
174
  }
163
175
  }
@@ -166,11 +178,89 @@ function replaceOverride(content, root, lookup, update) {
166
178
  return (0, devkit_1.applyChangesToString)(content, changes);
167
179
  }
168
180
  /**
169
- * Adding require statement to the top of the file
181
+ * Adding import statement to the top of the file
182
+ * The imports are added based on a few rules:
183
+ * 1. If it's a default import and matches the variable, return content unchanged.
184
+ * 2. If it's a named import and the variables are not part of the import object, add them.
185
+ * 3. If no existing import and variable is a string, add a default import.
186
+ * 4. If no existing import and variable is an array, add it as an object import.
170
187
  */
171
188
  function addImportToFlatConfig(content, variable, imp) {
172
189
  const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
173
190
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
191
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
192
+ if (format === 'mjs') {
193
+ return addESMImportToFlatConfig(source, printer, content, variable, imp);
194
+ }
195
+ return addCJSImportToFlatConfig(source, printer, content, variable, imp);
196
+ }
197
+ function addESMImportToFlatConfig(source, printer, content, variable, imp) {
198
+ let existingImport;
199
+ ts.forEachChild(source, (node) => {
200
+ if (ts.isImportDeclaration(node) &&
201
+ ts.isStringLiteral(node.moduleSpecifier) &&
202
+ node.moduleSpecifier.text === imp) {
203
+ existingImport = node;
204
+ }
205
+ });
206
+ // Rule 1:
207
+ if (existingImport &&
208
+ typeof variable === 'string' &&
209
+ existingImport.importClause?.name?.getText() === variable) {
210
+ return content;
211
+ }
212
+ // Rule 2:
213
+ if (existingImport &&
214
+ existingImport.importClause?.namedBindings &&
215
+ Array.isArray(variable)) {
216
+ const namedImports = existingImport.importClause
217
+ .namedBindings;
218
+ const existingElements = namedImports.elements;
219
+ // Filter out variables that are already imported
220
+ const newVariables = variable.filter((v) => !existingElements.some((e) => e.name.getText() === v));
221
+ if (newVariables.length === 0) {
222
+ return content;
223
+ }
224
+ const newImportSpecifiers = newVariables.map((v) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(v)));
225
+ const lastElement = existingElements[existingElements.length - 1];
226
+ const insertIndex = lastElement
227
+ ? lastElement.getEnd()
228
+ : namedImports.getEnd();
229
+ const insertText = printer.printList(ts.ListFormat.NamedImportsOrExportsElements, ts.factory.createNodeArray(newImportSpecifiers), source);
230
+ return (0, devkit_1.applyChangesToString)(content, [
231
+ {
232
+ type: devkit_1.ChangeType.Insert,
233
+ index: insertIndex,
234
+ text: `, ${insertText}`,
235
+ },
236
+ ]);
237
+ }
238
+ // Rule 3:
239
+ if (!existingImport && typeof variable === 'string') {
240
+ const defaultImport = ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, ts.factory.createIdentifier(variable), undefined), ts.factory.createStringLiteral(imp));
241
+ const insert = printer.printNode(ts.EmitHint.Unspecified, defaultImport, source);
242
+ return (0, devkit_1.applyChangesToString)(content, [
243
+ {
244
+ type: devkit_1.ChangeType.Insert,
245
+ index: 0,
246
+ text: `${insert}\n`,
247
+ },
248
+ ]);
249
+ }
250
+ // Rule 4:
251
+ if (!existingImport && Array.isArray(variable)) {
252
+ const objectImport = ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(variable.map((v) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(v))))), ts.factory.createStringLiteral(imp));
253
+ const insert = printer.printNode(ts.EmitHint.Unspecified, objectImport, source);
254
+ return (0, devkit_1.applyChangesToString)(content, [
255
+ {
256
+ type: devkit_1.ChangeType.Insert,
257
+ index: 0,
258
+ text: `${insert}\n`,
259
+ },
260
+ ]);
261
+ }
262
+ }
263
+ function addCJSImportToFlatConfig(source, printer, content, variable, imp) {
174
264
  const foundBindingVars = ts.forEachChild(source, function analyze(node) {
175
265
  // we can only combine object binding patterns
176
266
  if (!Array.isArray(variable)) {
@@ -239,11 +329,48 @@ function addImportToFlatConfig(content, variable, imp) {
239
329
  },
240
330
  ]);
241
331
  }
332
+ function existsAsNamedOrDefaultImport(node, variable) {
333
+ const isNamed = node.importClause.namedBindings &&
334
+ ts.isNamedImports(node.importClause.namedBindings);
335
+ if (Array.isArray(variable)) {
336
+ return isNamed || variable.includes(node.importClause?.name?.getText());
337
+ }
338
+ return ((node.importClause.namedBindings &&
339
+ ts.isNamedImports(node.importClause.namedBindings)) ||
340
+ node.importClause?.name?.getText() === variable);
341
+ }
242
342
  /**
243
343
  * Remove an import from flat config
244
344
  */
245
345
  function removeImportFromFlatConfig(content, variable, imp) {
246
346
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
347
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
348
+ if (format === 'mjs') {
349
+ return removeImportFromFlatConfigESM(source, content, variable, imp);
350
+ }
351
+ else {
352
+ return removeImportFromFlatConfigCJS(source, content, variable, imp);
353
+ }
354
+ }
355
+ function removeImportFromFlatConfigESM(source, content, variable, imp) {
356
+ const changes = [];
357
+ ts.forEachChild(source, (node) => {
358
+ // we can only combine object binding patterns
359
+ if (ts.isImportDeclaration(node) &&
360
+ ts.isStringLiteral(node.moduleSpecifier) &&
361
+ node.moduleSpecifier.text === imp &&
362
+ node.importClause &&
363
+ existsAsNamedOrDefaultImport(node, variable)) {
364
+ changes.push({
365
+ type: devkit_1.ChangeType.Delete,
366
+ start: node.pos,
367
+ length: node.end - node.pos,
368
+ });
369
+ }
370
+ });
371
+ return (0, devkit_1.applyChangesToString)(content, changes);
372
+ }
373
+ function removeImportFromFlatConfigCJS(source, content, variable, imp) {
247
374
  const changes = [];
248
375
  ts.forEachChild(source, (node) => {
249
376
  // we can only combine object binding patterns
@@ -266,13 +393,44 @@ function removeImportFromFlatConfig(content, variable, imp) {
266
393
  return (0, devkit_1.applyChangesToString)(content, changes);
267
394
  }
268
395
  /**
269
- * Injects new ts.expression to the end of the module.exports array.
396
+ * Injects new ts.expression to the end of the module.exports or export default array.
270
397
  */
271
398
  function addBlockToFlatConfigExport(content, config, options = {
272
399
  insertAtTheEnd: true,
273
400
  }) {
274
401
  const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
275
402
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
403
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
404
+ // find the export default array statement
405
+ if (format === 'mjs') {
406
+ return addBlockToFlatConfigExportESM(content, config, source, printer, options);
407
+ }
408
+ else {
409
+ return addBlockToFlatConfigExportCJS(content, config, source, printer, options);
410
+ }
411
+ }
412
+ function addBlockToFlatConfigExportESM(content, config, source, printer, options = {
413
+ insertAtTheEnd: true,
414
+ }) {
415
+ const exportDefaultStatement = source.statements.find((statement) => ts.isExportAssignment(statement) &&
416
+ ts.isArrayLiteralExpression(statement.expression));
417
+ if (!exportDefaultStatement)
418
+ return content;
419
+ const exportArrayLiteral = exportDefaultStatement.expression;
420
+ const updatedArrayElements = options.insertAtTheEnd
421
+ ? [...exportArrayLiteral.elements, config]
422
+ : [config, ...exportArrayLiteral.elements];
423
+ const updatedExportDefault = ts.factory.createExportAssignment(undefined, false, ts.factory.createArrayLiteralExpression(updatedArrayElements, true));
424
+ // update the existing export default array
425
+ const updatedStatements = source.statements.map((statement) => statement === exportDefaultStatement ? updatedExportDefault : statement);
426
+ const updatedSource = ts.factory.updateSourceFile(source, updatedStatements);
427
+ return printer
428
+ .printFile(updatedSource)
429
+ .replace(/export default/, '\nexport default');
430
+ }
431
+ function addBlockToFlatConfigExportCJS(content, config, source, printer, options = {
432
+ insertAtTheEnd: true,
433
+ }) {
276
434
  const exportsArray = ts.forEachChild(source, function analyze(node) {
277
435
  if (ts.isExpressionStatement(node) &&
278
436
  ts.isBinaryExpression(node.expression) &&
@@ -315,28 +473,47 @@ function addBlockToFlatConfigExport(content, config, options = {
315
473
  }
316
474
  function removePlugin(content, pluginName, pluginImport) {
317
475
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
476
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
318
477
  const changes = [];
478
+ if (format === 'mjs') {
479
+ ts.forEachChild(source, function analyze(node) {
480
+ if (ts.isImportDeclaration(node) &&
481
+ ts.isStringLiteral(node.moduleSpecifier) &&
482
+ node.moduleSpecifier.text === pluginImport) {
483
+ const importClause = node.importClause;
484
+ if ((importClause && importClause.name) ||
485
+ (importClause.namedBindings &&
486
+ ts.isNamedImports(importClause.namedBindings))) {
487
+ changes.push({
488
+ type: devkit_1.ChangeType.Delete,
489
+ start: node.pos,
490
+ length: node.end - node.pos,
491
+ });
492
+ }
493
+ }
494
+ });
495
+ }
496
+ else {
497
+ ts.forEachChild(source, function analyze(node) {
498
+ if (ts.isVariableStatement(node) &&
499
+ ts.isVariableDeclaration(node.declarationList.declarations[0]) &&
500
+ ts.isCallExpression(node.declarationList.declarations[0].initializer) &&
501
+ node.declarationList.declarations[0].initializer.arguments.length &&
502
+ ts.isStringLiteral(node.declarationList.declarations[0].initializer.arguments[0]) &&
503
+ node.declarationList.declarations[0].initializer.arguments[0].text ===
504
+ pluginImport) {
505
+ changes.push({
506
+ type: devkit_1.ChangeType.Delete,
507
+ start: node.pos,
508
+ length: node.end - node.pos,
509
+ });
510
+ }
511
+ });
512
+ }
319
513
  ts.forEachChild(source, function analyze(node) {
320
- if (ts.isVariableStatement(node) &&
321
- ts.isVariableDeclaration(node.declarationList.declarations[0]) &&
322
- ts.isCallExpression(node.declarationList.declarations[0].initializer) &&
323
- node.declarationList.declarations[0].initializer.arguments.length &&
324
- ts.isStringLiteral(node.declarationList.declarations[0].initializer.arguments[0]) &&
325
- node.declarationList.declarations[0].initializer.arguments[0].text ===
326
- pluginImport) {
327
- changes.push({
328
- type: devkit_1.ChangeType.Delete,
329
- start: node.pos,
330
- length: node.end - node.pos,
331
- });
332
- }
333
- });
334
- ts.forEachChild(source, function analyze(node) {
335
- if (ts.isExpressionStatement(node) &&
336
- ts.isBinaryExpression(node.expression) &&
337
- node.expression.left.getText() === 'module.exports' &&
338
- ts.isArrayLiteralExpression(node.expression.right)) {
339
- const blockElements = node.expression.right.elements;
514
+ if (ts.isExportAssignment(node) &&
515
+ ts.isArrayLiteralExpression(node.expression)) {
516
+ const blockElements = node.expression.elements;
340
517
  blockElements.forEach((element) => {
341
518
  if (ts.isObjectLiteralExpression(element)) {
342
519
  const pluginsElem = element.properties.find((prop) => prop.name?.getText() === 'plugins');
@@ -426,7 +603,12 @@ function removePlugin(content, pluginName, pluginImport) {
426
603
  function removeCompatExtends(content, compatExtends) {
427
604
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
428
605
  const changes = [];
429
- findAllBlocks(source)?.forEach((node) => {
606
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
607
+ const exportsArray = format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
608
+ if (!exportsArray) {
609
+ return content;
610
+ }
611
+ exportsArray.forEach((node) => {
430
612
  if (ts.isSpreadElement(node) &&
431
613
  ts.isCallExpression(node.expression) &&
432
614
  ts.isArrowFunction(node.expression.arguments[0]) &&
@@ -460,9 +642,14 @@ function removeCompatExtends(content, compatExtends) {
460
642
  }
461
643
  function removePredefinedConfigs(content, moduleImport, moduleVariable, configs) {
462
644
  const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
645
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
463
646
  const changes = [];
464
647
  let removeImport = true;
465
- findAllBlocks(source)?.forEach((node) => {
648
+ const exportsArray = format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
649
+ if (!exportsArray) {
650
+ return content;
651
+ }
652
+ exportsArray.forEach((node) => {
466
653
  if (ts.isSpreadElement(node) &&
467
654
  ts.isElementAccessExpression(node.expression) &&
468
655
  ts.isPropertyAccessExpression(node.expression.expression) &&
@@ -507,14 +694,22 @@ function addPluginsToExportsBlock(content, plugins) {
507
694
  * Adds compat if missing to flat config
508
695
  */
509
696
  function addFlatCompatToFlatConfig(content) {
510
- let result = content;
511
- result = addImportToFlatConfig(result, 'js', '@eslint/js');
697
+ const result = addImportToFlatConfig(content, 'js', '@eslint/js');
698
+ const format = content.includes('export default') ? 'mjs' : 'cjs';
512
699
  if (result.includes('const compat = new FlatCompat')) {
513
700
  return result;
514
701
  }
515
- result = addImportToFlatConfig(result, ['FlatCompat'], '@eslint/eslintrc');
516
- const index = result.indexOf('module.exports');
517
- return (0, devkit_1.applyChangesToString)(result, [
702
+ if (format === 'mjs') {
703
+ return addFlatCompatToFlatConfigESM(result);
704
+ }
705
+ else {
706
+ return addFlatCompatToFlatConfigCJS(result);
707
+ }
708
+ }
709
+ function addFlatCompatToFlatConfigCJS(content) {
710
+ content = addImportToFlatConfig(content, ['FlatCompat'], '@eslint/eslintrc');
711
+ const index = content.indexOf('module.exports');
712
+ return (0, devkit_1.applyChangesToString)(content, [
518
713
  {
519
714
  type: devkit_1.ChangeType.Insert,
520
715
  index: index - 1,
@@ -527,25 +722,62 @@ const compat = new FlatCompat({
527
722
  },
528
723
  ]);
529
724
  }
725
+ function addFlatCompatToFlatConfigESM(content) {
726
+ const importsToAdd = [
727
+ { variable: 'js', module: '@eslint/js' },
728
+ { variable: ['fileURLToPath'], module: 'url' },
729
+ { variable: ['dirname'], module: 'path' },
730
+ { variable: ['FlatCompat'], module: '@eslint/eslintrc' },
731
+ ];
732
+ for (const { variable, module } of importsToAdd) {
733
+ content = addImportToFlatConfig(content, variable, module);
734
+ }
735
+ const index = content.indexOf('export default');
736
+ return (0, devkit_1.applyChangesToString)(content, [
737
+ {
738
+ type: devkit_1.ChangeType.Insert,
739
+ index: index - 1,
740
+ text: `
741
+ const compat = new FlatCompat({
742
+ baseDirectory: dirname(fileURLToPath(import.meta.url)),
743
+ recommendedConfig: js.configs.recommended,
744
+ });\n
745
+ `,
746
+ },
747
+ ]);
748
+ }
530
749
  /**
531
750
  * Generate node list representing the imports and the exports blocks
532
751
  * Optionally add flat compat initialization
533
752
  */
534
- function createNodeList(importsMap, exportElements) {
753
+ function createNodeList(importsMap, exportElements, format) {
535
754
  const importsList = [];
536
- // generateRequire(varName, imp, ts.factory);
537
755
  Array.from(importsMap.entries()).forEach(([imp, varName]) => {
538
- importsList.push(generateRequire(varName, imp));
756
+ if (format === 'mjs') {
757
+ importsList.push(generateESMImport(varName, imp));
758
+ }
759
+ else {
760
+ importsList.push(generateRequire(varName, imp));
761
+ }
539
762
  });
763
+ const exports = format === 'mjs'
764
+ ? generateESMExport(exportElements)
765
+ : generateCJSExport(exportElements);
540
766
  return ts.factory.createNodeArray([
541
767
  // add plugin imports
542
768
  ...importsList,
543
769
  ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.JS),
544
- // creates:
545
- // module.exports = [ ... ];
546
- ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('module'), ts.factory.createIdentifier('exports')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createArrayLiteralExpression(exportElements, true))),
770
+ exports,
547
771
  ]);
548
772
  }
773
+ function generateESMExport(elements) {
774
+ // creates: export default = [...]
775
+ return ts.factory.createExportAssignment(undefined, false, ts.factory.createArrayLiteralExpression(elements, true));
776
+ }
777
+ function generateCJSExport(elements) {
778
+ // creates: module.exports = [...]
779
+ return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('module'), ts.factory.createIdentifier('exports')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createArrayLiteralExpression(elements, true)));
780
+ }
549
781
  function generateSpreadElement(name) {
550
782
  return ts.factory.createSpreadElement(ts.factory.createIdentifier(name));
551
783
  }
@@ -563,12 +795,17 @@ function generatePluginExtendsElementWithCompatFixup(plugin) {
563
795
  function stringifyNodeList(nodes) {
564
796
  const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
565
797
  const resultFile = ts.createSourceFile('', '', ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
566
- return (printer
798
+ const result = printer
567
799
  .printList(ts.ListFormat.MultiLine, nodes, resultFile)
568
800
  // add new line before compat initialization
569
- .replace(/const compat = new FlatCompat/, '\nconst compat = new FlatCompat')
570
- // add new line before module.exports = ...
571
- .replace(/module\.exports/, '\nmodule.exports'));
801
+ .replace(/const compat = new FlatCompat/, '\nconst compat = new FlatCompat');
802
+ if (result.includes('export default')) {
803
+ return result // add new line before export default = ...
804
+ .replace(/export default/, '\nexport default');
805
+ }
806
+ else {
807
+ return result.replace(/module.exports/, '\nmodule.exports');
808
+ }
572
809
  }
573
810
  /**
574
811
  * generates AST require statement
@@ -578,6 +815,26 @@ function generateRequire(variableName, imp) {
578
815
  ts.factory.createVariableDeclaration(variableName, undefined, undefined, ts.factory.createCallExpression(ts.factory.createIdentifier('require'), undefined, [ts.factory.createStringLiteral(imp)])),
579
816
  ], ts.NodeFlags.Const));
580
817
  }
818
+ // Top level imports
819
+ function generateESMImport(variableName, imp) {
820
+ let importClause;
821
+ if (typeof variableName === 'string') {
822
+ // For single variable import e.g import foo from 'module';
823
+ importClause = ts.factory.createImportClause(false, ts.factory.createIdentifier(variableName), undefined);
824
+ }
825
+ else {
826
+ // For object binding pattern import e.g import { a, b, c } from 'module';
827
+ importClause = ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(variableName.elements.map((element) => {
828
+ const propertyName = element.propertyName
829
+ ? ts.isIdentifier(element.propertyName)
830
+ ? element.propertyName
831
+ : ts.factory.createIdentifier(element.propertyName.getText())
832
+ : undefined;
833
+ return ts.factory.createImportSpecifier(false, propertyName, element.name);
834
+ })));
835
+ }
836
+ return ts.factory.createImportDeclaration(undefined, importClause, ts.factory.createStringLiteral(imp));
837
+ }
581
838
  /**
582
839
  * FROM: https://github.com/eslint/rewrite/blob/e2a7ec809db20e638abbad250d105ddbde88a8d5/packages/migrate-config/src/migrate-config.js#L222
583
840
  *
@@ -603,7 +860,7 @@ function overrideNeedsCompat(override) {
603
860
  * Generates an AST object or spread element representing a modern flat config entry,
604
861
  * based on a given legacy eslintrc JSON override object
605
862
  */
606
- function generateFlatOverride(_override) {
863
+ function generateFlatOverride(_override, format) {
607
864
  const override = mapFilePaths(_override);
608
865
  // We do not need the compat tooling for this override
609
866
  if (!overrideNeedsCompat(override)) {
@@ -659,12 +916,10 @@ function generateFlatOverride(_override) {
659
916
  return propertyAssignment;
660
917
  }
661
918
  else {
662
- // Change parser to require statement.
663
- return ts.factory.createPropertyAssignment('parser', ts.factory.createCallExpression(ts.factory.createIdentifier('require'), undefined, [
664
- ts.factory.createStringLiteral(override['languageOptions']?.['parserOptions']?.parser ??
665
- override['languageOptions']?.parser ??
666
- override.parser),
667
- ]));
919
+ // Change parser to import statement.
920
+ return format === 'mjs'
921
+ ? generateESMParserImport(override)
922
+ : generateCJSParserImport(override);
668
923
  }
669
924
  },
670
925
  });
@@ -719,6 +974,20 @@ function generateFlatOverride(_override) {
719
974
  ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createParenthesizedExpression(ts.factory.createObjectLiteralExpression(objectLiteralElements, true))),
720
975
  ]));
721
976
  }
977
+ function generateESMParserImport(override) {
978
+ return ts.factory.createPropertyAssignment('parser', ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createIdentifier('import'), undefined, [
979
+ ts.factory.createStringLiteral(override['languageOptions']?.['parserOptions']?.parser ??
980
+ override['languageOptions']?.parser ??
981
+ override.parser),
982
+ ])));
983
+ }
984
+ function generateCJSParserImport(override) {
985
+ return ts.factory.createPropertyAssignment('parser', ts.factory.createCallExpression(ts.factory.createIdentifier('require'), undefined, [
986
+ ts.factory.createStringLiteral(override['languageOptions']?.['parserOptions']?.parser ??
987
+ override['languageOptions']?.parser ??
988
+ override.parser),
989
+ ]));
990
+ }
722
991
  function generateFlatPredefinedConfig(predefinedConfigName, moduleName = 'nx', spread = true) {
723
992
  const node = ts.factory.createElementAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(moduleName), ts.factory.createIdentifier('configs')), ts.factory.createStringLiteral(predefinedConfigName));
724
993
  return spread ? ts.factory.createSpreadElement(node) : node;
@@ -5,6 +5,7 @@ exports.lintWorkspaceRulesProjectGenerator = lintWorkspaceRulesProjectGenerator;
5
5
  const devkit_1 = require("@nx/devkit");
6
6
  const js_1 = require("@nx/js");
7
7
  const add_swc_dependencies_1 = require("@nx/js/src/utils/swc/add-swc-dependencies");
8
+ const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup");
8
9
  const path_1 = require("path");
9
10
  const versions_1 = require("../../utils/versions");
10
11
  const workspace_lint_rules_1 = require("../../utils/workspace-lint-rules");
@@ -47,7 +48,7 @@ async function lintWorkspaceRulesProjectGenerator(tree, options = {}) {
47
48
  supportTsx: false,
48
49
  skipSerializers: true,
49
50
  setupFile: 'none',
50
- compiler: 'tsc',
51
+ compiler: (0, ts_solution_setup_1.isUsingTsSolutionSetup)(tree) ? 'swc' : 'tsc',
51
52
  skipFormat: true,
52
53
  }));
53
54
  (0, devkit_1.updateJson)(tree, (0, path_1.join)(workspace_lint_rules_1.workspaceLintPluginDir, 'tsconfig.spec.json'), (json) => {
@@ -230,7 +230,7 @@ function getRootForDirectory(directory, roots) {
230
230
  function getProjectUsingESLintConfig(configFilePath, projectRoot, eslintVersion, options, context) {
231
231
  const rootEslintConfig = [
232
232
  config_file_1.baseEsLintConfigFile,
233
- config_file_1.baseEsLintFlatConfigFile,
233
+ ...config_file_1.BASE_ESLINT_CONFIG_FILENAMES,
234
234
  ...config_file_1.ESLINT_CONFIG_FILENAMES,
235
235
  ].find((f) => (0, node_fs_1.existsSync)((0, posix_1.join)(context.workspaceRoot, f)));
236
236
  // Add a lint target for each child project without an eslint config, with the root level config as an input
@@ -1,8 +1,9 @@
1
1
  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
+ export declare const BASE_ESLINT_CONFIG_FILENAMES: string[];
4
5
  export declare const baseEsLintConfigFile = ".eslintrc.base.json";
5
- export declare const baseEsLintFlatConfigFile = "eslint.base.config.cjs";
6
+ export declare const baseEsLintFlatConfigFile = "eslint.base.config.mjs";
6
7
  export declare const legacyBaseEsLintFlatConfigFile = "eslint.base.config.js";
7
8
  export declare function isFlatConfig(configFilePath: string): boolean;
8
9
  export declare function findFlatConfigFile(directory: string, workspaceRoot: string): string | null;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.legacyBaseEsLintFlatConfigFile = 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.BASE_ESLINT_CONFIG_FILENAMES = 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;
@@ -20,8 +20,9 @@ exports.ESLINT_CONFIG_FILENAMES = [
20
20
  ...exports.ESLINT_OLD_CONFIG_FILENAMES,
21
21
  ...exports.ESLINT_FLAT_CONFIG_FILENAMES,
22
22
  ];
23
+ exports.BASE_ESLINT_CONFIG_FILENAMES = flat_config_1.baseEslintConfigFilenames;
23
24
  exports.baseEsLintConfigFile = '.eslintrc.base.json';
24
- exports.baseEsLintFlatConfigFile = 'eslint.base.config.cjs';
25
+ exports.baseEsLintFlatConfigFile = 'eslint.base.config.mjs';
25
26
  // Make sure we can handle previous file extension as well for migrations or custom generators.
26
27
  exports.legacyBaseEsLintFlatConfigFile = 'eslint.base.config.js';
27
28
  function isFlatConfig(configFilePath) {
@@ -1,4 +1,5 @@
1
1
  import { Tree } from '@nx/devkit';
2
2
  export declare const eslintFlatConfigFilenames: string[];
3
+ export declare const baseEslintConfigFilenames: string[];
3
4
  export declare function getRootESLintFlatConfigFilename(tree: Tree): string;
4
5
  export declare function useFlatConfig(tree?: Tree): boolean;
@@ -1,13 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.eslintFlatConfigFilenames = void 0;
3
+ exports.baseEslintConfigFilenames = exports.eslintFlatConfigFilenames = void 0;
4
4
  exports.getRootESLintFlatConfigFilename = getRootESLintFlatConfigFilename;
5
5
  exports.useFlatConfig = useFlatConfig;
6
6
  const semver_1 = require("semver");
7
- // todo: add support for eslint.config.mjs,
8
7
  exports.eslintFlatConfigFilenames = [
9
8
  'eslint.config.cjs',
10
9
  'eslint.config.js',
10
+ 'eslint.config.mjs',
11
+ ];
12
+ exports.baseEslintConfigFilenames = [
13
+ 'eslint.base.js',
14
+ 'eslint.base.config.cjs',
15
+ 'eslint.base.config.js',
16
+ 'eslint.base.config.mjs',
11
17
  ];
12
18
  function getRootESLintFlatConfigFilename(tree) {
13
19
  for (const file of exports.eslintFlatConfigFilenames) {
@@ -3,6 +3,6 @@ export declare const eslintVersion = "~8.57.0";
3
3
  export declare const eslintrcVersion = "^2.1.1";
4
4
  export declare const eslintConfigPrettierVersion = "^9.0.0";
5
5
  export declare const typescriptESLintVersion = "^7.16.0";
6
- export declare const eslint9__typescriptESLintVersion = "^8.13.0";
6
+ export declare const eslint9__typescriptESLintVersion = "^8.19.0";
7
7
  export declare const eslint9__eslintVersion = "^9.8.0";
8
8
  export declare const eslintCompat = "^1.1.1";