@nx/eslint 17.0.0-beta.8

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