@rotki/eslint-plugin 0.7.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
1
2
  import createDebug from 'debug';
2
- import { extname } from 'node:path';
3
3
  import * as compat from 'eslint-compat-utils';
4
+ import { extname } from 'node:path';
4
5
  import { pascalCase, kebabCase } from 'scule';
5
- import { TSESTree } from '@typescript-eslint/utils';
6
6
 
7
- const version = "0.7.0";
7
+ const version = "1.1.0";
8
8
  const pkg = {
9
9
  version: version};
10
10
 
@@ -15,6 +15,63 @@ function getSourceCode(context) {
15
15
  return compat.getSourceCode(context);
16
16
  }
17
17
 
18
+ function createRecommended(plugin, name, flat) {
19
+ const rules = Object.fromEntries(Object.entries(plugin.rules).filter(([_key, rule]) => rule.meta.recommended === "recommended" && !rule.meta.deprecated).map(([key]) => [`${name}/${key}`, 2]));
20
+ if (flat) {
21
+ return {
22
+ plugins: {
23
+ [name]: plugin
24
+ },
25
+ rules
26
+ };
27
+ } else {
28
+ return {
29
+ plugins: [name],
30
+ rules
31
+ };
32
+ }
33
+ }
34
+
35
+ function getStringLiteralValue(node, stringOnly = false) {
36
+ if (node.type === "Literal") {
37
+ if (node.value == null) {
38
+ if (!stringOnly && node.bigint != null)
39
+ return node.bigint;
40
+ return null;
41
+ }
42
+ if (typeof node.value === "string")
43
+ return node.value;
44
+ if (!stringOnly)
45
+ return String(node.value);
46
+ return null;
47
+ }
48
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
49
+ return node.quasis[0].value.cooked;
50
+ }
51
+ return null;
52
+ }
53
+ function getStaticPropertyName(node) {
54
+ if (node.type === "Property" || node.type === "MethodDefinition") {
55
+ if (!node.computed) {
56
+ const key2 = node.key;
57
+ if (key2.type === "Identifier")
58
+ return key2.name;
59
+ }
60
+ const key = node.key;
61
+ return getStringLiteralValue(key);
62
+ } else if (node.type === "MemberExpression") {
63
+ if (!node.computed) {
64
+ const property2 = node.property;
65
+ if (property2.type === "Identifier")
66
+ return property2.name;
67
+ return null;
68
+ }
69
+ const property = node.property;
70
+ return getStringLiteralValue(property);
71
+ }
72
+ return null;
73
+ }
74
+
18
75
  const blobUrl = "https://rotki.github.io/eslint-plugin/rules/";
19
76
  function RuleCreator(urlCreator) {
20
77
  return function createNamedRule({
@@ -77,65 +134,170 @@ function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor,
77
134
  );
78
135
  }
79
136
 
80
- function getStringLiteralValue(node, stringOnly = false) {
81
- if (node.type === "Literal") {
82
- if (node.value == null) {
83
- if (!stringOnly && node.bigint != null)
84
- return node.bigint;
85
- return null;
86
- }
87
- if (typeof node.value === "string")
88
- return node.value;
89
- if (!stringOnly)
90
- return String(node.value);
91
- return null;
92
- }
93
- if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
94
- return node.quasis[0].value.cooked;
137
+ const debug$3 = createDebug("@rotki/eslint-plugin:consistent-ref-type-annotation");
138
+ const RULE_NAME$6 = "consistent-ref-type-annotation";
139
+ const FIXABLE_METHODS = ["ref", "computed"];
140
+ function checkAssignmentDeclaration(context, node, declaration, options) {
141
+ const source = getSourceCode(context);
142
+ const { allowInference } = options;
143
+ let declarationTypeArguments;
144
+ const init = declaration.init;
145
+ if (!(init && init.type === TSESTree.AST_NODE_TYPES.CallExpression))
146
+ return;
147
+ const callee = init.callee;
148
+ if (!(callee && callee.type === TSESTree.AST_NODE_TYPES.Identifier))
149
+ return;
150
+ if (!Array.prototype.includes.call(FIXABLE_METHODS, callee.name))
151
+ return;
152
+ const name = callee.name;
153
+ debug$3(`found ${name}, checking type arguments`);
154
+ const initializationTypeArguments = init.typeArguments;
155
+ const typeAnnotation = declaration.id.typeAnnotation;
156
+ if (typeAnnotation) {
157
+ const typeNode = typeAnnotation.typeAnnotation;
158
+ if (typeNode && typeNode.type === TSESTree.AST_NODE_TYPES.TSTypeReference)
159
+ declarationTypeArguments = typeNode.typeArguments;
95
160
  }
96
- return null;
97
- }
98
- function getStaticPropertyName(node) {
99
- if (node.type === "Property" || node.type === "MethodDefinition") {
100
- if (!node.computed) {
101
- const key2 = node.key;
102
- if (key2.type === "Identifier")
103
- return key2.name;
104
- }
105
- const key = node.key;
106
- return getStringLiteralValue(key);
107
- } else if (node.type === "MemberExpression") {
108
- if (!node.computed) {
109
- const property2 = node.property;
110
- if (property2.type === "Identifier")
111
- return property2.name;
112
- return null;
161
+ if (initializationTypeArguments && !declarationTypeArguments)
162
+ return;
163
+ debug$3(`generating report for ${name}`);
164
+ if (!initializationTypeArguments && !declarationTypeArguments) {
165
+ if (allowInference) {
166
+ debug$3("type inference is allowed");
167
+ } else {
168
+ context.report({
169
+ data: {
170
+ name
171
+ },
172
+ messageId: "missingType",
173
+ node
174
+ });
113
175
  }
114
- const property = node.property;
115
- return getStringLiteralValue(property);
176
+ return;
116
177
  }
117
- return null;
178
+ context.report({
179
+ data: {
180
+ name
181
+ },
182
+ fix(fixer) {
183
+ const fixes = [];
184
+ if (!initializationTypeArguments && callee)
185
+ fixes.push(fixer.insertTextAfter(callee, source.getText(declarationTypeArguments)));
186
+ if (typeAnnotation)
187
+ fixes.push(fixer.remove(typeAnnotation));
188
+ return fixes;
189
+ },
190
+ messageId: "inconsistent",
191
+ node
192
+ });
118
193
  }
119
-
120
- function createRecommended(plugin, name, flat) {
121
- const rules = Object.fromEntries(Object.entries(plugin.rules).filter(([_key, rule]) => rule.meta.recommended === "recommended" && !rule.meta.deprecated).map(([key]) => [`${name}/${key}`, 2]));
122
- if (flat) {
194
+ const consistentRefTypeAnnotation = createEslintRule({
195
+ create(context, optionsWithDefault) {
196
+ const options = optionsWithDefault[0] || {};
197
+ const allowInference = options.allowInference;
123
198
  return {
124
- plugins: {
125
- [name]: plugin
126
- },
127
- rules
199
+ VariableDeclaration: (node) => {
200
+ const declarations = node.declarations;
201
+ for (const declaration of declarations) {
202
+ if (declaration.type !== TSESTree.AST_NODE_TYPES.VariableDeclarator)
203
+ continue;
204
+ checkAssignmentDeclaration(context, node, declaration, { allowInference });
205
+ }
206
+ }
128
207
  };
129
- } else {
208
+ },
209
+ defaultOptions: [{ allowInference: false }],
210
+ meta: {
211
+ docs: {
212
+ description: "Ensures consistent type annotation position for ref, computed assignments",
213
+ recommendation: "recommended"
214
+ },
215
+ fixable: "code",
216
+ messages: {
217
+ inconsistent: `Generic type annotation for the {{ name }} call was not on the right side`,
218
+ missingType: `variable assignment for {{ name }} is missing the type annotation`
219
+ },
220
+ schema: [
221
+ {
222
+ additionalProperties: false,
223
+ properties: {
224
+ allowInference: {
225
+ type: "boolean"
226
+ }
227
+ },
228
+ type: "object"
229
+ }
230
+ ],
231
+ type: "problem"
232
+ },
233
+ name: RULE_NAME$6
234
+ });
235
+
236
+ const RULE_NAME$5 = "max-dependencies";
237
+ const maxDependencies = createEslintRule({
238
+ create(context, [options]) {
239
+ let dependencyCount = 0;
240
+ let firstImportNode = null;
130
241
  return {
131
- plugins: [name],
132
- rules
242
+ ImportDeclaration(node) {
243
+ if (!firstImportNode) {
244
+ firstImportNode = node;
245
+ }
246
+ if (options.ignoreTypeImports && node.importKind === "type") {
247
+ return;
248
+ }
249
+ dependencyCount++;
250
+ },
251
+ "Program:exit": function() {
252
+ if (dependencyCount > options.max) {
253
+ context.report({
254
+ data: {
255
+ count: dependencyCount,
256
+ max: options.max
257
+ },
258
+ messageId: "maxDependencies",
259
+ node: firstImportNode
260
+ });
261
+ }
262
+ }
133
263
  };
134
- }
135
- }
264
+ },
265
+ defaultOptions: [
266
+ {
267
+ ignoreTypeImports: false,
268
+ max: 20
269
+ }
270
+ ],
271
+ meta: {
272
+ docs: {
273
+ description: "enforce a maximum number of dependencies per file",
274
+ recommendation: "recommended"
275
+ },
276
+ messages: {
277
+ maxDependencies: "Maximum number of dependencies ({{ max }}) exceeded ({{ count }})."
278
+ },
279
+ schema: [
280
+ {
281
+ additionalProperties: false,
282
+ properties: {
283
+ ignoreTypeImports: {
284
+ type: "boolean"
285
+ },
286
+ max: {
287
+ minimum: 0,
288
+ type: "integer"
289
+ }
290
+ },
291
+ type: "object"
292
+ }
293
+ ],
294
+ type: "suggestion"
295
+ },
296
+ name: RULE_NAME$5
297
+ });
136
298
 
137
- const RULE_NAME$5 = "no-deprecated-classes";
138
- const debug$3 = createDebug("@rotki/eslint-plugin:no-deprecated-classes");
299
+ const RULE_NAME$4 = "no-deprecated-classes";
300
+ const debug$2 = createDebug("@rotki/eslint-plugin:no-deprecated-classes");
139
301
  const replacements$2 = [
140
302
  ["d-block", "block"],
141
303
  ["d-flex", "flex"],
@@ -187,7 +349,7 @@ function findReplacement(className) {
187
349
  return replace(matches);
188
350
  }
189
351
  }
190
- return undefined;
352
+ return void 0;
191
353
  }
192
354
  function getRange(node) {
193
355
  if (node.type === "VAttribute" && node.value && node.value.range)
@@ -195,7 +357,7 @@ function getRange(node) {
195
357
  return node.range;
196
358
  }
197
359
  function reportReplacement(className, replacement, node, context, position = 1) {
198
- debug$3(`found replacement ${replacement} for ${className}`);
360
+ debug$2(`found replacement ${replacement} for ${className}`);
199
361
  const source = getSourceCode(context);
200
362
  const initialRange = getRange(node);
201
363
  const range = [
@@ -308,11 +470,11 @@ const noDeprecatedClasses = createEslintRule({
308
470
  schema: [],
309
471
  type: "problem"
310
472
  },
311
- name: RULE_NAME$5
473
+ name: RULE_NAME$4
312
474
  });
313
475
 
314
- const debug$2 = createDebug("@rotki/eslint-plugin:no-deprecated-components");
315
- const RULE_NAME$4 = "no-deprecated-components";
476
+ const debug$1 = createDebug("@rotki/eslint-plugin:no-deprecated-components");
477
+ const RULE_NAME$3 = "no-deprecated-components";
316
478
  const vuetify = {
317
479
  VApp: true,
318
480
  VAppBar: true,
@@ -365,7 +527,7 @@ const noDeprecatedComponents = createEslintRule({
365
527
  return;
366
528
  const replacement = replacements$1[tag];
367
529
  if (replacement || legacy && skipInLegacy.includes(tag)) {
368
- debug$2(`${tag} has been deprecated`);
530
+ debug$1(`${tag} has been deprecated`);
369
531
  context.report({
370
532
  data: {
371
533
  name: tag
@@ -374,7 +536,7 @@ const noDeprecatedComponents = createEslintRule({
374
536
  node: element
375
537
  });
376
538
  } else {
377
- debug$2(`${tag} has will be removed`);
539
+ debug$1(`${tag} has will be removed`);
378
540
  context.report({
379
541
  data: {
380
542
  name: tag
@@ -417,11 +579,11 @@ const noDeprecatedComponents = createEslintRule({
417
579
  ],
418
580
  type: "problem"
419
581
  },
420
- name: RULE_NAME$4
582
+ name: RULE_NAME$3
421
583
  });
422
584
 
423
- const debug$1 = createDebug("@rotki/eslint-plugin:no-deprecated-props");
424
- const RULE_NAME$3 = "no-deprecated-props";
585
+ const debug = createDebug("@rotki/eslint-plugin:no-deprecated-props");
586
+ const RULE_NAME$2 = "no-deprecated-props";
425
587
  const replacements = {
426
588
  RuiRadio: {
427
589
  internalValue: "value"
@@ -433,7 +595,7 @@ function hasReplacement(tag) {
433
595
  function getPropName(node) {
434
596
  if (node.directive) {
435
597
  if (node.key.argument?.type !== "VIdentifier")
436
- return undefined;
598
+ return void 0;
437
599
  return kebabCase(node.key.argument.rawName);
438
600
  }
439
601
  return kebabCase(node.key.rawName);
@@ -447,16 +609,16 @@ const noDeprecatedProps = createEslintRule({
447
609
  const tag = pascalCase(node.parent.parent.rawName);
448
610
  if (!hasReplacement(tag))
449
611
  return;
450
- debug$1(`${tag} has replacement properties`);
612
+ debug(`${tag} has replacement properties`);
451
613
  const propName = getPropName(node);
452
614
  const propNameNode = node.directive ? node.key.argument : node.key;
453
615
  if (!propName || !propNameNode) {
454
- debug$1("could not get prop name and/or node");
616
+ debug("could not get prop name and/or node");
455
617
  return;
456
618
  }
457
619
  Object.entries(replacements[tag]).forEach(([prop, replacement]) => {
458
620
  if (kebabCase(prop) === propName) {
459
- debug$1(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
621
+ debug(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
460
622
  context.report({
461
623
  data: {
462
624
  prop,
@@ -486,24 +648,27 @@ const noDeprecatedProps = createEslintRule({
486
648
  schema: [],
487
649
  type: "problem"
488
650
  },
489
- name: RULE_NAME$3
651
+ name: RULE_NAME$2
490
652
  });
491
653
 
492
- const RULE_NAME$2 = "no-legacy-library-import";
493
- const legacyLibrary = "@rotki/ui-library-compat";
494
- const newLibrary = "@rotki/ui-library";
495
- const noLegacyLibraryImport = createEslintRule({
654
+ const RULE_NAME$1 = "no-dot-ts-imports";
655
+ const noDotTsImport = createEslintRule({
496
656
  create(context) {
497
657
  return {
498
658
  ImportDeclaration(node) {
499
- if (!node.source.value.startsWith(legacyLibrary))
659
+ const importDeclaration = node.source.value;
660
+ if (!importDeclaration.endsWith(".ts"))
500
661
  return;
501
- const replacement = node.source.value.replace(legacyLibrary, newLibrary);
662
+ const lastIndexOfExtension = importDeclaration.lastIndexOf(".ts");
663
+ const replacement = importDeclaration.substring(0, lastIndexOfExtension);
502
664
  context.report({
665
+ data: {
666
+ import: importDeclaration
667
+ },
503
668
  fix(fixer) {
504
669
  return fixer.replaceText(node.source, `'${replacement}'`);
505
670
  },
506
- messageId: "replacedWith",
671
+ messageId: "invalidTSExtension",
507
672
  node: node.source
508
673
  });
509
674
  }
@@ -512,136 +677,34 @@ const noLegacyLibraryImport = createEslintRule({
512
677
  defaultOptions: [],
513
678
  meta: {
514
679
  docs: {
515
- description: `Reports and replaces imports of ${legacyLibrary} with ${newLibrary}`,
680
+ description: "Checks and replaces .ts extension in import statements.",
516
681
  recommendation: "recommended"
517
682
  },
518
683
  fixable: "code",
519
684
  messages: {
520
- replacedWith: `${legacyLibrary} has been replaced by ${newLibrary}`
685
+ invalidTSExtension: `'{{ import }}' has a .ts extension, please remove it'`
521
686
  },
522
687
  schema: [],
523
688
  type: "problem"
524
689
  },
525
- name: RULE_NAME$2
526
- });
527
-
528
- const debug = createDebug("@rotki/eslint-plugin:consistent-ref-type-annotation");
529
- const RULE_NAME$1 = "consistent-ref-type-annotation";
530
- const FIXABLE_METHODS = ["ref", "computed"];
531
- function checkAssignmentDeclaration(context, node, declaration, options) {
532
- const source = getSourceCode(context);
533
- const { allowInference } = options;
534
- let declarationTypeArguments;
535
- const init = declaration.init;
536
- if (!(init && init.type === TSESTree.AST_NODE_TYPES.CallExpression))
537
- return;
538
- const callee = init.callee;
539
- if (!(callee && callee.type === TSESTree.AST_NODE_TYPES.Identifier))
540
- return;
541
- if (!Array.prototype.includes.call(FIXABLE_METHODS, callee.name))
542
- return;
543
- const name = callee.name;
544
- debug(`found ${name}, checking type arguments`);
545
- const initializationTypeArguments = init.typeArguments;
546
- const typeAnnotation = declaration.id.typeAnnotation;
547
- if (typeAnnotation) {
548
- const typeNode = typeAnnotation.typeAnnotation;
549
- if (typeNode && typeNode.type === TSESTree.AST_NODE_TYPES.TSTypeReference)
550
- declarationTypeArguments = typeNode.typeArguments;
551
- }
552
- if (initializationTypeArguments && !declarationTypeArguments)
553
- return;
554
- debug(`generating report for ${name}`);
555
- if (!initializationTypeArguments && !declarationTypeArguments) {
556
- if (allowInference) {
557
- debug("type inference is allowed");
558
- } else {
559
- context.report({
560
- data: {
561
- name
562
- },
563
- messageId: "missingType",
564
- node
565
- });
566
- }
567
- return;
568
- }
569
- context.report({
570
- data: {
571
- name
572
- },
573
- fix(fixer) {
574
- const fixes = [];
575
- if (!initializationTypeArguments && callee)
576
- fixes.push(fixer.insertTextAfter(callee, source.getText(declarationTypeArguments)));
577
- if (typeAnnotation)
578
- fixes.push(fixer.remove(typeAnnotation));
579
- return fixes;
580
- },
581
- messageId: "inconsistent",
582
- node
583
- });
584
- }
585
- const consistentRefTypeAnnotation = createEslintRule({
586
- create(context, optionsWithDefault) {
587
- const options = optionsWithDefault[0] || {};
588
- const allowInference = options.allowInference;
589
- return {
590
- VariableDeclaration: (node) => {
591
- const declarations = node.declarations;
592
- for (const declaration of declarations) {
593
- if (declaration.type !== TSESTree.AST_NODE_TYPES.VariableDeclarator)
594
- continue;
595
- checkAssignmentDeclaration(context, node, declaration, { allowInference });
596
- }
597
- }
598
- };
599
- },
600
- defaultOptions: [{ allowInference: false }],
601
- meta: {
602
- docs: {
603
- description: "Ensures consistent type annotation position for ref, computed assignments",
604
- recommendation: "recommended"
605
- },
606
- fixable: "code",
607
- messages: {
608
- inconsistent: `Generic type annotation for the {{ name }} call was not on the right side`,
609
- missingType: `variable assignment for {{ name }} is missing the type annotation`
610
- },
611
- schema: [
612
- {
613
- additionalProperties: false,
614
- properties: {
615
- allowInference: {
616
- type: "boolean"
617
- }
618
- },
619
- type: "object"
620
- }
621
- ],
622
- type: "problem"
623
- },
624
690
  name: RULE_NAME$1
625
691
  });
626
692
 
627
- const RULE_NAME = "no-dot-ts-imports";
628
- const noDotTsImport = createEslintRule({
693
+ const RULE_NAME = "no-legacy-library-import";
694
+ const legacyLibrary = "@rotki/ui-library-compat";
695
+ const newLibrary = "@rotki/ui-library";
696
+ const noLegacyLibraryImport = createEslintRule({
629
697
  create(context) {
630
698
  return {
631
699
  ImportDeclaration(node) {
632
- const importDeclaration = node.source.value;
633
- if (!importDeclaration.endsWith(".ts"))
700
+ if (!node.source.value.startsWith(legacyLibrary))
634
701
  return;
635
- const lastIndexOfExtension = importDeclaration.lastIndexOf(".ts");
636
- const replacement = importDeclaration.substring(0, lastIndexOfExtension);
702
+ const replacement = node.source.value.replace(legacyLibrary, newLibrary);
637
703
  context.report({
638
- data: {
639
- import: importDeclaration
640
- },
641
704
  fix(fixer) {
642
705
  return fixer.replaceText(node.source, `'${replacement}'`);
643
706
  },
644
- messageId: "invalidTSExtension",
707
+ messageId: "replacedWith",
645
708
  node: node.source
646
709
  });
647
710
  }
@@ -650,12 +713,12 @@ const noDotTsImport = createEslintRule({
650
713
  defaultOptions: [],
651
714
  meta: {
652
715
  docs: {
653
- description: "Checks and replaces .ts extension in import statements.",
716
+ description: `Reports and replaces imports of ${legacyLibrary} with ${newLibrary}`,
654
717
  recommendation: "recommended"
655
718
  },
656
719
  fixable: "code",
657
720
  messages: {
658
- invalidTSExtension: `'{{ import }}' has a .ts extension, please remove it'`
721
+ replacedWith: `${legacyLibrary} has been replaced by ${newLibrary}`
659
722
  },
660
723
  schema: [],
661
724
  type: "problem"
@@ -670,6 +733,7 @@ const plugin = {
670
733
  },
671
734
  rules: {
672
735
  "consistent-ref-type-annotation": consistentRefTypeAnnotation,
736
+ "max-dependencies": maxDependencies,
673
737
  "no-deprecated-classes": noDeprecatedClasses,
674
738
  "no-deprecated-components": noDeprecatedComponents,
675
739
  "no-deprecated-props": noDeprecatedProps,