@rotki/eslint-plugin 0.3.1 → 0.4.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.cjs CHANGED
@@ -4,6 +4,7 @@ const createDebug = require('debug');
4
4
  const node_path = require('node:path');
5
5
  const compat = require('eslint-compat-utils');
6
6
  const scule = require('scule');
7
+ const utils = require('@typescript-eslint/utils');
7
8
 
8
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
9
10
 
@@ -23,8 +24,8 @@ const createDebug__default = /*#__PURE__*/_interopDefaultCompat(createDebug);
23
24
  const compat__namespace = /*#__PURE__*/_interopNamespaceCompat(compat);
24
25
 
25
26
  const name = "@rotki/eslint-plugin";
26
- const version = "0.3.1";
27
- const packageManager = "pnpm@8.15.0";
27
+ const version = "0.4.0";
28
+ const packageManager = "pnpm@9.4.0";
28
29
  const type = "module";
29
30
  const license = "AGPL-3.0";
30
31
  const bugs = {
@@ -59,6 +60,7 @@ const scripts = {
59
60
  dev: "unbuild --stub",
60
61
  prepublishOnly: "pnpm run build",
61
62
  test: "vitest",
63
+ "test:coverage": "vitest run --coverage",
62
64
  "new": "node --experimental-specifier-resolution=node --loader ts-node/esm ./scripts/new-rule.ts",
63
65
  docs: "vitepress dev docs",
64
66
  "docs:build": "pnpm run generate && vitepress build docs",
@@ -70,39 +72,40 @@ const peerDependencies = {
70
72
  eslint: "^8.0.0 || ^9.0.0"
71
73
  };
72
74
  const dependencies = {
73
- "@typescript-eslint/utils": "6.19.1",
74
- debug: "4.3.4",
75
- "eslint-compat-utils": "0.4.1",
75
+ "@typescript-eslint/utils": "7.15.0",
76
+ debug: "4.3.5",
77
+ "eslint-compat-utils": "0.5.1",
76
78
  "jsonc-eslint-parser": "2.4.0",
77
- scule: "1.2.0",
78
- "vue-eslint-parser": "9.4.2",
79
- "yaml-eslint-parser": "1.2.2"
79
+ scule: "1.3.0",
80
+ "vue-eslint-parser": "9.4.3",
81
+ "yaml-eslint-parser": "1.2.3"
80
82
  };
81
83
  const devDependencies = {
82
- "@commitlint/cli": "18.6.0",
83
- "@commitlint/config-conventional": "18.6.0",
84
- "@rotki/eslint-config": "2.4.4",
84
+ "@commitlint/cli": "19.3.0",
85
+ "@commitlint/config-conventional": "19.2.2",
86
+ "@rotki/eslint-config": "2.9.0",
85
87
  "@types/debug": "4.1.12",
86
- "@types/eslint": "8.56.2",
88
+ "@types/eslint": "8.56.10",
87
89
  "@types/node": "20",
88
- "@typescript-eslint/eslint-plugin": "6.19.1",
89
- "@typescript-eslint/parser": "6.19.1",
90
- "@typescript-eslint/rule-tester": "6.19.1",
91
- bumpp: "9.3.0",
92
- debug: "4.3.4",
93
- eslint: "8.56.0",
94
- husky: "9.0.7",
95
- "lint-staged": "15.2.0",
96
- rimraf: "5.0.5",
90
+ "@typescript-eslint/eslint-plugin": "7.15.0",
91
+ "@typescript-eslint/parser": "7.15.0",
92
+ "@typescript-eslint/rule-tester": "7.15.0",
93
+ "@vitest/coverage-v8": "1.6.0",
94
+ bumpp: "9.4.1",
95
+ debug: "4.3.5",
96
+ eslint: "8.57.0",
97
+ husky: "9.0.11",
98
+ "lint-staged": "15.2.7",
99
+ rimraf: "5.0.8",
97
100
  "ts-node": "10.9.2",
98
- typescript: "5.3.3",
101
+ typescript: "5.5.3",
99
102
  unbuild: "2.0.0",
100
- vitepress: "1.0.0-rc.40",
101
- vitest: "1.2.2"
103
+ vitepress: "1.2.3",
104
+ vitest: "1.6.0"
102
105
  };
103
106
  const engines = {
104
107
  node: ">=20",
105
- pnpm: ">=8 <9"
108
+ pnpm: ">=9 <10"
106
109
  };
107
110
  const pkg = {
108
111
  name: name,
@@ -179,6 +182,8 @@ const createEslintRule = RuleCreator(
179
182
  function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor, options) {
180
183
  const sourceCode = getSourceCode(context);
181
184
  const parserServices = sourceCode.parserServices;
185
+ if (!parserServices)
186
+ throw new Error("missing parserServices");
182
187
  if (!("defineTemplateBodyVisitor" in parserServices) || parserServices.defineTemplateBodyVisitor == null) {
183
188
  const filename = getFilename(context);
184
189
  if (node_path.extname(filename) === ".vue") {
@@ -252,8 +257,8 @@ function createRecommended(plugin, name, flat) {
252
257
  }
253
258
  }
254
259
 
255
- const RULE_NAME$3 = "no-deprecated-classes";
256
- const debug$2 = createDebug__default("@rotki/eslint-plugin:no-deprecated-classes");
260
+ const RULE_NAME$4 = "no-deprecated-classes";
261
+ const debug$3 = createDebug__default("@rotki/eslint-plugin:no-deprecated-classes");
257
262
  const replacements$2 = [
258
263
  ["d-block", "block"],
259
264
  ["d-flex", "flex"],
@@ -275,7 +280,18 @@ const replacements$2 = [
275
280
  /^font-weight-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/,
276
281
  ([weight]) => `font-${weight}`
277
282
  ],
278
- [/^text-(capitalize|uppercase|lowercase)$/, ([casing]) => casing]
283
+ [/^text-(capitalize|uppercase|lowercase)$/, ([casing]) => casing],
284
+ ["text--secondary", "text-rui-text-secondary"],
285
+ ["white--text", "text-white"],
286
+ ["primary--text", "text-rui-primary"],
287
+ [
288
+ /^([mp])([abelr-txy]?)-n(\d)$/,
289
+ ([type, position, size]) => `-${type}${position === "a" ? "" : position}-${size}`
290
+ ],
291
+ [
292
+ /^([mp])a-(\d)$/,
293
+ ([type, size]) => `${type}-${size}`
294
+ ]
279
295
  ];
280
296
  function isString(replacement) {
281
297
  return typeof replacement[0] === "string";
@@ -302,7 +318,7 @@ function getRange(node) {
302
318
  return node.range;
303
319
  }
304
320
  function reportReplacement(className, replacement, node, context, position = 1) {
305
- debug$2(`found replacement ${replacement} for ${className}`);
321
+ debug$3(`found replacement ${replacement} for ${className}`);
306
322
  const source = getSourceCode(context);
307
323
  const initialRange = getRange(node);
308
324
  const range = [
@@ -415,11 +431,11 @@ const noDeprecatedClasses = createEslintRule({
415
431
  schema: [],
416
432
  type: "problem"
417
433
  },
418
- name: RULE_NAME$3
434
+ name: RULE_NAME$4
419
435
  });
420
436
 
421
- const debug$1 = createDebug__default("@rotki/eslint-plugin:no-deprecated-components");
422
- const RULE_NAME$2 = "no-deprecated-components";
437
+ const debug$2 = createDebug__default("@rotki/eslint-plugin:no-deprecated-components");
438
+ const RULE_NAME$3 = "no-deprecated-components";
423
439
  const vuetify = {
424
440
  VApp: true,
425
441
  VAppBar: true,
@@ -466,13 +482,13 @@ const noDeprecatedComponents = createEslintRule({
466
482
  VElement(element) {
467
483
  const tag = scule.pascalCase(element.rawName);
468
484
  const sourceCode = getSourceCode(context);
469
- if (!("getTemplateBodyTokenStore" in sourceCode.parserServices))
485
+ if (sourceCode?.parserServices && !("getTemplateBodyTokenStore" in sourceCode.parserServices))
470
486
  throw new Error("cannot find getTemplateBodyTokenStore in parserServices");
471
487
  if (!hasReplacement$1(tag))
472
488
  return;
473
489
  const replacement = replacements$1[tag];
474
490
  if (replacement || legacy && skipInLegacy.includes(tag)) {
475
- debug$1(`${tag} has been deprecated`);
491
+ debug$2(`${tag} has been deprecated`);
476
492
  context.report({
477
493
  data: {
478
494
  name: tag
@@ -481,7 +497,7 @@ const noDeprecatedComponents = createEslintRule({
481
497
  node: element
482
498
  });
483
499
  } else {
484
- debug$1(`${tag} has will be removed`);
500
+ debug$2(`${tag} has will be removed`);
485
501
  context.report({
486
502
  data: {
487
503
  name: tag
@@ -524,11 +540,11 @@ const noDeprecatedComponents = createEslintRule({
524
540
  ],
525
541
  type: "problem"
526
542
  },
527
- name: RULE_NAME$2
543
+ name: RULE_NAME$3
528
544
  });
529
545
 
530
- const debug = createDebug__default("@rotki/eslint-plugin:no-deprecated-props");
531
- const RULE_NAME$1 = "no-deprecated-props";
546
+ const debug$1 = createDebug__default("@rotki/eslint-plugin:no-deprecated-props");
547
+ const RULE_NAME$2 = "no-deprecated-props";
532
548
  const replacements = {
533
549
  RuiRadio: {
534
550
  internalValue: "value"
@@ -554,16 +570,16 @@ const noDeprecatedProps = createEslintRule({
554
570
  const tag = scule.pascalCase(node.parent.parent.rawName);
555
571
  if (!hasReplacement(tag))
556
572
  return;
557
- debug(`${tag} has replacement properties`);
573
+ debug$1(`${tag} has replacement properties`);
558
574
  const propName = getPropName(node);
559
575
  const propNameNode = node.directive ? node.key.argument : node.key;
560
576
  if (!propName || !propNameNode) {
561
- debug("could not get prop name and/or node");
577
+ debug$1("could not get prop name and/or node");
562
578
  return;
563
579
  }
564
580
  Object.entries(replacements[tag]).forEach(([prop, replacement]) => {
565
581
  if (scule.kebabCase(prop) === propName) {
566
- debug(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
582
+ debug$1(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
567
583
  context.report({
568
584
  data: {
569
585
  prop,
@@ -592,10 +608,10 @@ const noDeprecatedProps = createEslintRule({
592
608
  schema: [],
593
609
  type: "problem"
594
610
  },
595
- name: RULE_NAME$1
611
+ name: RULE_NAME$2
596
612
  });
597
613
 
598
- const RULE_NAME = "no-legacy-library-import";
614
+ const RULE_NAME$1 = "no-legacy-library-import";
599
615
  const legacyLibrary = "@rotki/ui-library-compat";
600
616
  const newLibrary = "@rotki/ui-library";
601
617
  const noLegacyLibraryImport = createEslintRule({
@@ -627,6 +643,105 @@ const noLegacyLibraryImport = createEslintRule({
627
643
  schema: [],
628
644
  type: "problem"
629
645
  },
646
+ name: RULE_NAME$1
647
+ });
648
+
649
+ const debug = createDebug__default("@rotki/eslint-plugin:consistent-ref-type-annotation");
650
+ const RULE_NAME = "consistent-ref-type-annotation";
651
+ const FIXABLE_METHODS = ["ref", "computed"];
652
+ function checkAssignmentDeclaration(context, node, declaration, options) {
653
+ const source = getSourceCode(context);
654
+ const { allowInference } = options;
655
+ let declarationTypeArguments;
656
+ const init = declaration.init;
657
+ if (!(init && init.type === utils.TSESTree.AST_NODE_TYPES.CallExpression))
658
+ return;
659
+ const callee = init.callee;
660
+ if (!(callee && callee.type === utils.TSESTree.AST_NODE_TYPES.Identifier))
661
+ return;
662
+ if (!Array.prototype.includes.call(FIXABLE_METHODS, callee.name))
663
+ return;
664
+ const name = callee.name;
665
+ debug(`found ${name}, checking type arguments`);
666
+ const initializationTypeArguments = init.typeArguments;
667
+ const typeAnnotation = declaration.id.typeAnnotation;
668
+ if (typeAnnotation) {
669
+ const typeNode = typeAnnotation.typeAnnotation;
670
+ if (typeNode && typeNode.type === utils.TSESTree.AST_NODE_TYPES.TSTypeReference)
671
+ declarationTypeArguments = typeNode.typeArguments;
672
+ }
673
+ if (initializationTypeArguments && !declarationTypeArguments)
674
+ return;
675
+ debug(`generating report for ${name}`);
676
+ if (!initializationTypeArguments && !declarationTypeArguments) {
677
+ if (allowInference) {
678
+ debug("type inference is allowed");
679
+ } else {
680
+ context.report({
681
+ data: {
682
+ name
683
+ },
684
+ messageId: "missingType",
685
+ node
686
+ });
687
+ }
688
+ return;
689
+ }
690
+ context.report({
691
+ data: {
692
+ name
693
+ },
694
+ fix(fixer) {
695
+ const fixes = [];
696
+ if (!initializationTypeArguments && callee)
697
+ fixes.push(fixer.insertTextAfter(callee, source.getText(declarationTypeArguments)));
698
+ if (typeAnnotation)
699
+ fixes.push(fixer.remove(typeAnnotation));
700
+ return fixes;
701
+ },
702
+ messageId: "inconsistent",
703
+ node
704
+ });
705
+ }
706
+ const consistentRefTypeAnnotation = createEslintRule({
707
+ create(context, optionsWithDefault) {
708
+ const options = optionsWithDefault[0] || {};
709
+ const allowInference = options.allowInference;
710
+ return {
711
+ VariableDeclaration: (node) => {
712
+ const declarations = node.declarations;
713
+ for (const declaration of declarations) {
714
+ if (declaration.type !== utils.TSESTree.AST_NODE_TYPES.VariableDeclarator)
715
+ continue;
716
+ checkAssignmentDeclaration(context, node, declaration, { allowInference });
717
+ }
718
+ }
719
+ };
720
+ },
721
+ defaultOptions: [{ allowInference: false }],
722
+ meta: {
723
+ docs: {
724
+ description: "Ensures consistent type annotation position for ref, computed assignments",
725
+ recommended: "recommended"
726
+ },
727
+ fixable: "code",
728
+ messages: {
729
+ inconsistent: `Generic type annotation for the {{ name }} call was not on the right side`,
730
+ missingType: `variable assignment for {{ name }} is missing the type annotation`
731
+ },
732
+ schema: [
733
+ {
734
+ additionalProperties: false,
735
+ properties: {
736
+ allowInference: {
737
+ type: "boolean"
738
+ }
739
+ },
740
+ type: "object"
741
+ }
742
+ ],
743
+ type: "problem"
744
+ },
630
745
  name: RULE_NAME
631
746
  });
632
747
 
@@ -636,6 +751,7 @@ const plugin = {
636
751
  version: pkg.version
637
752
  },
638
753
  rules: {
754
+ "consistent-ref-type-annotation": consistentRefTypeAnnotation,
639
755
  "no-deprecated-classes": noDeprecatedClasses,
640
756
  "no-deprecated-components": noDeprecatedComponents,
641
757
  "no-deprecated-props": noDeprecatedProps,
package/dist/index.d.cts CHANGED
@@ -4,18 +4,23 @@ interface PluginRuleModule<TOptions extends readonly unknown[] = []> extends Rul
4
4
  defaultOptions: TOptions;
5
5
  }
6
6
 
7
- type Options = [{
7
+ type Options$1 = [{
8
8
  legacy: boolean;
9
9
  }];
10
10
 
11
+ type Options = [{
12
+ allowInference: boolean;
13
+ }];
14
+
11
15
  declare const plugin: {
12
16
  meta: {
13
17
  name: string;
14
18
  version: string;
15
19
  };
16
20
  rules: {
21
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
17
22
  'no-deprecated-classes': PluginRuleModule<[]>;
18
- 'no-deprecated-components': PluginRuleModule<Options>;
23
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
19
24
  'no-deprecated-props': PluginRuleModule<[]>;
20
25
  'no-legacy-library-import': PluginRuleModule<[]>;
21
26
  };
@@ -27,8 +32,9 @@ declare const _default: {
27
32
  version: string;
28
33
  };
29
34
  rules: {
35
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
30
36
  'no-deprecated-classes': PluginRuleModule<[]>;
31
- 'no-deprecated-components': PluginRuleModule<Options>;
37
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
32
38
  'no-deprecated-props': PluginRuleModule<[]>;
33
39
  'no-legacy-library-import': PluginRuleModule<[]>;
34
40
  };
@@ -42,8 +48,9 @@ declare const _default: {
42
48
  version: string;
43
49
  };
44
50
  rules: {
51
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
45
52
  'no-deprecated-classes': PluginRuleModule<[]>;
46
- 'no-deprecated-components': PluginRuleModule<Options>;
53
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
47
54
  'no-deprecated-props': PluginRuleModule<[]>;
48
55
  'no-legacy-library-import': PluginRuleModule<[]>;
49
56
  };
@@ -66,8 +73,9 @@ declare const _default: {
66
73
  version: string;
67
74
  };
68
75
  rules: {
76
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
69
77
  'no-deprecated-classes': PluginRuleModule<[]>;
70
- 'no-deprecated-components': PluginRuleModule<Options>;
78
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
71
79
  'no-deprecated-props': PluginRuleModule<[]>;
72
80
  'no-legacy-library-import': PluginRuleModule<[]>;
73
81
  };
package/dist/index.d.mts CHANGED
@@ -4,18 +4,23 @@ interface PluginRuleModule<TOptions extends readonly unknown[] = []> extends Rul
4
4
  defaultOptions: TOptions;
5
5
  }
6
6
 
7
- type Options = [{
7
+ type Options$1 = [{
8
8
  legacy: boolean;
9
9
  }];
10
10
 
11
+ type Options = [{
12
+ allowInference: boolean;
13
+ }];
14
+
11
15
  declare const plugin: {
12
16
  meta: {
13
17
  name: string;
14
18
  version: string;
15
19
  };
16
20
  rules: {
21
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
17
22
  'no-deprecated-classes': PluginRuleModule<[]>;
18
- 'no-deprecated-components': PluginRuleModule<Options>;
23
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
19
24
  'no-deprecated-props': PluginRuleModule<[]>;
20
25
  'no-legacy-library-import': PluginRuleModule<[]>;
21
26
  };
@@ -27,8 +32,9 @@ declare const _default: {
27
32
  version: string;
28
33
  };
29
34
  rules: {
35
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
30
36
  'no-deprecated-classes': PluginRuleModule<[]>;
31
- 'no-deprecated-components': PluginRuleModule<Options>;
37
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
32
38
  'no-deprecated-props': PluginRuleModule<[]>;
33
39
  'no-legacy-library-import': PluginRuleModule<[]>;
34
40
  };
@@ -42,8 +48,9 @@ declare const _default: {
42
48
  version: string;
43
49
  };
44
50
  rules: {
51
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
45
52
  'no-deprecated-classes': PluginRuleModule<[]>;
46
- 'no-deprecated-components': PluginRuleModule<Options>;
53
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
47
54
  'no-deprecated-props': PluginRuleModule<[]>;
48
55
  'no-legacy-library-import': PluginRuleModule<[]>;
49
56
  };
@@ -66,8 +73,9 @@ declare const _default: {
66
73
  version: string;
67
74
  };
68
75
  rules: {
76
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
69
77
  'no-deprecated-classes': PluginRuleModule<[]>;
70
- 'no-deprecated-components': PluginRuleModule<Options>;
78
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
71
79
  'no-deprecated-props': PluginRuleModule<[]>;
72
80
  'no-legacy-library-import': PluginRuleModule<[]>;
73
81
  };
package/dist/index.d.ts CHANGED
@@ -4,18 +4,23 @@ interface PluginRuleModule<TOptions extends readonly unknown[] = []> extends Rul
4
4
  defaultOptions: TOptions;
5
5
  }
6
6
 
7
- type Options = [{
7
+ type Options$1 = [{
8
8
  legacy: boolean;
9
9
  }];
10
10
 
11
+ type Options = [{
12
+ allowInference: boolean;
13
+ }];
14
+
11
15
  declare const plugin: {
12
16
  meta: {
13
17
  name: string;
14
18
  version: string;
15
19
  };
16
20
  rules: {
21
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
17
22
  'no-deprecated-classes': PluginRuleModule<[]>;
18
- 'no-deprecated-components': PluginRuleModule<Options>;
23
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
19
24
  'no-deprecated-props': PluginRuleModule<[]>;
20
25
  'no-legacy-library-import': PluginRuleModule<[]>;
21
26
  };
@@ -27,8 +32,9 @@ declare const _default: {
27
32
  version: string;
28
33
  };
29
34
  rules: {
35
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
30
36
  'no-deprecated-classes': PluginRuleModule<[]>;
31
- 'no-deprecated-components': PluginRuleModule<Options>;
37
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
32
38
  'no-deprecated-props': PluginRuleModule<[]>;
33
39
  'no-legacy-library-import': PluginRuleModule<[]>;
34
40
  };
@@ -42,8 +48,9 @@ declare const _default: {
42
48
  version: string;
43
49
  };
44
50
  rules: {
51
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
45
52
  'no-deprecated-classes': PluginRuleModule<[]>;
46
- 'no-deprecated-components': PluginRuleModule<Options>;
53
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
47
54
  'no-deprecated-props': PluginRuleModule<[]>;
48
55
  'no-legacy-library-import': PluginRuleModule<[]>;
49
56
  };
@@ -66,8 +73,9 @@ declare const _default: {
66
73
  version: string;
67
74
  };
68
75
  rules: {
76
+ 'consistent-ref-type-annotation': PluginRuleModule<Options>;
69
77
  'no-deprecated-classes': PluginRuleModule<[]>;
70
- 'no-deprecated-components': PluginRuleModule<Options>;
78
+ 'no-deprecated-components': PluginRuleModule<Options$1>;
71
79
  'no-deprecated-props': PluginRuleModule<[]>;
72
80
  'no-legacy-library-import': PluginRuleModule<[]>;
73
81
  };
package/dist/index.mjs CHANGED
@@ -2,10 +2,11 @@ import createDebug from 'debug';
2
2
  import { extname } from 'node:path';
3
3
  import * as compat from 'eslint-compat-utils';
4
4
  import { pascalCase, kebabCase } from 'scule';
5
+ import { TSESTree } from '@typescript-eslint/utils';
5
6
 
6
7
  const name = "@rotki/eslint-plugin";
7
- const version = "0.3.1";
8
- const packageManager = "pnpm@8.15.0";
8
+ const version = "0.4.0";
9
+ const packageManager = "pnpm@9.4.0";
9
10
  const type = "module";
10
11
  const license = "AGPL-3.0";
11
12
  const bugs = {
@@ -40,6 +41,7 @@ const scripts = {
40
41
  dev: "unbuild --stub",
41
42
  prepublishOnly: "pnpm run build",
42
43
  test: "vitest",
44
+ "test:coverage": "vitest run --coverage",
43
45
  "new": "node --experimental-specifier-resolution=node --loader ts-node/esm ./scripts/new-rule.ts",
44
46
  docs: "vitepress dev docs",
45
47
  "docs:build": "pnpm run generate && vitepress build docs",
@@ -51,39 +53,40 @@ const peerDependencies = {
51
53
  eslint: "^8.0.0 || ^9.0.0"
52
54
  };
53
55
  const dependencies = {
54
- "@typescript-eslint/utils": "6.19.1",
55
- debug: "4.3.4",
56
- "eslint-compat-utils": "0.4.1",
56
+ "@typescript-eslint/utils": "7.15.0",
57
+ debug: "4.3.5",
58
+ "eslint-compat-utils": "0.5.1",
57
59
  "jsonc-eslint-parser": "2.4.0",
58
- scule: "1.2.0",
59
- "vue-eslint-parser": "9.4.2",
60
- "yaml-eslint-parser": "1.2.2"
60
+ scule: "1.3.0",
61
+ "vue-eslint-parser": "9.4.3",
62
+ "yaml-eslint-parser": "1.2.3"
61
63
  };
62
64
  const devDependencies = {
63
- "@commitlint/cli": "18.6.0",
64
- "@commitlint/config-conventional": "18.6.0",
65
- "@rotki/eslint-config": "2.4.4",
65
+ "@commitlint/cli": "19.3.0",
66
+ "@commitlint/config-conventional": "19.2.2",
67
+ "@rotki/eslint-config": "2.9.0",
66
68
  "@types/debug": "4.1.12",
67
- "@types/eslint": "8.56.2",
69
+ "@types/eslint": "8.56.10",
68
70
  "@types/node": "20",
69
- "@typescript-eslint/eslint-plugin": "6.19.1",
70
- "@typescript-eslint/parser": "6.19.1",
71
- "@typescript-eslint/rule-tester": "6.19.1",
72
- bumpp: "9.3.0",
73
- debug: "4.3.4",
74
- eslint: "8.56.0",
75
- husky: "9.0.7",
76
- "lint-staged": "15.2.0",
77
- rimraf: "5.0.5",
71
+ "@typescript-eslint/eslint-plugin": "7.15.0",
72
+ "@typescript-eslint/parser": "7.15.0",
73
+ "@typescript-eslint/rule-tester": "7.15.0",
74
+ "@vitest/coverage-v8": "1.6.0",
75
+ bumpp: "9.4.1",
76
+ debug: "4.3.5",
77
+ eslint: "8.57.0",
78
+ husky: "9.0.11",
79
+ "lint-staged": "15.2.7",
80
+ rimraf: "5.0.8",
78
81
  "ts-node": "10.9.2",
79
- typescript: "5.3.3",
82
+ typescript: "5.5.3",
80
83
  unbuild: "2.0.0",
81
- vitepress: "1.0.0-rc.40",
82
- vitest: "1.2.2"
84
+ vitepress: "1.2.3",
85
+ vitest: "1.6.0"
83
86
  };
84
87
  const engines = {
85
88
  node: ">=20",
86
- pnpm: ">=8 <9"
89
+ pnpm: ">=9 <10"
87
90
  };
88
91
  const pkg = {
89
92
  name: name,
@@ -160,6 +163,8 @@ const createEslintRule = RuleCreator(
160
163
  function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor, options) {
161
164
  const sourceCode = getSourceCode(context);
162
165
  const parserServices = sourceCode.parserServices;
166
+ if (!parserServices)
167
+ throw new Error("missing parserServices");
163
168
  if (!("defineTemplateBodyVisitor" in parserServices) || parserServices.defineTemplateBodyVisitor == null) {
164
169
  const filename = getFilename(context);
165
170
  if (extname(filename) === ".vue") {
@@ -233,8 +238,8 @@ function createRecommended(plugin, name, flat) {
233
238
  }
234
239
  }
235
240
 
236
- const RULE_NAME$3 = "no-deprecated-classes";
237
- const debug$2 = createDebug("@rotki/eslint-plugin:no-deprecated-classes");
241
+ const RULE_NAME$4 = "no-deprecated-classes";
242
+ const debug$3 = createDebug("@rotki/eslint-plugin:no-deprecated-classes");
238
243
  const replacements$2 = [
239
244
  ["d-block", "block"],
240
245
  ["d-flex", "flex"],
@@ -256,7 +261,18 @@ const replacements$2 = [
256
261
  /^font-weight-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/,
257
262
  ([weight]) => `font-${weight}`
258
263
  ],
259
- [/^text-(capitalize|uppercase|lowercase)$/, ([casing]) => casing]
264
+ [/^text-(capitalize|uppercase|lowercase)$/, ([casing]) => casing],
265
+ ["text--secondary", "text-rui-text-secondary"],
266
+ ["white--text", "text-white"],
267
+ ["primary--text", "text-rui-primary"],
268
+ [
269
+ /^([mp])([abelr-txy]?)-n(\d)$/,
270
+ ([type, position, size]) => `-${type}${position === "a" ? "" : position}-${size}`
271
+ ],
272
+ [
273
+ /^([mp])a-(\d)$/,
274
+ ([type, size]) => `${type}-${size}`
275
+ ]
260
276
  ];
261
277
  function isString(replacement) {
262
278
  return typeof replacement[0] === "string";
@@ -283,7 +299,7 @@ function getRange(node) {
283
299
  return node.range;
284
300
  }
285
301
  function reportReplacement(className, replacement, node, context, position = 1) {
286
- debug$2(`found replacement ${replacement} for ${className}`);
302
+ debug$3(`found replacement ${replacement} for ${className}`);
287
303
  const source = getSourceCode(context);
288
304
  const initialRange = getRange(node);
289
305
  const range = [
@@ -396,11 +412,11 @@ const noDeprecatedClasses = createEslintRule({
396
412
  schema: [],
397
413
  type: "problem"
398
414
  },
399
- name: RULE_NAME$3
415
+ name: RULE_NAME$4
400
416
  });
401
417
 
402
- const debug$1 = createDebug("@rotki/eslint-plugin:no-deprecated-components");
403
- const RULE_NAME$2 = "no-deprecated-components";
418
+ const debug$2 = createDebug("@rotki/eslint-plugin:no-deprecated-components");
419
+ const RULE_NAME$3 = "no-deprecated-components";
404
420
  const vuetify = {
405
421
  VApp: true,
406
422
  VAppBar: true,
@@ -447,13 +463,13 @@ const noDeprecatedComponents = createEslintRule({
447
463
  VElement(element) {
448
464
  const tag = pascalCase(element.rawName);
449
465
  const sourceCode = getSourceCode(context);
450
- if (!("getTemplateBodyTokenStore" in sourceCode.parserServices))
466
+ if (sourceCode?.parserServices && !("getTemplateBodyTokenStore" in sourceCode.parserServices))
451
467
  throw new Error("cannot find getTemplateBodyTokenStore in parserServices");
452
468
  if (!hasReplacement$1(tag))
453
469
  return;
454
470
  const replacement = replacements$1[tag];
455
471
  if (replacement || legacy && skipInLegacy.includes(tag)) {
456
- debug$1(`${tag} has been deprecated`);
472
+ debug$2(`${tag} has been deprecated`);
457
473
  context.report({
458
474
  data: {
459
475
  name: tag
@@ -462,7 +478,7 @@ const noDeprecatedComponents = createEslintRule({
462
478
  node: element
463
479
  });
464
480
  } else {
465
- debug$1(`${tag} has will be removed`);
481
+ debug$2(`${tag} has will be removed`);
466
482
  context.report({
467
483
  data: {
468
484
  name: tag
@@ -505,11 +521,11 @@ const noDeprecatedComponents = createEslintRule({
505
521
  ],
506
522
  type: "problem"
507
523
  },
508
- name: RULE_NAME$2
524
+ name: RULE_NAME$3
509
525
  });
510
526
 
511
- const debug = createDebug("@rotki/eslint-plugin:no-deprecated-props");
512
- const RULE_NAME$1 = "no-deprecated-props";
527
+ const debug$1 = createDebug("@rotki/eslint-plugin:no-deprecated-props");
528
+ const RULE_NAME$2 = "no-deprecated-props";
513
529
  const replacements = {
514
530
  RuiRadio: {
515
531
  internalValue: "value"
@@ -535,16 +551,16 @@ const noDeprecatedProps = createEslintRule({
535
551
  const tag = pascalCase(node.parent.parent.rawName);
536
552
  if (!hasReplacement(tag))
537
553
  return;
538
- debug(`${tag} has replacement properties`);
554
+ debug$1(`${tag} has replacement properties`);
539
555
  const propName = getPropName(node);
540
556
  const propNameNode = node.directive ? node.key.argument : node.key;
541
557
  if (!propName || !propNameNode) {
542
- debug("could not get prop name and/or node");
558
+ debug$1("could not get prop name and/or node");
543
559
  return;
544
560
  }
545
561
  Object.entries(replacements[tag]).forEach(([prop, replacement]) => {
546
562
  if (kebabCase(prop) === propName) {
547
- debug(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
563
+ debug$1(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
548
564
  context.report({
549
565
  data: {
550
566
  prop,
@@ -573,10 +589,10 @@ const noDeprecatedProps = createEslintRule({
573
589
  schema: [],
574
590
  type: "problem"
575
591
  },
576
- name: RULE_NAME$1
592
+ name: RULE_NAME$2
577
593
  });
578
594
 
579
- const RULE_NAME = "no-legacy-library-import";
595
+ const RULE_NAME$1 = "no-legacy-library-import";
580
596
  const legacyLibrary = "@rotki/ui-library-compat";
581
597
  const newLibrary = "@rotki/ui-library";
582
598
  const noLegacyLibraryImport = createEslintRule({
@@ -608,6 +624,105 @@ const noLegacyLibraryImport = createEslintRule({
608
624
  schema: [],
609
625
  type: "problem"
610
626
  },
627
+ name: RULE_NAME$1
628
+ });
629
+
630
+ const debug = createDebug("@rotki/eslint-plugin:consistent-ref-type-annotation");
631
+ const RULE_NAME = "consistent-ref-type-annotation";
632
+ const FIXABLE_METHODS = ["ref", "computed"];
633
+ function checkAssignmentDeclaration(context, node, declaration, options) {
634
+ const source = getSourceCode(context);
635
+ const { allowInference } = options;
636
+ let declarationTypeArguments;
637
+ const init = declaration.init;
638
+ if (!(init && init.type === TSESTree.AST_NODE_TYPES.CallExpression))
639
+ return;
640
+ const callee = init.callee;
641
+ if (!(callee && callee.type === TSESTree.AST_NODE_TYPES.Identifier))
642
+ return;
643
+ if (!Array.prototype.includes.call(FIXABLE_METHODS, callee.name))
644
+ return;
645
+ const name = callee.name;
646
+ debug(`found ${name}, checking type arguments`);
647
+ const initializationTypeArguments = init.typeArguments;
648
+ const typeAnnotation = declaration.id.typeAnnotation;
649
+ if (typeAnnotation) {
650
+ const typeNode = typeAnnotation.typeAnnotation;
651
+ if (typeNode && typeNode.type === TSESTree.AST_NODE_TYPES.TSTypeReference)
652
+ declarationTypeArguments = typeNode.typeArguments;
653
+ }
654
+ if (initializationTypeArguments && !declarationTypeArguments)
655
+ return;
656
+ debug(`generating report for ${name}`);
657
+ if (!initializationTypeArguments && !declarationTypeArguments) {
658
+ if (allowInference) {
659
+ debug("type inference is allowed");
660
+ } else {
661
+ context.report({
662
+ data: {
663
+ name
664
+ },
665
+ messageId: "missingType",
666
+ node
667
+ });
668
+ }
669
+ return;
670
+ }
671
+ context.report({
672
+ data: {
673
+ name
674
+ },
675
+ fix(fixer) {
676
+ const fixes = [];
677
+ if (!initializationTypeArguments && callee)
678
+ fixes.push(fixer.insertTextAfter(callee, source.getText(declarationTypeArguments)));
679
+ if (typeAnnotation)
680
+ fixes.push(fixer.remove(typeAnnotation));
681
+ return fixes;
682
+ },
683
+ messageId: "inconsistent",
684
+ node
685
+ });
686
+ }
687
+ const consistentRefTypeAnnotation = createEslintRule({
688
+ create(context, optionsWithDefault) {
689
+ const options = optionsWithDefault[0] || {};
690
+ const allowInference = options.allowInference;
691
+ return {
692
+ VariableDeclaration: (node) => {
693
+ const declarations = node.declarations;
694
+ for (const declaration of declarations) {
695
+ if (declaration.type !== TSESTree.AST_NODE_TYPES.VariableDeclarator)
696
+ continue;
697
+ checkAssignmentDeclaration(context, node, declaration, { allowInference });
698
+ }
699
+ }
700
+ };
701
+ },
702
+ defaultOptions: [{ allowInference: false }],
703
+ meta: {
704
+ docs: {
705
+ description: "Ensures consistent type annotation position for ref, computed assignments",
706
+ recommended: "recommended"
707
+ },
708
+ fixable: "code",
709
+ messages: {
710
+ inconsistent: `Generic type annotation for the {{ name }} call was not on the right side`,
711
+ missingType: `variable assignment for {{ name }} is missing the type annotation`
712
+ },
713
+ schema: [
714
+ {
715
+ additionalProperties: false,
716
+ properties: {
717
+ allowInference: {
718
+ type: "boolean"
719
+ }
720
+ },
721
+ type: "object"
722
+ }
723
+ ],
724
+ type: "problem"
725
+ },
611
726
  name: RULE_NAME
612
727
  });
613
728
 
@@ -617,6 +732,7 @@ const plugin = {
617
732
  version: pkg.version
618
733
  },
619
734
  rules: {
735
+ "consistent-ref-type-annotation": consistentRefTypeAnnotation,
620
736
  "no-deprecated-classes": noDeprecatedClasses,
621
737
  "no-deprecated-components": noDeprecatedComponents,
622
738
  "no-deprecated-props": noDeprecatedProps,
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "@rotki/eslint-plugin",
3
- "version": "0.3.1",
4
- "packageManager": "pnpm@8.15.0",
3
+ "version": "0.4.0",
5
4
  "type": "module",
6
5
  "license": "AGPL-3.0",
7
6
  "bugs": {
@@ -30,39 +29,40 @@
30
29
  "eslint": "^8.0.0 || ^9.0.0"
31
30
  },
32
31
  "dependencies": {
33
- "@typescript-eslint/utils": "6.19.1",
34
- "debug": "4.3.4",
35
- "eslint-compat-utils": "0.4.1",
32
+ "@typescript-eslint/utils": "7.15.0",
33
+ "debug": "4.3.5",
34
+ "eslint-compat-utils": "0.5.1",
36
35
  "jsonc-eslint-parser": "2.4.0",
37
- "scule": "1.2.0",
38
- "vue-eslint-parser": "9.4.2",
39
- "yaml-eslint-parser": "1.2.2"
36
+ "scule": "1.3.0",
37
+ "vue-eslint-parser": "9.4.3",
38
+ "yaml-eslint-parser": "1.2.3"
40
39
  },
41
40
  "devDependencies": {
42
- "@commitlint/cli": "18.6.0",
43
- "@commitlint/config-conventional": "18.6.0",
44
- "@rotki/eslint-config": "2.4.4",
41
+ "@commitlint/cli": "19.3.0",
42
+ "@commitlint/config-conventional": "19.2.2",
43
+ "@rotki/eslint-config": "2.9.0",
45
44
  "@types/debug": "4.1.12",
46
- "@types/eslint": "8.56.2",
45
+ "@types/eslint": "8.56.10",
47
46
  "@types/node": "20",
48
- "@typescript-eslint/eslint-plugin": "6.19.1",
49
- "@typescript-eslint/parser": "6.19.1",
50
- "@typescript-eslint/rule-tester": "6.19.1",
51
- "bumpp": "9.3.0",
52
- "debug": "4.3.4",
53
- "eslint": "8.56.0",
54
- "husky": "9.0.7",
55
- "lint-staged": "15.2.0",
56
- "rimraf": "5.0.5",
47
+ "@typescript-eslint/eslint-plugin": "7.15.0",
48
+ "@typescript-eslint/parser": "7.15.0",
49
+ "@typescript-eslint/rule-tester": "7.15.0",
50
+ "@vitest/coverage-v8": "1.6.0",
51
+ "bumpp": "9.4.1",
52
+ "debug": "4.3.5",
53
+ "eslint": "8.57.0",
54
+ "husky": "9.0.11",
55
+ "lint-staged": "15.2.7",
56
+ "rimraf": "5.0.8",
57
57
  "ts-node": "10.9.2",
58
- "typescript": "5.3.3",
58
+ "typescript": "5.5.3",
59
59
  "unbuild": "2.0.0",
60
- "vitepress": "1.0.0-rc.40",
61
- "vitest": "1.2.2"
60
+ "vitepress": "1.2.3",
61
+ "vitest": "1.6.0"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=20",
65
- "pnpm": ">=8 <9"
65
+ "pnpm": ">=9 <10"
66
66
  },
67
67
  "lint-staged": {
68
68
  "*.{js,cjs,ts,vue,yml,json,md}": "eslint"
@@ -76,6 +76,7 @@
76
76
  "build": "unbuild",
77
77
  "dev": "unbuild --stub",
78
78
  "test": "vitest",
79
+ "test:coverage": "vitest run --coverage",
79
80
  "new": "node --experimental-specifier-resolution=node --loader ts-node/esm ./scripts/new-rule.ts",
80
81
  "docs": "vitepress dev docs",
81
82
  "docs:build": "pnpm run generate && vitepress build docs",