@rotki/eslint-plugin 1.3.2 → 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/README.md +3 -15
- package/dist/index.d.mts +31 -9
- package/dist/index.d.ts +31 -9
- package/dist/index.mjs +616 -305
- package/package.json +27 -26
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils';
|
|
2
|
-
import * as compat from 'eslint-compat-utils';
|
|
3
2
|
import debugFactory from 'debug';
|
|
4
3
|
import { extname, resolve } from 'node:path';
|
|
5
4
|
import { pascalCase, kebabCase } from 'scule';
|
|
@@ -7,32 +6,35 @@ import { statSync, readFileSync } from 'node:fs';
|
|
|
7
6
|
import { globSync } from 'tinyglobby';
|
|
8
7
|
import { parse } from 'vue-eslint-parser';
|
|
9
8
|
|
|
10
|
-
const version = "1.
|
|
9
|
+
const version = "1.5.0";
|
|
11
10
|
const pkg = {
|
|
12
11
|
version: version};
|
|
13
12
|
|
|
14
13
|
function getFilename(context) {
|
|
15
|
-
return
|
|
14
|
+
return context.filename;
|
|
16
15
|
}
|
|
17
16
|
function getSourceCode(context) {
|
|
18
|
-
return
|
|
17
|
+
return context.sourceCode;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
const COMPOSABLE_PATTERN = /^use[A-Z]/;
|
|
22
21
|
function isComposableName(name) {
|
|
23
22
|
return COMPOSABLE_PATTERN.test(name);
|
|
24
23
|
}
|
|
24
|
+
function isComposableNode(node) {
|
|
25
|
+
if (node.type === AST_NODE_TYPES.FunctionDeclaration)
|
|
26
|
+
return !!node.id && isComposableName(node.id.name);
|
|
27
|
+
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression || node.type === AST_NODE_TYPES.FunctionExpression) {
|
|
28
|
+
const parent = node.parent;
|
|
29
|
+
return parent?.type === AST_NODE_TYPES.VariableDeclarator && parent.id.type === AST_NODE_TYPES.Identifier && isComposableName(parent.id.name);
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
25
33
|
function getEnclosingComposable(node) {
|
|
26
34
|
let current = node.parent;
|
|
27
35
|
while (current) {
|
|
28
|
-
if (
|
|
36
|
+
if (isComposableNode(current))
|
|
29
37
|
return current;
|
|
30
|
-
if (current.type === AST_NODE_TYPES.ArrowFunctionExpression || current.type === AST_NODE_TYPES.FunctionExpression) {
|
|
31
|
-
const parent = current.parent;
|
|
32
|
-
if (parent?.type === AST_NODE_TYPES.VariableDeclarator && parent.id.type === AST_NODE_TYPES.Identifier && isComposableName(parent.id.name)) {
|
|
33
|
-
return current;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
38
|
current = current.parent;
|
|
37
39
|
}
|
|
38
40
|
return void 0;
|
|
@@ -64,22 +66,18 @@ function createRecommended(plugin, name, flat) {
|
|
|
64
66
|
return createConfig(plugin, name, flat, "recommended");
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
function getLiteralValue(node, stringOnly) {
|
|
70
|
+
if (node.value == null)
|
|
71
|
+
return !stringOnly && node.bigint != null ? node.bigint : null;
|
|
72
|
+
if (typeof node.value === "string")
|
|
73
|
+
return node.value;
|
|
74
|
+
return stringOnly ? null : String(node.value);
|
|
75
|
+
}
|
|
67
76
|
function getStringLiteralValue(node, stringOnly = false) {
|
|
68
|
-
if (node.type === "Literal")
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return node.bigint;
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
if (typeof node.value === "string")
|
|
75
|
-
return node.value;
|
|
76
|
-
if (!stringOnly)
|
|
77
|
-
return String(node.value);
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
77
|
+
if (node.type === "Literal")
|
|
78
|
+
return getLiteralValue(node, stringOnly);
|
|
79
|
+
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1)
|
|
81
80
|
return node.quasis[0].value.cooked;
|
|
82
|
-
}
|
|
83
81
|
return null;
|
|
84
82
|
}
|
|
85
83
|
function getStaticPropertyName(node) {
|
|
@@ -129,13 +127,13 @@ function createRule({
|
|
|
129
127
|
meta
|
|
130
128
|
}) {
|
|
131
129
|
return {
|
|
132
|
-
create: (
|
|
130
|
+
create: (context) => {
|
|
133
131
|
const optionsWithDefault = context.options.map((options, index) => ({
|
|
134
132
|
...defaultOptions[index] || {},
|
|
135
133
|
...options || {}
|
|
136
134
|
}));
|
|
137
135
|
return create(context, optionsWithDefault);
|
|
138
|
-
}
|
|
136
|
+
},
|
|
139
137
|
defaultOptions,
|
|
140
138
|
meta
|
|
141
139
|
};
|
|
@@ -166,7 +164,7 @@ function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor,
|
|
|
166
164
|
);
|
|
167
165
|
}
|
|
168
166
|
|
|
169
|
-
const RULE_NAME$
|
|
167
|
+
const RULE_NAME$h = "composable-input-flexibility";
|
|
170
168
|
function checkParamForRef(param) {
|
|
171
169
|
let annotation;
|
|
172
170
|
if (param.type === AST_NODE_TYPES.Identifier && param.typeAnnotation) {
|
|
@@ -179,44 +177,73 @@ function checkParamForRef(param) {
|
|
|
179
177
|
}
|
|
180
178
|
return void 0;
|
|
181
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
|
+
}
|
|
182
205
|
const composableInputFlexibility = createEslintRule({
|
|
183
206
|
create(context) {
|
|
184
207
|
const autofix = context.options[0]?.autofix ?? false;
|
|
185
208
|
return {
|
|
186
209
|
FunctionDeclaration: (node) => {
|
|
187
|
-
if (!node.id || !isComposableName(node.id.name))
|
|
210
|
+
if (!node.id || !isComposableName(node.id.name) || !node.body)
|
|
188
211
|
return;
|
|
189
|
-
checkParams(node.params);
|
|
212
|
+
checkParams(node.params, node);
|
|
190
213
|
},
|
|
191
214
|
VariableDeclarator: (node) => {
|
|
192
215
|
if (node.id.type !== AST_NODE_TYPES.Identifier || !isComposableName(node.id.name))
|
|
193
216
|
return;
|
|
194
217
|
if (node.init?.type === AST_NODE_TYPES.ArrowFunctionExpression || node.init?.type === AST_NODE_TYPES.FunctionExpression) {
|
|
195
|
-
checkParams(node.init.params);
|
|
218
|
+
checkParams(node.init.params, node.init);
|
|
196
219
|
}
|
|
197
220
|
}
|
|
198
221
|
};
|
|
199
|
-
function checkParams(params) {
|
|
222
|
+
function checkParams(params, fn) {
|
|
223
|
+
const scope = context.sourceCode.getScope(fn);
|
|
200
224
|
for (const param of params) {
|
|
201
225
|
const refType = checkParamForRef(param);
|
|
202
|
-
if (refType)
|
|
203
|
-
|
|
204
|
-
|
|
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: [{
|
|
205
238
|
fix(fixer) {
|
|
206
239
|
return fixer.replaceText(refType.typeName, "MaybeRefOrGetter");
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}]
|
|
215
|
-
},
|
|
216
|
-
messageId: "preferMaybeRefOrGetter",
|
|
217
|
-
node: refType
|
|
218
|
-
});
|
|
219
|
-
}
|
|
240
|
+
},
|
|
241
|
+
messageId: "suggestMaybeRefOrGetter"
|
|
242
|
+
}]
|
|
243
|
+
},
|
|
244
|
+
messageId: "preferMaybeRefOrGetter",
|
|
245
|
+
node: refType
|
|
246
|
+
});
|
|
220
247
|
}
|
|
221
248
|
}
|
|
222
249
|
},
|
|
@@ -247,10 +274,10 @@ const composableInputFlexibility = createEslintRule({
|
|
|
247
274
|
],
|
|
248
275
|
type: "suggestion"
|
|
249
276
|
},
|
|
250
|
-
name: RULE_NAME$
|
|
277
|
+
name: RULE_NAME$h
|
|
251
278
|
});
|
|
252
279
|
|
|
253
|
-
const RULE_NAME$
|
|
280
|
+
const RULE_NAME$g = "composable-naming-convention";
|
|
254
281
|
function getComposableFunction(node) {
|
|
255
282
|
if (node.type === AST_NODE_TYPES.FunctionDeclaration) {
|
|
256
283
|
if (!node.id || !isComposableName(node.id.name))
|
|
@@ -279,6 +306,9 @@ const composableNamingConvention = createEslintRule({
|
|
|
279
306
|
checkComposable(node);
|
|
280
307
|
}
|
|
281
308
|
};
|
|
309
|
+
function isMismatchedConvention(typeName, prefix, suffix, expected) {
|
|
310
|
+
return !!typeName && typeName.startsWith(prefix) && typeName.endsWith(suffix) && typeName !== expected;
|
|
311
|
+
}
|
|
282
312
|
function checkComposable(node) {
|
|
283
313
|
const info = getComposableFunction(node);
|
|
284
314
|
if (!info)
|
|
@@ -288,26 +318,14 @@ const composableNamingConvention = createEslintRule({
|
|
|
288
318
|
const expectedReturn = `Use${pascalName}Return`;
|
|
289
319
|
for (const param of info.params) {
|
|
290
320
|
const annotation = getParamTypeAnnotation(param);
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (typeName && typeName.startsWith("Use") && typeName.endsWith("Options") && typeName !== expectedOptions) {
|
|
295
|
-
context.report({
|
|
296
|
-
data: { expected: expectedOptions, got: typeName },
|
|
297
|
-
messageId: "optionsNaming",
|
|
298
|
-
node: param
|
|
299
|
-
});
|
|
300
|
-
}
|
|
321
|
+
const typeName = annotation ? getTypeReferenceName(annotation) : void 0;
|
|
322
|
+
if (isMismatchedConvention(typeName, "Use", "Options", expectedOptions))
|
|
323
|
+
context.report({ data: { expected: expectedOptions, got: typeName }, messageId: "optionsNaming", node: param });
|
|
301
324
|
}
|
|
302
325
|
if (info.returnType) {
|
|
303
326
|
const returnTypeName = getTypeReferenceName(info.returnType.typeAnnotation);
|
|
304
|
-
if (returnTypeName
|
|
305
|
-
context.report({
|
|
306
|
-
data: { expected: expectedReturn, got: returnTypeName },
|
|
307
|
-
messageId: "returnNaming",
|
|
308
|
-
node: info.returnType
|
|
309
|
-
});
|
|
310
|
-
}
|
|
327
|
+
if (isMismatchedConvention(returnTypeName, "Use", "Return", expectedReturn))
|
|
328
|
+
context.report({ data: { expected: expectedReturn, got: returnTypeName }, messageId: "returnNaming", node: info.returnType });
|
|
311
329
|
}
|
|
312
330
|
}
|
|
313
331
|
function getParamTypeAnnotation(param) {
|
|
@@ -332,10 +350,10 @@ const composableNamingConvention = createEslintRule({
|
|
|
332
350
|
schema: [],
|
|
333
351
|
type: "suggestion"
|
|
334
352
|
},
|
|
335
|
-
name: RULE_NAME$
|
|
353
|
+
name: RULE_NAME$g
|
|
336
354
|
});
|
|
337
355
|
|
|
338
|
-
const RULE_NAME$
|
|
356
|
+
const RULE_NAME$f = "composable-no-default-export";
|
|
339
357
|
const composableNoDefaultExport = createEslintRule({
|
|
340
358
|
create(context) {
|
|
341
359
|
let hasComposable = false;
|
|
@@ -375,10 +393,10 @@ const composableNoDefaultExport = createEslintRule({
|
|
|
375
393
|
schema: [],
|
|
376
394
|
type: "problem"
|
|
377
395
|
},
|
|
378
|
-
name: RULE_NAME$
|
|
396
|
+
name: RULE_NAME$f
|
|
379
397
|
});
|
|
380
398
|
|
|
381
|
-
const RULE_NAME$
|
|
399
|
+
const RULE_NAME$e = "composable-prefer-shallowref";
|
|
382
400
|
const composablePreferShallowref = createEslintRule({
|
|
383
401
|
create(context) {
|
|
384
402
|
const autofix = context.options[0]?.autofix ?? false;
|
|
@@ -437,10 +455,10 @@ const composablePreferShallowref = createEslintRule({
|
|
|
437
455
|
],
|
|
438
456
|
type: "suggestion"
|
|
439
457
|
},
|
|
440
|
-
name: RULE_NAME$
|
|
458
|
+
name: RULE_NAME$e
|
|
441
459
|
});
|
|
442
460
|
|
|
443
|
-
const RULE_NAME$
|
|
461
|
+
const RULE_NAME$d = "composable-require-cleanup";
|
|
444
462
|
const SIDE_EFFECT_CALLS = /* @__PURE__ */ new Set(["addEventListener", "setInterval", "setTimeout"]);
|
|
445
463
|
const SIDE_EFFECT_CONSTRUCTORS = /* @__PURE__ */ new Set(["MutationObserver", "ResizeObserver", "IntersectionObserver"]);
|
|
446
464
|
const CLEANUP_HOOKS = /* @__PURE__ */ new Set(["onUnmounted", "onBeforeUnmount", "onScopeDispose", "tryOnScopeDispose"]);
|
|
@@ -534,10 +552,11 @@ const composableRequireCleanup = createEslintRule({
|
|
|
534
552
|
schema: [],
|
|
535
553
|
type: "problem"
|
|
536
554
|
},
|
|
537
|
-
name: RULE_NAME$
|
|
555
|
+
name: RULE_NAME$d
|
|
538
556
|
});
|
|
539
557
|
|
|
540
|
-
const RULE_NAME$
|
|
558
|
+
const RULE_NAME$c = "composable-return-readonly";
|
|
559
|
+
const DEFAULT_WRITABLE_PREFIXES = ["model"];
|
|
541
560
|
const REACTIVE_CREATORS = /* @__PURE__ */ new Set(["ref", "shallowRef"]);
|
|
542
561
|
const READONLY_CREATORS = /* @__PURE__ */ new Set(["computed"]);
|
|
543
562
|
function isReadonlyCall(node) {
|
|
@@ -546,9 +565,49 @@ function isReadonlyCall(node) {
|
|
|
546
565
|
const composableReturnReadonly = createEslintRule({
|
|
547
566
|
create(context) {
|
|
548
567
|
const autofix = context.options[0]?.autofix ?? false;
|
|
568
|
+
const writablePrefixes = context.options[0]?.writablePrefixes ?? DEFAULT_WRITABLE_PREFIXES;
|
|
549
569
|
const source = getSourceCode(context);
|
|
550
570
|
const reactiveVars = /* @__PURE__ */ new Set();
|
|
551
571
|
const readonlyVars = /* @__PURE__ */ new Set();
|
|
572
|
+
function isWritableByConvention(name) {
|
|
573
|
+
return writablePrefixes.some((prefix) => {
|
|
574
|
+
if (!name.startsWith(prefix))
|
|
575
|
+
return false;
|
|
576
|
+
const next = name[prefix.length];
|
|
577
|
+
return next === void 0 || next >= "A" && next <= "Z";
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
function reportWithFixOrSuggest(prop, name, messageId, suggestMessageId, fixFn) {
|
|
581
|
+
context.report({
|
|
582
|
+
data: { name },
|
|
583
|
+
...autofix ? { fix: fixFn } : { suggest: [{ data: { name }, fix: fixFn, messageId: suggestMessageId }] },
|
|
584
|
+
messageId,
|
|
585
|
+
node: prop
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
function checkUnnecessaryReadonly(prop) {
|
|
589
|
+
const valueNode = prop.shorthand ? null : prop.value;
|
|
590
|
+
if (!valueNode || !isReadonlyCall(valueNode))
|
|
591
|
+
return false;
|
|
592
|
+
const innerName = valueNode.arguments[0].name;
|
|
593
|
+
if (!readonlyVars.has(innerName))
|
|
594
|
+
return false;
|
|
595
|
+
reportWithFixOrSuggest(prop, innerName, "unnecessaryReadonly", "suggestRemoveReadonly", (fixer) => fixer.replaceText(valueNode, innerName));
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
function checkMissingReadonly(prop) {
|
|
599
|
+
if (prop.shorthand && prop.key.type === AST_NODE_TYPES.Identifier && reactiveVars.has(prop.key.name)) {
|
|
600
|
+
const keyName = prop.key.name;
|
|
601
|
+
if (isWritableByConvention(keyName))
|
|
602
|
+
return;
|
|
603
|
+
reportWithFixOrSuggest(prop, keyName, "wrapReadonly", "suggestWrapReadonly", (fixer) => fixer.replaceText(prop, `${keyName}: readonly(${keyName})`));
|
|
604
|
+
} else if (!prop.shorthand && prop.value.type === AST_NODE_TYPES.Identifier && reactiveVars.has(prop.value.name)) {
|
|
605
|
+
const valueName = prop.value.name;
|
|
606
|
+
if (isWritableByConvention(valueName))
|
|
607
|
+
return;
|
|
608
|
+
reportWithFixOrSuggest(prop, valueName, "wrapReadonly", "suggestWrapReadonly", (fixer) => fixer.replaceText(prop.value, `readonly(${prop.value.type === AST_NODE_TYPES.Identifier ? prop.value.name : source.getText(prop.value)})`));
|
|
609
|
+
}
|
|
610
|
+
}
|
|
552
611
|
return {
|
|
553
612
|
ReturnStatement: (node) => {
|
|
554
613
|
if (!getEnclosingComposable(node))
|
|
@@ -558,39 +617,8 @@ const composableReturnReadonly = createEslintRule({
|
|
|
558
617
|
for (const prop of node.argument.properties) {
|
|
559
618
|
if (prop.type !== AST_NODE_TYPES.Property)
|
|
560
619
|
continue;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const innerName = valueNode.arguments[0].name;
|
|
564
|
-
if (readonlyVars.has(innerName)) {
|
|
565
|
-
const fixFn = (fixer) => fixer.replaceText(valueNode, innerName);
|
|
566
|
-
context.report({
|
|
567
|
-
data: { name: innerName },
|
|
568
|
-
...autofix ? { fix: fixFn } : { suggest: [{ data: { name: innerName }, fix: fixFn, messageId: "suggestRemoveReadonly" }] },
|
|
569
|
-
messageId: "unnecessaryReadonly",
|
|
570
|
-
node: prop
|
|
571
|
-
});
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
if (prop.shorthand && prop.key.type === AST_NODE_TYPES.Identifier && reactiveVars.has(prop.key.name)) {
|
|
576
|
-
const keyName = prop.key.name;
|
|
577
|
-
const fixFn = (fixer) => fixer.replaceText(prop, `${keyName}: readonly(${keyName})`);
|
|
578
|
-
context.report({
|
|
579
|
-
data: { name: keyName },
|
|
580
|
-
...autofix ? { fix: fixFn } : { suggest: [{ data: { name: keyName }, fix: fixFn, messageId: "suggestWrapReadonly" }] },
|
|
581
|
-
messageId: "wrapReadonly",
|
|
582
|
-
node: prop
|
|
583
|
-
});
|
|
584
|
-
} else if (!prop.shorthand && prop.value.type === AST_NODE_TYPES.Identifier && reactiveVars.has(prop.value.name)) {
|
|
585
|
-
const valueName = prop.value.name;
|
|
586
|
-
const fixFn = (fixer) => fixer.replaceText(prop.value, `readonly(${prop.value.type === AST_NODE_TYPES.Identifier ? prop.value.name : source.getText(prop.value)})`);
|
|
587
|
-
context.report({
|
|
588
|
-
data: { name: valueName },
|
|
589
|
-
...autofix ? { fix: fixFn } : { suggest: [{ data: { name: valueName }, fix: fixFn, messageId: "suggestWrapReadonly" }] },
|
|
590
|
-
messageId: "wrapReadonly",
|
|
591
|
-
node: prop
|
|
592
|
-
});
|
|
593
|
-
}
|
|
620
|
+
if (!checkUnnecessaryReadonly(prop))
|
|
621
|
+
checkMissingReadonly(prop);
|
|
594
622
|
}
|
|
595
623
|
},
|
|
596
624
|
VariableDeclarator: (node) => {
|
|
@@ -609,7 +637,7 @@ const composableReturnReadonly = createEslintRule({
|
|
|
609
637
|
}
|
|
610
638
|
};
|
|
611
639
|
},
|
|
612
|
-
defaultOptions: [{ autofix: false }],
|
|
640
|
+
defaultOptions: [{ autofix: false, writablePrefixes: DEFAULT_WRITABLE_PREFIXES }],
|
|
613
641
|
meta: {
|
|
614
642
|
docs: {
|
|
615
643
|
description: "Require returned refs from composables to be wrapped with readonly()",
|
|
@@ -631,6 +659,12 @@ const composableReturnReadonly = createEslintRule({
|
|
|
631
659
|
default: false,
|
|
632
660
|
description: "Enable auto-fix. When disabled, the fix is available as a suggestion.",
|
|
633
661
|
type: "boolean"
|
|
662
|
+
},
|
|
663
|
+
writablePrefixes: {
|
|
664
|
+
default: DEFAULT_WRITABLE_PREFIXES,
|
|
665
|
+
description: "Returned variables whose name starts with one of these prefixes are exempt from the readonly() requirement (e.g. refs intended for v-model binding).",
|
|
666
|
+
items: { type: "string" },
|
|
667
|
+
type: "array"
|
|
634
668
|
}
|
|
635
669
|
},
|
|
636
670
|
type: "object"
|
|
@@ -638,10 +672,10 @@ const composableReturnReadonly = createEslintRule({
|
|
|
638
672
|
],
|
|
639
673
|
type: "suggestion"
|
|
640
674
|
},
|
|
641
|
-
name: RULE_NAME$
|
|
675
|
+
name: RULE_NAME$c
|
|
642
676
|
});
|
|
643
677
|
|
|
644
|
-
const RULE_NAME$
|
|
678
|
+
const RULE_NAME$b = "composable-ssr-safety";
|
|
645
679
|
const BROWSER_GLOBALS = /* @__PURE__ */ new Set(["window", "document", "navigator"]);
|
|
646
680
|
const LIFECYCLE_HOOKS = /* @__PURE__ */ new Set(["onMounted", "onBeforeMount"]);
|
|
647
681
|
function isInsideLifecycleHook(node) {
|
|
@@ -668,15 +702,12 @@ function isInsideTypeofCheck(node) {
|
|
|
668
702
|
}
|
|
669
703
|
return false;
|
|
670
704
|
}
|
|
705
|
+
function isTypeofBrowserGlobal(node) {
|
|
706
|
+
return node.type === AST_NODE_TYPES.UnaryExpression && node.operator === "typeof" && node.argument.type === AST_NODE_TYPES.Identifier && BROWSER_GLOBALS.has(node.argument.name);
|
|
707
|
+
}
|
|
671
708
|
function hasTypeofCheck(node) {
|
|
672
|
-
if (node.type === AST_NODE_TYPES.BinaryExpression)
|
|
673
|
-
|
|
674
|
-
return true;
|
|
675
|
-
}
|
|
676
|
-
if (node.right.type === AST_NODE_TYPES.UnaryExpression && node.right.operator === "typeof" && node.right.argument.type === AST_NODE_TYPES.Identifier && BROWSER_GLOBALS.has(node.right.argument.name)) {
|
|
677
|
-
return true;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
709
|
+
if (node.type === AST_NODE_TYPES.BinaryExpression)
|
|
710
|
+
return isTypeofBrowserGlobal(node.left) || isTypeofBrowserGlobal(node.right);
|
|
680
711
|
if (node.type === AST_NODE_TYPES.LogicalExpression)
|
|
681
712
|
return hasTypeofCheck(node.left) || hasTypeofCheck(node.right);
|
|
682
713
|
return false;
|
|
@@ -732,56 +763,51 @@ const composableSsrSafety = createEslintRule({
|
|
|
732
763
|
schema: [],
|
|
733
764
|
type: "problem"
|
|
734
765
|
},
|
|
735
|
-
name: RULE_NAME$
|
|
766
|
+
name: RULE_NAME$b
|
|
736
767
|
});
|
|
737
768
|
|
|
738
|
-
const debug$
|
|
739
|
-
const RULE_NAME$
|
|
769
|
+
const debug$7 = debugFactory("@rotki/eslint-plugin:consistent-ref-type-annotation");
|
|
770
|
+
const RULE_NAME$a = "consistent-ref-type-annotation";
|
|
740
771
|
const FIXABLE_METHODS = /* @__PURE__ */ new Set(["ref", "computed"]);
|
|
741
|
-
function
|
|
742
|
-
let declarationTypeArguments;
|
|
772
|
+
function getFixableCallExpression(declaration) {
|
|
743
773
|
const init = declaration.init;
|
|
744
|
-
if (!
|
|
745
|
-
return;
|
|
774
|
+
if (!init || init.type !== TSESTree.AST_NODE_TYPES.CallExpression)
|
|
775
|
+
return void 0;
|
|
746
776
|
const callee = init.callee;
|
|
747
|
-
if (!
|
|
748
|
-
return;
|
|
749
|
-
|
|
777
|
+
if (!callee || callee.type !== TSESTree.AST_NODE_TYPES.Identifier || !FIXABLE_METHODS.has(callee.name))
|
|
778
|
+
return void 0;
|
|
779
|
+
return { callee, init };
|
|
780
|
+
}
|
|
781
|
+
function getDeclarationTypeArguments(declaration) {
|
|
782
|
+
const typeNode = declaration.id.typeAnnotation?.typeAnnotation;
|
|
783
|
+
if (typeNode?.type === TSESTree.AST_NODE_TYPES.TSTypeReference)
|
|
784
|
+
return typeNode.typeArguments;
|
|
785
|
+
return void 0;
|
|
786
|
+
}
|
|
787
|
+
function checkAssignmentDeclaration(context, source, node, declaration, allowInference) {
|
|
788
|
+
const call = getFixableCallExpression(declaration);
|
|
789
|
+
if (!call)
|
|
750
790
|
return;
|
|
791
|
+
const { callee, init } = call;
|
|
751
792
|
const name = callee.name;
|
|
752
|
-
debug$
|
|
753
|
-
const
|
|
793
|
+
debug$7(`found ${name}, checking type arguments`);
|
|
794
|
+
const initTypeArgs = init.typeArguments;
|
|
795
|
+
const declTypeArgs = getDeclarationTypeArguments(declaration);
|
|
754
796
|
const typeAnnotation = declaration.id.typeAnnotation;
|
|
755
|
-
if (
|
|
756
|
-
const typeNode = typeAnnotation.typeAnnotation;
|
|
757
|
-
if (typeNode && typeNode.type === TSESTree.AST_NODE_TYPES.TSTypeReference)
|
|
758
|
-
declarationTypeArguments = typeNode.typeArguments;
|
|
759
|
-
}
|
|
760
|
-
if (initializationTypeArguments && !declarationTypeArguments)
|
|
797
|
+
if (initTypeArgs && !declTypeArgs)
|
|
761
798
|
return;
|
|
762
|
-
debug$
|
|
763
|
-
if (!
|
|
764
|
-
if (allowInference)
|
|
765
|
-
|
|
766
|
-
} else {
|
|
767
|
-
context.report({
|
|
768
|
-
data: {
|
|
769
|
-
name
|
|
770
|
-
},
|
|
771
|
-
messageId: "missingType",
|
|
772
|
-
node
|
|
773
|
-
});
|
|
774
|
-
}
|
|
799
|
+
debug$7(`generating report for ${name}`);
|
|
800
|
+
if (!initTypeArgs && !declTypeArgs) {
|
|
801
|
+
if (!allowInference)
|
|
802
|
+
context.report({ data: { name }, messageId: "missingType", node });
|
|
775
803
|
return;
|
|
776
804
|
}
|
|
777
805
|
context.report({
|
|
778
|
-
data: {
|
|
779
|
-
name
|
|
780
|
-
},
|
|
806
|
+
data: { name },
|
|
781
807
|
fix(fixer) {
|
|
782
808
|
const fixes = [];
|
|
783
|
-
if (!
|
|
784
|
-
fixes.push(fixer.insertTextAfter(callee, source.getText(
|
|
809
|
+
if (!initTypeArgs && callee)
|
|
810
|
+
fixes.push(fixer.insertTextAfter(callee, source.getText(declTypeArgs)));
|
|
785
811
|
if (typeAnnotation)
|
|
786
812
|
fixes.push(fixer.remove(typeAnnotation));
|
|
787
813
|
return fixes;
|
|
@@ -830,10 +856,10 @@ const consistentRefTypeAnnotation = createEslintRule({
|
|
|
830
856
|
],
|
|
831
857
|
type: "problem"
|
|
832
858
|
},
|
|
833
|
-
name: RULE_NAME$
|
|
859
|
+
name: RULE_NAME$a
|
|
834
860
|
});
|
|
835
861
|
|
|
836
|
-
const RULE_NAME$
|
|
862
|
+
const RULE_NAME$9 = "max-dependencies";
|
|
837
863
|
const maxDependencies = createEslintRule({
|
|
838
864
|
create(context, [options]) {
|
|
839
865
|
let dependencyCount = 0;
|
|
@@ -893,11 +919,11 @@ const maxDependencies = createEslintRule({
|
|
|
893
919
|
],
|
|
894
920
|
type: "suggestion"
|
|
895
921
|
},
|
|
896
|
-
name: RULE_NAME$
|
|
922
|
+
name: RULE_NAME$9
|
|
897
923
|
});
|
|
898
924
|
|
|
899
|
-
const RULE_NAME$
|
|
900
|
-
const debug$
|
|
925
|
+
const RULE_NAME$8 = "no-deprecated-classes";
|
|
926
|
+
const debug$6 = debugFactory("@rotki/eslint-plugin:no-deprecated-classes");
|
|
901
927
|
const stringReplacements = /* @__PURE__ */ new Map([
|
|
902
928
|
["d-block", "block"],
|
|
903
929
|
["d-flex", "flex"],
|
|
@@ -951,7 +977,7 @@ function getRange(node) {
|
|
|
951
977
|
return node.range;
|
|
952
978
|
}
|
|
953
979
|
function reportReplacement(className, replacement, node, context, source, position = 1) {
|
|
954
|
-
debug$
|
|
980
|
+
debug$6(`found replacement ${replacement} for ${className}`);
|
|
955
981
|
const initialRange = getRange(node);
|
|
956
982
|
const range = [
|
|
957
983
|
initialRange[0] + position,
|
|
@@ -973,51 +999,62 @@ function reportReplacement(className, replacement, node, context, source, positi
|
|
|
973
999
|
messageId: "replacedWith"
|
|
974
1000
|
});
|
|
975
1001
|
}
|
|
1002
|
+
function splitClassNames(classNames, reportNode) {
|
|
1003
|
+
return classNames.split(/\s+/).map((className) => ({ className, position: classNames.indexOf(className) + 1, reportNode }));
|
|
1004
|
+
}
|
|
1005
|
+
function* extractFromObject(node) {
|
|
1006
|
+
for (const prop of node.properties) {
|
|
1007
|
+
if (prop.type !== "Property")
|
|
1008
|
+
continue;
|
|
1009
|
+
const classNames = getStaticPropertyName(prop);
|
|
1010
|
+
if (classNames)
|
|
1011
|
+
yield* splitClassNames(classNames, prop.key);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
function* extractFromArray(node) {
|
|
1015
|
+
for (const element of node.elements) {
|
|
1016
|
+
if (element == null || element.type === "SpreadElement")
|
|
1017
|
+
continue;
|
|
1018
|
+
yield* extractClassNames(element);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
function* extractFromTemplateLiteral(node) {
|
|
1022
|
+
for (const templateElement of node.quasis) {
|
|
1023
|
+
const classNames = templateElement.value.cooked;
|
|
1024
|
+
if (classNames !== null)
|
|
1025
|
+
yield* splitClassNames(classNames, templateElement);
|
|
1026
|
+
}
|
|
1027
|
+
for (const expr of node.expressions)
|
|
1028
|
+
yield* extractClassNames(expr, true);
|
|
1029
|
+
}
|
|
1030
|
+
function* extractFromBinary(node) {
|
|
1031
|
+
if (node.operator !== "+")
|
|
1032
|
+
return;
|
|
1033
|
+
if (node.left.type !== "PrivateIdentifier")
|
|
1034
|
+
yield* extractClassNames(node.left, true);
|
|
1035
|
+
yield* extractClassNames(node.right, true);
|
|
1036
|
+
}
|
|
976
1037
|
function* extractClassNames(node, textOnly = false) {
|
|
977
1038
|
if (node.type === "Literal") {
|
|
978
|
-
|
|
979
|
-
yield* classNames.split(/\s+/).map((className) => ({ className, position: classNames.indexOf(className) + 1, reportNode: node }));
|
|
1039
|
+
yield* splitClassNames(`${node.value}`, node);
|
|
980
1040
|
return;
|
|
981
1041
|
}
|
|
982
1042
|
if (node.type === "TemplateLiteral") {
|
|
983
|
-
|
|
984
|
-
const classNames = templateElement.value.cooked;
|
|
985
|
-
if (classNames === null)
|
|
986
|
-
continue;
|
|
987
|
-
yield* classNames.split(/\s+/).map((className) => ({ className, position: classNames.indexOf(className) + 1, reportNode: templateElement }));
|
|
988
|
-
}
|
|
989
|
-
for (const expr of node.expressions)
|
|
990
|
-
yield* extractClassNames(expr, true);
|
|
1043
|
+
yield* extractFromTemplateLiteral(node);
|
|
991
1044
|
return;
|
|
992
1045
|
}
|
|
993
1046
|
if (node.type === "BinaryExpression") {
|
|
994
|
-
|
|
995
|
-
return;
|
|
996
|
-
yield* extractClassNames(node.left, true);
|
|
997
|
-
yield* extractClassNames(node.right, true);
|
|
1047
|
+
yield* extractFromBinary(node);
|
|
998
1048
|
return;
|
|
999
1049
|
}
|
|
1000
1050
|
if (textOnly)
|
|
1001
1051
|
return;
|
|
1002
1052
|
if (node.type === "ObjectExpression") {
|
|
1003
|
-
|
|
1004
|
-
if (prop.type !== "Property")
|
|
1005
|
-
continue;
|
|
1006
|
-
const classNames = getStaticPropertyName(prop);
|
|
1007
|
-
if (!classNames)
|
|
1008
|
-
continue;
|
|
1009
|
-
yield* classNames.split(/\s+/).map((className) => ({ className, position: classNames.indexOf(className) + 1, reportNode: prop.key }));
|
|
1010
|
-
}
|
|
1053
|
+
yield* extractFromObject(node);
|
|
1011
1054
|
return;
|
|
1012
1055
|
}
|
|
1013
1056
|
if (node.type === "ArrayExpression") {
|
|
1014
|
-
|
|
1015
|
-
if (element == null)
|
|
1016
|
-
continue;
|
|
1017
|
-
if (element.type === "SpreadElement")
|
|
1018
|
-
continue;
|
|
1019
|
-
yield* extractClassNames(element);
|
|
1020
|
-
}
|
|
1057
|
+
yield* extractFromArray(node);
|
|
1021
1058
|
}
|
|
1022
1059
|
if (node.type === "ConditionalExpression") {
|
|
1023
1060
|
yield* extractClassNames(node.consequent);
|
|
@@ -1064,11 +1101,11 @@ const noDeprecatedClasses = createEslintRule({
|
|
|
1064
1101
|
schema: [],
|
|
1065
1102
|
type: "problem"
|
|
1066
1103
|
},
|
|
1067
|
-
name: RULE_NAME$
|
|
1104
|
+
name: RULE_NAME$8
|
|
1068
1105
|
});
|
|
1069
1106
|
|
|
1070
|
-
const debug$
|
|
1071
|
-
const RULE_NAME$
|
|
1107
|
+
const debug$5 = debugFactory("@rotki/eslint-plugin:no-deprecated-components");
|
|
1108
|
+
const RULE_NAME$7 = "no-deprecated-components";
|
|
1072
1109
|
const vuetify = {
|
|
1073
1110
|
VApp: true,
|
|
1074
1111
|
VAppBar: true,
|
|
@@ -1105,7 +1142,7 @@ const skipInLegacy = /* @__PURE__ */ new Set([
|
|
|
1105
1142
|
"Fragment"
|
|
1106
1143
|
]);
|
|
1107
1144
|
function hasReplacement$1(tag) {
|
|
1108
|
-
return Object.
|
|
1145
|
+
return Object.hasOwn(replacements$1, tag);
|
|
1109
1146
|
}
|
|
1110
1147
|
const noDeprecatedComponents = createEslintRule({
|
|
1111
1148
|
create(context, optionsWithDefault) {
|
|
@@ -1121,7 +1158,7 @@ const noDeprecatedComponents = createEslintRule({
|
|
|
1121
1158
|
return;
|
|
1122
1159
|
const replacement = replacements$1[tag];
|
|
1123
1160
|
if (replacement || legacy && skipInLegacy.has(tag)) {
|
|
1124
|
-
debug$
|
|
1161
|
+
debug$5(`${tag} has been deprecated`);
|
|
1125
1162
|
context.report({
|
|
1126
1163
|
data: {
|
|
1127
1164
|
name: tag
|
|
@@ -1130,7 +1167,7 @@ const noDeprecatedComponents = createEslintRule({
|
|
|
1130
1167
|
node: element
|
|
1131
1168
|
});
|
|
1132
1169
|
} else {
|
|
1133
|
-
debug$
|
|
1170
|
+
debug$5(`${tag} has will be removed`);
|
|
1134
1171
|
context.report({
|
|
1135
1172
|
data: {
|
|
1136
1173
|
name: tag
|
|
@@ -1173,11 +1210,10 @@ const noDeprecatedComponents = createEslintRule({
|
|
|
1173
1210
|
],
|
|
1174
1211
|
type: "problem"
|
|
1175
1212
|
},
|
|
1176
|
-
name: RULE_NAME$
|
|
1213
|
+
name: RULE_NAME$7
|
|
1177
1214
|
});
|
|
1178
1215
|
|
|
1179
|
-
const
|
|
1180
|
-
const RULE_NAME$4 = "no-deprecated-props";
|
|
1216
|
+
const RULE_NAME$6 = "no-deprecated-props";
|
|
1181
1217
|
const replacements = {
|
|
1182
1218
|
RuiRadio: {
|
|
1183
1219
|
internalValue: "value"
|
|
@@ -1200,37 +1236,34 @@ function getPropName(node) {
|
|
|
1200
1236
|
}
|
|
1201
1237
|
return kebabCase(node.key.rawName);
|
|
1202
1238
|
}
|
|
1239
|
+
function isNonBindDirective(node) {
|
|
1240
|
+
return node.directive && node.value?.type === "VExpressionContainer" && (node.key.name.name !== "bind" || !node.key.argument);
|
|
1241
|
+
}
|
|
1242
|
+
function getPropNameNode(node) {
|
|
1243
|
+
return node.directive ? node.key.argument : node.key;
|
|
1244
|
+
}
|
|
1203
1245
|
const noDeprecatedProps = createEslintRule({
|
|
1204
1246
|
create(context) {
|
|
1205
1247
|
return defineTemplateBodyVisitor(context, {
|
|
1206
1248
|
VAttribute(node) {
|
|
1207
|
-
if (
|
|
1249
|
+
if (isNonBindDirective(node))
|
|
1208
1250
|
return;
|
|
1209
1251
|
const tag = pascalCase(node.parent.parent.rawName);
|
|
1210
1252
|
if (!hasReplacement(tag))
|
|
1211
1253
|
return;
|
|
1212
|
-
debug$3(`${tag} has replacement properties`);
|
|
1213
1254
|
const propName = getPropName(node);
|
|
1214
|
-
const propNameNode = node
|
|
1215
|
-
if (!propName || !propNameNode)
|
|
1216
|
-
debug$3("could not get prop name and/or node");
|
|
1255
|
+
const propNameNode = getPropNameNode(node);
|
|
1256
|
+
if (!propName || !propNameNode)
|
|
1217
1257
|
return;
|
|
1218
|
-
}
|
|
1219
1258
|
const match = replacementMaps.get(tag)?.get(propName);
|
|
1220
|
-
if (match)
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
return fixer.replaceText(propNameNode, match.replacement);
|
|
1229
|
-
},
|
|
1230
|
-
messageId: "replacedWith",
|
|
1231
|
-
node: propNameNode
|
|
1232
|
-
});
|
|
1233
|
-
}
|
|
1259
|
+
if (!match)
|
|
1260
|
+
return;
|
|
1261
|
+
context.report({
|
|
1262
|
+
data: { prop: match.original, replacement: match.replacement },
|
|
1263
|
+
fix: (fixer) => fixer.replaceText(propNameNode, match.replacement),
|
|
1264
|
+
messageId: "replacedWith",
|
|
1265
|
+
node: propNameNode
|
|
1266
|
+
});
|
|
1234
1267
|
}
|
|
1235
1268
|
});
|
|
1236
1269
|
},
|
|
@@ -1247,10 +1280,10 @@ const noDeprecatedProps = createEslintRule({
|
|
|
1247
1280
|
schema: [],
|
|
1248
1281
|
type: "problem"
|
|
1249
1282
|
},
|
|
1250
|
-
name: RULE_NAME$
|
|
1283
|
+
name: RULE_NAME$6
|
|
1251
1284
|
});
|
|
1252
1285
|
|
|
1253
|
-
const RULE_NAME$
|
|
1286
|
+
const RULE_NAME$5 = "no-dot-ts-imports";
|
|
1254
1287
|
const noDotTsImport = createEslintRule({
|
|
1255
1288
|
create(context) {
|
|
1256
1289
|
return {
|
|
@@ -1285,10 +1318,10 @@ const noDotTsImport = createEslintRule({
|
|
|
1285
1318
|
schema: [],
|
|
1286
1319
|
type: "problem"
|
|
1287
1320
|
},
|
|
1288
|
-
name: RULE_NAME$
|
|
1321
|
+
name: RULE_NAME$5
|
|
1289
1322
|
});
|
|
1290
1323
|
|
|
1291
|
-
const RULE_NAME$
|
|
1324
|
+
const RULE_NAME$4 = "no-legacy-library-import";
|
|
1292
1325
|
const legacyLibrary = "@rotki/ui-library-compat";
|
|
1293
1326
|
const newLibrary = "@rotki/ui-library";
|
|
1294
1327
|
const noLegacyLibraryImport = createEslintRule({
|
|
@@ -1321,6 +1354,261 @@ const noLegacyLibraryImport = createEslintRule({
|
|
|
1321
1354
|
schema: [],
|
|
1322
1355
|
type: "problem"
|
|
1323
1356
|
},
|
|
1357
|
+
name: RULE_NAME$4
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
const RULE_NAME$3 = "no-redundant-flex-row";
|
|
1361
|
+
const debug$4 = debugFactory("@rotki/eslint-plugin:no-redundant-flex-row");
|
|
1362
|
+
function stripImportant(token) {
|
|
1363
|
+
return token.replace(/^!/, "").replace(/!$/, "");
|
|
1364
|
+
}
|
|
1365
|
+
function isUnconditional(token) {
|
|
1366
|
+
return !token.includes(":");
|
|
1367
|
+
}
|
|
1368
|
+
function isFlexDisplay(token) {
|
|
1369
|
+
if (!isUnconditional(token))
|
|
1370
|
+
return false;
|
|
1371
|
+
const base = stripImportant(token);
|
|
1372
|
+
return base === "flex" || base === "inline-flex";
|
|
1373
|
+
}
|
|
1374
|
+
function isFlexRow(token) {
|
|
1375
|
+
if (!isUnconditional(token))
|
|
1376
|
+
return false;
|
|
1377
|
+
return stripImportant(token) === "flex-row";
|
|
1378
|
+
}
|
|
1379
|
+
function tokenize(value) {
|
|
1380
|
+
const tokens = [];
|
|
1381
|
+
const matcher = /\S+/g;
|
|
1382
|
+
let match;
|
|
1383
|
+
while ((match = matcher.exec(value)) !== null)
|
|
1384
|
+
tokens.push({ start: match.index, text: match[0] });
|
|
1385
|
+
return tokens;
|
|
1386
|
+
}
|
|
1387
|
+
function removalRange(value, token, contentStart) {
|
|
1388
|
+
let startRel = token.start;
|
|
1389
|
+
let endRel = token.start + token.text.length;
|
|
1390
|
+
if (startRel > 0 && /\s/.test(value[startRel - 1])) {
|
|
1391
|
+
while (startRel > 0 && /\s/.test(value[startRel - 1]))
|
|
1392
|
+
startRel--;
|
|
1393
|
+
} else {
|
|
1394
|
+
while (endRel < value.length && /\s/.test(value[endRel]))
|
|
1395
|
+
endRel++;
|
|
1396
|
+
}
|
|
1397
|
+
return [contentStart + startRel, contentStart + endRel];
|
|
1398
|
+
}
|
|
1399
|
+
function reportRedundant(source, context, sourceCode) {
|
|
1400
|
+
const tokens = tokenize(source.value);
|
|
1401
|
+
if (!tokens.some((token) => isFlexDisplay(token.text)))
|
|
1402
|
+
return;
|
|
1403
|
+
for (const token of tokens) {
|
|
1404
|
+
if (!isFlexRow(token.text))
|
|
1405
|
+
continue;
|
|
1406
|
+
debug$4(`found redundant flex-row token '${token.text}'`);
|
|
1407
|
+
const tokenRange = [
|
|
1408
|
+
source.contentStart + token.start,
|
|
1409
|
+
source.contentStart + token.start + token.text.length
|
|
1410
|
+
];
|
|
1411
|
+
const removal = removalRange(source.value, token, source.contentStart);
|
|
1412
|
+
context.report({
|
|
1413
|
+
data: { className: token.text },
|
|
1414
|
+
fix(fixer) {
|
|
1415
|
+
return fixer.removeRange(removal);
|
|
1416
|
+
},
|
|
1417
|
+
loc: {
|
|
1418
|
+
end: sourceCode.getLocFromIndex(tokenRange[1]),
|
|
1419
|
+
start: sourceCode.getLocFromIndex(tokenRange[0])
|
|
1420
|
+
},
|
|
1421
|
+
messageId: "redundantFlexRow"
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
function plainStringLists(expression) {
|
|
1426
|
+
if (expression.type === "Literal" && typeof expression.value === "string")
|
|
1427
|
+
return [{ contentStart: expression.range[0] + 1, value: expression.value }];
|
|
1428
|
+
if (expression.type === "TemplateLiteral" && expression.expressions.length === 0 && expression.quasis.length === 1) {
|
|
1429
|
+
const quasi = expression.quasis[0];
|
|
1430
|
+
const cooked = quasi.value.cooked;
|
|
1431
|
+
if (cooked !== null)
|
|
1432
|
+
return [{ contentStart: quasi.range[0] + 1, value: cooked }];
|
|
1433
|
+
}
|
|
1434
|
+
return [];
|
|
1435
|
+
}
|
|
1436
|
+
const noRedundantFlexRow = createEslintRule({
|
|
1437
|
+
create(context) {
|
|
1438
|
+
const sourceCode = getSourceCode(context);
|
|
1439
|
+
return defineTemplateBodyVisitor(context, {
|
|
1440
|
+
'VAttribute[directive=false][key.name="class"]': function(node) {
|
|
1441
|
+
if (!node.value || !node.value.value || !node.value.range)
|
|
1442
|
+
return;
|
|
1443
|
+
reportRedundant(
|
|
1444
|
+
{ contentStart: node.value.range[0] + 1, value: node.value.value },
|
|
1445
|
+
context,
|
|
1446
|
+
sourceCode
|
|
1447
|
+
);
|
|
1448
|
+
},
|
|
1449
|
+
"VAttribute[directive=true][key.name.name='bind'][key.argument.name='class'] > VExpressionContainer.value": function(node) {
|
|
1450
|
+
if (!node.expression)
|
|
1451
|
+
return;
|
|
1452
|
+
for (const source of plainStringLists(node.expression))
|
|
1453
|
+
reportRedundant(source, context, sourceCode);
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
},
|
|
1457
|
+
defaultOptions: [],
|
|
1458
|
+
meta: {
|
|
1459
|
+
docs: {
|
|
1460
|
+
description: "disallow redundant `flex-row` since `flex` already defaults to the row direction",
|
|
1461
|
+
recommendation: "recommended"
|
|
1462
|
+
},
|
|
1463
|
+
fixable: "code",
|
|
1464
|
+
messages: {
|
|
1465
|
+
redundantFlexRow: `'{{ className }}' is redundant because 'flex' already lays out in the row direction; remove it, or keep it only behind a conditional variant (e.g. 'sm:flex-row')`
|
|
1466
|
+
},
|
|
1467
|
+
schema: [],
|
|
1468
|
+
type: "suggestion"
|
|
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
|
+
},
|
|
1324
1612
|
name: RULE_NAME$2
|
|
1325
1613
|
});
|
|
1326
1614
|
|
|
@@ -1337,18 +1625,39 @@ function isI18nCallExpression(node) {
|
|
|
1337
1625
|
return I18N_FUNCTION_NAMES.has(callee.name);
|
|
1338
1626
|
return callee.type === "MemberExpression" && !callee.computed && isAstNode(callee.property) && callee.property.type === "Identifier" && typeof callee.property.name === "string" && I18N_MEMBER_NAMES.has(callee.property.name);
|
|
1339
1627
|
}
|
|
1340
|
-
function
|
|
1341
|
-
|
|
1342
|
-
const expressions = arg.expressions;
|
|
1343
|
-
if (!Array.isArray(quasis) || !Array.isArray(expressions) || quasis.length === 0)
|
|
1628
|
+
function getQuasiText(quasi) {
|
|
1629
|
+
if (!quasi || typeof quasi !== "object" || !("value" in quasi))
|
|
1344
1630
|
return void 0;
|
|
1345
|
-
const
|
|
1346
|
-
if (!firstQuasi || typeof firstQuasi !== "object" || !("value" in firstQuasi))
|
|
1347
|
-
return void 0;
|
|
1348
|
-
const quasiObj = firstQuasi.value;
|
|
1631
|
+
const quasiObj = quasi.value;
|
|
1349
1632
|
if (!quasiObj || typeof quasiObj !== "object")
|
|
1350
1633
|
return void 0;
|
|
1351
|
-
|
|
1634
|
+
if ("cooked" in quasiObj && typeof quasiObj.cooked === "string")
|
|
1635
|
+
return quasiObj.cooked;
|
|
1636
|
+
if ("raw" in quasiObj && typeof quasiObj.raw === "string")
|
|
1637
|
+
return quasiObj.raw;
|
|
1638
|
+
return void 0;
|
|
1639
|
+
}
|
|
1640
|
+
function getTemplateLiteralText(arg) {
|
|
1641
|
+
const { expressions, quasis } = arg;
|
|
1642
|
+
if (!Array.isArray(quasis) || !Array.isArray(expressions) || quasis.length === 0)
|
|
1643
|
+
return void 0;
|
|
1644
|
+
return getQuasiText(quasis[0]);
|
|
1645
|
+
}
|
|
1646
|
+
function isStaticTemplateLiteral(arg) {
|
|
1647
|
+
return Array.isArray(arg.expressions) && arg.expressions.length === 0 && Array.isArray(arg.quasis) && arg.quasis.length === 1;
|
|
1648
|
+
}
|
|
1649
|
+
function extractKeyFromArg(arg) {
|
|
1650
|
+
if (arg.type === "Literal" && typeof arg.value === "string")
|
|
1651
|
+
return arg.value;
|
|
1652
|
+
if (arg.type === "TemplateLiteral") {
|
|
1653
|
+
const text = getTemplateLiteralText(arg);
|
|
1654
|
+
if (text === void 0)
|
|
1655
|
+
return void 0;
|
|
1656
|
+
if (isStaticTemplateLiteral(arg))
|
|
1657
|
+
return text;
|
|
1658
|
+
return text ? `${text}*` : void 0;
|
|
1659
|
+
}
|
|
1660
|
+
return void 0;
|
|
1352
1661
|
}
|
|
1353
1662
|
function extractKeysFromCallExpression(node, keys) {
|
|
1354
1663
|
if (!isI18nCallExpression(node))
|
|
@@ -1359,17 +1668,9 @@ function extractKeysFromCallExpression(node, keys) {
|
|
|
1359
1668
|
const arg = args[0];
|
|
1360
1669
|
if (!isAstNode(arg))
|
|
1361
1670
|
return;
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
const text = getTemplateLiteralText(arg);
|
|
1366
|
-
if (text === void 0)
|
|
1367
|
-
return;
|
|
1368
|
-
if (arg.expressions && Array.isArray(arg.expressions) && arg.expressions.length === 0 && Array.isArray(arg.quasis) && arg.quasis.length === 1)
|
|
1369
|
-
keys.add(text);
|
|
1370
|
-
else if (text)
|
|
1371
|
-
keys.add(`${text}*`);
|
|
1372
|
-
}
|
|
1671
|
+
const key = extractKeyFromArg(arg);
|
|
1672
|
+
if (key)
|
|
1673
|
+
keys.add(key);
|
|
1373
1674
|
}
|
|
1374
1675
|
const TS_AST_CHILD_KEYS = /* @__PURE__ */ new Set([
|
|
1375
1676
|
"alternate",
|
|
@@ -1446,34 +1747,44 @@ function getDirectiveExpression(attr) {
|
|
|
1446
1747
|
function extractKeysFromVueTemplate(templateBody, keys) {
|
|
1447
1748
|
walkVueTemplateNode(templateBody, keys);
|
|
1448
1749
|
}
|
|
1750
|
+
function collectI18nTKeys(element, keys) {
|
|
1751
|
+
if (element.rawName !== "i18n-t" && element.name !== "i18n-t")
|
|
1752
|
+
return;
|
|
1753
|
+
for (const attr of element.startTag.attributes) {
|
|
1754
|
+
if (isKeypathAttribute(attr))
|
|
1755
|
+
keys.add(attr.value.value);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
function collectAttributeKeys(element, keys) {
|
|
1759
|
+
for (const attr of element.startTag.attributes) {
|
|
1760
|
+
const expr = getDirectiveExpression(attr);
|
|
1761
|
+
if (!expr)
|
|
1762
|
+
continue;
|
|
1763
|
+
if (isVTDirective(attr) && expr.type === "Literal" && typeof expr.value === "string")
|
|
1764
|
+
keys.add(expr.value);
|
|
1765
|
+
walkTsAst(expr, keys);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
function walkChildren(node, keys) {
|
|
1769
|
+
if (!("children" in node) || !Array.isArray(node.children))
|
|
1770
|
+
return;
|
|
1771
|
+
for (const child of node.children) {
|
|
1772
|
+
if (child && typeof child === "object")
|
|
1773
|
+
walkVueTemplateNode(child, keys);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1449
1776
|
function walkVueTemplateNode(node, keys) {
|
|
1450
1777
|
if (!node || typeof node !== "object")
|
|
1451
1778
|
return;
|
|
1452
1779
|
if (isVElement(node)) {
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
keys.add(attr.value.value);
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
for (const attr of node.startTag.attributes) {
|
|
1460
|
-
const expr = getDirectiveExpression(attr);
|
|
1461
|
-
if (!expr)
|
|
1462
|
-
continue;
|
|
1463
|
-
if (isVTDirective(attr) && expr.type === "Literal" && typeof expr.value === "string")
|
|
1464
|
-
keys.add(expr.value);
|
|
1465
|
-
walkTsAst(expr, keys);
|
|
1466
|
-
}
|
|
1467
|
-
for (const child of node.children)
|
|
1468
|
-
walkVueTemplateNode(child, keys);
|
|
1780
|
+
collectI18nTKeys(node, keys);
|
|
1781
|
+
collectAttributeKeys(node, keys);
|
|
1782
|
+
walkChildren(node, keys);
|
|
1469
1783
|
} else if (isVExpressionContainer(node)) {
|
|
1470
1784
|
if (node.expression)
|
|
1471
1785
|
walkTsAst(node.expression, keys);
|
|
1472
|
-
} else
|
|
1473
|
-
|
|
1474
|
-
if (child && typeof child === "object")
|
|
1475
|
-
walkVueTemplateNode(child, keys);
|
|
1476
|
-
}
|
|
1786
|
+
} else {
|
|
1787
|
+
walkChildren(node, keys);
|
|
1477
1788
|
}
|
|
1478
1789
|
}
|
|
1479
1790
|
function extractKeysFromSfcI18nBlock(content, keys) {
|
|
@@ -1527,45 +1838,42 @@ ${content}
|
|
|
1527
1838
|
return void 0;
|
|
1528
1839
|
}
|
|
1529
1840
|
}
|
|
1530
|
-
function
|
|
1531
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1532
|
-
let mtimeMs;
|
|
1841
|
+
function readFileWithMtime(filePath) {
|
|
1533
1842
|
try {
|
|
1534
|
-
mtimeMs = statSync(filePath).mtimeMs;
|
|
1843
|
+
const mtimeMs = statSync(filePath).mtimeMs;
|
|
1844
|
+
const content = readFileSync(filePath, "utf-8");
|
|
1845
|
+
return { content, mtimeMs };
|
|
1535
1846
|
} catch {
|
|
1536
|
-
return
|
|
1537
|
-
}
|
|
1538
|
-
const cached = fileCache.get(filePath);
|
|
1539
|
-
if (cached && cached.mtimeMs === mtimeMs) {
|
|
1540
|
-
return cached.keys;
|
|
1541
|
-
}
|
|
1542
|
-
let content;
|
|
1543
|
-
try {
|
|
1544
|
-
content = readFileSync(filePath, "utf-8");
|
|
1545
|
-
} catch {
|
|
1546
|
-
return keys;
|
|
1547
|
-
}
|
|
1548
|
-
const isVue = filePath.endsWith(".vue");
|
|
1549
|
-
const pattern = isVue ? VUE_I18N_PATTERN : I18N_CALL_PATTERN;
|
|
1550
|
-
if (!pattern.test(content)) {
|
|
1551
|
-
fileCache.set(filePath, { keys, mtimeMs });
|
|
1552
|
-
return keys;
|
|
1847
|
+
return void 0;
|
|
1553
1848
|
}
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
return keys;
|
|
1849
|
+
}
|
|
1850
|
+
function extractKeysFromAst(ast, content, isVue, keys) {
|
|
1557
1851
|
if (isVue) {
|
|
1558
|
-
if (ast.templateBody)
|
|
1852
|
+
if (ast.templateBody)
|
|
1559
1853
|
extractKeysFromVueTemplate(ast.templateBody, keys);
|
|
1560
|
-
}
|
|
1561
1854
|
extractKeysFromSfcI18nBlock(content, keys);
|
|
1562
1855
|
}
|
|
1563
1856
|
if (ast.body) {
|
|
1564
|
-
for (const node of ast.body)
|
|
1857
|
+
for (const node of ast.body)
|
|
1565
1858
|
walkTsAst(node, keys);
|
|
1566
|
-
}
|
|
1567
1859
|
}
|
|
1568
|
-
|
|
1860
|
+
}
|
|
1861
|
+
function collectKeysFromFile(filePath) {
|
|
1862
|
+
const file = readFileWithMtime(filePath);
|
|
1863
|
+
if (!file)
|
|
1864
|
+
return /* @__PURE__ */ new Set();
|
|
1865
|
+
const cached = fileCache.get(filePath);
|
|
1866
|
+
if (cached && cached.mtimeMs === file.mtimeMs)
|
|
1867
|
+
return cached.keys;
|
|
1868
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1869
|
+
const isVue = filePath.endsWith(".vue");
|
|
1870
|
+
const pattern = isVue ? VUE_I18N_PATTERN : I18N_CALL_PATTERN;
|
|
1871
|
+
if (pattern.test(file.content)) {
|
|
1872
|
+
const ast = parseSourceFile(file.content, filePath);
|
|
1873
|
+
if (ast)
|
|
1874
|
+
extractKeysFromAst(ast, file.content, isVue, keys);
|
|
1875
|
+
}
|
|
1876
|
+
fileCache.set(filePath, { keys, mtimeMs: file.mtimeMs });
|
|
1569
1877
|
return keys;
|
|
1570
1878
|
}
|
|
1571
1879
|
function collectAllUsedKeys(srcDir, extensions) {
|
|
@@ -1639,21 +1947,22 @@ const debug = debugFactory("@rotki/eslint-plugin:no-unused-i18n-keys");
|
|
|
1639
1947
|
function isLocaleFile(filename) {
|
|
1640
1948
|
return /(?:locales?|i18n|translations?|messages?|lang)\b/i.test(filename);
|
|
1641
1949
|
}
|
|
1642
|
-
function
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1950
|
+
function isProgramWithBody(ast) {
|
|
1951
|
+
return !!ast && typeof ast === "object" && "type" in ast && ast.type === "Program" && "body" in ast && Array.isArray(ast.body) && ast.body.length > 0;
|
|
1952
|
+
}
|
|
1953
|
+
function getProgramFirstBodyType(ast) {
|
|
1954
|
+
if (!isProgramWithBody(ast))
|
|
1955
|
+
return void 0;
|
|
1647
1956
|
const first = ast.body[0];
|
|
1648
|
-
|
|
1957
|
+
if (!first || typeof first !== "object" || !("type" in first) || typeof first.type !== "string")
|
|
1958
|
+
return void 0;
|
|
1959
|
+
return first.type;
|
|
1960
|
+
}
|
|
1961
|
+
function isJsonProgram(ast) {
|
|
1962
|
+
return getProgramFirstBodyType(ast) === "JSONExpressionStatement";
|
|
1649
1963
|
}
|
|
1650
1964
|
function isYamlProgram(ast) {
|
|
1651
|
-
|
|
1652
|
-
return false;
|
|
1653
|
-
if (!("body" in ast) || !Array.isArray(ast.body) || ast.body.length === 0)
|
|
1654
|
-
return false;
|
|
1655
|
-
const first = ast.body[0];
|
|
1656
|
-
return first !== null && typeof first === "object" && "type" in first && first.type === "YAMLDocument";
|
|
1965
|
+
return getProgramFirstBodyType(ast) === "YAMLDocument";
|
|
1657
1966
|
}
|
|
1658
1967
|
function buildJsonKeyPaths(node, prefix, paths) {
|
|
1659
1968
|
for (const prop of node.properties) {
|
|
@@ -1954,6 +2263,8 @@ const plugin = {
|
|
|
1954
2263
|
"no-deprecated-props": noDeprecatedProps,
|
|
1955
2264
|
"no-dot-ts-imports": noDotTsImport,
|
|
1956
2265
|
"no-legacy-library-import": noLegacyLibraryImport,
|
|
2266
|
+
"no-redundant-flex-row": noRedundantFlexRow,
|
|
2267
|
+
"no-shared-pinia": noSharedPinia,
|
|
1957
2268
|
"no-unused-i18n-keys": noUnusedI18nKeys,
|
|
1958
2269
|
"require-jsdoc-on-composable-options": requireJsdocOnComposableOptions
|
|
1959
2270
|
}
|