@rotki/eslint-plugin 0.3.2 → 0.5.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.2";
27
- const packageManager = "pnpm@8.15.0";
27
+ const version = "0.5.0";
28
+ const packageManager = "pnpm@9.7.0";
28
29
  const type = "module";
29
30
  const license = "AGPL-3.0";
30
31
  const bugs = {
@@ -63,48 +64,48 @@ const scripts = {
63
64
  "new": "node --experimental-specifier-resolution=node --loader ts-node/esm ./scripts/new-rule.ts",
64
65
  docs: "vitepress dev docs",
65
66
  "docs:build": "pnpm run generate && vitepress build docs",
66
- prepare: "husky install",
67
+ prepare: "husky",
67
68
  typecheck: "tsc --noEmit",
68
69
  release: "bumpp -r --no-push"
69
70
  };
70
71
  const peerDependencies = {
71
- eslint: "^8.0.0 || ^9.0.0"
72
+ eslint: "^9.0.0"
72
73
  };
73
74
  const dependencies = {
74
- "@typescript-eslint/utils": "6.19.1",
75
- debug: "4.3.4",
76
- "eslint-compat-utils": "0.4.1",
75
+ "@typescript-eslint/utils": "8.1.0",
76
+ debug: "4.3.6",
77
+ "eslint-compat-utils": "0.5.1",
77
78
  "jsonc-eslint-parser": "2.4.0",
78
- scule: "1.2.0",
79
- "vue-eslint-parser": "9.4.2",
80
- "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"
81
82
  };
82
83
  const devDependencies = {
83
- "@commitlint/cli": "18.6.0",
84
- "@commitlint/config-conventional": "18.6.0",
85
- "@rotki/eslint-config": "2.4.4",
84
+ "@commitlint/cli": "19.4.0",
85
+ "@commitlint/config-conventional": "19.2.2",
86
+ "@rotki/eslint-config": "3.0.0",
86
87
  "@types/debug": "4.1.12",
87
- "@types/eslint": "8.56.2",
88
+ "@types/eslint": "9.6.0",
88
89
  "@types/node": "20",
89
- "@typescript-eslint/eslint-plugin": "6.19.1",
90
- "@typescript-eslint/parser": "6.19.1",
91
- "@typescript-eslint/rule-tester": "6.19.1",
92
- "@vitest/coverage-v8": "^1.2.2",
93
- bumpp: "9.3.0",
94
- debug: "4.3.4",
95
- eslint: "8.56.0",
96
- husky: "9.0.7",
97
- "lint-staged": "15.2.0",
98
- rimraf: "5.0.5",
90
+ "@typescript-eslint/eslint-plugin": "8.1.0",
91
+ "@typescript-eslint/parser": "8.1.0",
92
+ "@typescript-eslint/rule-tester": "8.1.0",
93
+ "@vitest/coverage-v8": "2.0.5",
94
+ bumpp: "9.5.1",
95
+ debug: "4.3.6",
96
+ eslint: "9.9.0",
97
+ husky: "9.1.4",
98
+ "lint-staged": "15.2.9",
99
+ rimraf: "6.0.1",
99
100
  "ts-node": "10.9.2",
100
- typescript: "5.3.3",
101
+ typescript: "5.5.4",
101
102
  unbuild: "2.0.0",
102
- vitepress: "1.0.0-rc.40",
103
- vitest: "1.2.2"
103
+ vitepress: "1.3.2",
104
+ vitest: "2.0.5"
104
105
  };
105
106
  const engines = {
106
107
  node: ">=20",
107
- pnpm: ">=8 <9"
108
+ pnpm: ">=9 <10"
108
109
  };
109
110
  const pkg = {
110
111
  name: name,
@@ -181,6 +182,8 @@ const createEslintRule = RuleCreator(
181
182
  function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor, options) {
182
183
  const sourceCode = getSourceCode(context);
183
184
  const parserServices = sourceCode.parserServices;
185
+ if (!parserServices)
186
+ throw new Error("missing parserServices");
184
187
  if (!("defineTemplateBodyVisitor" in parserServices) || parserServices.defineTemplateBodyVisitor == null) {
185
188
  const filename = getFilename(context);
186
189
  if (node_path.extname(filename) === ".vue") {
@@ -211,8 +214,9 @@ function getStringLiteralValue(node, stringOnly = false) {
211
214
  return String(node.value);
212
215
  return null;
213
216
  }
214
- if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1)
217
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
215
218
  return node.quasis[0].value.cooked;
219
+ }
216
220
  return null;
217
221
  }
218
222
  function getStaticPropertyName(node) {
@@ -254,8 +258,8 @@ function createRecommended(plugin, name, flat) {
254
258
  }
255
259
  }
256
260
 
257
- const RULE_NAME$3 = "no-deprecated-classes";
258
- const debug$2 = createDebug__default("@rotki/eslint-plugin:no-deprecated-classes");
261
+ const RULE_NAME$4 = "no-deprecated-classes";
262
+ const debug$3 = createDebug__default("@rotki/eslint-plugin:no-deprecated-classes");
259
263
  const replacements$2 = [
260
264
  ["d-block", "block"],
261
265
  ["d-flex", "flex"],
@@ -315,7 +319,7 @@ function getRange(node) {
315
319
  return node.range;
316
320
  }
317
321
  function reportReplacement(className, replacement, node, context, position = 1) {
318
- debug$2(`found replacement ${replacement} for ${className}`);
322
+ debug$3(`found replacement ${replacement} for ${className}`);
319
323
  const source = getSourceCode(context);
320
324
  const initialRange = getRange(node);
321
325
  const range = [
@@ -419,7 +423,7 @@ const noDeprecatedClasses = createEslintRule({
419
423
  meta: {
420
424
  docs: {
421
425
  description: "disallow the usage of vuetify css classes since they are replaced with tailwindcss",
422
- recommended: "recommended"
426
+ recommendation: "recommended"
423
427
  },
424
428
  fixable: "code",
425
429
  messages: {
@@ -428,11 +432,11 @@ const noDeprecatedClasses = createEslintRule({
428
432
  schema: [],
429
433
  type: "problem"
430
434
  },
431
- name: RULE_NAME$3
435
+ name: RULE_NAME$4
432
436
  });
433
437
 
434
- const debug$1 = createDebug__default("@rotki/eslint-plugin:no-deprecated-components");
435
- const RULE_NAME$2 = "no-deprecated-components";
438
+ const debug$2 = createDebug__default("@rotki/eslint-plugin:no-deprecated-components");
439
+ const RULE_NAME$3 = "no-deprecated-components";
436
440
  const vuetify = {
437
441
  VApp: true,
438
442
  VAppBar: true,
@@ -479,13 +483,13 @@ const noDeprecatedComponents = createEslintRule({
479
483
  VElement(element) {
480
484
  const tag = scule.pascalCase(element.rawName);
481
485
  const sourceCode = getSourceCode(context);
482
- if (!("getTemplateBodyTokenStore" in sourceCode.parserServices))
486
+ if (sourceCode?.parserServices && !("getTemplateBodyTokenStore" in sourceCode.parserServices))
483
487
  throw new Error("cannot find getTemplateBodyTokenStore in parserServices");
484
488
  if (!hasReplacement$1(tag))
485
489
  return;
486
490
  const replacement = replacements$1[tag];
487
491
  if (replacement || legacy && skipInLegacy.includes(tag)) {
488
- debug$1(`${tag} has been deprecated`);
492
+ debug$2(`${tag} has been deprecated`);
489
493
  context.report({
490
494
  data: {
491
495
  name: tag
@@ -494,7 +498,7 @@ const noDeprecatedComponents = createEslintRule({
494
498
  node: element
495
499
  });
496
500
  } else {
497
- debug$1(`${tag} has will be removed`);
501
+ debug$2(`${tag} has will be removed`);
498
502
  context.report({
499
503
  data: {
500
504
  name: tag
@@ -516,7 +520,7 @@ const noDeprecatedComponents = createEslintRule({
516
520
  meta: {
517
521
  docs: {
518
522
  description: "Removes deprecated classes that do not exist anymore",
519
- recommended: "recommended"
523
+ recommendation: "recommended"
520
524
  },
521
525
  fixable: "code",
522
526
  messages: {
@@ -537,11 +541,11 @@ const noDeprecatedComponents = createEslintRule({
537
541
  ],
538
542
  type: "problem"
539
543
  },
540
- name: RULE_NAME$2
544
+ name: RULE_NAME$3
541
545
  });
542
546
 
543
- const debug = createDebug__default("@rotki/eslint-plugin:no-deprecated-props");
544
- const RULE_NAME$1 = "no-deprecated-props";
547
+ const debug$1 = createDebug__default("@rotki/eslint-plugin:no-deprecated-props");
548
+ const RULE_NAME$2 = "no-deprecated-props";
545
549
  const replacements = {
546
550
  RuiRadio: {
547
551
  internalValue: "value"
@@ -567,16 +571,16 @@ const noDeprecatedProps = createEslintRule({
567
571
  const tag = scule.pascalCase(node.parent.parent.rawName);
568
572
  if (!hasReplacement(tag))
569
573
  return;
570
- debug(`${tag} has replacement properties`);
574
+ debug$1(`${tag} has replacement properties`);
571
575
  const propName = getPropName(node);
572
576
  const propNameNode = node.directive ? node.key.argument : node.key;
573
577
  if (!propName || !propNameNode) {
574
- debug("could not get prop name and/or node");
578
+ debug$1("could not get prop name and/or node");
575
579
  return;
576
580
  }
577
581
  Object.entries(replacements[tag]).forEach(([prop, replacement]) => {
578
582
  if (scule.kebabCase(prop) === propName) {
579
- debug(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
583
+ debug$1(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
580
584
  context.report({
581
585
  data: {
582
586
  prop,
@@ -605,10 +609,10 @@ const noDeprecatedProps = createEslintRule({
605
609
  schema: [],
606
610
  type: "problem"
607
611
  },
608
- name: RULE_NAME$1
612
+ name: RULE_NAME$2
609
613
  });
610
614
 
611
- const RULE_NAME = "no-legacy-library-import";
615
+ const RULE_NAME$1 = "no-legacy-library-import";
612
616
  const legacyLibrary = "@rotki/ui-library-compat";
613
617
  const newLibrary = "@rotki/ui-library";
614
618
  const noLegacyLibraryImport = createEslintRule({
@@ -640,6 +644,105 @@ const noLegacyLibraryImport = createEslintRule({
640
644
  schema: [],
641
645
  type: "problem"
642
646
  },
647
+ name: RULE_NAME$1
648
+ });
649
+
650
+ const debug = createDebug__default("@rotki/eslint-plugin:consistent-ref-type-annotation");
651
+ const RULE_NAME = "consistent-ref-type-annotation";
652
+ const FIXABLE_METHODS = ["ref", "computed"];
653
+ function checkAssignmentDeclaration(context, node, declaration, options) {
654
+ const source = getSourceCode(context);
655
+ const { allowInference } = options;
656
+ let declarationTypeArguments;
657
+ const init = declaration.init;
658
+ if (!(init && init.type === utils.TSESTree.AST_NODE_TYPES.CallExpression))
659
+ return;
660
+ const callee = init.callee;
661
+ if (!(callee && callee.type === utils.TSESTree.AST_NODE_TYPES.Identifier))
662
+ return;
663
+ if (!Array.prototype.includes.call(FIXABLE_METHODS, callee.name))
664
+ return;
665
+ const name = callee.name;
666
+ debug(`found ${name}, checking type arguments`);
667
+ const initializationTypeArguments = init.typeArguments;
668
+ const typeAnnotation = declaration.id.typeAnnotation;
669
+ if (typeAnnotation) {
670
+ const typeNode = typeAnnotation.typeAnnotation;
671
+ if (typeNode && typeNode.type === utils.TSESTree.AST_NODE_TYPES.TSTypeReference)
672
+ declarationTypeArguments = typeNode.typeArguments;
673
+ }
674
+ if (initializationTypeArguments && !declarationTypeArguments)
675
+ return;
676
+ debug(`generating report for ${name}`);
677
+ if (!initializationTypeArguments && !declarationTypeArguments) {
678
+ if (allowInference) {
679
+ debug("type inference is allowed");
680
+ } else {
681
+ context.report({
682
+ data: {
683
+ name
684
+ },
685
+ messageId: "missingType",
686
+ node
687
+ });
688
+ }
689
+ return;
690
+ }
691
+ context.report({
692
+ data: {
693
+ name
694
+ },
695
+ fix(fixer) {
696
+ const fixes = [];
697
+ if (!initializationTypeArguments && callee)
698
+ fixes.push(fixer.insertTextAfter(callee, source.getText(declarationTypeArguments)));
699
+ if (typeAnnotation)
700
+ fixes.push(fixer.remove(typeAnnotation));
701
+ return fixes;
702
+ },
703
+ messageId: "inconsistent",
704
+ node
705
+ });
706
+ }
707
+ const consistentRefTypeAnnotation = createEslintRule({
708
+ create(context, optionsWithDefault) {
709
+ const options = optionsWithDefault[0] || {};
710
+ const allowInference = options.allowInference;
711
+ return {
712
+ VariableDeclaration: (node) => {
713
+ const declarations = node.declarations;
714
+ for (const declaration of declarations) {
715
+ if (declaration.type !== utils.TSESTree.AST_NODE_TYPES.VariableDeclarator)
716
+ continue;
717
+ checkAssignmentDeclaration(context, node, declaration, { allowInference });
718
+ }
719
+ }
720
+ };
721
+ },
722
+ defaultOptions: [{ allowInference: false }],
723
+ meta: {
724
+ docs: {
725
+ description: "Ensures consistent type annotation position for ref, computed assignments",
726
+ recommendation: "recommended"
727
+ },
728
+ fixable: "code",
729
+ messages: {
730
+ inconsistent: `Generic type annotation for the {{ name }} call was not on the right side`,
731
+ missingType: `variable assignment for {{ name }} is missing the type annotation`
732
+ },
733
+ schema: [
734
+ {
735
+ additionalProperties: false,
736
+ properties: {
737
+ allowInference: {
738
+ type: "boolean"
739
+ }
740
+ },
741
+ type: "object"
742
+ }
743
+ ],
744
+ type: "problem"
745
+ },
643
746
  name: RULE_NAME
644
747
  });
645
748
 
@@ -649,6 +752,7 @@ const plugin = {
649
752
  version: pkg.version
650
753
  },
651
754
  rules: {
755
+ "consistent-ref-type-annotation": consistentRefTypeAnnotation,
652
756
  "no-deprecated-classes": noDeprecatedClasses,
653
757
  "no-deprecated-components": noDeprecatedComponents,
654
758
  "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.2";
8
- const packageManager = "pnpm@8.15.0";
8
+ const version = "0.5.0";
9
+ const packageManager = "pnpm@9.7.0";
9
10
  const type = "module";
10
11
  const license = "AGPL-3.0";
11
12
  const bugs = {
@@ -44,48 +45,48 @@ const scripts = {
44
45
  "new": "node --experimental-specifier-resolution=node --loader ts-node/esm ./scripts/new-rule.ts",
45
46
  docs: "vitepress dev docs",
46
47
  "docs:build": "pnpm run generate && vitepress build docs",
47
- prepare: "husky install",
48
+ prepare: "husky",
48
49
  typecheck: "tsc --noEmit",
49
50
  release: "bumpp -r --no-push"
50
51
  };
51
52
  const peerDependencies = {
52
- eslint: "^8.0.0 || ^9.0.0"
53
+ eslint: "^9.0.0"
53
54
  };
54
55
  const dependencies = {
55
- "@typescript-eslint/utils": "6.19.1",
56
- debug: "4.3.4",
57
- "eslint-compat-utils": "0.4.1",
56
+ "@typescript-eslint/utils": "8.1.0",
57
+ debug: "4.3.6",
58
+ "eslint-compat-utils": "0.5.1",
58
59
  "jsonc-eslint-parser": "2.4.0",
59
- scule: "1.2.0",
60
- "vue-eslint-parser": "9.4.2",
61
- "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"
62
63
  };
63
64
  const devDependencies = {
64
- "@commitlint/cli": "18.6.0",
65
- "@commitlint/config-conventional": "18.6.0",
66
- "@rotki/eslint-config": "2.4.4",
65
+ "@commitlint/cli": "19.4.0",
66
+ "@commitlint/config-conventional": "19.2.2",
67
+ "@rotki/eslint-config": "3.0.0",
67
68
  "@types/debug": "4.1.12",
68
- "@types/eslint": "8.56.2",
69
+ "@types/eslint": "9.6.0",
69
70
  "@types/node": "20",
70
- "@typescript-eslint/eslint-plugin": "6.19.1",
71
- "@typescript-eslint/parser": "6.19.1",
72
- "@typescript-eslint/rule-tester": "6.19.1",
73
- "@vitest/coverage-v8": "^1.2.2",
74
- bumpp: "9.3.0",
75
- debug: "4.3.4",
76
- eslint: "8.56.0",
77
- husky: "9.0.7",
78
- "lint-staged": "15.2.0",
79
- rimraf: "5.0.5",
71
+ "@typescript-eslint/eslint-plugin": "8.1.0",
72
+ "@typescript-eslint/parser": "8.1.0",
73
+ "@typescript-eslint/rule-tester": "8.1.0",
74
+ "@vitest/coverage-v8": "2.0.5",
75
+ bumpp: "9.5.1",
76
+ debug: "4.3.6",
77
+ eslint: "9.9.0",
78
+ husky: "9.1.4",
79
+ "lint-staged": "15.2.9",
80
+ rimraf: "6.0.1",
80
81
  "ts-node": "10.9.2",
81
- typescript: "5.3.3",
82
+ typescript: "5.5.4",
82
83
  unbuild: "2.0.0",
83
- vitepress: "1.0.0-rc.40",
84
- vitest: "1.2.2"
84
+ vitepress: "1.3.2",
85
+ vitest: "2.0.5"
85
86
  };
86
87
  const engines = {
87
88
  node: ">=20",
88
- pnpm: ">=8 <9"
89
+ pnpm: ">=9 <10"
89
90
  };
90
91
  const pkg = {
91
92
  name: name,
@@ -162,6 +163,8 @@ const createEslintRule = RuleCreator(
162
163
  function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor, options) {
163
164
  const sourceCode = getSourceCode(context);
164
165
  const parserServices = sourceCode.parserServices;
166
+ if (!parserServices)
167
+ throw new Error("missing parserServices");
165
168
  if (!("defineTemplateBodyVisitor" in parserServices) || parserServices.defineTemplateBodyVisitor == null) {
166
169
  const filename = getFilename(context);
167
170
  if (extname(filename) === ".vue") {
@@ -192,8 +195,9 @@ function getStringLiteralValue(node, stringOnly = false) {
192
195
  return String(node.value);
193
196
  return null;
194
197
  }
195
- if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1)
198
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
196
199
  return node.quasis[0].value.cooked;
200
+ }
197
201
  return null;
198
202
  }
199
203
  function getStaticPropertyName(node) {
@@ -235,8 +239,8 @@ function createRecommended(plugin, name, flat) {
235
239
  }
236
240
  }
237
241
 
238
- const RULE_NAME$3 = "no-deprecated-classes";
239
- const debug$2 = createDebug("@rotki/eslint-plugin:no-deprecated-classes");
242
+ const RULE_NAME$4 = "no-deprecated-classes";
243
+ const debug$3 = createDebug("@rotki/eslint-plugin:no-deprecated-classes");
240
244
  const replacements$2 = [
241
245
  ["d-block", "block"],
242
246
  ["d-flex", "flex"],
@@ -296,7 +300,7 @@ function getRange(node) {
296
300
  return node.range;
297
301
  }
298
302
  function reportReplacement(className, replacement, node, context, position = 1) {
299
- debug$2(`found replacement ${replacement} for ${className}`);
303
+ debug$3(`found replacement ${replacement} for ${className}`);
300
304
  const source = getSourceCode(context);
301
305
  const initialRange = getRange(node);
302
306
  const range = [
@@ -400,7 +404,7 @@ const noDeprecatedClasses = createEslintRule({
400
404
  meta: {
401
405
  docs: {
402
406
  description: "disallow the usage of vuetify css classes since they are replaced with tailwindcss",
403
- recommended: "recommended"
407
+ recommendation: "recommended"
404
408
  },
405
409
  fixable: "code",
406
410
  messages: {
@@ -409,11 +413,11 @@ const noDeprecatedClasses = createEslintRule({
409
413
  schema: [],
410
414
  type: "problem"
411
415
  },
412
- name: RULE_NAME$3
416
+ name: RULE_NAME$4
413
417
  });
414
418
 
415
- const debug$1 = createDebug("@rotki/eslint-plugin:no-deprecated-components");
416
- const RULE_NAME$2 = "no-deprecated-components";
419
+ const debug$2 = createDebug("@rotki/eslint-plugin:no-deprecated-components");
420
+ const RULE_NAME$3 = "no-deprecated-components";
417
421
  const vuetify = {
418
422
  VApp: true,
419
423
  VAppBar: true,
@@ -460,13 +464,13 @@ const noDeprecatedComponents = createEslintRule({
460
464
  VElement(element) {
461
465
  const tag = pascalCase(element.rawName);
462
466
  const sourceCode = getSourceCode(context);
463
- if (!("getTemplateBodyTokenStore" in sourceCode.parserServices))
467
+ if (sourceCode?.parserServices && !("getTemplateBodyTokenStore" in sourceCode.parserServices))
464
468
  throw new Error("cannot find getTemplateBodyTokenStore in parserServices");
465
469
  if (!hasReplacement$1(tag))
466
470
  return;
467
471
  const replacement = replacements$1[tag];
468
472
  if (replacement || legacy && skipInLegacy.includes(tag)) {
469
- debug$1(`${tag} has been deprecated`);
473
+ debug$2(`${tag} has been deprecated`);
470
474
  context.report({
471
475
  data: {
472
476
  name: tag
@@ -475,7 +479,7 @@ const noDeprecatedComponents = createEslintRule({
475
479
  node: element
476
480
  });
477
481
  } else {
478
- debug$1(`${tag} has will be removed`);
482
+ debug$2(`${tag} has will be removed`);
479
483
  context.report({
480
484
  data: {
481
485
  name: tag
@@ -497,7 +501,7 @@ const noDeprecatedComponents = createEslintRule({
497
501
  meta: {
498
502
  docs: {
499
503
  description: "Removes deprecated classes that do not exist anymore",
500
- recommended: "recommended"
504
+ recommendation: "recommended"
501
505
  },
502
506
  fixable: "code",
503
507
  messages: {
@@ -518,11 +522,11 @@ const noDeprecatedComponents = createEslintRule({
518
522
  ],
519
523
  type: "problem"
520
524
  },
521
- name: RULE_NAME$2
525
+ name: RULE_NAME$3
522
526
  });
523
527
 
524
- const debug = createDebug("@rotki/eslint-plugin:no-deprecated-props");
525
- const RULE_NAME$1 = "no-deprecated-props";
528
+ const debug$1 = createDebug("@rotki/eslint-plugin:no-deprecated-props");
529
+ const RULE_NAME$2 = "no-deprecated-props";
526
530
  const replacements = {
527
531
  RuiRadio: {
528
532
  internalValue: "value"
@@ -548,16 +552,16 @@ const noDeprecatedProps = createEslintRule({
548
552
  const tag = pascalCase(node.parent.parent.rawName);
549
553
  if (!hasReplacement(tag))
550
554
  return;
551
- debug(`${tag} has replacement properties`);
555
+ debug$1(`${tag} has replacement properties`);
552
556
  const propName = getPropName(node);
553
557
  const propNameNode = node.directive ? node.key.argument : node.key;
554
558
  if (!propName || !propNameNode) {
555
- debug("could not get prop name and/or node");
559
+ debug$1("could not get prop name and/or node");
556
560
  return;
557
561
  }
558
562
  Object.entries(replacements[tag]).forEach(([prop, replacement]) => {
559
563
  if (kebabCase(prop) === propName) {
560
- debug(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
564
+ debug$1(`preparing a replacement for ${tag}:${propName} -> ${replacement}`);
561
565
  context.report({
562
566
  data: {
563
567
  prop,
@@ -586,10 +590,10 @@ const noDeprecatedProps = createEslintRule({
586
590
  schema: [],
587
591
  type: "problem"
588
592
  },
589
- name: RULE_NAME$1
593
+ name: RULE_NAME$2
590
594
  });
591
595
 
592
- const RULE_NAME = "no-legacy-library-import";
596
+ const RULE_NAME$1 = "no-legacy-library-import";
593
597
  const legacyLibrary = "@rotki/ui-library-compat";
594
598
  const newLibrary = "@rotki/ui-library";
595
599
  const noLegacyLibraryImport = createEslintRule({
@@ -621,6 +625,105 @@ const noLegacyLibraryImport = createEslintRule({
621
625
  schema: [],
622
626
  type: "problem"
623
627
  },
628
+ name: RULE_NAME$1
629
+ });
630
+
631
+ const debug = createDebug("@rotki/eslint-plugin:consistent-ref-type-annotation");
632
+ const RULE_NAME = "consistent-ref-type-annotation";
633
+ const FIXABLE_METHODS = ["ref", "computed"];
634
+ function checkAssignmentDeclaration(context, node, declaration, options) {
635
+ const source = getSourceCode(context);
636
+ const { allowInference } = options;
637
+ let declarationTypeArguments;
638
+ const init = declaration.init;
639
+ if (!(init && init.type === TSESTree.AST_NODE_TYPES.CallExpression))
640
+ return;
641
+ const callee = init.callee;
642
+ if (!(callee && callee.type === TSESTree.AST_NODE_TYPES.Identifier))
643
+ return;
644
+ if (!Array.prototype.includes.call(FIXABLE_METHODS, callee.name))
645
+ return;
646
+ const name = callee.name;
647
+ debug(`found ${name}, checking type arguments`);
648
+ const initializationTypeArguments = init.typeArguments;
649
+ const typeAnnotation = declaration.id.typeAnnotation;
650
+ if (typeAnnotation) {
651
+ const typeNode = typeAnnotation.typeAnnotation;
652
+ if (typeNode && typeNode.type === TSESTree.AST_NODE_TYPES.TSTypeReference)
653
+ declarationTypeArguments = typeNode.typeArguments;
654
+ }
655
+ if (initializationTypeArguments && !declarationTypeArguments)
656
+ return;
657
+ debug(`generating report for ${name}`);
658
+ if (!initializationTypeArguments && !declarationTypeArguments) {
659
+ if (allowInference) {
660
+ debug("type inference is allowed");
661
+ } else {
662
+ context.report({
663
+ data: {
664
+ name
665
+ },
666
+ messageId: "missingType",
667
+ node
668
+ });
669
+ }
670
+ return;
671
+ }
672
+ context.report({
673
+ data: {
674
+ name
675
+ },
676
+ fix(fixer) {
677
+ const fixes = [];
678
+ if (!initializationTypeArguments && callee)
679
+ fixes.push(fixer.insertTextAfter(callee, source.getText(declarationTypeArguments)));
680
+ if (typeAnnotation)
681
+ fixes.push(fixer.remove(typeAnnotation));
682
+ return fixes;
683
+ },
684
+ messageId: "inconsistent",
685
+ node
686
+ });
687
+ }
688
+ const consistentRefTypeAnnotation = createEslintRule({
689
+ create(context, optionsWithDefault) {
690
+ const options = optionsWithDefault[0] || {};
691
+ const allowInference = options.allowInference;
692
+ return {
693
+ VariableDeclaration: (node) => {
694
+ const declarations = node.declarations;
695
+ for (const declaration of declarations) {
696
+ if (declaration.type !== TSESTree.AST_NODE_TYPES.VariableDeclarator)
697
+ continue;
698
+ checkAssignmentDeclaration(context, node, declaration, { allowInference });
699
+ }
700
+ }
701
+ };
702
+ },
703
+ defaultOptions: [{ allowInference: false }],
704
+ meta: {
705
+ docs: {
706
+ description: "Ensures consistent type annotation position for ref, computed assignments",
707
+ recommendation: "recommended"
708
+ },
709
+ fixable: "code",
710
+ messages: {
711
+ inconsistent: `Generic type annotation for the {{ name }} call was not on the right side`,
712
+ missingType: `variable assignment for {{ name }} is missing the type annotation`
713
+ },
714
+ schema: [
715
+ {
716
+ additionalProperties: false,
717
+ properties: {
718
+ allowInference: {
719
+ type: "boolean"
720
+ }
721
+ },
722
+ type: "object"
723
+ }
724
+ ],
725
+ type: "problem"
726
+ },
624
727
  name: RULE_NAME
625
728
  });
626
729
 
@@ -630,6 +733,7 @@ const plugin = {
630
733
  version: pkg.version
631
734
  },
632
735
  rules: {
736
+ "consistent-ref-type-annotation": consistentRefTypeAnnotation,
633
737
  "no-deprecated-classes": noDeprecatedClasses,
634
738
  "no-deprecated-components": noDeprecatedComponents,
635
739
  "no-deprecated-props": noDeprecatedProps,
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "@rotki/eslint-plugin",
3
- "version": "0.3.2",
4
- "packageManager": "pnpm@8.15.0",
3
+ "version": "0.5.0",
5
4
  "type": "module",
6
5
  "license": "AGPL-3.0",
7
6
  "bugs": {
@@ -27,43 +26,43 @@
27
26
  },
28
27
  "sideEffects": false,
29
28
  "peerDependencies": {
30
- "eslint": "^8.0.0 || ^9.0.0"
29
+ "eslint": "^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": "8.1.0",
33
+ "debug": "4.3.6",
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.4.0",
42
+ "@commitlint/config-conventional": "19.2.2",
43
+ "@rotki/eslint-config": "3.0.0",
45
44
  "@types/debug": "4.1.12",
46
- "@types/eslint": "8.56.2",
45
+ "@types/eslint": "9.6.0",
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
- "@vitest/coverage-v8": "^1.2.2",
52
- "bumpp": "9.3.0",
53
- "debug": "4.3.4",
54
- "eslint": "8.56.0",
55
- "husky": "9.0.7",
56
- "lint-staged": "15.2.0",
57
- "rimraf": "5.0.5",
47
+ "@typescript-eslint/eslint-plugin": "8.1.0",
48
+ "@typescript-eslint/parser": "8.1.0",
49
+ "@typescript-eslint/rule-tester": "8.1.0",
50
+ "@vitest/coverage-v8": "2.0.5",
51
+ "bumpp": "9.5.1",
52
+ "debug": "4.3.6",
53
+ "eslint": "9.9.0",
54
+ "husky": "9.1.4",
55
+ "lint-staged": "15.2.9",
56
+ "rimraf": "6.0.1",
58
57
  "ts-node": "10.9.2",
59
- "typescript": "5.3.3",
58
+ "typescript": "5.5.4",
60
59
  "unbuild": "2.0.0",
61
- "vitepress": "1.0.0-rc.40",
62
- "vitest": "1.2.2"
60
+ "vitepress": "1.3.2",
61
+ "vitest": "2.0.5"
63
62
  },
64
63
  "engines": {
65
64
  "node": ">=20",
66
- "pnpm": ">=8 <9"
65
+ "pnpm": ">=9 <10"
67
66
  },
68
67
  "lint-staged": {
69
68
  "*.{js,cjs,ts,vue,yml,json,md}": "eslint"