@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.
- package/migrations.json +40 -0
- package/package.json +4 -4
- package/src/generators/convert-to-flat-config/converters/json-converter.d.ts +1 -1
- package/src/generators/convert-to-flat-config/converters/json-converter.js +6 -6
- package/src/generators/convert-to-flat-config/generator.js +17 -16
- package/src/generators/convert-to-flat-config/schema.d.ts +2 -0
- package/src/generators/convert-to-inferred/convert-to-inferred.js +1 -0
- package/src/generators/init/global-eslint-config.d.ts +1 -1
- package/src/generators/init/global-eslint-config.js +5 -5
- package/src/generators/init/init-migration.d.ts +1 -1
- package/src/generators/init/init-migration.js +15 -5
- package/src/generators/init/init.d.ts +1 -0
- package/src/generators/init/init.js +17 -6
- package/src/generators/lint-project/lint-project.d.ts +1 -0
- package/src/generators/lint-project/lint-project.js +27 -11
- package/src/generators/lint-project/setup-root-eslint.d.ts +1 -0
- package/src/generators/lint-project/setup-root-eslint.js +2 -1
- package/src/generators/utils/eslint-file.d.ts +1 -0
- package/src/generators/utils/eslint-file.js +54 -14
- package/src/generators/utils/flat-config/ast-utils.d.ts +10 -4
- package/src/generators/utils/flat-config/ast-utils.js +328 -59
- package/src/generators/workspace-rules-project/workspace-rules-project.js +2 -1
- package/src/plugins/plugin.js +1 -1
- package/src/utils/config-file.d.ts +2 -1
- package/src/utils/config-file.js +3 -2
- package/src/utils/flat-config.d.ts +1 -0
- package/src/utils/flat-config.js +8 -2
- package/src/utils/versions.d.ts +1 -1
- 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
|
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
|
-
|
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
|
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
|
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
|
-
|
155
|
-
.
|
156
|
-
|
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
|
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.
|
321
|
-
ts.
|
322
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
511
|
-
|
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
|
-
|
516
|
-
|
517
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
571
|
-
|
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
|
663
|
-
return
|
664
|
-
|
665
|
-
|
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) => {
|
package/src/plugins/plugin.js
CHANGED
@@ -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.
|
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.
|
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;
|
package/src/utils/config-file.js
CHANGED
@@ -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.
|
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;
|
package/src/utils/flat-config.js
CHANGED
@@ -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) {
|
package/src/utils/versions.d.ts
CHANGED
@@ -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.
|
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";
|