@nx/eslint 20.4.0-beta.0 → 20.4.0-beta.2
Sign up to get free protection for your applications and to get access to all the features.
- 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";
|