@rotki/eslint-plugin 1.4.0 → 1.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.d.mts CHANGED
@@ -4,7 +4,7 @@ interface PluginRuleModule<TOptions extends readonly unknown[] = []> extends Rul
4
4
  defaultOptions: TOptions;
5
5
  }
6
6
 
7
- type Options$6 = [
7
+ type Options$7 = [
8
8
  {
9
9
  extensions: string[];
10
10
  ignoreKeys: string[];
@@ -12,6 +12,11 @@ type Options$6 = [
12
12
  }
13
13
  ];
14
14
 
15
+ interface Options$6 {
16
+ factories: string[];
17
+ testFilePattern: string;
18
+ }
19
+
15
20
  type Options$5 = [{
16
21
  legacy: boolean;
17
22
  }];
@@ -59,7 +64,8 @@ declare const plugin: {
59
64
  'no-dot-ts-imports': PluginRuleModule<[]>;
60
65
  'no-legacy-library-import': PluginRuleModule<[]>;
61
66
  'no-redundant-flex-row': PluginRuleModule<[]>;
62
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
67
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
68
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
63
69
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
64
70
  };
65
71
  };
@@ -85,7 +91,8 @@ declare const _default: {
85
91
  'no-dot-ts-imports': PluginRuleModule<[]>;
86
92
  'no-legacy-library-import': PluginRuleModule<[]>;
87
93
  'no-redundant-flex-row': PluginRuleModule<[]>;
88
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
94
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
95
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
89
96
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
90
97
  };
91
98
  } & {
@@ -113,7 +120,8 @@ declare const _default: {
113
120
  'no-dot-ts-imports': PluginRuleModule<[]>;
114
121
  'no-legacy-library-import': PluginRuleModule<[]>;
115
122
  'no-redundant-flex-row': PluginRuleModule<[]>;
116
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
123
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
124
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
117
125
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
118
126
  };
119
127
  };
@@ -150,7 +158,8 @@ declare const _default: {
150
158
  'no-dot-ts-imports': PluginRuleModule<[]>;
151
159
  'no-legacy-library-import': PluginRuleModule<[]>;
152
160
  'no-redundant-flex-row': PluginRuleModule<[]>;
153
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
161
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
162
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
154
163
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
155
164
  };
156
165
  };
@@ -187,7 +196,8 @@ declare const _default: {
187
196
  'no-dot-ts-imports': PluginRuleModule<[]>;
188
197
  'no-legacy-library-import': PluginRuleModule<[]>;
189
198
  'no-redundant-flex-row': PluginRuleModule<[]>;
190
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
199
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
200
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
191
201
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
192
202
  };
193
203
  };
@@ -224,7 +234,8 @@ declare const _default: {
224
234
  'no-dot-ts-imports': PluginRuleModule<[]>;
225
235
  'no-legacy-library-import': PluginRuleModule<[]>;
226
236
  'no-redundant-flex-row': PluginRuleModule<[]>;
227
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
237
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
238
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
228
239
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
229
240
  };
230
241
  };
@@ -261,7 +272,8 @@ declare const _default: {
261
272
  'no-dot-ts-imports': PluginRuleModule<[]>;
262
273
  'no-legacy-library-import': PluginRuleModule<[]>;
263
274
  'no-redundant-flex-row': PluginRuleModule<[]>;
264
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
275
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
276
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
265
277
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
266
278
  };
267
279
  };
@@ -298,7 +310,8 @@ declare const _default: {
298
310
  'no-dot-ts-imports': PluginRuleModule<[]>;
299
311
  'no-legacy-library-import': PluginRuleModule<[]>;
300
312
  'no-redundant-flex-row': PluginRuleModule<[]>;
301
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
313
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
314
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
302
315
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
303
316
  };
304
317
  };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ interface PluginRuleModule<TOptions extends readonly unknown[] = []> extends Rul
4
4
  defaultOptions: TOptions;
5
5
  }
6
6
 
7
- type Options$6 = [
7
+ type Options$7 = [
8
8
  {
9
9
  extensions: string[];
10
10
  ignoreKeys: string[];
@@ -12,6 +12,11 @@ type Options$6 = [
12
12
  }
13
13
  ];
14
14
 
15
+ interface Options$6 {
16
+ factories: string[];
17
+ testFilePattern: string;
18
+ }
19
+
15
20
  type Options$5 = [{
16
21
  legacy: boolean;
17
22
  }];
@@ -59,7 +64,8 @@ declare const plugin: {
59
64
  'no-dot-ts-imports': PluginRuleModule<[]>;
60
65
  'no-legacy-library-import': PluginRuleModule<[]>;
61
66
  'no-redundant-flex-row': PluginRuleModule<[]>;
62
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
67
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
68
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
63
69
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
64
70
  };
65
71
  };
@@ -85,7 +91,8 @@ declare const _default: {
85
91
  'no-dot-ts-imports': PluginRuleModule<[]>;
86
92
  'no-legacy-library-import': PluginRuleModule<[]>;
87
93
  'no-redundant-flex-row': PluginRuleModule<[]>;
88
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
94
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
95
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
89
96
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
90
97
  };
91
98
  } & {
@@ -113,7 +120,8 @@ declare const _default: {
113
120
  'no-dot-ts-imports': PluginRuleModule<[]>;
114
121
  'no-legacy-library-import': PluginRuleModule<[]>;
115
122
  'no-redundant-flex-row': PluginRuleModule<[]>;
116
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
123
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
124
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
117
125
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
118
126
  };
119
127
  };
@@ -150,7 +158,8 @@ declare const _default: {
150
158
  'no-dot-ts-imports': PluginRuleModule<[]>;
151
159
  'no-legacy-library-import': PluginRuleModule<[]>;
152
160
  'no-redundant-flex-row': PluginRuleModule<[]>;
153
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
161
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
162
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
154
163
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
155
164
  };
156
165
  };
@@ -187,7 +196,8 @@ declare const _default: {
187
196
  'no-dot-ts-imports': PluginRuleModule<[]>;
188
197
  'no-legacy-library-import': PluginRuleModule<[]>;
189
198
  'no-redundant-flex-row': PluginRuleModule<[]>;
190
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
199
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
200
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
191
201
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
192
202
  };
193
203
  };
@@ -224,7 +234,8 @@ declare const _default: {
224
234
  'no-dot-ts-imports': PluginRuleModule<[]>;
225
235
  'no-legacy-library-import': PluginRuleModule<[]>;
226
236
  'no-redundant-flex-row': PluginRuleModule<[]>;
227
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
237
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
238
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
228
239
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
229
240
  };
230
241
  };
@@ -261,7 +272,8 @@ declare const _default: {
261
272
  'no-dot-ts-imports': PluginRuleModule<[]>;
262
273
  'no-legacy-library-import': PluginRuleModule<[]>;
263
274
  'no-redundant-flex-row': PluginRuleModule<[]>;
264
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
275
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
276
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
265
277
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
266
278
  };
267
279
  };
@@ -298,7 +310,8 @@ declare const _default: {
298
310
  'no-dot-ts-imports': PluginRuleModule<[]>;
299
311
  'no-legacy-library-import': PluginRuleModule<[]>;
300
312
  'no-redundant-flex-row': PluginRuleModule<[]>;
301
- 'no-unused-i18n-keys': PluginRuleModule<Options$6>;
313
+ 'no-shared-pinia': PluginRuleModule<[Options$6]>;
314
+ 'no-unused-i18n-keys': PluginRuleModule<Options$7>;
302
315
  'require-jsdoc-on-composable-options': PluginRuleModule<[]>;
303
316
  };
304
317
  };
package/dist/index.mjs CHANGED
@@ -6,7 +6,7 @@ import { statSync, readFileSync } from 'node:fs';
6
6
  import { globSync } from 'tinyglobby';
7
7
  import { parse } from 'vue-eslint-parser';
8
8
 
9
- const version = "1.4.0";
9
+ const version = "1.5.0";
10
10
  const pkg = {
11
11
  version: version};
12
12
 
@@ -127,13 +127,13 @@ function createRule({
127
127
  meta
128
128
  }) {
129
129
  return {
130
- create: ((context) => {
130
+ create: (context) => {
131
131
  const optionsWithDefault = context.options.map((options, index) => ({
132
132
  ...defaultOptions[index] || {},
133
133
  ...options || {}
134
134
  }));
135
135
  return create(context, optionsWithDefault);
136
- }),
136
+ },
137
137
  defaultOptions,
138
138
  meta
139
139
  };
@@ -164,7 +164,7 @@ function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor,
164
164
  );
165
165
  }
166
166
 
167
- const RULE_NAME$g = "composable-input-flexibility";
167
+ const RULE_NAME$h = "composable-input-flexibility";
168
168
  function checkParamForRef(param) {
169
169
  let annotation;
170
170
  if (param.type === AST_NODE_TYPES.Identifier && param.typeAnnotation) {
@@ -177,44 +177,73 @@ function checkParamForRef(param) {
177
177
  }
178
178
  return void 0;
179
179
  }
180
+ function getParamName(param) {
181
+ if (param.type === AST_NODE_TYPES.Identifier)
182
+ return param.name;
183
+ if (param.type === AST_NODE_TYPES.AssignmentPattern && param.left.type === AST_NODE_TYPES.Identifier)
184
+ return param.left.name;
185
+ return void 0;
186
+ }
187
+ function isWriteReference(reference) {
188
+ const { identifier } = reference;
189
+ const parent = identifier.parent;
190
+ if (parent.type === AST_NODE_TYPES.CallExpression) {
191
+ return parent.callee.type === AST_NODE_TYPES.Identifier && parent.callee.name === "set" && parent.arguments[0] === identifier;
192
+ }
193
+ if (parent.type === AST_NODE_TYPES.MemberExpression && parent.object === identifier && !parent.computed && parent.property.type === AST_NODE_TYPES.Identifier && parent.property.name === "value") {
194
+ const grandparent = parent.parent;
195
+ if (grandparent.type === AST_NODE_TYPES.AssignmentExpression)
196
+ return grandparent.left === parent;
197
+ return grandparent.type === AST_NODE_TYPES.UpdateExpression;
198
+ }
199
+ return false;
200
+ }
201
+ function isWrittenParam(scope, name) {
202
+ const variable = scope.variables.find((candidate) => candidate.name === name);
203
+ return variable?.references.some(isWriteReference) ?? false;
204
+ }
180
205
  const composableInputFlexibility = createEslintRule({
181
206
  create(context) {
182
207
  const autofix = context.options[0]?.autofix ?? false;
183
208
  return {
184
209
  FunctionDeclaration: (node) => {
185
- if (!node.id || !isComposableName(node.id.name))
210
+ if (!node.id || !isComposableName(node.id.name) || !node.body)
186
211
  return;
187
- checkParams(node.params);
212
+ checkParams(node.params, node);
188
213
  },
189
214
  VariableDeclarator: (node) => {
190
215
  if (node.id.type !== AST_NODE_TYPES.Identifier || !isComposableName(node.id.name))
191
216
  return;
192
217
  if (node.init?.type === AST_NODE_TYPES.ArrowFunctionExpression || node.init?.type === AST_NODE_TYPES.FunctionExpression) {
193
- checkParams(node.init.params);
218
+ checkParams(node.init.params, node.init);
194
219
  }
195
220
  }
196
221
  };
197
- function checkParams(params) {
222
+ function checkParams(params, fn) {
223
+ const scope = context.sourceCode.getScope(fn);
198
224
  for (const param of params) {
199
225
  const refType = checkParamForRef(param);
200
- if (refType) {
201
- context.report({
202
- ...autofix ? {
226
+ if (!refType)
227
+ continue;
228
+ const name = getParamName(param);
229
+ if (name && isWrittenParam(scope, name))
230
+ continue;
231
+ context.report({
232
+ ...autofix ? {
233
+ fix(fixer) {
234
+ return fixer.replaceText(refType.typeName, "MaybeRefOrGetter");
235
+ }
236
+ } : {
237
+ suggest: [{
203
238
  fix(fixer) {
204
239
  return fixer.replaceText(refType.typeName, "MaybeRefOrGetter");
205
- }
206
- } : {
207
- suggest: [{
208
- fix(fixer) {
209
- return fixer.replaceText(refType.typeName, "MaybeRefOrGetter");
210
- },
211
- messageId: "suggestMaybeRefOrGetter"
212
- }]
213
- },
214
- messageId: "preferMaybeRefOrGetter",
215
- node: refType
216
- });
217
- }
240
+ },
241
+ messageId: "suggestMaybeRefOrGetter"
242
+ }]
243
+ },
244
+ messageId: "preferMaybeRefOrGetter",
245
+ node: refType
246
+ });
218
247
  }
219
248
  }
220
249
  },
@@ -245,10 +274,10 @@ const composableInputFlexibility = createEslintRule({
245
274
  ],
246
275
  type: "suggestion"
247
276
  },
248
- name: RULE_NAME$g
277
+ name: RULE_NAME$h
249
278
  });
250
279
 
251
- const RULE_NAME$f = "composable-naming-convention";
280
+ const RULE_NAME$g = "composable-naming-convention";
252
281
  function getComposableFunction(node) {
253
282
  if (node.type === AST_NODE_TYPES.FunctionDeclaration) {
254
283
  if (!node.id || !isComposableName(node.id.name))
@@ -321,10 +350,10 @@ const composableNamingConvention = createEslintRule({
321
350
  schema: [],
322
351
  type: "suggestion"
323
352
  },
324
- name: RULE_NAME$f
353
+ name: RULE_NAME$g
325
354
  });
326
355
 
327
- const RULE_NAME$e = "composable-no-default-export";
356
+ const RULE_NAME$f = "composable-no-default-export";
328
357
  const composableNoDefaultExport = createEslintRule({
329
358
  create(context) {
330
359
  let hasComposable = false;
@@ -364,10 +393,10 @@ const composableNoDefaultExport = createEslintRule({
364
393
  schema: [],
365
394
  type: "problem"
366
395
  },
367
- name: RULE_NAME$e
396
+ name: RULE_NAME$f
368
397
  });
369
398
 
370
- const RULE_NAME$d = "composable-prefer-shallowref";
399
+ const RULE_NAME$e = "composable-prefer-shallowref";
371
400
  const composablePreferShallowref = createEslintRule({
372
401
  create(context) {
373
402
  const autofix = context.options[0]?.autofix ?? false;
@@ -426,10 +455,10 @@ const composablePreferShallowref = createEslintRule({
426
455
  ],
427
456
  type: "suggestion"
428
457
  },
429
- name: RULE_NAME$d
458
+ name: RULE_NAME$e
430
459
  });
431
460
 
432
- const RULE_NAME$c = "composable-require-cleanup";
461
+ const RULE_NAME$d = "composable-require-cleanup";
433
462
  const SIDE_EFFECT_CALLS = /* @__PURE__ */ new Set(["addEventListener", "setInterval", "setTimeout"]);
434
463
  const SIDE_EFFECT_CONSTRUCTORS = /* @__PURE__ */ new Set(["MutationObserver", "ResizeObserver", "IntersectionObserver"]);
435
464
  const CLEANUP_HOOKS = /* @__PURE__ */ new Set(["onUnmounted", "onBeforeUnmount", "onScopeDispose", "tryOnScopeDispose"]);
@@ -523,10 +552,10 @@ const composableRequireCleanup = createEslintRule({
523
552
  schema: [],
524
553
  type: "problem"
525
554
  },
526
- name: RULE_NAME$c
555
+ name: RULE_NAME$d
527
556
  });
528
557
 
529
- const RULE_NAME$b = "composable-return-readonly";
558
+ const RULE_NAME$c = "composable-return-readonly";
530
559
  const DEFAULT_WRITABLE_PREFIXES = ["model"];
531
560
  const REACTIVE_CREATORS = /* @__PURE__ */ new Set(["ref", "shallowRef"]);
532
561
  const READONLY_CREATORS = /* @__PURE__ */ new Set(["computed"]);
@@ -643,10 +672,10 @@ const composableReturnReadonly = createEslintRule({
643
672
  ],
644
673
  type: "suggestion"
645
674
  },
646
- name: RULE_NAME$b
675
+ name: RULE_NAME$c
647
676
  });
648
677
 
649
- const RULE_NAME$a = "composable-ssr-safety";
678
+ const RULE_NAME$b = "composable-ssr-safety";
650
679
  const BROWSER_GLOBALS = /* @__PURE__ */ new Set(["window", "document", "navigator"]);
651
680
  const LIFECYCLE_HOOKS = /* @__PURE__ */ new Set(["onMounted", "onBeforeMount"]);
652
681
  function isInsideLifecycleHook(node) {
@@ -734,11 +763,11 @@ const composableSsrSafety = createEslintRule({
734
763
  schema: [],
735
764
  type: "problem"
736
765
  },
737
- name: RULE_NAME$a
766
+ name: RULE_NAME$b
738
767
  });
739
768
 
740
- const debug$6 = debugFactory("@rotki/eslint-plugin:consistent-ref-type-annotation");
741
- const RULE_NAME$9 = "consistent-ref-type-annotation";
769
+ const debug$7 = debugFactory("@rotki/eslint-plugin:consistent-ref-type-annotation");
770
+ const RULE_NAME$a = "consistent-ref-type-annotation";
742
771
  const FIXABLE_METHODS = /* @__PURE__ */ new Set(["ref", "computed"]);
743
772
  function getFixableCallExpression(declaration) {
744
773
  const init = declaration.init;
@@ -761,13 +790,13 @@ function checkAssignmentDeclaration(context, source, node, declaration, allowInf
761
790
  return;
762
791
  const { callee, init } = call;
763
792
  const name = callee.name;
764
- debug$6(`found ${name}, checking type arguments`);
793
+ debug$7(`found ${name}, checking type arguments`);
765
794
  const initTypeArgs = init.typeArguments;
766
795
  const declTypeArgs = getDeclarationTypeArguments(declaration);
767
796
  const typeAnnotation = declaration.id.typeAnnotation;
768
797
  if (initTypeArgs && !declTypeArgs)
769
798
  return;
770
- debug$6(`generating report for ${name}`);
799
+ debug$7(`generating report for ${name}`);
771
800
  if (!initTypeArgs && !declTypeArgs) {
772
801
  if (!allowInference)
773
802
  context.report({ data: { name }, messageId: "missingType", node });
@@ -827,10 +856,10 @@ const consistentRefTypeAnnotation = createEslintRule({
827
856
  ],
828
857
  type: "problem"
829
858
  },
830
- name: RULE_NAME$9
859
+ name: RULE_NAME$a
831
860
  });
832
861
 
833
- const RULE_NAME$8 = "max-dependencies";
862
+ const RULE_NAME$9 = "max-dependencies";
834
863
  const maxDependencies = createEslintRule({
835
864
  create(context, [options]) {
836
865
  let dependencyCount = 0;
@@ -890,11 +919,11 @@ const maxDependencies = createEslintRule({
890
919
  ],
891
920
  type: "suggestion"
892
921
  },
893
- name: RULE_NAME$8
922
+ name: RULE_NAME$9
894
923
  });
895
924
 
896
- const RULE_NAME$7 = "no-deprecated-classes";
897
- const debug$5 = debugFactory("@rotki/eslint-plugin:no-deprecated-classes");
925
+ const RULE_NAME$8 = "no-deprecated-classes";
926
+ const debug$6 = debugFactory("@rotki/eslint-plugin:no-deprecated-classes");
898
927
  const stringReplacements = /* @__PURE__ */ new Map([
899
928
  ["d-block", "block"],
900
929
  ["d-flex", "flex"],
@@ -948,7 +977,7 @@ function getRange(node) {
948
977
  return node.range;
949
978
  }
950
979
  function reportReplacement(className, replacement, node, context, source, position = 1) {
951
- debug$5(`found replacement ${replacement} for ${className}`);
980
+ debug$6(`found replacement ${replacement} for ${className}`);
952
981
  const initialRange = getRange(node);
953
982
  const range = [
954
983
  initialRange[0] + position,
@@ -1072,11 +1101,11 @@ const noDeprecatedClasses = createEslintRule({
1072
1101
  schema: [],
1073
1102
  type: "problem"
1074
1103
  },
1075
- name: RULE_NAME$7
1104
+ name: RULE_NAME$8
1076
1105
  });
1077
1106
 
1078
- const debug$4 = debugFactory("@rotki/eslint-plugin:no-deprecated-components");
1079
- const RULE_NAME$6 = "no-deprecated-components";
1107
+ const debug$5 = debugFactory("@rotki/eslint-plugin:no-deprecated-components");
1108
+ const RULE_NAME$7 = "no-deprecated-components";
1080
1109
  const vuetify = {
1081
1110
  VApp: true,
1082
1111
  VAppBar: true,
@@ -1129,7 +1158,7 @@ const noDeprecatedComponents = createEslintRule({
1129
1158
  return;
1130
1159
  const replacement = replacements$1[tag];
1131
1160
  if (replacement || legacy && skipInLegacy.has(tag)) {
1132
- debug$4(`${tag} has been deprecated`);
1161
+ debug$5(`${tag} has been deprecated`);
1133
1162
  context.report({
1134
1163
  data: {
1135
1164
  name: tag
@@ -1138,7 +1167,7 @@ const noDeprecatedComponents = createEslintRule({
1138
1167
  node: element
1139
1168
  });
1140
1169
  } else {
1141
- debug$4(`${tag} has will be removed`);
1170
+ debug$5(`${tag} has will be removed`);
1142
1171
  context.report({
1143
1172
  data: {
1144
1173
  name: tag
@@ -1181,10 +1210,10 @@ const noDeprecatedComponents = createEslintRule({
1181
1210
  ],
1182
1211
  type: "problem"
1183
1212
  },
1184
- name: RULE_NAME$6
1213
+ name: RULE_NAME$7
1185
1214
  });
1186
1215
 
1187
- const RULE_NAME$5 = "no-deprecated-props";
1216
+ const RULE_NAME$6 = "no-deprecated-props";
1188
1217
  const replacements = {
1189
1218
  RuiRadio: {
1190
1219
  internalValue: "value"
@@ -1251,10 +1280,10 @@ const noDeprecatedProps = createEslintRule({
1251
1280
  schema: [],
1252
1281
  type: "problem"
1253
1282
  },
1254
- name: RULE_NAME$5
1283
+ name: RULE_NAME$6
1255
1284
  });
1256
1285
 
1257
- const RULE_NAME$4 = "no-dot-ts-imports";
1286
+ const RULE_NAME$5 = "no-dot-ts-imports";
1258
1287
  const noDotTsImport = createEslintRule({
1259
1288
  create(context) {
1260
1289
  return {
@@ -1289,10 +1318,10 @@ const noDotTsImport = createEslintRule({
1289
1318
  schema: [],
1290
1319
  type: "problem"
1291
1320
  },
1292
- name: RULE_NAME$4
1321
+ name: RULE_NAME$5
1293
1322
  });
1294
1323
 
1295
- const RULE_NAME$3 = "no-legacy-library-import";
1324
+ const RULE_NAME$4 = "no-legacy-library-import";
1296
1325
  const legacyLibrary = "@rotki/ui-library-compat";
1297
1326
  const newLibrary = "@rotki/ui-library";
1298
1327
  const noLegacyLibraryImport = createEslintRule({
@@ -1325,11 +1354,11 @@ const noLegacyLibraryImport = createEslintRule({
1325
1354
  schema: [],
1326
1355
  type: "problem"
1327
1356
  },
1328
- name: RULE_NAME$3
1357
+ name: RULE_NAME$4
1329
1358
  });
1330
1359
 
1331
- const RULE_NAME$2 = "no-redundant-flex-row";
1332
- const debug$3 = debugFactory("@rotki/eslint-plugin:no-redundant-flex-row");
1360
+ const RULE_NAME$3 = "no-redundant-flex-row";
1361
+ const debug$4 = debugFactory("@rotki/eslint-plugin:no-redundant-flex-row");
1333
1362
  function stripImportant(token) {
1334
1363
  return token.replace(/^!/, "").replace(/!$/, "");
1335
1364
  }
@@ -1374,7 +1403,7 @@ function reportRedundant(source, context, sourceCode) {
1374
1403
  for (const token of tokens) {
1375
1404
  if (!isFlexRow(token.text))
1376
1405
  continue;
1377
- debug$3(`found redundant flex-row token '${token.text}'`);
1406
+ debug$4(`found redundant flex-row token '${token.text}'`);
1378
1407
  const tokenRange = [
1379
1408
  source.contentStart + token.start,
1380
1409
  source.contentStart + token.start + token.text.length
@@ -1438,6 +1467,148 @@ const noRedundantFlexRow = createEslintRule({
1438
1467
  schema: [],
1439
1468
  type: "suggestion"
1440
1469
  },
1470
+ name: RULE_NAME$3
1471
+ });
1472
+
1473
+ const RULE_NAME$2 = "no-shared-pinia";
1474
+ const debug$3 = debugFactory("@rotki/eslint-plugin:no-shared-pinia");
1475
+ const DESCRIBE_NAMES = /* @__PURE__ */ new Set(["describe", "fdescribe", "xdescribe", "suite"]);
1476
+ const BEFORE_EACH_NAMES = /* @__PURE__ */ new Set(["beforeEach"]);
1477
+ const DEFAULT_FACTORIES = ["createPinia", "createCustomPinia", "createTestingPinia"];
1478
+ const DEFAULT_TEST_FILE_PATTERN = "\\.(spec|test)\\.[cm]?[jt]sx?$";
1479
+ function isFunctionNode(node) {
1480
+ return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
1481
+ }
1482
+ function getRootCalleeName(callee) {
1483
+ if (callee.type === "Identifier")
1484
+ return callee.name;
1485
+ if (callee.type === "MemberExpression")
1486
+ return getRootCalleeName(callee.object);
1487
+ if (callee.type === "CallExpression")
1488
+ return getRootCalleeName(callee.callee);
1489
+ return null;
1490
+ }
1491
+ function callbackOwnerName(fn) {
1492
+ const parent = fn.parent;
1493
+ if (!parent || parent.type !== "CallExpression")
1494
+ return null;
1495
+ return getRootCalleeName(parent.callee);
1496
+ }
1497
+ function isBeforeEachCallback(fn) {
1498
+ const owner = callbackOwnerName(fn);
1499
+ return owner != null && BEFORE_EACH_NAMES.has(owner);
1500
+ }
1501
+ function isDescribeCallback(fn) {
1502
+ const owner = callbackOwnerName(fn);
1503
+ return owner != null && DESCRIBE_NAMES.has(owner);
1504
+ }
1505
+ function nearestFunction(node) {
1506
+ let current = node.parent;
1507
+ while (current) {
1508
+ if (isFunctionNode(current))
1509
+ return current;
1510
+ current = current.parent;
1511
+ }
1512
+ return null;
1513
+ }
1514
+ function buildTestFileMatcher(pattern) {
1515
+ const regex = new RegExp(pattern, "u");
1516
+ return (filename) => regex.test(filename);
1517
+ }
1518
+ const noSharedPinia = createEslintRule({
1519
+ create(context, optionsWithDefault) {
1520
+ const options = optionsWithDefault[0] ?? {};
1521
+ const factories = new Set(options.factories ?? DEFAULT_FACTORIES);
1522
+ const isTestFile = buildTestFileMatcher(options.testFilePattern ?? DEFAULT_TEST_FILE_PATTERN);
1523
+ const filename = getFilename(context);
1524
+ if (!isTestFile(filename)) {
1525
+ debug$3(`skipping non-test file '${filename}'`);
1526
+ return {};
1527
+ }
1528
+ let program = null;
1529
+ const candidates = [];
1530
+ const freshContainers = /* @__PURE__ */ new Set();
1531
+ return {
1532
+ CallExpression(node) {
1533
+ const name = getRootCalleeName(node.callee);
1534
+ if (name == null || !factories.has(name) || program == null)
1535
+ return;
1536
+ const enclosing = nearestFunction(node);
1537
+ if (enclosing != null && isBeforeEachCallback(enclosing)) {
1538
+ freshContainers.add(nearestFunction(enclosing) ?? program);
1539
+ return;
1540
+ }
1541
+ if (enclosing != null && !isDescribeCallback(enclosing))
1542
+ return;
1543
+ candidates.push({
1544
+ atModuleScope: enclosing == null,
1545
+ container: enclosing ?? program,
1546
+ factory: name,
1547
+ node
1548
+ });
1549
+ },
1550
+ Program(node) {
1551
+ program = node;
1552
+ },
1553
+ "Program:exit": function() {
1554
+ const isProtected = (container) => {
1555
+ let current = container;
1556
+ while (current != null) {
1557
+ if (freshContainers.has(current))
1558
+ return true;
1559
+ if (current.type === "Program")
1560
+ break;
1561
+ current = nearestFunction(current) ?? program;
1562
+ }
1563
+ return false;
1564
+ };
1565
+ for (const candidate of candidates) {
1566
+ if (isProtected(candidate.container))
1567
+ continue;
1568
+ debug$3(`found shared pinia via '${candidate.factory}' ${candidate.atModuleScope ? "at module scope" : "in describe body"}`);
1569
+ context.report({
1570
+ data: {
1571
+ factory: candidate.factory,
1572
+ location: candidate.atModuleScope ? "at module scope" : "in the `describe` body"
1573
+ },
1574
+ messageId: "sharedPinia",
1575
+ node: candidate.node
1576
+ });
1577
+ }
1578
+ }
1579
+ };
1580
+ },
1581
+ defaultOptions: [
1582
+ {
1583
+ factories: DEFAULT_FACTORIES,
1584
+ testFilePattern: DEFAULT_TEST_FILE_PATTERN
1585
+ }
1586
+ ],
1587
+ meta: {
1588
+ docs: {
1589
+ description: "disallow a Pinia instance shared across tests via `describe`-body or module scope",
1590
+ recommendation: "recommended"
1591
+ },
1592
+ messages: {
1593
+ sharedPinia: "`{{ factory }}()` is called {{ location }}, so the Pinia instance is shared across every test and store state leaks between them. Create a fresh instance per test with `beforeEach(() => setActivePinia(createPinia()))`, or add an inline disable if the shared instance is intentional."
1594
+ },
1595
+ schema: [
1596
+ {
1597
+ additionalProperties: false,
1598
+ properties: {
1599
+ factories: {
1600
+ items: { type: "string" },
1601
+ type: "array"
1602
+ },
1603
+ testFilePattern: {
1604
+ type: "string"
1605
+ }
1606
+ },
1607
+ type: "object"
1608
+ }
1609
+ ],
1610
+ type: "problem"
1611
+ },
1441
1612
  name: RULE_NAME$2
1442
1613
  });
1443
1614
 
@@ -2093,6 +2264,7 @@ const plugin = {
2093
2264
  "no-dot-ts-imports": noDotTsImport,
2094
2265
  "no-legacy-library-import": noLegacyLibraryImport,
2095
2266
  "no-redundant-flex-row": noRedundantFlexRow,
2267
+ "no-shared-pinia": noSharedPinia,
2096
2268
  "no-unused-i18n-keys": noUnusedI18nKeys,
2097
2269
  "require-jsdoc-on-composable-options": requireJsdocOnComposableOptions
2098
2270
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rotki/eslint-plugin",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0",
6
6
  "bugs": {
@@ -28,37 +28,37 @@
28
28
  "eslint": "^9.20.0 || ^10.0.0"
29
29
  },
30
30
  "dependencies": {
31
- "@typescript-eslint/utils": "8.60.1",
31
+ "@typescript-eslint/utils": "8.64.0",
32
32
  "debug": "4.4.3",
33
33
  "jsonc-eslint-parser": "3.1.0",
34
34
  "scule": "1.3.0",
35
35
  "tinyglobby": "0.2.17",
36
36
  "vue-eslint-parser": "10.4.1",
37
- "yaml-eslint-parser": "2.0.0"
37
+ "yaml-eslint-parser": "2.1.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@commitlint/cli": "21.0.2",
41
- "@commitlint/config-conventional": "21.0.2",
42
- "@rotki/eslint-config": "6.1.0",
43
- "@rotki/eslint-plugin": "1.3.2",
40
+ "@commitlint/cli": "21.2.1",
41
+ "@commitlint/config-conventional": "21.2.0",
42
+ "@rotki/eslint-config": "6.2.0",
43
+ "@rotki/eslint-plugin": "1.4.0",
44
44
  "@types/debug": "4.1.13",
45
- "@types/node": "24.13.1",
46
- "@typescript-eslint/eslint-plugin": "8.60.1",
47
- "@typescript-eslint/parser": "8.60.1",
48
- "@typescript-eslint/rule-tester": "8.60.1",
49
- "@vitest/coverage-v8": "4.1.8",
45
+ "@types/node": "24.13.3",
46
+ "@typescript-eslint/eslint-plugin": "8.64.0",
47
+ "@typescript-eslint/parser": "8.64.0",
48
+ "@typescript-eslint/rule-tester": "8.64.0",
49
+ "@vitest/coverage-v8": "4.1.10",
50
50
  "bumpp": "11.1.0",
51
- "eslint": "10.4.1",
51
+ "eslint": "10.7.0",
52
52
  "husky": "9.1.7",
53
- "lint-staged": "17.0.7",
54
- "oxc-minify": "0.135.0",
55
- "prettier": "3.8.1",
53
+ "lint-staged": "17.0.8",
54
+ "oxc-minify": "0.140.0",
55
+ "prettier": "3.9.5",
56
56
  "rimraf": "6.1.3",
57
- "tsx": "4.22.4",
57
+ "tsx": "4.23.1",
58
58
  "typescript": "6.0.3",
59
59
  "unbuild": "3.6.1",
60
- "vitepress": "2.0.0-alpha.17",
61
- "vitest": "4.1.8"
60
+ "vitepress": "2.0.0-alpha.18",
61
+ "vitest": "4.1.10"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=24 <25",