@nx/eslint 0.0.0-pr-22179-271588f

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 (86) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +66 -0
  3. package/executors.json +10 -0
  4. package/generators.json +28 -0
  5. package/index.d.ts +4 -0
  6. package/index.js +14 -0
  7. package/migrations.json +138 -0
  8. package/package.json +52 -0
  9. package/plugin.d.ts +1 -0
  10. package/plugin.js +5 -0
  11. package/src/executors/lint/hasher.d.ts +9 -0
  12. package/src/executors/lint/hasher.js +43 -0
  13. package/src/executors/lint/lint.impl.d.ts +5 -0
  14. package/src/executors/lint/lint.impl.js +174 -0
  15. package/src/executors/lint/schema.d.ts +40 -0
  16. package/src/executors/lint/schema.json +148 -0
  17. package/src/executors/lint/utility/eslint-utils.d.ts +6 -0
  18. package/src/executors/lint/utility/eslint-utils.js +75 -0
  19. package/src/generators/convert-to-flat-config/converters/json-converter.d.ts +11 -0
  20. package/src/generators/convert-to-flat-config/converters/json-converter.js +175 -0
  21. package/src/generators/convert-to-flat-config/generator.d.ts +4 -0
  22. package/src/generators/convert-to-flat-config/generator.js +139 -0
  23. package/src/generators/convert-to-flat-config/schema.d.ts +3 -0
  24. package/src/generators/convert-to-flat-config/schema.json +17 -0
  25. package/src/generators/init/global-eslint-config.d.ts +29 -0
  26. package/src/generators/init/global-eslint-config.js +102 -0
  27. package/src/generators/init/init-migration.d.ts +3 -0
  28. package/src/generators/init/init-migration.js +137 -0
  29. package/src/generators/init/init.d.ts +9 -0
  30. package/src/generators/init/init.js +93 -0
  31. package/src/generators/init/schema.json +28 -0
  32. package/src/generators/lint-project/lint-project.d.ts +22 -0
  33. package/src/generators/lint-project/lint-project.js +227 -0
  34. package/src/generators/lint-project/setup-root-eslint.d.ts +7 -0
  35. package/src/generators/lint-project/setup-root-eslint.js +33 -0
  36. package/src/generators/utils/eslint-file.d.ts +16 -0
  37. package/src/generators/utils/eslint-file.js +287 -0
  38. package/src/generators/utils/eslint-targets.d.ts +2 -0
  39. package/src/generators/utils/eslint-targets.js +18 -0
  40. package/src/generators/utils/flat-config/ast-utils.d.ts +60 -0
  41. package/src/generators/utils/flat-config/ast-utils.js +573 -0
  42. package/src/generators/utils/flat-config/path-utils.d.ts +3 -0
  43. package/src/generators/utils/flat-config/path-utils.js +28 -0
  44. package/src/generators/utils/linter.d.ts +4 -0
  45. package/src/generators/utils/linter.js +8 -0
  46. package/src/generators/utils/plugin.d.ts +2 -0
  47. package/src/generators/utils/plugin.js +11 -0
  48. package/src/generators/workspace-rule/files/__name__.spec.ts__tmpl__ +11 -0
  49. package/src/generators/workspace-rule/files/__name__.ts__tmpl__ +37 -0
  50. package/src/generators/workspace-rule/schema.json +26 -0
  51. package/src/generators/workspace-rule/workspace-rule.d.ts +6 -0
  52. package/src/generators/workspace-rule/workspace-rule.js +78 -0
  53. package/src/generators/workspace-rules-project/files/index.ts__tmpl__ +27 -0
  54. package/src/generators/workspace-rules-project/files/tsconfig.json__tmpl__ +14 -0
  55. package/src/generators/workspace-rules-project/files/tsconfig.lint.json__tmpl__ +9 -0
  56. package/src/generators/workspace-rules-project/schema.json +23 -0
  57. package/src/generators/workspace-rules-project/workspace-rules-project.d.ts +8 -0
  58. package/src/generators/workspace-rules-project/workspace-rules-project.js +82 -0
  59. package/src/migrations/update-15-0-0/add-eslint-inputs.d.ts +2 -0
  60. package/src/migrations/update-15-0-0/add-eslint-inputs.js +27 -0
  61. package/src/migrations/update-15-7-1/add-eslint-ignore.d.ts +2 -0
  62. package/src/migrations/update-15-7-1/add-eslint-ignore.js +36 -0
  63. package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.d.ts +2 -0
  64. package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.js +9 -0
  65. package/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.d.ts +2 -0
  66. package/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.js +44 -0
  67. package/src/migrations/update-17-0-0-rename-to-eslint/update-17-0-0-rename-to-eslint.d.ts +2 -0
  68. package/src/migrations/update-17-0-0-rename-to-eslint/update-17-0-0-rename-to-eslint.js +47 -0
  69. package/src/migrations/update-17-1-0/update-typescript-eslint.d.ts +2 -0
  70. package/src/migrations/update-17-1-0/update-typescript-eslint.js +74 -0
  71. package/src/migrations/update-17-2-0/simplify-eslint-patterns.d.ts +2 -0
  72. package/src/migrations/update-17-2-0/simplify-eslint-patterns.js +46 -0
  73. package/src/migrations/update-17-2-9/move-options-to-target-defaults.d.ts +2 -0
  74. package/src/migrations/update-17-2-9/move-options-to-target-defaults.js +107 -0
  75. package/src/plugins/plugin.d.ts +5 -0
  76. package/src/plugins/plugin.js +92 -0
  77. package/src/utils/config-file.d.ts +5 -0
  78. package/src/utils/config-file.js +35 -0
  79. package/src/utils/flat-config.d.ts +2 -0
  80. package/src/utils/flat-config.js +7 -0
  81. package/src/utils/rules-requiring-type-checking.d.ts +3 -0
  82. package/src/utils/rules-requiring-type-checking.js +84 -0
  83. package/src/utils/versions.d.ts +5 -0
  84. package/src/utils/versions.js +8 -0
  85. package/src/utils/workspace-lint-rules.d.ts +1 -0
  86. package/src/utils/workspace-lint-rules.js +5 -0
@@ -0,0 +1,573 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateAst = exports.mapFilePaths = exports.generateFlatOverride = exports.generateRequire = exports.stringifyNodeList = exports.generatePluginExtendsElement = exports.generateSpreadElement = exports.createNodeList = exports.addCompatToFlatConfig = exports.addPluginsToExportsBlock = exports.removeCompatExtends = exports.removePlugin = exports.addBlockToFlatConfigExport = exports.addImportToFlatConfig = exports.replaceOverride = exports.hasOverride = exports.removeOverridesFromLintConfig = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const ts = require("typescript");
6
+ const path_utils_1 = require("./path-utils");
7
+ /**
8
+ * Remove all overrides from the config file
9
+ */
10
+ function removeOverridesFromLintConfig(content) {
11
+ const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
12
+ const exportsArray = findAllBlocks(source);
13
+ if (!exportsArray) {
14
+ return content;
15
+ }
16
+ const changes = [];
17
+ exportsArray.forEach((node, i) => {
18
+ if (isOverride(node)) {
19
+ const commaOffset = i < exportsArray.length - 1 || exportsArray.hasTrailingComma ? 1 : 0;
20
+ changes.push({
21
+ type: devkit_1.ChangeType.Delete,
22
+ start: node.pos,
23
+ length: node.end - node.pos + commaOffset,
24
+ });
25
+ }
26
+ });
27
+ return (0, devkit_1.applyChangesToString)(content, changes);
28
+ }
29
+ exports.removeOverridesFromLintConfig = removeOverridesFromLintConfig;
30
+ function findAllBlocks(source) {
31
+ return ts.forEachChild(source, function analyze(node) {
32
+ if (ts.isExpressionStatement(node) &&
33
+ ts.isBinaryExpression(node.expression) &&
34
+ node.expression.left.getText() === 'module.exports' &&
35
+ ts.isArrayLiteralExpression(node.expression.right)) {
36
+ return node.expression.right.elements;
37
+ }
38
+ });
39
+ }
40
+ function isOverride(node) {
41
+ return ((ts.isObjectLiteralExpression(node) &&
42
+ node.properties.some((p) => p.name.getText() === 'files')) ||
43
+ // detect ...compat.config(...).map(...)
44
+ (ts.isSpreadElement(node) &&
45
+ ts.isCallExpression(node.expression) &&
46
+ ts.isPropertyAccessExpression(node.expression.expression) &&
47
+ ts.isArrowFunction(node.expression.arguments[0]) &&
48
+ ts.isParenthesizedExpression(node.expression.arguments[0].body)));
49
+ }
50
+ function hasOverride(content, lookup) {
51
+ const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
52
+ const exportsArray = findAllBlocks(source);
53
+ if (!exportsArray) {
54
+ return false;
55
+ }
56
+ for (const node of exportsArray) {
57
+ if (isOverride(node)) {
58
+ let objSource;
59
+ if (ts.isObjectLiteralExpression(node)) {
60
+ objSource = node.getFullText();
61
+ }
62
+ else {
63
+ const fullNodeText = node['expression'].arguments[0].body.expression.getFullText();
64
+ // strip any spread elements
65
+ objSource = fullNodeText.replace(/\s*\.\.\.[a-zA-Z0-9_]+,?\n?/, '');
66
+ }
67
+ const data = (0, devkit_1.parseJson)(objSource
68
+ // ensure property names have double quotes so that JSON.parse works
69
+ .replace(/'/g, '"')
70
+ .replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": '));
71
+ if (lookup(data)) {
72
+ return true;
73
+ }
74
+ }
75
+ }
76
+ return false;
77
+ }
78
+ exports.hasOverride = hasOverride;
79
+ const STRIP_SPREAD_ELEMENTS = /\s*\.\.\.[a-zA-Z0-9_]+,?\n?/g;
80
+ function parseTextToJson(text) {
81
+ return (0, devkit_1.parseJson)(text
82
+ // ensure property names have double quotes so that JSON.parse works
83
+ .replace(/'/g, '"')
84
+ .replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": '));
85
+ }
86
+ /**
87
+ * Finds an override matching the lookup function and applies the update function to it
88
+ */
89
+ function replaceOverride(content, root, lookup, update) {
90
+ const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
91
+ const exportsArray = findAllBlocks(source);
92
+ if (!exportsArray) {
93
+ return content;
94
+ }
95
+ const changes = [];
96
+ exportsArray.forEach((node) => {
97
+ if (isOverride(node)) {
98
+ let objSource;
99
+ let start, end;
100
+ if (ts.isObjectLiteralExpression(node)) {
101
+ objSource = node.getFullText();
102
+ start = node.properties.pos + 1; // keep leading line break
103
+ end = node.properties.end;
104
+ }
105
+ else {
106
+ const fullNodeText = node['expression'].arguments[0].body.expression.getFullText();
107
+ // strip any spread elements
108
+ objSource = fullNodeText.replace(STRIP_SPREAD_ELEMENTS, '');
109
+ start =
110
+ node['expression'].arguments[0].body.expression.properties.pos +
111
+ (fullNodeText.length - objSource.length);
112
+ end = node['expression'].arguments[0].body.expression.properties.end;
113
+ }
114
+ const data = parseTextToJson(objSource);
115
+ if (lookup(data)) {
116
+ changes.push({
117
+ type: devkit_1.ChangeType.Delete,
118
+ start,
119
+ length: end - start,
120
+ });
121
+ const updatedData = update(data);
122
+ if (updatedData) {
123
+ mapFilePaths(updatedData);
124
+ changes.push({
125
+ type: devkit_1.ChangeType.Insert,
126
+ index: start,
127
+ text: JSON.stringify(updatedData, null, 2).slice(2, -2), // remove curly braces and start/end line breaks since we are injecting just properties
128
+ });
129
+ }
130
+ }
131
+ }
132
+ });
133
+ return (0, devkit_1.applyChangesToString)(content, changes);
134
+ }
135
+ exports.replaceOverride = replaceOverride;
136
+ /**
137
+ * Adding require statement to the top of the file
138
+ */
139
+ function addImportToFlatConfig(content, variable, imp) {
140
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
141
+ const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
142
+ const foundBindingVars = ts.forEachChild(source, function analyze(node) {
143
+ // we can only combine object binding patterns
144
+ if (!Array.isArray(variable)) {
145
+ return;
146
+ }
147
+ if (ts.isVariableStatement(node) &&
148
+ ts.isVariableDeclaration(node.declarationList.declarations[0]) &&
149
+ ts.isObjectBindingPattern(node.declarationList.declarations[0].name) &&
150
+ ts.isCallExpression(node.declarationList.declarations[0].initializer) &&
151
+ node.declarationList.declarations[0].initializer.expression.getText() ===
152
+ 'require' &&
153
+ ts.isStringLiteral(node.declarationList.declarations[0].initializer.arguments[0]) &&
154
+ node.declarationList.declarations[0].initializer.arguments[0].text ===
155
+ imp) {
156
+ return node.declarationList.declarations[0].name.elements;
157
+ }
158
+ });
159
+ if (foundBindingVars && Array.isArray(variable)) {
160
+ const newVariables = variable.filter((v) => !foundBindingVars.some((fv) => v === fv.name.getText()));
161
+ if (newVariables.length === 0) {
162
+ return content;
163
+ }
164
+ const isMultiLine = foundBindingVars.hasTrailingComma;
165
+ const pos = foundBindingVars.end;
166
+ const nodes = ts.factory.createNodeArray(newVariables.map((v) => ts.factory.createBindingElement(undefined, undefined, v)));
167
+ const insert = printer.printList(ts.ListFormat.ObjectBindingPatternElements, nodes, source);
168
+ return (0, devkit_1.applyChangesToString)(content, [
169
+ {
170
+ type: devkit_1.ChangeType.Insert,
171
+ index: pos,
172
+ text: isMultiLine ? `,\n${insert}` : `,${insert}`,
173
+ },
174
+ ]);
175
+ }
176
+ const hasSameIdentifierVar = ts.forEachChild(source, function analyze(node) {
177
+ // we are searching for a single variable
178
+ if (Array.isArray(variable)) {
179
+ return;
180
+ }
181
+ if (ts.isVariableStatement(node) &&
182
+ ts.isVariableDeclaration(node.declarationList.declarations[0]) &&
183
+ ts.isIdentifier(node.declarationList.declarations[0].name) &&
184
+ node.declarationList.declarations[0].name.getText() === variable &&
185
+ ts.isCallExpression(node.declarationList.declarations[0].initializer) &&
186
+ node.declarationList.declarations[0].initializer.expression.getText() ===
187
+ 'require' &&
188
+ ts.isStringLiteral(node.declarationList.declarations[0].initializer.arguments[0]) &&
189
+ node.declarationList.declarations[0].initializer.arguments[0].text ===
190
+ imp) {
191
+ return true;
192
+ }
193
+ });
194
+ if (hasSameIdentifierVar) {
195
+ return content;
196
+ }
197
+ // the import was not found, create a new one
198
+ const requireStatement = generateRequire(typeof variable === 'string'
199
+ ? variable
200
+ : ts.factory.createObjectBindingPattern(variable.map((v) => ts.factory.createBindingElement(undefined, undefined, v))), imp);
201
+ const insert = printer.printNode(ts.EmitHint.Unspecified, requireStatement, source);
202
+ return (0, devkit_1.applyChangesToString)(content, [
203
+ {
204
+ type: devkit_1.ChangeType.Insert,
205
+ index: 0,
206
+ text: `${insert}\n`,
207
+ },
208
+ ]);
209
+ }
210
+ exports.addImportToFlatConfig = addImportToFlatConfig;
211
+ /**
212
+ * Injects new ts.expression to the end of the module.exports array.
213
+ */
214
+ function addBlockToFlatConfigExport(content, config, options = {
215
+ insertAtTheEnd: true,
216
+ }) {
217
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
218
+ const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
219
+ const exportsArray = ts.forEachChild(source, function analyze(node) {
220
+ if (ts.isExpressionStatement(node) &&
221
+ ts.isBinaryExpression(node.expression) &&
222
+ node.expression.left.getText() === 'module.exports' &&
223
+ ts.isArrayLiteralExpression(node.expression.right)) {
224
+ return node.expression.right.elements;
225
+ }
226
+ });
227
+ const insert = printer.printNode(ts.EmitHint.Expression, config, source);
228
+ if (options.insertAtTheEnd) {
229
+ const index = exportsArray.length > 0
230
+ ? exportsArray.at(exportsArray.length - 1).end
231
+ : exportsArray.pos;
232
+ return (0, devkit_1.applyChangesToString)(content, [
233
+ {
234
+ type: devkit_1.ChangeType.Insert,
235
+ index,
236
+ text: `,\n${insert}`,
237
+ },
238
+ ]);
239
+ }
240
+ else {
241
+ const index = exportsArray.length > 0 ? exportsArray.at(0).pos : exportsArray.pos;
242
+ return (0, devkit_1.applyChangesToString)(content, [
243
+ {
244
+ type: devkit_1.ChangeType.Insert,
245
+ index,
246
+ text: `\n${insert},`,
247
+ },
248
+ ]);
249
+ }
250
+ }
251
+ exports.addBlockToFlatConfigExport = addBlockToFlatConfigExport;
252
+ function removePlugin(content, pluginName, pluginImport) {
253
+ const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
254
+ const changes = [];
255
+ ts.forEachChild(source, function analyze(node) {
256
+ if (ts.isVariableStatement(node) &&
257
+ ts.isVariableDeclaration(node.declarationList.declarations[0]) &&
258
+ ts.isCallExpression(node.declarationList.declarations[0].initializer) &&
259
+ node.declarationList.declarations[0].initializer.arguments.length &&
260
+ ts.isStringLiteral(node.declarationList.declarations[0].initializer.arguments[0]) &&
261
+ node.declarationList.declarations[0].initializer.arguments[0].text ===
262
+ pluginImport) {
263
+ changes.push({
264
+ type: devkit_1.ChangeType.Delete,
265
+ start: node.pos,
266
+ length: node.end - node.pos,
267
+ });
268
+ }
269
+ });
270
+ ts.forEachChild(source, function analyze(node) {
271
+ if (ts.isExpressionStatement(node) &&
272
+ ts.isBinaryExpression(node.expression) &&
273
+ node.expression.left.getText() === 'module.exports' &&
274
+ ts.isArrayLiteralExpression(node.expression.right)) {
275
+ const blockElements = node.expression.right.elements;
276
+ blockElements.forEach((element) => {
277
+ if (ts.isObjectLiteralExpression(element)) {
278
+ const pluginsElem = element.properties.find((prop) => prop.name?.getText() === 'plugins');
279
+ if (!pluginsElem) {
280
+ return;
281
+ }
282
+ if (ts.isArrayLiteralExpression(pluginsElem.initializer)) {
283
+ const pluginsArray = pluginsElem.initializer;
284
+ const plugins = parseTextToJson(pluginsElem.initializer
285
+ .getText()
286
+ .replace(STRIP_SPREAD_ELEMENTS, ''));
287
+ if (plugins.length > 1) {
288
+ changes.push({
289
+ type: devkit_1.ChangeType.Delete,
290
+ start: pluginsArray.pos,
291
+ length: pluginsArray.end - pluginsArray.pos,
292
+ });
293
+ changes.push({
294
+ type: devkit_1.ChangeType.Insert,
295
+ index: pluginsArray.pos,
296
+ text: JSON.stringify(plugins.filter((p) => p !== pluginName)),
297
+ });
298
+ }
299
+ else {
300
+ const keys = element.properties.map((prop) => prop.name?.getText());
301
+ if (keys.length > 1) {
302
+ const removeComma = keys.indexOf('plugins') < keys.length - 1 ||
303
+ element.properties.hasTrailingComma;
304
+ changes.push({
305
+ type: devkit_1.ChangeType.Delete,
306
+ start: pluginsElem.pos + (removeComma ? 1 : 0),
307
+ length: pluginsElem.end - pluginsElem.pos + (removeComma ? 1 : 0),
308
+ });
309
+ }
310
+ else {
311
+ const removeComma = blockElements.indexOf(element) < blockElements.length - 1 ||
312
+ blockElements.hasTrailingComma;
313
+ changes.push({
314
+ type: devkit_1.ChangeType.Delete,
315
+ start: element.pos + (removeComma ? 1 : 0),
316
+ length: element.end - element.pos + (removeComma ? 1 : 0),
317
+ });
318
+ }
319
+ }
320
+ }
321
+ else if (ts.isObjectLiteralExpression(pluginsElem.initializer)) {
322
+ const pluginsObj = pluginsElem.initializer;
323
+ if (pluginsElem.initializer.properties.length > 1) {
324
+ const plugin = pluginsObj.properties.find((prop) => prop.name?.['text'] === pluginName);
325
+ const removeComma = pluginsObj.properties.indexOf(plugin) <
326
+ pluginsObj.properties.length - 1 ||
327
+ pluginsObj.properties.hasTrailingComma;
328
+ changes.push({
329
+ type: devkit_1.ChangeType.Delete,
330
+ start: plugin.pos + (removeComma ? 1 : 0),
331
+ length: plugin.end - plugin.pos + (removeComma ? 1 : 0),
332
+ });
333
+ }
334
+ else {
335
+ const keys = element.properties.map((prop) => prop.name?.getText());
336
+ if (keys.length > 1) {
337
+ const removeComma = keys.indexOf('plugins') < keys.length - 1 ||
338
+ element.properties.hasTrailingComma;
339
+ changes.push({
340
+ type: devkit_1.ChangeType.Delete,
341
+ start: pluginsElem.pos + (removeComma ? 1 : 0),
342
+ length: pluginsElem.end - pluginsElem.pos + (removeComma ? 1 : 0),
343
+ });
344
+ }
345
+ else {
346
+ const removeComma = blockElements.indexOf(element) < blockElements.length - 1 ||
347
+ blockElements.hasTrailingComma;
348
+ changes.push({
349
+ type: devkit_1.ChangeType.Delete,
350
+ start: element.pos + (removeComma ? 1 : 0),
351
+ length: element.end - element.pos + (removeComma ? 1 : 0),
352
+ });
353
+ }
354
+ }
355
+ }
356
+ }
357
+ });
358
+ }
359
+ });
360
+ return (0, devkit_1.applyChangesToString)(content, changes);
361
+ }
362
+ exports.removePlugin = removePlugin;
363
+ function removeCompatExtends(content, compatExtends) {
364
+ const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
365
+ const changes = [];
366
+ findAllBlocks(source).forEach((node) => {
367
+ if (ts.isSpreadElement(node) &&
368
+ ts.isCallExpression(node.expression) &&
369
+ ts.isArrowFunction(node.expression.arguments[0]) &&
370
+ ts.isParenthesizedExpression(node.expression.arguments[0].body) &&
371
+ ts.isPropertyAccessExpression(node.expression.expression) &&
372
+ ts.isCallExpression(node.expression.expression.expression)) {
373
+ const callExp = node.expression.expression.expression;
374
+ if (((callExp.expression.getText() === 'compat.config' &&
375
+ callExp.arguments[0].getText().includes('extends')) ||
376
+ callExp.expression.getText() === 'compat.extends') &&
377
+ compatExtends.some((ext) => callExp.arguments[0].getText().includes(ext))) {
378
+ // remove the whole node
379
+ changes.push({
380
+ type: devkit_1.ChangeType.Delete,
381
+ start: node.pos,
382
+ length: node.end - node.pos,
383
+ });
384
+ // and replace it with new one
385
+ const paramName = node.expression.arguments[0].parameters[0].name.getText();
386
+ const body = node.expression.arguments[0].body.expression.getFullText();
387
+ changes.push({
388
+ type: devkit_1.ChangeType.Insert,
389
+ index: node.pos,
390
+ text: '\n' +
391
+ body.replace(new RegExp('[ \t]s*...' + paramName + '[ \t]*,?\\s*', 'g'), ''),
392
+ });
393
+ }
394
+ }
395
+ });
396
+ return (0, devkit_1.applyChangesToString)(content, changes);
397
+ }
398
+ exports.removeCompatExtends = removeCompatExtends;
399
+ /**
400
+ * Add plugins block to the top of the export blocks
401
+ */
402
+ function addPluginsToExportsBlock(content, plugins) {
403
+ const pluginsBlock = ts.factory.createObjectLiteralExpression([
404
+ ts.factory.createPropertyAssignment('plugins', ts.factory.createObjectLiteralExpression(plugins.map(({ name, varName }) => {
405
+ return ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(name), ts.factory.createIdentifier(varName));
406
+ }))),
407
+ ], false);
408
+ return addBlockToFlatConfigExport(content, pluginsBlock, {
409
+ insertAtTheEnd: false,
410
+ });
411
+ }
412
+ exports.addPluginsToExportsBlock = addPluginsToExportsBlock;
413
+ /**
414
+ * Adds compat if missing to flat config
415
+ */
416
+ function addCompatToFlatConfig(content) {
417
+ let result = content;
418
+ result = addImportToFlatConfig(result, 'js', '@eslint/js');
419
+ if (result.includes('const compat = new FlatCompat')) {
420
+ return result;
421
+ }
422
+ result = addImportToFlatConfig(result, 'FlatCompat', '@eslint/eslintrc');
423
+ const index = result.indexOf('module.exports');
424
+ return (0, devkit_1.applyChangesToString)(result, [
425
+ {
426
+ type: devkit_1.ChangeType.Insert,
427
+ index: index - 1,
428
+ text: `${DEFAULT_FLAT_CONFIG}\n`,
429
+ },
430
+ ]);
431
+ }
432
+ exports.addCompatToFlatConfig = addCompatToFlatConfig;
433
+ const DEFAULT_FLAT_CONFIG = `
434
+ const compat = new FlatCompat({
435
+ baseDirectory: __dirname,
436
+ recommendedConfig: js.configs.recommended,
437
+ });
438
+ `;
439
+ /**
440
+ * Generate node list representing the imports and the exports blocks
441
+ * Optionally add flat compat initialization
442
+ */
443
+ function createNodeList(importsMap, exportElements, isFlatCompatNeeded) {
444
+ const importsList = [];
445
+ if (isFlatCompatNeeded) {
446
+ importsMap.set('@eslint/js', 'js');
447
+ importsList.push(generateRequire(ts.factory.createObjectBindingPattern([
448
+ ts.factory.createBindingElement(undefined, undefined, 'FlatCompat'),
449
+ ]), '@eslint/eslintrc'));
450
+ }
451
+ // generateRequire(varName, imp, ts.factory);
452
+ Array.from(importsMap.entries()).forEach(([imp, varName]) => {
453
+ importsList.push(generateRequire(varName, imp));
454
+ });
455
+ return ts.factory.createNodeArray([
456
+ // add plugin imports
457
+ ...importsList,
458
+ ts.createSourceFile('', isFlatCompatNeeded ? DEFAULT_FLAT_CONFIG : '', ts.ScriptTarget.Latest, false, ts.ScriptKind.JS),
459
+ // creates:
460
+ // module.exports = [ ... ];
461
+ 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))),
462
+ ]);
463
+ }
464
+ exports.createNodeList = createNodeList;
465
+ function generateSpreadElement(name) {
466
+ return ts.factory.createSpreadElement(ts.factory.createIdentifier(name));
467
+ }
468
+ exports.generateSpreadElement = generateSpreadElement;
469
+ function generatePluginExtendsElement(plugins) {
470
+ return ts.factory.createSpreadElement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('compat'), ts.factory.createIdentifier('extends')), undefined, plugins.map((plugin) => ts.factory.createStringLiteral(plugin))));
471
+ }
472
+ exports.generatePluginExtendsElement = generatePluginExtendsElement;
473
+ /**
474
+ * Stringifies TS nodes to file content string
475
+ */
476
+ function stringifyNodeList(nodes) {
477
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
478
+ const resultFile = ts.createSourceFile('', '', ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
479
+ return (printer
480
+ .printList(ts.ListFormat.MultiLine, nodes, resultFile)
481
+ // add new line before compat initialization
482
+ .replace(/const compat = new FlatCompat/, '\nconst compat = new FlatCompat')
483
+ // add new line before module.exports = ...
484
+ .replace(/module\.exports/, '\nmodule.exports'));
485
+ }
486
+ exports.stringifyNodeList = stringifyNodeList;
487
+ /**
488
+ * generates AST require statement
489
+ */
490
+ function generateRequire(variableName, imp) {
491
+ return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
492
+ ts.factory.createVariableDeclaration(variableName, undefined, undefined, ts.factory.createCallExpression(ts.factory.createIdentifier('require'), undefined, [ts.factory.createStringLiteral(imp)])),
493
+ ], ts.NodeFlags.Const));
494
+ }
495
+ exports.generateRequire = generateRequire;
496
+ /**
497
+ * Generates AST object or spread element based on JSON override object
498
+ */
499
+ function generateFlatOverride(override) {
500
+ mapFilePaths(override);
501
+ if (!override.env &&
502
+ !override.extends &&
503
+ !override.plugins &&
504
+ !override.parser) {
505
+ return generateAst(override);
506
+ }
507
+ const { files, excludedFiles, rules, ...rest } = override;
508
+ const objectLiteralElements = [
509
+ ts.factory.createSpreadAssignment(ts.factory.createIdentifier('config')),
510
+ ];
511
+ addTSObjectProperty(objectLiteralElements, 'files', files);
512
+ addTSObjectProperty(objectLiteralElements, 'excludedFiles', excludedFiles);
513
+ addTSObjectProperty(objectLiteralElements, 'rules', rules);
514
+ return ts.factory.createSpreadElement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('compat'), ts.factory.createIdentifier('config')), undefined, [generateAst(rest)]), ts.factory.createIdentifier('map')), undefined, [
515
+ ts.factory.createArrowFunction(undefined, undefined, [
516
+ ts.factory.createParameterDeclaration(undefined, undefined, 'config'),
517
+ ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createParenthesizedExpression(ts.factory.createObjectLiteralExpression(objectLiteralElements, true))),
518
+ ]));
519
+ }
520
+ exports.generateFlatOverride = generateFlatOverride;
521
+ function mapFilePaths(override) {
522
+ if (override.files) {
523
+ override.files = Array.isArray(override.files)
524
+ ? override.files
525
+ : [override.files];
526
+ override.files = override.files.map((file) => (0, path_utils_1.mapFilePath)(file));
527
+ }
528
+ if (override.excludedFiles) {
529
+ override.excludedFiles = Array.isArray(override.excludedFiles)
530
+ ? override.excludedFiles
531
+ : [override.excludedFiles];
532
+ override.excludedFiles = override.excludedFiles.map((file) => (0, path_utils_1.mapFilePath)(file));
533
+ }
534
+ }
535
+ exports.mapFilePaths = mapFilePaths;
536
+ function addTSObjectProperty(elements, key, value) {
537
+ if (value) {
538
+ elements.push(ts.factory.createPropertyAssignment(key, generateAst(value)));
539
+ }
540
+ }
541
+ /**
542
+ * Generates an AST from a JSON-type input
543
+ */
544
+ function generateAst(input) {
545
+ if (Array.isArray(input)) {
546
+ return ts.factory.createArrayLiteralExpression(input.map((item) => generateAst(item)), input.length > 1 // multiline only if more than one item
547
+ );
548
+ }
549
+ if (input === null) {
550
+ return ts.factory.createNull();
551
+ }
552
+ if (typeof input === 'object') {
553
+ return ts.factory.createObjectLiteralExpression(Object.entries(input)
554
+ .filter(([_, value]) => value !== undefined)
555
+ .map(([key, value]) => ts.factory.createPropertyAssignment(isValidKey(key) ? key : ts.factory.createStringLiteral(key), generateAst(value))), Object.keys(input).length > 1 // multiline only if more than one property
556
+ );
557
+ }
558
+ if (typeof input === 'string') {
559
+ return ts.factory.createStringLiteral(input);
560
+ }
561
+ if (typeof input === 'number') {
562
+ return ts.factory.createNumericLiteral(input);
563
+ }
564
+ if (typeof input === 'boolean') {
565
+ return (input ? ts.factory.createTrue() : ts.factory.createFalse());
566
+ }
567
+ // since we are parsing JSON, this should never happen
568
+ throw new Error(`Unknown type: ${typeof input} `);
569
+ }
570
+ exports.generateAst = generateAst;
571
+ function isValidKey(key) {
572
+ return /^[a-zA-Z0-9_]+$/.test(key);
573
+ }
@@ -0,0 +1,3 @@
1
+ import type { Linter } from 'eslint';
2
+ export declare function updateFiles(override: Linter.ConfigOverride<Linter.RulesRecord>): Linter.ConfigOverride<Linter.RulesRecord>;
3
+ export declare function mapFilePath(filePath: string): string;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapFilePath = exports.updateFiles = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ function updateFiles(override) {
6
+ if (override.files) {
7
+ override.files = Array.isArray(override.files)
8
+ ? override.files
9
+ : [override.files];
10
+ override.files = override.files.map((file) => mapFilePath(file));
11
+ }
12
+ return override;
13
+ }
14
+ exports.updateFiles = updateFiles;
15
+ function mapFilePath(filePath) {
16
+ if (filePath.startsWith('!')) {
17
+ const fileWithoutBang = filePath.slice(1);
18
+ if (fileWithoutBang.startsWith('*.')) {
19
+ return `!${(0, devkit_1.joinPathFragments)('**', fileWithoutBang)}`;
20
+ }
21
+ return filePath;
22
+ }
23
+ if (filePath.startsWith('*.')) {
24
+ return (0, devkit_1.joinPathFragments)('**', filePath);
25
+ }
26
+ return filePath;
27
+ }
28
+ exports.mapFilePath = mapFilePath;
@@ -0,0 +1,4 @@
1
+ export declare enum Linter {
2
+ EsLint = "eslint",
3
+ None = "none"
4
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Linter = void 0;
4
+ var Linter;
5
+ (function (Linter) {
6
+ Linter["EsLint"] = "eslint";
7
+ Linter["None"] = "none";
8
+ })(Linter || (exports.Linter = Linter = {}));
@@ -0,0 +1,2 @@
1
+ import { Tree } from '@nx/devkit';
2
+ export declare function hasEslintPlugin(tree: Tree): boolean;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasEslintPlugin = void 0;
4
+ const devkit_1 = require("@nx/devkit");
5
+ function hasEslintPlugin(tree) {
6
+ const nxJson = (0, devkit_1.readNxJson)(tree);
7
+ return nxJson.plugins?.some((p) => typeof p === 'string'
8
+ ? p === '@nx/eslint/plugin'
9
+ : p.plugin === '@nx/eslint/plugin');
10
+ }
11
+ exports.hasEslintPlugin = hasEslintPlugin;
@@ -0,0 +1,11 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ import { rule, RULE_NAME } from './<%= name %>';
3
+
4
+ const ruleTester = new TSESLint.RuleTester({
5
+ parser: require.resolve('@typescript-eslint/parser'),
6
+ });
7
+
8
+ ruleTester.run(RULE_NAME, rule, {
9
+ valid: [`const example = true;`],
10
+ invalid: [],
11
+ });