@vitest/eslint-plugin 1.1.10 → 1.1.11
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 +5 -5
- package/dist/index.cjs +48 -6
- package/dist/index.mjs +48 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|

|
|
4
4
|
[](https://github.com/veritem/eslint-plugin-vitest/actions/workflows/ci.yml)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
ESLint plugin for Vitest
|
|
7
7
|
|
|
8
8
|
### Installation
|
|
9
9
|
|
|
@@ -21,7 +21,7 @@ npm install @vitest/eslint-plugin --save-dev
|
|
|
21
21
|
|
|
22
22
|
### Usage
|
|
23
23
|
|
|
24
|
-
Make sure you're running
|
|
24
|
+
Make sure you're running ESLint `v9.0.0` or higher for the latest version of this plugin to work. The following example is how your `eslint.config.js` should be setup for this plugin to work for you.
|
|
25
25
|
|
|
26
26
|
```js
|
|
27
27
|
import vitest from "@vitest/eslint-plugin";
|
|
@@ -40,7 +40,7 @@ export default [
|
|
|
40
40
|
];
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
If you're not using the latest version of
|
|
43
|
+
If you're not using the latest version of ESLint (version `v8.57.0` or lower) you can setup this plugin using the following configuration
|
|
44
44
|
|
|
45
45
|
Add `vitest` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
|
|
46
46
|
|
|
@@ -65,7 +65,7 @@ Then configure the rules you want to use under the rules section.
|
|
|
65
65
|
}
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
If you're using old
|
|
68
|
+
If you're using old ESLint configuration, make sure to use legacy key like the following
|
|
69
69
|
|
|
70
70
|
```js
|
|
71
71
|
{
|
|
@@ -171,7 +171,7 @@ export default [
|
|
|
171
171
|
| [require-to-throw-message](docs/rules/require-to-throw-message.md) | require toThrow() to be called with an error message | | 🌐 | | | |
|
|
172
172
|
| [require-top-level-describe](docs/rules/require-top-level-describe.md) | enforce that all tests are in a top-level describe | | 🌐 | | | |
|
|
173
173
|
| [valid-describe-callback](docs/rules/valid-describe-callback.md) | enforce valid describe callback | ✅ | | | | |
|
|
174
|
-
| [valid-expect](docs/rules/valid-expect.md) | enforce valid `expect()` usage | ✅ | |
|
|
174
|
+
| [valid-expect](docs/rules/valid-expect.md) | enforce valid `expect()` usage | ✅ | | 🔧 | | |
|
|
175
175
|
| [valid-title](docs/rules/valid-title.md) | enforce valid titles | ✅ | | 🔧 | | |
|
|
176
176
|
|
|
177
177
|
<!-- end auto-generated rules list -->
|
package/dist/index.cjs
CHANGED
|
@@ -23,7 +23,7 @@ function _interopNamespaceCompat(e) {
|
|
|
23
23
|
const path__namespace = /*#__PURE__*/_interopNamespaceCompat(path);
|
|
24
24
|
const ts__default = /*#__PURE__*/_interopDefaultCompat(ts);
|
|
25
25
|
|
|
26
|
-
const version = "1.1.
|
|
26
|
+
const version = "1.1.10";
|
|
27
27
|
|
|
28
28
|
function createEslintRule(rule) {
|
|
29
29
|
const createRule = utils.ESLintUtils.RuleCreator(
|
|
@@ -2344,6 +2344,11 @@ const getPromiseCallExpressionNode = (node) => {
|
|
|
2344
2344
|
return null;
|
|
2345
2345
|
};
|
|
2346
2346
|
const promiseArrayExceptionKey = ({ start, end }) => `${start.line}:${start.column}-${end.line}:${end.column}`;
|
|
2347
|
+
const getNormalizeFunctionExpression = (functionExpression) => {
|
|
2348
|
+
if (functionExpression.parent.type === utils.AST_NODE_TYPES.Property && functionExpression.type === utils.AST_NODE_TYPES.FunctionExpression)
|
|
2349
|
+
return functionExpression.parent;
|
|
2350
|
+
return functionExpression;
|
|
2351
|
+
};
|
|
2347
2352
|
function getParentIfThenified(node) {
|
|
2348
2353
|
const grandParentNode = node.parent?.parent;
|
|
2349
2354
|
if (grandParentNode && grandParentNode.type === utils.AST_NODE_TYPES.CallExpression && grandParentNode.callee.type === utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(grandParentNode.callee.property) && ["then", "catch"].includes(getAccessorValue(grandParentNode.callee.property)) && grandParentNode.parent)
|
|
@@ -2353,6 +2358,13 @@ function getParentIfThenified(node) {
|
|
|
2353
2358
|
const findPromiseCallExpressionNode = (node) => node.parent?.parent && [utils.AST_NODE_TYPES.CallExpression, utils.AST_NODE_TYPES.ArrayExpression].includes(
|
|
2354
2359
|
node.parent.type
|
|
2355
2360
|
) ? getPromiseCallExpressionNode(node.parent) : null;
|
|
2361
|
+
const findFirstFunctionExpression = ({
|
|
2362
|
+
parent
|
|
2363
|
+
}) => {
|
|
2364
|
+
if (!parent)
|
|
2365
|
+
return null;
|
|
2366
|
+
return isFunction(parent) ? parent : findFirstFunctionExpression(parent);
|
|
2367
|
+
};
|
|
2356
2368
|
const isAcceptableReturnNode = (node, allowReturn) => {
|
|
2357
2369
|
if (allowReturn && node.type === utils.AST_NODE_TYPES.ReturnStatement)
|
|
2358
2370
|
return true;
|
|
@@ -2380,6 +2392,7 @@ const validExpect = createEslintRule({
|
|
|
2380
2392
|
promisesWithAsyncAssertionsMustBeAwaited: "Promises which return async assertions must be awaited{{orReturned}}"
|
|
2381
2393
|
},
|
|
2382
2394
|
type: "suggestion",
|
|
2395
|
+
fixable: "code",
|
|
2383
2396
|
schema: [
|
|
2384
2397
|
{
|
|
2385
2398
|
type: "object",
|
|
@@ -2413,6 +2426,7 @@ const validExpect = createEslintRule({
|
|
|
2413
2426
|
}],
|
|
2414
2427
|
create: (context, [{ alwaysAwait, asyncMatchers = defaultAsyncMatchers$1, minArgs = 1, maxArgs = 1 }]) => {
|
|
2415
2428
|
const arrayExceptions = /* @__PURE__ */ new Set();
|
|
2429
|
+
const descriptors = [];
|
|
2416
2430
|
const pushPromiseArrayException = (loc) => arrayExceptions.add(promiseArrayExceptionKey(loc));
|
|
2417
2431
|
const promiseArrayExceptionExists = (loc) => arrayExceptions.has(promiseArrayExceptionKey(loc));
|
|
2418
2432
|
const findTopMostMemberExpression = (node) => {
|
|
@@ -2507,19 +2521,47 @@ const validExpect = createEslintRule({
|
|
|
2507
2521
|
if (!parentNode?.parent || !shouldBeAwaited)
|
|
2508
2522
|
return;
|
|
2509
2523
|
const isParentArrayExpression = parentNode.parent.type === utils.AST_NODE_TYPES.ArrayExpression;
|
|
2510
|
-
const orReturned = alwaysAwait ? "" : " or returned";
|
|
2511
2524
|
const targetNode = getParentIfThenified(parentNode);
|
|
2512
2525
|
const finalNode = findPromiseCallExpressionNode(targetNode) || targetNode;
|
|
2513
2526
|
if (finalNode.parent && !isAcceptableReturnNode(finalNode.parent, !alwaysAwait) && !promiseArrayExceptionExists(finalNode.loc)) {
|
|
2514
|
-
|
|
2515
|
-
loc: finalNode.loc,
|
|
2516
|
-
data: { orReturned },
|
|
2527
|
+
descriptors.push({
|
|
2517
2528
|
messageId: finalNode === targetNode ? "asyncMustBeAwaited" : "promisesWithAsyncAssertionsMustBeAwaited",
|
|
2518
|
-
node
|
|
2529
|
+
node: finalNode
|
|
2519
2530
|
});
|
|
2520
2531
|
if (isParentArrayExpression)
|
|
2521
2532
|
pushPromiseArrayException(finalNode.loc);
|
|
2522
2533
|
}
|
|
2534
|
+
},
|
|
2535
|
+
"Program:exit"() {
|
|
2536
|
+
const fixes = [];
|
|
2537
|
+
descriptors.forEach(({ node, messageId }, index) => {
|
|
2538
|
+
const orReturned = alwaysAwait ? "" : " or returned";
|
|
2539
|
+
context.report({
|
|
2540
|
+
loc: node.loc,
|
|
2541
|
+
data: { orReturned },
|
|
2542
|
+
messageId,
|
|
2543
|
+
node,
|
|
2544
|
+
fix(fixer) {
|
|
2545
|
+
const functionExpression = findFirstFunctionExpression(node);
|
|
2546
|
+
if (!functionExpression)
|
|
2547
|
+
return null;
|
|
2548
|
+
const foundAsyncFixer = fixes.some((fix) => fix.text === "async ");
|
|
2549
|
+
if (!functionExpression.async && !foundAsyncFixer) {
|
|
2550
|
+
const targetFunction = getNormalizeFunctionExpression(functionExpression);
|
|
2551
|
+
fixes.push(fixer.insertTextBefore(targetFunction, "async "));
|
|
2552
|
+
}
|
|
2553
|
+
const returnStatement = node.parent?.type === utils.AST_NODE_TYPES.ReturnStatement ? node.parent : null;
|
|
2554
|
+
if (alwaysAwait && returnStatement) {
|
|
2555
|
+
const sourceCodeText = context.sourceCode.getText(returnStatement);
|
|
2556
|
+
const replacedText = sourceCodeText.replace("return", "await");
|
|
2557
|
+
fixes.push(fixer.replaceText(returnStatement, replacedText));
|
|
2558
|
+
} else {
|
|
2559
|
+
fixes.push(fixer.insertTextBefore(node, "await "));
|
|
2560
|
+
}
|
|
2561
|
+
return index === descriptors.length - 1 ? fixes : null;
|
|
2562
|
+
}
|
|
2563
|
+
});
|
|
2564
|
+
});
|
|
2523
2565
|
}
|
|
2524
2566
|
};
|
|
2525
2567
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { isAbsolute, posix } from 'node:path';
|
|
|
4
4
|
import ts from 'typescript';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
6
|
|
|
7
|
-
const version = "1.1.
|
|
7
|
+
const version = "1.1.10";
|
|
8
8
|
|
|
9
9
|
function createEslintRule(rule) {
|
|
10
10
|
const createRule = ESLintUtils.RuleCreator(
|
|
@@ -2325,6 +2325,11 @@ const getPromiseCallExpressionNode = (node) => {
|
|
|
2325
2325
|
return null;
|
|
2326
2326
|
};
|
|
2327
2327
|
const promiseArrayExceptionKey = ({ start, end }) => `${start.line}:${start.column}-${end.line}:${end.column}`;
|
|
2328
|
+
const getNormalizeFunctionExpression = (functionExpression) => {
|
|
2329
|
+
if (functionExpression.parent.type === AST_NODE_TYPES.Property && functionExpression.type === AST_NODE_TYPES.FunctionExpression)
|
|
2330
|
+
return functionExpression.parent;
|
|
2331
|
+
return functionExpression;
|
|
2332
|
+
};
|
|
2328
2333
|
function getParentIfThenified(node) {
|
|
2329
2334
|
const grandParentNode = node.parent?.parent;
|
|
2330
2335
|
if (grandParentNode && grandParentNode.type === AST_NODE_TYPES.CallExpression && grandParentNode.callee.type === AST_NODE_TYPES.MemberExpression && isSupportedAccessor(grandParentNode.callee.property) && ["then", "catch"].includes(getAccessorValue(grandParentNode.callee.property)) && grandParentNode.parent)
|
|
@@ -2334,6 +2339,13 @@ function getParentIfThenified(node) {
|
|
|
2334
2339
|
const findPromiseCallExpressionNode = (node) => node.parent?.parent && [AST_NODE_TYPES.CallExpression, AST_NODE_TYPES.ArrayExpression].includes(
|
|
2335
2340
|
node.parent.type
|
|
2336
2341
|
) ? getPromiseCallExpressionNode(node.parent) : null;
|
|
2342
|
+
const findFirstFunctionExpression = ({
|
|
2343
|
+
parent
|
|
2344
|
+
}) => {
|
|
2345
|
+
if (!parent)
|
|
2346
|
+
return null;
|
|
2347
|
+
return isFunction(parent) ? parent : findFirstFunctionExpression(parent);
|
|
2348
|
+
};
|
|
2337
2349
|
const isAcceptableReturnNode = (node, allowReturn) => {
|
|
2338
2350
|
if (allowReturn && node.type === AST_NODE_TYPES.ReturnStatement)
|
|
2339
2351
|
return true;
|
|
@@ -2361,6 +2373,7 @@ const validExpect = createEslintRule({
|
|
|
2361
2373
|
promisesWithAsyncAssertionsMustBeAwaited: "Promises which return async assertions must be awaited{{orReturned}}"
|
|
2362
2374
|
},
|
|
2363
2375
|
type: "suggestion",
|
|
2376
|
+
fixable: "code",
|
|
2364
2377
|
schema: [
|
|
2365
2378
|
{
|
|
2366
2379
|
type: "object",
|
|
@@ -2394,6 +2407,7 @@ const validExpect = createEslintRule({
|
|
|
2394
2407
|
}],
|
|
2395
2408
|
create: (context, [{ alwaysAwait, asyncMatchers = defaultAsyncMatchers$1, minArgs = 1, maxArgs = 1 }]) => {
|
|
2396
2409
|
const arrayExceptions = /* @__PURE__ */ new Set();
|
|
2410
|
+
const descriptors = [];
|
|
2397
2411
|
const pushPromiseArrayException = (loc) => arrayExceptions.add(promiseArrayExceptionKey(loc));
|
|
2398
2412
|
const promiseArrayExceptionExists = (loc) => arrayExceptions.has(promiseArrayExceptionKey(loc));
|
|
2399
2413
|
const findTopMostMemberExpression = (node) => {
|
|
@@ -2488,19 +2502,47 @@ const validExpect = createEslintRule({
|
|
|
2488
2502
|
if (!parentNode?.parent || !shouldBeAwaited)
|
|
2489
2503
|
return;
|
|
2490
2504
|
const isParentArrayExpression = parentNode.parent.type === AST_NODE_TYPES.ArrayExpression;
|
|
2491
|
-
const orReturned = alwaysAwait ? "" : " or returned";
|
|
2492
2505
|
const targetNode = getParentIfThenified(parentNode);
|
|
2493
2506
|
const finalNode = findPromiseCallExpressionNode(targetNode) || targetNode;
|
|
2494
2507
|
if (finalNode.parent && !isAcceptableReturnNode(finalNode.parent, !alwaysAwait) && !promiseArrayExceptionExists(finalNode.loc)) {
|
|
2495
|
-
|
|
2496
|
-
loc: finalNode.loc,
|
|
2497
|
-
data: { orReturned },
|
|
2508
|
+
descriptors.push({
|
|
2498
2509
|
messageId: finalNode === targetNode ? "asyncMustBeAwaited" : "promisesWithAsyncAssertionsMustBeAwaited",
|
|
2499
|
-
node
|
|
2510
|
+
node: finalNode
|
|
2500
2511
|
});
|
|
2501
2512
|
if (isParentArrayExpression)
|
|
2502
2513
|
pushPromiseArrayException(finalNode.loc);
|
|
2503
2514
|
}
|
|
2515
|
+
},
|
|
2516
|
+
"Program:exit"() {
|
|
2517
|
+
const fixes = [];
|
|
2518
|
+
descriptors.forEach(({ node, messageId }, index) => {
|
|
2519
|
+
const orReturned = alwaysAwait ? "" : " or returned";
|
|
2520
|
+
context.report({
|
|
2521
|
+
loc: node.loc,
|
|
2522
|
+
data: { orReturned },
|
|
2523
|
+
messageId,
|
|
2524
|
+
node,
|
|
2525
|
+
fix(fixer) {
|
|
2526
|
+
const functionExpression = findFirstFunctionExpression(node);
|
|
2527
|
+
if (!functionExpression)
|
|
2528
|
+
return null;
|
|
2529
|
+
const foundAsyncFixer = fixes.some((fix) => fix.text === "async ");
|
|
2530
|
+
if (!functionExpression.async && !foundAsyncFixer) {
|
|
2531
|
+
const targetFunction = getNormalizeFunctionExpression(functionExpression);
|
|
2532
|
+
fixes.push(fixer.insertTextBefore(targetFunction, "async "));
|
|
2533
|
+
}
|
|
2534
|
+
const returnStatement = node.parent?.type === AST_NODE_TYPES.ReturnStatement ? node.parent : null;
|
|
2535
|
+
if (alwaysAwait && returnStatement) {
|
|
2536
|
+
const sourceCodeText = context.sourceCode.getText(returnStatement);
|
|
2537
|
+
const replacedText = sourceCodeText.replace("return", "await");
|
|
2538
|
+
fixes.push(fixer.replaceText(returnStatement, replacedText));
|
|
2539
|
+
} else {
|
|
2540
|
+
fixes.push(fixer.insertTextBefore(node, "await "));
|
|
2541
|
+
}
|
|
2542
|
+
return index === descriptors.length - 1 ? fixes : null;
|
|
2543
|
+
}
|
|
2544
|
+
});
|
|
2545
|
+
});
|
|
2504
2546
|
}
|
|
2505
2547
|
};
|
|
2506
2548
|
}
|