eslint-plugin-flawless 1.2.0 → 1.3.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 +1 -0
- package/dist/index.d.mts +37 -16
- package/dist/index.mjs +1 -1
- package/dist/oxlint.mjs +1 -1
- package/dist/{plugin-K2GOCG3Z.mjs → plugin-CDVxMgSG.mjs} +306 -85
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -211,6 +211,7 @@ Requires [type information](https://typescript-eslint.io/linting/typed-linting).
|
|
|
211
211
|
| [no-unnecessary-use-memo](src/rules/no-unnecessary-use-memo/documentation.md) | Disallow unnecessary usage of 'useMemo' | | |
|
|
212
212
|
| [padding-after-expect-assertions](src/rules/padding-after-expect-assertions/documentation.md) | Enforce a blank line after `expect.assertions` and `expect.hasAssertions` | 🔧 | |
|
|
213
213
|
| [prefer-destructuring-assignment](src/rules/prefer-destructuring-assignment/documentation.md) | Enforce destructuring assignment for component props | 🔧 | |
|
|
214
|
+
| [prefer-ending-with-an-expect](src/rules/prefer-ending-with-an-expect/documentation.md) | Prefer having the last statement in a test be an assertion | | |
|
|
214
215
|
| [prefer-parameter-destructuring](src/rules/prefer-parameter-destructuring/documentation.md) | Enforce destructuring parameters in the function signature | 🔧 | |
|
|
215
216
|
| [prefer-read-only-props](src/rules/prefer-read-only-props/documentation.md) | Enforce that function component props are read-only | 🔧 | 💭 |
|
|
216
217
|
| [purity](src/rules/purity/documentation.md) | Disallow impure calls such as `math.random` or `os.clock` during render | | |
|
package/dist/index.d.mts
CHANGED
|
@@ -51,7 +51,7 @@ declare const EXPLICIT = "useExplicitReturn";
|
|
|
51
51
|
declare const COMPLEX_EXPLICIT = "useExplicitReturnComplex";
|
|
52
52
|
type MessageIds$5 = typeof COMPLEX_EXPLICIT | typeof EXPLICIT | typeof IMPLICIT;
|
|
53
53
|
type ObjectReturnStyle = "always-explicit" | "complex-explicit" | "off";
|
|
54
|
-
type Options$
|
|
54
|
+
type Options$7 = [{
|
|
55
55
|
/** Always use explicit returns for JSX bodies. */
|
|
56
56
|
jsxAlwaysUseExplicitReturn?: boolean;
|
|
57
57
|
/** Maximum emitted line length (tab-expanded) before requiring an explicit return. */
|
|
@@ -86,7 +86,7 @@ interface JsxShorthandFragmentOptions {
|
|
|
86
86
|
*/
|
|
87
87
|
readonly mode?: Mode;
|
|
88
88
|
}
|
|
89
|
-
type Options$
|
|
89
|
+
type Options$6 = [JsxShorthandFragmentOptions?];
|
|
90
90
|
type Mode = "element" | "syntax";
|
|
91
91
|
//#endregion
|
|
92
92
|
//#region src/rules/max-lines-per-function/rule.d.ts
|
|
@@ -119,7 +119,7 @@ interface MaxLinesPerFunctionOptions {
|
|
|
119
119
|
*/
|
|
120
120
|
readonly skipComments?: boolean;
|
|
121
121
|
}
|
|
122
|
-
type Options$
|
|
122
|
+
type Options$5 = [MaxLinesPerFunctionOptions?];
|
|
123
123
|
//#endregion
|
|
124
124
|
//#region src/rules/naming-convention/utils/enums.d.ts
|
|
125
125
|
declare const Selector: {
|
|
@@ -246,7 +246,7 @@ interface NamingSelector {
|
|
|
246
246
|
//#endregion
|
|
247
247
|
//#region src/rules/naming-convention/rule.d.ts
|
|
248
248
|
type MessageIds$3 = "doesNotMatchFormat" | "doesNotMatchFormatForeignContract" | "doesNotMatchFormatTrimmed" | "doesNotMatchFormatTrimmedForeignContract" | "missingAffix" | "missingAffixForeignContract" | "missingUnderscore" | "missingUnderscoreForeignContract" | "satisfyCustom" | "satisfyCustomForeignContract" | "unexpectedUnderscore" | "unexpectedUnderscoreForeignContract";
|
|
249
|
-
type Options$
|
|
249
|
+
type Options$4 = Array<NamingSelector>;
|
|
250
250
|
//#endregion
|
|
251
251
|
//#region src/rules/no-unnecessary-use-callback/rule.d.ts
|
|
252
252
|
declare const MESSAGE_ID_DEFAULT$1 = "default";
|
|
@@ -258,6 +258,24 @@ declare const MESSAGE_ID_DEFAULT = "default";
|
|
|
258
258
|
declare const MESSAGE_ID_INSIDE_USE_EFFECT = "noUnnecessaryUseMemoInsideUseEffect";
|
|
259
259
|
type MessageIds$1 = typeof MESSAGE_ID_DEFAULT | typeof MESSAGE_ID_INSIDE_USE_EFFECT;
|
|
260
260
|
//#endregion
|
|
261
|
+
//#region src/rules/prefer-ending-with-an-expect/rule.d.ts
|
|
262
|
+
interface PreferEndingWithAnExpectOptions {
|
|
263
|
+
/**
|
|
264
|
+
* Callee names, in addition to a resolved vitest `it`/`test`, whose second
|
|
265
|
+
* argument is treated as a test body. Matched by exact dotted name (no scope
|
|
266
|
+
* resolution), for libraries with custom test blocks such as `each.test`.
|
|
267
|
+
*/
|
|
268
|
+
readonly additionalTestBlockFunctions?: ReadonlyArray<string>;
|
|
269
|
+
/**
|
|
270
|
+
* Function names, in addition to a resolved vitest `expect`, that count as an
|
|
271
|
+
* assertion when they end a test. Matched by name against the callee chain,
|
|
272
|
+
* so no scope resolution applies. A `*` matches a single dotted segment and
|
|
273
|
+
* `**` matches any number, allowing patterns such as `request.*.expect`.
|
|
274
|
+
*/
|
|
275
|
+
readonly assertFunctionNames?: ReadonlyArray<string>;
|
|
276
|
+
}
|
|
277
|
+
type Options$3 = [PreferEndingWithAnExpectOptions?];
|
|
278
|
+
//#endregion
|
|
261
279
|
//#region src/rules/prefer-parameter-destructuring/rule.d.ts
|
|
262
280
|
type Options$2 = [{
|
|
263
281
|
/**
|
|
@@ -305,11 +323,11 @@ declare const plugin: {
|
|
|
305
323
|
version: string;
|
|
306
324
|
};
|
|
307
325
|
rules: {
|
|
308
|
-
"arrow-return-style": FlawlessRuleModule<Options$
|
|
326
|
+
"arrow-return-style": FlawlessRuleModule<Options$7, MessageIds$5, Readonly<TSESLint.SourceCode>>;
|
|
309
327
|
"jsx-shorthand-boolean": FlawlessRuleModule<[], "setAttributeValue", Readonly<TSESLint.SourceCode>>;
|
|
310
|
-
"jsx-shorthand-fragment": FlawlessRuleModule<Options$
|
|
311
|
-
"max-lines-per-function": FlawlessRuleModule<Options$
|
|
312
|
-
"naming-convention": TSESLint.RuleModule<MessageIds$3, Options$
|
|
328
|
+
"jsx-shorthand-fragment": FlawlessRuleModule<Options$6, MessageIds$4, Readonly<TSESLint.SourceCode>>;
|
|
329
|
+
"max-lines-per-function": FlawlessRuleModule<Options$5, "exceed", Readonly<TSESLint.SourceCode>>;
|
|
330
|
+
"naming-convention": TSESLint.RuleModule<MessageIds$3, Options$4, PluginDocumentation, TSESLint.RuleListener> & {
|
|
313
331
|
name: string;
|
|
314
332
|
};
|
|
315
333
|
"no-export-default-arrow": FlawlessRuleModule<[], "disallowExportDefaultArrow", Readonly<TSESLint.SourceCode>>;
|
|
@@ -320,6 +338,7 @@ declare const plugin: {
|
|
|
320
338
|
"no-unnecessary-use-memo": FlawlessRuleModule<[], MessageIds$1, Readonly<TSESLint.SourceCode>>;
|
|
321
339
|
"padding-after-expect-assertions": FlawlessRuleModule<[], "missingPadding", Readonly<TSESLint.SourceCode>>;
|
|
322
340
|
"prefer-destructuring-assignment": FlawlessRuleModule<[], "default", Readonly<TSESLint.SourceCode>>;
|
|
341
|
+
"prefer-ending-with-an-expect": FlawlessRuleModule<Options$3, "mustEndWithExpect", Readonly<TSESLint.SourceCode>>;
|
|
323
342
|
"prefer-parameter-destructuring": FlawlessRuleModule<Options$2, "default", Readonly<TSESLint.SourceCode>>;
|
|
324
343
|
"prefer-read-only-props": TSESLint.RuleModule<"preferReadOnlyProps", [{
|
|
325
344
|
fixStyle?: "modifier" | "wrap";
|
|
@@ -350,11 +369,11 @@ declare const _default: {
|
|
|
350
369
|
version: string;
|
|
351
370
|
};
|
|
352
371
|
rules: {
|
|
353
|
-
"arrow-return-style": FlawlessRuleModule<Options$
|
|
372
|
+
"arrow-return-style": FlawlessRuleModule<Options$7, MessageIds$5, Readonly<import("${configDir}").SourceCode>>;
|
|
354
373
|
"jsx-shorthand-boolean": FlawlessRuleModule<[], "setAttributeValue", Readonly<import("${configDir}").SourceCode>>;
|
|
355
|
-
"jsx-shorthand-fragment": FlawlessRuleModule<Options$
|
|
356
|
-
"max-lines-per-function": FlawlessRuleModule<Options$
|
|
357
|
-
"naming-convention": import("${configDir}").RuleModule<MessageIds$3, Options$
|
|
374
|
+
"jsx-shorthand-fragment": FlawlessRuleModule<Options$6, MessageIds$4, Readonly<import("${configDir}").SourceCode>>;
|
|
375
|
+
"max-lines-per-function": FlawlessRuleModule<Options$5, "exceed", Readonly<import("${configDir}").SourceCode>>;
|
|
376
|
+
"naming-convention": import("${configDir}").RuleModule<MessageIds$3, Options$4, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
358
377
|
name: string;
|
|
359
378
|
};
|
|
360
379
|
"no-export-default-arrow": FlawlessRuleModule<[], "disallowExportDefaultArrow", Readonly<import("${configDir}").SourceCode>>;
|
|
@@ -365,6 +384,7 @@ declare const _default: {
|
|
|
365
384
|
"no-unnecessary-use-memo": FlawlessRuleModule<[], MessageIds$1, Readonly<import("${configDir}").SourceCode>>;
|
|
366
385
|
"padding-after-expect-assertions": FlawlessRuleModule<[], "missingPadding", Readonly<import("${configDir}").SourceCode>>;
|
|
367
386
|
"prefer-destructuring-assignment": FlawlessRuleModule<[], "default", Readonly<import("${configDir}").SourceCode>>;
|
|
387
|
+
"prefer-ending-with-an-expect": FlawlessRuleModule<Options$3, "mustEndWithExpect", Readonly<import("${configDir}").SourceCode>>;
|
|
368
388
|
"prefer-parameter-destructuring": FlawlessRuleModule<Options$2, "default", Readonly<import("${configDir}").SourceCode>>;
|
|
369
389
|
"prefer-read-only-props": import("${configDir}").RuleModule<"preferReadOnlyProps", [{
|
|
370
390
|
fixStyle?: "modifier" | "wrap";
|
|
@@ -392,11 +412,11 @@ declare const _default: {
|
|
|
392
412
|
version: string;
|
|
393
413
|
};
|
|
394
414
|
rules: {
|
|
395
|
-
"arrow-return-style": FlawlessRuleModule<Options$
|
|
415
|
+
"arrow-return-style": FlawlessRuleModule<Options$7, MessageIds$5, Readonly<import("${configDir}").SourceCode>>;
|
|
396
416
|
"jsx-shorthand-boolean": FlawlessRuleModule<[], "setAttributeValue", Readonly<import("${configDir}").SourceCode>>;
|
|
397
|
-
"jsx-shorthand-fragment": FlawlessRuleModule<Options$
|
|
398
|
-
"max-lines-per-function": FlawlessRuleModule<Options$
|
|
399
|
-
"naming-convention": import("${configDir}").RuleModule<MessageIds$3, Options$
|
|
417
|
+
"jsx-shorthand-fragment": FlawlessRuleModule<Options$6, MessageIds$4, Readonly<import("${configDir}").SourceCode>>;
|
|
418
|
+
"max-lines-per-function": FlawlessRuleModule<Options$5, "exceed", Readonly<import("${configDir}").SourceCode>>;
|
|
419
|
+
"naming-convention": import("${configDir}").RuleModule<MessageIds$3, Options$4, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
400
420
|
name: string;
|
|
401
421
|
};
|
|
402
422
|
"no-export-default-arrow": FlawlessRuleModule<[], "disallowExportDefaultArrow", Readonly<import("${configDir}").SourceCode>>;
|
|
@@ -407,6 +427,7 @@ declare const _default: {
|
|
|
407
427
|
"no-unnecessary-use-memo": FlawlessRuleModule<[], MessageIds$1, Readonly<import("${configDir}").SourceCode>>;
|
|
408
428
|
"padding-after-expect-assertions": FlawlessRuleModule<[], "missingPadding", Readonly<import("${configDir}").SourceCode>>;
|
|
409
429
|
"prefer-destructuring-assignment": FlawlessRuleModule<[], "default", Readonly<import("${configDir}").SourceCode>>;
|
|
430
|
+
"prefer-ending-with-an-expect": FlawlessRuleModule<Options$3, "mustEndWithExpect", Readonly<import("${configDir}").SourceCode>>;
|
|
410
431
|
"prefer-parameter-destructuring": FlawlessRuleModule<Options$2, "default", Readonly<import("${configDir}").SourceCode>>;
|
|
411
432
|
"prefer-read-only-props": import("${configDir}").RuleModule<"preferReadOnlyProps", [{
|
|
412
433
|
fixStyle?: "modifier" | "wrap";
|
package/dist/index.mjs
CHANGED
package/dist/oxlint.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import * as core from "@eslint-react/core";
|
|
|
14
14
|
import { getStaticTOMLValue } from "toml-eslint-parser";
|
|
15
15
|
//#region package.json
|
|
16
16
|
var name = "eslint-plugin-flawless";
|
|
17
|
-
var version = "1.
|
|
17
|
+
var version = "1.3.0";
|
|
18
18
|
var repository = {
|
|
19
19
|
"url": "git+https://github.com/christopher-buss/eslint-plugin-flawless.git",
|
|
20
20
|
"type": "git"
|
|
@@ -84,11 +84,11 @@ function createFlawlessRule({ createOnce, ...meta }) {
|
|
|
84
84
|
}
|
|
85
85
|
//#endregion
|
|
86
86
|
//#region src/rules/arrow-return-style/rule.ts
|
|
87
|
-
const RULE_NAME$
|
|
87
|
+
const RULE_NAME$17 = "arrow-return-style";
|
|
88
88
|
const IMPLICIT = "useImplicitReturn";
|
|
89
89
|
const EXPLICIT = "useExplicitReturn";
|
|
90
90
|
const COMPLEX_EXPLICIT = "useExplicitReturnComplex";
|
|
91
|
-
const DEFAULTS$
|
|
91
|
+
const DEFAULTS$2 = {
|
|
92
92
|
jsxAlwaysUseExplicitReturn: false,
|
|
93
93
|
maxLen: 80,
|
|
94
94
|
maxObjectProperties: 4,
|
|
@@ -232,9 +232,9 @@ function collectArrows(root) {
|
|
|
232
232
|
return arrows.sort((a, b) => a.range[0] - b.range[0]);
|
|
233
233
|
}
|
|
234
234
|
const arrowReturnStyle = createFlawlessRule({
|
|
235
|
-
name: RULE_NAME$
|
|
235
|
+
name: RULE_NAME$17,
|
|
236
236
|
createOnce(context) {
|
|
237
|
-
let config = DEFAULTS$
|
|
237
|
+
let config = DEFAULTS$2;
|
|
238
238
|
let sourceCode;
|
|
239
239
|
let lines;
|
|
240
240
|
let pendingConsults;
|
|
@@ -533,7 +533,7 @@ const arrowReturnStyle = createFlawlessRule({
|
|
|
533
533
|
},
|
|
534
534
|
"before": function() {
|
|
535
535
|
config = {
|
|
536
|
-
...DEFAULTS$
|
|
536
|
+
...DEFAULTS$2,
|
|
537
537
|
...context.options[0]
|
|
538
538
|
};
|
|
539
539
|
({sourceCode} = context);
|
|
@@ -545,7 +545,7 @@ const arrowReturnStyle = createFlawlessRule({
|
|
|
545
545
|
}
|
|
546
546
|
};
|
|
547
547
|
},
|
|
548
|
-
defaultOptions: [DEFAULTS$
|
|
548
|
+
defaultOptions: [DEFAULTS$2],
|
|
549
549
|
meta: {
|
|
550
550
|
docs: {
|
|
551
551
|
description: "Enforce arrow function return style based on line length",
|
|
@@ -598,23 +598,23 @@ const arrowReturnStyle = createFlawlessRule({
|
|
|
598
598
|
});
|
|
599
599
|
//#endregion
|
|
600
600
|
//#region src/rules/jsx-shorthand-boolean/rule.ts
|
|
601
|
-
const RULE_NAME$
|
|
602
|
-
const MESSAGE_ID$
|
|
603
|
-
const messages$
|
|
604
|
-
function createOnce$
|
|
601
|
+
const RULE_NAME$16 = "jsx-shorthand-boolean";
|
|
602
|
+
const MESSAGE_ID$8 = "setAttributeValue";
|
|
603
|
+
const messages$16 = { [MESSAGE_ID$8]: "Set an explicit value for boolean attribute '{{name}}'." };
|
|
604
|
+
function createOnce$11(context) {
|
|
605
605
|
return { JSXAttribute(node) {
|
|
606
606
|
if (node.value !== null) return;
|
|
607
607
|
context.report({
|
|
608
608
|
data: { name: context.sourceCode.getText(node.name) },
|
|
609
609
|
fix: (fixer) => fixer.insertTextAfter(node.name, "={true}"),
|
|
610
|
-
messageId: MESSAGE_ID$
|
|
610
|
+
messageId: MESSAGE_ID$8,
|
|
611
611
|
node
|
|
612
612
|
});
|
|
613
613
|
} };
|
|
614
614
|
}
|
|
615
615
|
const jsxShorthandBoolean = createFlawlessRule({
|
|
616
|
-
name: RULE_NAME$
|
|
617
|
-
createOnce: createOnce$
|
|
616
|
+
name: RULE_NAME$16,
|
|
617
|
+
createOnce: createOnce$11,
|
|
618
618
|
defaultOptions: [],
|
|
619
619
|
meta: {
|
|
620
620
|
docs: {
|
|
@@ -624,23 +624,23 @@ const jsxShorthandBoolean = createFlawlessRule({
|
|
|
624
624
|
},
|
|
625
625
|
fixable: "code",
|
|
626
626
|
hasSuggestions: false,
|
|
627
|
-
messages: messages$
|
|
627
|
+
messages: messages$16,
|
|
628
628
|
schema: [],
|
|
629
629
|
type: "suggestion"
|
|
630
630
|
}
|
|
631
631
|
});
|
|
632
632
|
//#endregion
|
|
633
633
|
//#region src/rules/jsx-shorthand-fragment/rule.ts
|
|
634
|
-
const RULE_NAME$
|
|
634
|
+
const RULE_NAME$15 = "jsx-shorthand-fragment";
|
|
635
635
|
const MESSAGE_ID_NAMED = "useNamedFragment";
|
|
636
636
|
const MESSAGE_ID_SHORTHAND = "useShorthandFragment";
|
|
637
637
|
const DEFAULT_MODE = "syntax";
|
|
638
638
|
const DEFAULT_FRAGMENT_NAME = "Fragment";
|
|
639
|
-
const messages$
|
|
639
|
+
const messages$15 = {
|
|
640
640
|
[MESSAGE_ID_NAMED]: "Use the '{{name}}' component instead of fragment shorthand syntax.",
|
|
641
641
|
[MESSAGE_ID_SHORTHAND]: "Use the fragment shorthand syntax '<>...</>' instead of the '{{name}}' component."
|
|
642
642
|
};
|
|
643
|
-
const schema$
|
|
643
|
+
const schema$4 = [{
|
|
644
644
|
additionalProperties: false,
|
|
645
645
|
properties: {
|
|
646
646
|
fragmentName: {
|
|
@@ -672,7 +672,7 @@ function jsxNameToString(node) {
|
|
|
672
672
|
}
|
|
673
673
|
return null;
|
|
674
674
|
}
|
|
675
|
-
function createOnce$
|
|
675
|
+
function createOnce$10(context) {
|
|
676
676
|
let mode;
|
|
677
677
|
let fragmentName;
|
|
678
678
|
let namedFragments;
|
|
@@ -719,8 +719,8 @@ function createOnce$9(context) {
|
|
|
719
719
|
};
|
|
720
720
|
}
|
|
721
721
|
const jsxShorthandFragment = createFlawlessRule({
|
|
722
|
-
name: RULE_NAME$
|
|
723
|
-
createOnce: createOnce$
|
|
722
|
+
name: RULE_NAME$15,
|
|
723
|
+
createOnce: createOnce$10,
|
|
724
724
|
defaultOptions: [{
|
|
725
725
|
fragmentName: DEFAULT_FRAGMENT_NAME,
|
|
726
726
|
mode: DEFAULT_MODE
|
|
@@ -737,8 +737,8 @@ const jsxShorthandFragment = createFlawlessRule({
|
|
|
737
737
|
},
|
|
738
738
|
fixable: "code",
|
|
739
739
|
hasSuggestions: false,
|
|
740
|
-
messages: messages$
|
|
741
|
-
schema: schema$
|
|
740
|
+
messages: messages$15,
|
|
741
|
+
schema: schema$4,
|
|
742
742
|
type: "suggestion"
|
|
743
743
|
}
|
|
744
744
|
});
|
|
@@ -877,19 +877,19 @@ function getOpeningParenOfParameters(node, sourceCode) {
|
|
|
877
877
|
}
|
|
878
878
|
//#endregion
|
|
879
879
|
//#region src/rules/max-lines-per-function/rule.ts
|
|
880
|
-
const RULE_NAME$
|
|
880
|
+
const RULE_NAME$14 = "max-lines-per-function";
|
|
881
881
|
const MESSAGE_ID_EXCEED = "exceed";
|
|
882
882
|
/** A line that is empty or holds only whitespace. */
|
|
883
883
|
const BLANK_LINE = /^\s*$/u;
|
|
884
|
-
const DEFAULTS = {
|
|
884
|
+
const DEFAULTS$1 = {
|
|
885
885
|
countFrom: "body",
|
|
886
886
|
IIFEs: false,
|
|
887
887
|
max: 50,
|
|
888
888
|
skipBlankLines: false,
|
|
889
889
|
skipComments: false
|
|
890
890
|
};
|
|
891
|
-
const messages$
|
|
892
|
-
const schema$
|
|
891
|
+
const messages$14 = { [MESSAGE_ID_EXCEED]: "{{name}} has too many lines ({{lineCount}}). Maximum allowed is {{maxLines}}." };
|
|
892
|
+
const schema$3 = [{
|
|
893
893
|
additionalProperties: false,
|
|
894
894
|
properties: {
|
|
895
895
|
countFrom: {
|
|
@@ -991,7 +991,7 @@ function getCountedLoc(countFrom, funcNode, reportNode) {
|
|
|
991
991
|
* @param context - The rule context.
|
|
992
992
|
* @returns The visitors measuring each function.
|
|
993
993
|
*/
|
|
994
|
-
function createOnce$
|
|
994
|
+
function createOnce$9(context) {
|
|
995
995
|
let commentLineNumbers;
|
|
996
996
|
let config;
|
|
997
997
|
let lines;
|
|
@@ -1032,11 +1032,11 @@ function createOnce$8(context) {
|
|
|
1032
1032
|
before() {
|
|
1033
1033
|
const options = context.options[0];
|
|
1034
1034
|
config = {
|
|
1035
|
-
countFrom: options?.countFrom ?? DEFAULTS.countFrom,
|
|
1036
|
-
IIFEs: options?.IIFEs ?? DEFAULTS.IIFEs,
|
|
1037
|
-
max: options?.max ?? DEFAULTS.max,
|
|
1038
|
-
skipBlankLines: options?.skipBlankLines ?? DEFAULTS.skipBlankLines,
|
|
1039
|
-
skipComments: options?.skipComments ?? DEFAULTS.skipComments
|
|
1035
|
+
countFrom: options?.countFrom ?? DEFAULTS$1.countFrom,
|
|
1036
|
+
IIFEs: options?.IIFEs ?? DEFAULTS$1.IIFEs,
|
|
1037
|
+
max: options?.max ?? DEFAULTS$1.max,
|
|
1038
|
+
skipBlankLines: options?.skipBlankLines ?? DEFAULTS$1.skipBlankLines,
|
|
1039
|
+
skipComments: options?.skipComments ?? DEFAULTS$1.skipComments
|
|
1040
1040
|
};
|
|
1041
1041
|
({sourceCode} = context);
|
|
1042
1042
|
lines = [...sourceCode.lines];
|
|
@@ -1047,19 +1047,19 @@ function createOnce$8(context) {
|
|
|
1047
1047
|
};
|
|
1048
1048
|
}
|
|
1049
1049
|
const maxLinesPerFunction = createFlawlessRule({
|
|
1050
|
-
name: RULE_NAME$
|
|
1051
|
-
createOnce: createOnce$
|
|
1052
|
-
defaultOptions: [DEFAULTS],
|
|
1050
|
+
name: RULE_NAME$14,
|
|
1051
|
+
createOnce: createOnce$9,
|
|
1052
|
+
defaultOptions: [DEFAULTS$1],
|
|
1053
1053
|
meta: {
|
|
1054
|
-
defaultOptions: [DEFAULTS],
|
|
1054
|
+
defaultOptions: [DEFAULTS$1],
|
|
1055
1055
|
docs: {
|
|
1056
1056
|
description: "Enforce a maximum number of lines of code in a function",
|
|
1057
1057
|
recommended: false,
|
|
1058
1058
|
requiresTypeChecking: false
|
|
1059
1059
|
},
|
|
1060
1060
|
hasSuggestions: false,
|
|
1061
|
-
messages: messages$
|
|
1062
|
-
schema: schema$
|
|
1061
|
+
messages: messages$14,
|
|
1062
|
+
schema: schema$3,
|
|
1063
1063
|
type: "suggestion"
|
|
1064
1064
|
}
|
|
1065
1065
|
});
|
|
@@ -2459,9 +2459,9 @@ const SCHEMA = {
|
|
|
2459
2459
|
};
|
|
2460
2460
|
//#endregion
|
|
2461
2461
|
//#region src/rules/naming-convention/rule.ts
|
|
2462
|
-
const RULE_NAME$
|
|
2462
|
+
const RULE_NAME$13 = "naming-convention";
|
|
2463
2463
|
const FOREIGN_CONTRACT_HINT = " If this is data conforming to an external shape, declare it with `satisfies` instead.";
|
|
2464
|
-
const messages$
|
|
2464
|
+
const messages$13 = {
|
|
2465
2465
|
doesNotMatchFormat: "{{type}} name `{{name}}` must match one of the following formats: {{formats}}",
|
|
2466
2466
|
doesNotMatchFormatForeignContract: `{{type}} name \`{{name}}\` must match one of the following formats: {{formats}}${FOREIGN_CONTRACT_HINT}`,
|
|
2467
2467
|
doesNotMatchFormatTrimmed: "{{type}} name `{{name}}` trimmed as `{{processedName}}` must match one of the following formats: {{formats}}",
|
|
@@ -2770,7 +2770,7 @@ function create$4(contextWithoutDefaults) {
|
|
|
2770
2770
|
}));
|
|
2771
2771
|
}
|
|
2772
2772
|
const namingConvention = createEslintRule({
|
|
2773
|
-
name: RULE_NAME$
|
|
2773
|
+
name: RULE_NAME$13,
|
|
2774
2774
|
create: create$4,
|
|
2775
2775
|
defaultOptions: camelCaseNamingConfig,
|
|
2776
2776
|
meta: {
|
|
@@ -2781,7 +2781,7 @@ const namingConvention = createEslintRule({
|
|
|
2781
2781
|
},
|
|
2782
2782
|
fixable: void 0,
|
|
2783
2783
|
hasSuggestions: false,
|
|
2784
|
-
messages: messages$
|
|
2784
|
+
messages: messages$13,
|
|
2785
2785
|
schema: SCHEMA,
|
|
2786
2786
|
type: "suggestion"
|
|
2787
2787
|
}
|
|
@@ -3022,9 +3022,9 @@ function requiresQuoting(node, target) {
|
|
|
3022
3022
|
}
|
|
3023
3023
|
//#endregion
|
|
3024
3024
|
//#region src/rules/no-export-default-arrow/rule.ts
|
|
3025
|
-
const RULE_NAME$
|
|
3026
|
-
const MESSAGE_ID$
|
|
3027
|
-
const messages$
|
|
3025
|
+
const RULE_NAME$12 = "no-export-default-arrow";
|
|
3026
|
+
const MESSAGE_ID$7 = "disallowExportDefaultArrow";
|
|
3027
|
+
const messages$12 = { [MESSAGE_ID$7]: "Assign the arrow function to a named constant instead of exporting it anonymously." };
|
|
3028
3028
|
/**
|
|
3029
3029
|
* Converts a filename stem to camelCase, treating `-`, `_`, and whitespace as
|
|
3030
3030
|
* word separators (`use-mouse` -> `useMouse`).
|
|
@@ -3102,7 +3102,7 @@ function createFixFunction({ arrowFunction, context, exportDeclaration, sourceCo
|
|
|
3102
3102
|
* @param context - The rule context.
|
|
3103
3103
|
* @returns The rule listener.
|
|
3104
3104
|
*/
|
|
3105
|
-
function createOnce$
|
|
3105
|
+
function createOnce$8(context) {
|
|
3106
3106
|
return { ArrowFunctionExpression(node) {
|
|
3107
3107
|
const { parent } = node;
|
|
3108
3108
|
if (parent.type !== AST_NODE_TYPES.ExportDefaultDeclaration) return;
|
|
@@ -3113,14 +3113,14 @@ function createOnce$7(context) {
|
|
|
3113
3113
|
exportDeclaration: parent,
|
|
3114
3114
|
sourceCode: context.sourceCode
|
|
3115
3115
|
}),
|
|
3116
|
-
messageId: MESSAGE_ID$
|
|
3116
|
+
messageId: MESSAGE_ID$7,
|
|
3117
3117
|
node
|
|
3118
3118
|
});
|
|
3119
3119
|
} };
|
|
3120
3120
|
}
|
|
3121
3121
|
const noExportDefaultArrow = createFlawlessRule({
|
|
3122
|
-
name: RULE_NAME$
|
|
3123
|
-
createOnce: createOnce$
|
|
3122
|
+
name: RULE_NAME$12,
|
|
3123
|
+
createOnce: createOnce$8,
|
|
3124
3124
|
defaultOptions: [],
|
|
3125
3125
|
meta: {
|
|
3126
3126
|
docs: {
|
|
@@ -3130,7 +3130,7 @@ const noExportDefaultArrow = createFlawlessRule({
|
|
|
3130
3130
|
},
|
|
3131
3131
|
fixable: "code",
|
|
3132
3132
|
hasSuggestions: false,
|
|
3133
|
-
messages: messages$
|
|
3133
|
+
messages: messages$12,
|
|
3134
3134
|
schema: [],
|
|
3135
3135
|
type: "suggestion"
|
|
3136
3136
|
}
|
|
@@ -3295,8 +3295,8 @@ function flatten(file, context) {
|
|
|
3295
3295
|
}
|
|
3296
3296
|
//#endregion
|
|
3297
3297
|
//#region src/rules/no-redundant-tsconfig-options/rule.ts
|
|
3298
|
-
const RULE_NAME$
|
|
3299
|
-
const MESSAGE_ID$
|
|
3298
|
+
const RULE_NAME$11 = "no-redundant-tsconfig-options";
|
|
3299
|
+
const MESSAGE_ID$6 = "redundant";
|
|
3300
3300
|
/**
|
|
3301
3301
|
* Compiler options TypeScript resolves case-insensitively; a child that re-sets
|
|
3302
3302
|
* one differing only in case is still redundant.
|
|
@@ -3331,7 +3331,7 @@ const PATH_KEYS = /* @__PURE__ */ new Set([
|
|
|
3331
3331
|
"tsBuildInfoFile",
|
|
3332
3332
|
"typeRoots"
|
|
3333
3333
|
]);
|
|
3334
|
-
const messages$
|
|
3334
|
+
const messages$11 = { [MESSAGE_ID$6]: "'{{option}}' is redundant: it is already set to this value by {{source}}. Remove it." };
|
|
3335
3335
|
/**
|
|
3336
3336
|
* Structural equality between two static JSON values. Strings compare
|
|
3337
3337
|
* case-insensitively when `caseInsensitive` is set (used for enum-valued
|
|
@@ -3445,7 +3445,7 @@ function create$3(context) {
|
|
|
3445
3445
|
},
|
|
3446
3446
|
fix: buildFix(property),
|
|
3447
3447
|
loc: property.key.loc,
|
|
3448
|
-
messageId: MESSAGE_ID$
|
|
3448
|
+
messageId: MESSAGE_ID$6
|
|
3449
3449
|
});
|
|
3450
3450
|
}
|
|
3451
3451
|
function buildFix(property) {
|
|
@@ -3503,7 +3503,7 @@ function create$3(context) {
|
|
|
3503
3503
|
} };
|
|
3504
3504
|
}
|
|
3505
3505
|
const noRedundantTsconfigOptions = createEslintRule({
|
|
3506
|
-
name: RULE_NAME$
|
|
3506
|
+
name: RULE_NAME$11,
|
|
3507
3507
|
create: create$3,
|
|
3508
3508
|
defaultOptions: [],
|
|
3509
3509
|
meta: {
|
|
@@ -3514,7 +3514,7 @@ const noRedundantTsconfigOptions = createEslintRule({
|
|
|
3514
3514
|
},
|
|
3515
3515
|
fixable: "code",
|
|
3516
3516
|
hasSuggestions: false,
|
|
3517
|
-
messages: messages$
|
|
3517
|
+
messages: messages$11,
|
|
3518
3518
|
schema: [],
|
|
3519
3519
|
type: "suggestion"
|
|
3520
3520
|
}
|
|
@@ -3757,15 +3757,15 @@ function resolveFactory(sourceCode, node) {
|
|
|
3757
3757
|
}
|
|
3758
3758
|
//#endregion
|
|
3759
3759
|
//#region src/rules/no-unnecessary-use-callback/rule.ts
|
|
3760
|
-
const RULE_NAME$
|
|
3760
|
+
const RULE_NAME$10 = "no-unnecessary-use-callback";
|
|
3761
3761
|
const MESSAGE_ID_DEFAULT$1 = "default";
|
|
3762
3762
|
const MESSAGE_ID_INSIDE_USE_EFFECT$1 = "noUnnecessaryUseCallbackInsideUseEffect";
|
|
3763
|
-
const messages$
|
|
3763
|
+
const messages$10 = {
|
|
3764
3764
|
[MESSAGE_ID_DEFAULT$1]: "An 'useCallback' with empty deps and no references to the component scope may be unnecessary.",
|
|
3765
3765
|
[MESSAGE_ID_INSIDE_USE_EFFECT$1]: "'{{name}}' is only used inside 1 useEffect, which may be unnecessary. You can move the computation into useEffect directly and merge the dependency arrays."
|
|
3766
3766
|
};
|
|
3767
3767
|
const noUnnecessaryUseCallback = createFlawlessRule({
|
|
3768
|
-
name: RULE_NAME$
|
|
3768
|
+
name: RULE_NAME$10,
|
|
3769
3769
|
createOnce: createUnnecessaryHookRule({
|
|
3770
3770
|
hook: "useCallback",
|
|
3771
3771
|
messageIds: {
|
|
@@ -3781,22 +3781,22 @@ const noUnnecessaryUseCallback = createFlawlessRule({
|
|
|
3781
3781
|
requiresTypeChecking: false
|
|
3782
3782
|
},
|
|
3783
3783
|
hasSuggestions: false,
|
|
3784
|
-
messages: messages$
|
|
3784
|
+
messages: messages$10,
|
|
3785
3785
|
schema: [],
|
|
3786
3786
|
type: "suggestion"
|
|
3787
3787
|
}
|
|
3788
3788
|
});
|
|
3789
3789
|
//#endregion
|
|
3790
3790
|
//#region src/rules/no-unnecessary-use-memo/rule.ts
|
|
3791
|
-
const RULE_NAME$
|
|
3791
|
+
const RULE_NAME$9 = "no-unnecessary-use-memo";
|
|
3792
3792
|
const MESSAGE_ID_DEFAULT = "default";
|
|
3793
3793
|
const MESSAGE_ID_INSIDE_USE_EFFECT = "noUnnecessaryUseMemoInsideUseEffect";
|
|
3794
|
-
const messages$
|
|
3794
|
+
const messages$9 = {
|
|
3795
3795
|
[MESSAGE_ID_DEFAULT]: "An 'useMemo' with empty deps and no references to the component scope may be unnecessary.",
|
|
3796
3796
|
[MESSAGE_ID_INSIDE_USE_EFFECT]: "'{{name}}' is only used inside 1 useEffect, which may be unnecessary. You can move the computation into useEffect directly and merge the dependency arrays."
|
|
3797
3797
|
};
|
|
3798
3798
|
const noUnnecessaryUseMemo = createFlawlessRule({
|
|
3799
|
-
name: RULE_NAME$
|
|
3799
|
+
name: RULE_NAME$9,
|
|
3800
3800
|
createOnce: createUnnecessaryHookRule({
|
|
3801
3801
|
hook: "useMemo",
|
|
3802
3802
|
messageIds: {
|
|
@@ -3812,16 +3812,16 @@ const noUnnecessaryUseMemo = createFlawlessRule({
|
|
|
3812
3812
|
requiresTypeChecking: false
|
|
3813
3813
|
},
|
|
3814
3814
|
hasSuggestions: false,
|
|
3815
|
-
messages: messages$
|
|
3815
|
+
messages: messages$9,
|
|
3816
3816
|
schema: [],
|
|
3817
3817
|
type: "suggestion"
|
|
3818
3818
|
}
|
|
3819
3819
|
});
|
|
3820
3820
|
//#endregion
|
|
3821
3821
|
//#region src/rules/padding-after-expect-assertions/rule.ts
|
|
3822
|
-
const RULE_NAME$
|
|
3823
|
-
const MESSAGE_ID$
|
|
3824
|
-
const messages$
|
|
3822
|
+
const RULE_NAME$8 = "padding-after-expect-assertions";
|
|
3823
|
+
const MESSAGE_ID$5 = "missingPadding";
|
|
3824
|
+
const messages$8 = { [MESSAGE_ID$5]: "Expected a blank line after '{{name}}'." };
|
|
3825
3825
|
/**
|
|
3826
3826
|
* Matches a statement that declares the expected assertion count at the top of
|
|
3827
3827
|
* a test: `expect.assertions(n)` or `expect.hasAssertions()`.
|
|
@@ -3896,7 +3896,7 @@ function findPaddingAnchor(sourceCode, node, nextNode) {
|
|
|
3896
3896
|
* @param context - The rule context.
|
|
3897
3897
|
* @returns The rule listener.
|
|
3898
3898
|
*/
|
|
3899
|
-
function createOnce$
|
|
3899
|
+
function createOnce$5(context) {
|
|
3900
3900
|
return { ExpressionStatement(node) {
|
|
3901
3901
|
const name = getAssertionName(node);
|
|
3902
3902
|
if (name === void 0) return;
|
|
@@ -3916,13 +3916,13 @@ function createOnce$4(context) {
|
|
|
3916
3916
|
return fixer.insertTextAfter(previousToken, gap === 0 ? "\n\n" : "\n");
|
|
3917
3917
|
},
|
|
3918
3918
|
loc: node.loc,
|
|
3919
|
-
messageId: MESSAGE_ID$
|
|
3919
|
+
messageId: MESSAGE_ID$5
|
|
3920
3920
|
});
|
|
3921
3921
|
} };
|
|
3922
3922
|
}
|
|
3923
3923
|
const paddingAfterExpectAssertions = createFlawlessRule({
|
|
3924
|
-
name: RULE_NAME$
|
|
3925
|
-
createOnce: createOnce$
|
|
3924
|
+
name: RULE_NAME$8,
|
|
3925
|
+
createOnce: createOnce$5,
|
|
3926
3926
|
defaultOptions: [],
|
|
3927
3927
|
meta: {
|
|
3928
3928
|
docs: {
|
|
@@ -3932,16 +3932,16 @@ const paddingAfterExpectAssertions = createFlawlessRule({
|
|
|
3932
3932
|
},
|
|
3933
3933
|
fixable: "whitespace",
|
|
3934
3934
|
hasSuggestions: false,
|
|
3935
|
-
messages: messages$
|
|
3935
|
+
messages: messages$8,
|
|
3936
3936
|
schema: [],
|
|
3937
3937
|
type: "layout"
|
|
3938
3938
|
}
|
|
3939
3939
|
});
|
|
3940
3940
|
//#endregion
|
|
3941
3941
|
//#region src/rules/prefer-destructuring-assignment/rule.ts
|
|
3942
|
-
const RULE_NAME$
|
|
3943
|
-
const MESSAGE_ID$
|
|
3944
|
-
const messages$
|
|
3942
|
+
const RULE_NAME$7 = "prefer-destructuring-assignment";
|
|
3943
|
+
const MESSAGE_ID$4 = "default";
|
|
3944
|
+
const messages$7 = { [MESSAGE_ID$4]: "Use destructuring assignment for component props." };
|
|
3945
3945
|
/**
|
|
3946
3946
|
* Identifiers that cannot be used as a binding name in strict-mode module code.
|
|
3947
3947
|
* A property may be accessed with such a name (`props.default`), but a shorthand
|
|
@@ -4067,7 +4067,7 @@ function reportComponent(context, scope, propsParameter, propertyVariable) {
|
|
|
4067
4067
|
const propertyNames = collectPropertyNames(memberReferences);
|
|
4068
4068
|
if (!(!hasNonMemberReference && propertyNames !== null && !wouldShadowExistingBinding(context.sourceCode, scope, propertyVariable, memberReferences))) {
|
|
4069
4069
|
for (const { member } of memberReferences) context.report({
|
|
4070
|
-
messageId: MESSAGE_ID$
|
|
4070
|
+
messageId: MESSAGE_ID$4,
|
|
4071
4071
|
node: member
|
|
4072
4072
|
});
|
|
4073
4073
|
return;
|
|
@@ -4084,7 +4084,7 @@ function reportComponent(context, scope, propsParameter, propertyVariable) {
|
|
|
4084
4084
|
if (index === 0) return [fixer.replaceTextRange([propsParameter.range[0], nameEnd], pattern), replaceAccess];
|
|
4085
4085
|
return replaceAccess;
|
|
4086
4086
|
},
|
|
4087
|
-
messageId: MESSAGE_ID$
|
|
4087
|
+
messageId: MESSAGE_ID$4,
|
|
4088
4088
|
node: member
|
|
4089
4089
|
});
|
|
4090
4090
|
}
|
|
@@ -4103,7 +4103,7 @@ function reportComponent(context, scope, propsParameter, propertyVariable) {
|
|
|
4103
4103
|
* @param context - The rule context.
|
|
4104
4104
|
* @returns The rule listener.
|
|
4105
4105
|
*/
|
|
4106
|
-
function createOnce$
|
|
4106
|
+
function createOnce$4(context) {
|
|
4107
4107
|
let collector = core.getFunctionComponentCollector(context);
|
|
4108
4108
|
const selectorKeys = Object.keys(collector.visitor);
|
|
4109
4109
|
const listener = {
|
|
@@ -4129,8 +4129,8 @@ function createOnce$3(context) {
|
|
|
4129
4129
|
return listener;
|
|
4130
4130
|
}
|
|
4131
4131
|
const preferDestructuringAssignment = createFlawlessRule({
|
|
4132
|
-
name: RULE_NAME$
|
|
4133
|
-
createOnce: createOnce$
|
|
4132
|
+
name: RULE_NAME$7,
|
|
4133
|
+
createOnce: createOnce$4,
|
|
4134
4134
|
defaultOptions: [],
|
|
4135
4135
|
meta: {
|
|
4136
4136
|
docs: {
|
|
@@ -4140,12 +4140,214 @@ const preferDestructuringAssignment = createFlawlessRule({
|
|
|
4140
4140
|
},
|
|
4141
4141
|
fixable: "code",
|
|
4142
4142
|
hasSuggestions: false,
|
|
4143
|
-
messages: messages$
|
|
4143
|
+
messages: messages$7,
|
|
4144
4144
|
schema: [],
|
|
4145
4145
|
type: "problem"
|
|
4146
4146
|
}
|
|
4147
4147
|
});
|
|
4148
4148
|
//#endregion
|
|
4149
|
+
//#region src/rules/prefer-ending-with-an-expect/rule.ts
|
|
4150
|
+
const RULE_NAME$6 = "prefer-ending-with-an-expect";
|
|
4151
|
+
const MESSAGE_ID$3 = "mustEndWithExpect";
|
|
4152
|
+
const DEFAULTS = {
|
|
4153
|
+
additionalTestBlockFunctions: [],
|
|
4154
|
+
assertFunctionNames: ["expect"]
|
|
4155
|
+
};
|
|
4156
|
+
/** Callee identifiers that name a vitest test block (`describe` is excluded). */
|
|
4157
|
+
const TEST_BLOCK_NAMES = /* @__PURE__ */ new Set(["it", "test"]);
|
|
4158
|
+
const messages$6 = { [MESSAGE_ID$3]: "Test should end with an assertion." };
|
|
4159
|
+
const schema$2 = [{
|
|
4160
|
+
additionalProperties: false,
|
|
4161
|
+
properties: {
|
|
4162
|
+
additionalTestBlockFunctions: {
|
|
4163
|
+
description: "Callee names, besides a resolved vitest it/test, whose second argument is a test body (matched by exact dotted name).",
|
|
4164
|
+
items: { type: "string" },
|
|
4165
|
+
type: "array"
|
|
4166
|
+
},
|
|
4167
|
+
assertFunctionNames: {
|
|
4168
|
+
description: "Function names, besides a resolved vitest expect, that count as an assertion (matched by name; * and ** are wildcards).",
|
|
4169
|
+
items: { type: "string" },
|
|
4170
|
+
type: "array"
|
|
4171
|
+
}
|
|
4172
|
+
},
|
|
4173
|
+
type: "object"
|
|
4174
|
+
}];
|
|
4175
|
+
/**
|
|
4176
|
+
* Tests a dotted callee name against the `assertFunctionNames` patterns. A `*`
|
|
4177
|
+
* stands for a single dotted segment and `**` for any run of segments, so
|
|
4178
|
+
* `request.*.expect` and `request.**.expect` both match a chained assertion.
|
|
4179
|
+
* Ported from eslint-plugin-jest.
|
|
4180
|
+
*
|
|
4181
|
+
* @param nodeName - The dotted callee name (e.g. `expect.toBe`).
|
|
4182
|
+
* @param patterns - The configured assertion-name patterns.
|
|
4183
|
+
* @returns `true` when any pattern matches the name.
|
|
4184
|
+
*/
|
|
4185
|
+
function matchesAssertFunctionName(nodeName, patterns) {
|
|
4186
|
+
return patterns.some((pattern) => {
|
|
4187
|
+
return new RegExp(`^${pattern.split(".").map((segment) => {
|
|
4188
|
+
if (segment === "**") return "[a-z\\d\\.]*";
|
|
4189
|
+
return segment.replace(/\*/gu, "[a-z\\d]*");
|
|
4190
|
+
}).join("\\.")}(\\.|$)`, "ui").test(nodeName);
|
|
4191
|
+
});
|
|
4192
|
+
}
|
|
4193
|
+
/**
|
|
4194
|
+
* Builds the dotted name of a callee chain, unwrapping intervening calls
|
|
4195
|
+
* (`request(app).get("/").expect` -> `request.get.expect`) the way
|
|
4196
|
+
* eslint-plugin-jest's `getNodeChain` does. A computed member access yields
|
|
4197
|
+
* `null`, since its property is not a static name.
|
|
4198
|
+
*
|
|
4199
|
+
* @param node - The callee node.
|
|
4200
|
+
* @returns The dotted name, or `null` when it cannot be built statically.
|
|
4201
|
+
*/
|
|
4202
|
+
function getNodeName(node) {
|
|
4203
|
+
if (node.type === AST_NODE_TYPES.Identifier) return node.name;
|
|
4204
|
+
if (node.type === AST_NODE_TYPES.CallExpression) return getNodeName(node.callee);
|
|
4205
|
+
if (node.type === AST_NODE_TYPES.MemberExpression && !node.computed && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
4206
|
+
const objectName = getNodeName(node.object);
|
|
4207
|
+
return objectName === null ? null : `${objectName}.${node.property.name}`;
|
|
4208
|
+
}
|
|
4209
|
+
return null;
|
|
4210
|
+
}
|
|
4211
|
+
/**
|
|
4212
|
+
* Walks a callee chain down to the identifier it is rooted at, stepping through
|
|
4213
|
+
* member accesses (`it.each` -> `it`) and intervening calls
|
|
4214
|
+
* (`it.each(cases)()` -> `it`).
|
|
4215
|
+
*
|
|
4216
|
+
* @param node - The callee node.
|
|
4217
|
+
* @returns The root identifier, or `null` when the chain is not rooted at one.
|
|
4218
|
+
*/
|
|
4219
|
+
function getRootIdentifier(node) {
|
|
4220
|
+
let current = node;
|
|
4221
|
+
for (;;) {
|
|
4222
|
+
if (current.type === AST_NODE_TYPES.Identifier) return current;
|
|
4223
|
+
if (current.type === AST_NODE_TYPES.CallExpression) {
|
|
4224
|
+
current = current.callee;
|
|
4225
|
+
continue;
|
|
4226
|
+
}
|
|
4227
|
+
if (current.type === AST_NODE_TYPES.MemberExpression) {
|
|
4228
|
+
current = current.object;
|
|
4229
|
+
continue;
|
|
4230
|
+
}
|
|
4231
|
+
return null;
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
/**
|
|
4235
|
+
* Resolves the vitest name an identifier refers to, mirroring how
|
|
4236
|
+
* eslint-plugin-jest resolves jest functions. An unresolved reference is a
|
|
4237
|
+
* global (vitest's `globals: true` / `@vitest/globals`); a named import from
|
|
4238
|
+
* `"vitest"` resolves to its imported name (so aliases work); anything bound to
|
|
4239
|
+
* a local variable, function, or parameter resolves to `null` and is ignored.
|
|
4240
|
+
*
|
|
4241
|
+
* @param sourceCode - Provides the scope used to look up the binding.
|
|
4242
|
+
* @param identifier - The identifier to resolve.
|
|
4243
|
+
* @returns The vitest name (`it`/`test`/`expect`/...), or `null` when the
|
|
4244
|
+
* identifier is a local binding rather than a vitest global or import.
|
|
4245
|
+
*/
|
|
4246
|
+
function resolveVitestName(sourceCode, identifier) {
|
|
4247
|
+
const variable = findVariable(sourceCode.getScope(identifier), identifier);
|
|
4248
|
+
if (variable === null) return identifier.name;
|
|
4249
|
+
const definition = variable.defs.at(0);
|
|
4250
|
+
if (definition === void 0) return identifier.name;
|
|
4251
|
+
if (definition.type !== DefinitionType.ImportBinding) return null;
|
|
4252
|
+
const importDefinition = definition;
|
|
4253
|
+
const declaration = importDefinition.parent;
|
|
4254
|
+
if (declaration.type !== AST_NODE_TYPES.ImportDeclaration || declaration.source.value !== "vitest") return null;
|
|
4255
|
+
const { node } = importDefinition;
|
|
4256
|
+
if (node.type === AST_NODE_TYPES.ImportSpecifier && node.imported.type === AST_NODE_TYPES.Identifier) return node.imported.name;
|
|
4257
|
+
return null;
|
|
4258
|
+
}
|
|
4259
|
+
/**
|
|
4260
|
+
* Returns the node that ends a test body: the last statement of a block body
|
|
4261
|
+
* (unwrapped to its expression when it is an expression statement), or the
|
|
4262
|
+
* expression of a concise arrow body. Ported from eslint-plugin-jest.
|
|
4263
|
+
*
|
|
4264
|
+
* @param func - The test callback.
|
|
4265
|
+
* @returns The ending node, or `null` for an empty block body.
|
|
4266
|
+
*/
|
|
4267
|
+
function getLastStatement(func) {
|
|
4268
|
+
if (func.body.type !== AST_NODE_TYPES.BlockStatement) return func.body;
|
|
4269
|
+
const lastStatement = func.body.body.at(-1);
|
|
4270
|
+
if (lastStatement === void 0) return null;
|
|
4271
|
+
if (lastStatement.type === AST_NODE_TYPES.ExpressionStatement) return lastStatement.expression;
|
|
4272
|
+
return lastStatement;
|
|
4273
|
+
}
|
|
4274
|
+
/**
|
|
4275
|
+
* Flags a test whose last statement is not an assertion, a common sign of an
|
|
4276
|
+
* unfinished test. Ported from eslint-plugin-jest's
|
|
4277
|
+
* `prefer-ending-with-an-expect`, with vitest-aware resolution of `it`/`test`
|
|
4278
|
+
* and `expect`.
|
|
4279
|
+
*
|
|
4280
|
+
* @param context - The rule context.
|
|
4281
|
+
* @returns The rule listener.
|
|
4282
|
+
*/
|
|
4283
|
+
function createOnce$3(context) {
|
|
4284
|
+
let config;
|
|
4285
|
+
let sourceCode;
|
|
4286
|
+
/**
|
|
4287
|
+
* Determines whether a call is a test block whose second argument is a body.
|
|
4288
|
+
*
|
|
4289
|
+
* @param node - The call to inspect.
|
|
4290
|
+
* @returns `true` when the call opens a vitest (or configured) test.
|
|
4291
|
+
*/
|
|
4292
|
+
function isTestBlock(node) {
|
|
4293
|
+
const root = getRootIdentifier(node.callee);
|
|
4294
|
+
if (root !== null && TEST_BLOCK_NAMES.has(resolveVitestName(sourceCode, root) ?? "")) return true;
|
|
4295
|
+
const name = getNodeName(node.callee);
|
|
4296
|
+
return name !== null && config.additionalTestBlockFunctions.includes(name);
|
|
4297
|
+
}
|
|
4298
|
+
/**
|
|
4299
|
+
* Determines whether a call is an assertion: a resolved vitest `expect`, or a
|
|
4300
|
+
* call whose dotted name matches an `assertFunctionNames` pattern.
|
|
4301
|
+
*
|
|
4302
|
+
* @param node - The call ending the test body.
|
|
4303
|
+
* @returns `true` when the call counts as an assertion.
|
|
4304
|
+
*/
|
|
4305
|
+
function isAssertion(node) {
|
|
4306
|
+
const root = getRootIdentifier(node.callee);
|
|
4307
|
+
if (root !== null && resolveVitestName(sourceCode, root) === "expect") return true;
|
|
4308
|
+
return matchesAssertFunctionName(getNodeName(node.callee) ?? "", config.assertFunctionNames);
|
|
4309
|
+
}
|
|
4310
|
+
return {
|
|
4311
|
+
before() {
|
|
4312
|
+
const options = context.options[0];
|
|
4313
|
+
config = {
|
|
4314
|
+
additionalTestBlockFunctions: options?.additionalTestBlockFunctions ?? DEFAULTS.additionalTestBlockFunctions,
|
|
4315
|
+
assertFunctionNames: options?.assertFunctionNames ?? DEFAULTS.assertFunctionNames
|
|
4316
|
+
};
|
|
4317
|
+
({sourceCode} = context);
|
|
4318
|
+
},
|
|
4319
|
+
CallExpression(node) {
|
|
4320
|
+
if (!isTestBlock(node)) return;
|
|
4321
|
+
const callback = node.arguments[1];
|
|
4322
|
+
if (callback === void 0 || !ASTUtils.isFunction(callback)) return;
|
|
4323
|
+
let lastStatement = getLastStatement(callback);
|
|
4324
|
+
if (lastStatement?.type === AST_NODE_TYPES.AwaitExpression) lastStatement = lastStatement.argument;
|
|
4325
|
+
if (lastStatement?.type === AST_NODE_TYPES.CallExpression && isAssertion(lastStatement)) return;
|
|
4326
|
+
context.report({
|
|
4327
|
+
messageId: MESSAGE_ID$3,
|
|
4328
|
+
node: node.callee
|
|
4329
|
+
});
|
|
4330
|
+
}
|
|
4331
|
+
};
|
|
4332
|
+
}
|
|
4333
|
+
const preferEndingWithAnExpect = createFlawlessRule({
|
|
4334
|
+
name: RULE_NAME$6,
|
|
4335
|
+
createOnce: createOnce$3,
|
|
4336
|
+
defaultOptions: [DEFAULTS],
|
|
4337
|
+
meta: {
|
|
4338
|
+
defaultOptions: [DEFAULTS],
|
|
4339
|
+
docs: {
|
|
4340
|
+
description: "Prefer having the last statement in a test be an assertion",
|
|
4341
|
+
recommended: false,
|
|
4342
|
+
requiresTypeChecking: false
|
|
4343
|
+
},
|
|
4344
|
+
hasSuggestions: false,
|
|
4345
|
+
messages: messages$6,
|
|
4346
|
+
schema: schema$2,
|
|
4347
|
+
type: "suggestion"
|
|
4348
|
+
}
|
|
4349
|
+
});
|
|
4350
|
+
//#endregion
|
|
4149
4351
|
//#region src/rules/prefer-parameter-destructuring/rule.ts
|
|
4150
4352
|
const RULE_NAME$5 = "prefer-parameter-destructuring";
|
|
4151
4353
|
const MESSAGE_ID$2 = "default";
|
|
@@ -5201,16 +5403,21 @@ const purity = createFlawlessRule({
|
|
|
5201
5403
|
});
|
|
5202
5404
|
//#endregion
|
|
5203
5405
|
//#region src/rules/react-namespace/imports.ts
|
|
5406
|
+
/** Matches a bare npm scope (`@rbxts`) with no package segment. */
|
|
5407
|
+
const SCOPE_PATTERN = /^@[^/]+$/;
|
|
5204
5408
|
/**
|
|
5205
|
-
* Whether a module specifier refers to the configured React
|
|
5206
|
-
*
|
|
5409
|
+
* Whether a module specifier refers to the configured React package. Matches the
|
|
5410
|
+
* exact package or any subpath (`@rbxts/react` and `@rbxts/react/jsx-runtime`),
|
|
5411
|
+
* but never a sibling package (`@rbxts/react-roblox`), because the `/` boundary
|
|
5412
|
+
* excludes it.
|
|
5207
5413
|
*
|
|
5208
5414
|
* @param value - The `source.value` of an import declaration.
|
|
5209
5415
|
* @param importSource - The configured React import source.
|
|
5210
5416
|
* @returns True when the specifier resolves to the React package.
|
|
5211
5417
|
*/
|
|
5212
5418
|
function matchesSource(value, importSource) {
|
|
5213
|
-
|
|
5419
|
+
const reactPackage = resolveReactPackage(importSource);
|
|
5420
|
+
return value === reactPackage || value.startsWith(`${reactPackage}/`);
|
|
5214
5421
|
}
|
|
5215
5422
|
/**
|
|
5216
5423
|
* Collects the import declarations in a program that come from the React source.
|
|
@@ -5339,6 +5546,19 @@ function findNamedSpecifier(declarations, name) {
|
|
|
5339
5546
|
};
|
|
5340
5547
|
return null;
|
|
5341
5548
|
}
|
|
5549
|
+
/**
|
|
5550
|
+
* Resolves the React package the `react-x` `importSource` setting points at. The
|
|
5551
|
+
* setting may be a package (`react`, `@rbxts/react`) or a bare npm scope
|
|
5552
|
+
* (`@rbxts`, as Roblox configs set it). A scope names no package, so treating it
|
|
5553
|
+
* as one claims every sibling (`@rbxts/react-roblox`, `@rbxts/flux`) as React;
|
|
5554
|
+
* the React package under a scope is `<scope>/react`.
|
|
5555
|
+
*
|
|
5556
|
+
* @param importSource - The configured `react-x` import source.
|
|
5557
|
+
* @returns The React package name to match against.
|
|
5558
|
+
*/
|
|
5559
|
+
function resolveReactPackage(importSource) {
|
|
5560
|
+
return SCOPE_PATTERN.test(importSource) ? `${importSource}/react` : importSource;
|
|
5561
|
+
}
|
|
5342
5562
|
function isNamedSpecifier(specifier) {
|
|
5343
5563
|
return specifier.type === AST_NODE_TYPES.ImportSpecifier;
|
|
5344
5564
|
}
|
|
@@ -5811,6 +6031,7 @@ const plugin = {
|
|
|
5811
6031
|
"no-unnecessary-use-memo": noUnnecessaryUseMemo,
|
|
5812
6032
|
"padding-after-expect-assertions": paddingAfterExpectAssertions,
|
|
5813
6033
|
"prefer-destructuring-assignment": preferDestructuringAssignment,
|
|
6034
|
+
"prefer-ending-with-an-expect": preferEndingWithAnExpect,
|
|
5814
6035
|
"prefer-parameter-destructuring": preferParameterDestructuring,
|
|
5815
6036
|
"prefer-read-only-props": preferReadOnlyProps,
|
|
5816
6037
|
"purity": purity,
|