@venizia/dev-configs 0.4.6 → 0.4.7-1
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/LICENSE.md +1 -3
- package/README.md +1 -1
- package/dist/checks/catalog.d.ts +4 -2
- package/dist/checks/catalog.d.ts.map +1 -1
- package/dist/checks/catalog.js +72 -68
- package/dist/checks/catalog.js.map +1 -1
- package/dist/checks/module-cycles.d.ts +11 -9
- package/dist/checks/module-cycles.d.ts.map +1 -1
- package/dist/checks/module-cycles.js +32 -30
- package/dist/checks/module-cycles.js.map +1 -1
- package/dist/checks/purity/manifest.d.ts +44 -38
- package/dist/checks/purity/manifest.d.ts.map +1 -1
- package/dist/checks/purity/manifest.js +121 -115
- package/dist/checks/purity/manifest.js.map +1 -1
- package/dist/checks/purity/probe.d.ts +22 -18
- package/dist/checks/purity/probe.d.ts.map +1 -1
- package/dist/checks/purity/probe.js +101 -99
- package/dist/checks/purity/probe.js.map +1 -1
- package/dist/checks/purity/run.d.ts +18 -16
- package/dist/checks/purity/run.d.ts.map +1 -1
- package/dist/checks/purity/run.js +90 -86
- package/dist/checks/purity/run.js.map +1 -1
- package/dist/checks/test-controls.d.ts +17 -15
- package/dist/checks/test-controls.d.ts.map +1 -1
- package/dist/checks/test-controls.js +71 -69
- package/dist/checks/test-controls.js.map +1 -1
- package/dist/checks/test-dead-code.d.ts +5 -3
- package/dist/checks/test-dead-code.d.ts.map +1 -1
- package/dist/checks/test-dead-code.js +23 -21
- package/dist/checks/test-dead-code.js.map +1 -1
- package/dist/checks/test-mutate.d.ts +12 -7
- package/dist/checks/test-mutate.d.ts.map +1 -1
- package/dist/checks/test-mutate.js +59 -56
- package/dist/checks/test-mutate.js.map +1 -1
- package/dist/checks/test-weight.d.ts +11 -8
- package/dist/checks/test-weight.d.ts.map +1 -1
- package/dist/checks/test-weight.js +98 -94
- package/dist/checks/test-weight.js.map +1 -1
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +11 -8
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/codemod/async-style.d.ts +62 -0
- package/dist/cli/codemod/async-style.d.ts.map +1 -0
- package/dist/cli/codemod/async-style.js +248 -0
- package/dist/cli/codemod/async-style.js.map +1 -0
- package/dist/cli/codemod/const-classes.d.ts.map +1 -1
- package/dist/cli/codemod/const-classes.js +2 -1
- package/dist/cli/codemod/const-classes.js.map +1 -1
- package/dist/cli/codemod/drop-redundant-casts.d.ts.map +1 -1
- package/dist/cli/codemod/drop-redundant-casts.js +2 -1
- package/dist/cli/codemod/drop-redundant-casts.js.map +1 -1
- package/dist/cli/codemod/files.d.ts +7 -0
- package/dist/cli/codemod/files.d.ts.map +1 -1
- package/dist/cli/codemod/files.js +33 -0
- package/dist/cli/codemod/files.js.map +1 -1
- package/dist/cli/codemod/index.d.ts.map +1 -1
- package/dist/cli/codemod/index.js +12 -3
- package/dist/cli/codemod/index.js.map +1 -1
- package/dist/cli/codemod/model-settings.d.ts +7 -5
- package/dist/cli/codemod/model-settings.d.ts.map +1 -1
- package/dist/cli/codemod/model-settings.js +293 -291
- package/dist/cli/codemod/model-settings.js.map +1 -1
- package/dist/cli/codemod/namespace-functions/index.d.ts +20 -0
- package/dist/cli/codemod/namespace-functions/index.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/index.js +182 -0
- package/dist/cli/codemod/namespace-functions/index.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/logger-scope.d.ts +32 -0
- package/dist/cli/codemod/namespace-functions/logger-scope.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/logger-scope.js +128 -0
- package/dist/cli/codemod/namespace-functions/logger-scope.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mapping.d.ts +66 -0
- package/dist/cli/codemod/namespace-functions/mapping.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mapping.js +99 -0
- package/dist/cli/codemod/namespace-functions/mapping.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/boot.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/boot.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/boot.js +28 -0
- package/dist/cli/codemod/namespace-functions/mappings/boot.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/connectors.d.ts +4 -0
- package/dist/cli/codemod/namespace-functions/mappings/connectors.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/connectors.js +218 -0
- package/dist/cli/codemod/namespace-functions/mappings/connectors.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-server.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-server.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-server.js +162 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-server.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-worker.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-worker.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-worker.js +18 -0
- package/dist/cli/codemod/namespace-functions/mappings/core-worker.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/dev-configs.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/dev-configs.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/dev-configs.js +192 -0
- package/dist/cli/codemod/namespace-functions/mappings/dev-configs.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/filter.d.ts +7 -0
- package/dist/cli/codemod/namespace-functions/mappings/filter.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/filter.js +30 -0
- package/dist/cli/codemod/namespace-functions/mappings/filter.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/helpers.d.ts +16 -0
- package/dist/cli/codemod/namespace-functions/mappings/helpers.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/helpers.js +535 -0
- package/dist/cli/codemod/namespace-functions/mappings/helpers.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/index.d.ts +4 -0
- package/dist/cli/codemod/namespace-functions/mappings/index.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/index.js +25 -0
- package/dist/cli/codemod/namespace-functions/mappings/index.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/inversion.d.ts +10 -0
- package/dist/cli/codemod/namespace-functions/mappings/inversion.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/inversion.js +81 -0
- package/dist/cli/codemod/namespace-functions/mappings/inversion.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/kernel.d.ts +17 -0
- package/dist/cli/codemod/namespace-functions/mappings/kernel.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/mappings/kernel.js +229 -0
- package/dist/cli/codemod/namespace-functions/mappings/kernel.js.map +1 -0
- package/dist/cli/codemod/namespace-functions/rewriter.d.ts +61 -0
- package/dist/cli/codemod/namespace-functions/rewriter.d.ts.map +1 -0
- package/dist/cli/codemod/namespace-functions/rewriter.js +564 -0
- package/dist/cli/codemod/namespace-functions/rewriter.js.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/compilers/bun.d.ts +1 -1
- package/dist/compilers/bun.js +1 -1
- package/dist/compilers/bun.js.map +1 -1
- package/dist/linters/house/await-support.d.ts +74 -0
- package/dist/linters/house/await-support.d.ts.map +1 -0
- package/dist/linters/house/await-support.js +238 -0
- package/dist/linters/house/await-support.js.map +1 -0
- package/dist/linters/house/common.d.ts +15 -9
- package/dist/linters/house/common.d.ts.map +1 -1
- package/dist/linters/house/common.js +20 -15
- package/dist/linters/house/common.js.map +1 -1
- package/dist/linters/house/configs.d.ts +26 -0
- package/dist/linters/house/configs.d.ts.map +1 -1
- package/dist/linters/house/configs.js +55 -1
- package/dist/linters/house/configs.js.map +1 -1
- package/dist/linters/house/const-class-values.rule.d.ts.map +1 -1
- package/dist/linters/house/const-class-values.rule.js +27 -19
- package/dist/linters/house/const-class-values.rule.js.map +1 -1
- package/dist/linters/house/effect-cleanup-consistency.rule.d.ts +17 -0
- package/dist/linters/house/effect-cleanup-consistency.rule.d.ts.map +1 -0
- package/dist/linters/house/effect-cleanup-consistency.rule.js +87 -0
- package/dist/linters/house/effect-cleanup-consistency.rule.js.map +1 -0
- package/dist/linters/house/hook-support.d.ts +29 -0
- package/dist/linters/house/hook-support.d.ts.map +1 -0
- package/dist/linters/house/hook-support.js +72 -0
- package/dist/linters/house/hook-support.js.map +1 -0
- package/dist/linters/house/index.d.ts +9 -0
- package/dist/linters/house/index.d.ts.map +1 -1
- package/dist/linters/house/index.js +9 -0
- package/dist/linters/house/index.js.map +1 -1
- package/dist/linters/house/max-params-own.rule.d.ts.map +1 -1
- package/dist/linters/house/max-params-own.rule.js +33 -24
- package/dist/linters/house/max-params-own.rule.js.map +1 -1
- package/dist/linters/house/no-async-hook-callback.rule.d.ts +18 -0
- package/dist/linters/house/no-async-hook-callback.rule.d.ts.map +1 -0
- package/dist/linters/house/no-async-hook-callback.rule.js +50 -0
- package/dist/linters/house/no-async-hook-callback.rule.js.map +1 -0
- package/dist/linters/house/no-await-promise-chain.rule.d.ts +24 -0
- package/dist/linters/house/no-await-promise-chain.rule.d.ts.map +1 -0
- package/dist/linters/house/no-await-promise-chain.rule.js +298 -0
- package/dist/linters/house/no-await-promise-chain.rule.js.map +1 -0
- package/dist/linters/house/no-inline-await.rule.d.ts +20 -0
- package/dist/linters/house/no-inline-await.rule.d.ts.map +1 -0
- package/dist/linters/house/no-inline-await.rule.js +70 -0
- package/dist/linters/house/no-inline-await.rule.js.map +1 -0
- package/dist/linters/house/no-new-error.rule.js +4 -4
- package/dist/linters/house/no-new-error.rule.js.map +1 -1
- package/dist/linters/house/no-return-await.rule.d.ts +15 -0
- package/dist/linters/house/no-return-await.rule.d.ts.map +1 -0
- package/dist/linters/house/no-return-await.rule.js +92 -0
- package/dist/linters/house/no-return-await.rule.js.map +1 -0
- package/dist/linters/house/no-silent-catch.rule.d.ts +22 -0
- package/dist/linters/house/no-silent-catch.rule.d.ts.map +1 -0
- package/dist/linters/house/no-silent-catch.rule.js +88 -0
- package/dist/linters/house/no-silent-catch.rule.js.map +1 -0
- package/dist/linters/house/plugin.d.ts.map +1 -1
- package/dist/linters/house/plugin.js +12 -0
- package/dist/linters/house/plugin.js.map +1 -1
- package/dist/linters/house/promise-chain-support.d.ts +49 -0
- package/dist/linters/house/promise-chain-support.d.ts.map +1 -0
- package/dist/linters/house/promise-chain-support.js +188 -0
- package/dist/linters/house/promise-chain-support.js.map +1 -0
- package/dist/linters/rules/no-restricted-properties.rule.js +1 -1
- package/dist/linters/rules/no-restricted-properties.rule.js.map +1 -1
- package/dist/testing/rejection.helper.d.ts +25 -20
- package/dist/testing/rejection.helper.d.ts.map +1 -1
- package/dist/testing/rejection.helper.js +33 -31
- package/dist/testing/rejection.helper.js.map +1 -1
- package/dist/testing/repo-tmp.d.ts +19 -9
- package/dist/testing/repo-tmp.d.ts.map +1 -1
- package/dist/testing/repo-tmp.js +50 -48
- package/dist/testing/repo-tmp.js.map +1 -1
- package/dist/testing/spawn.helper.d.ts +12 -10
- package/dist/testing/spawn.helper.d.ts.map +1 -1
- package/dist/testing/spawn.helper.js +22 -16
- package/dist/testing/spawn.helper.js.map +1 -1
- package/dist/testing/tmpdir-preload.js +1 -1
- package/dist/testing/tmpdir-preload.js.map +1 -1
- package/package.json +2 -3
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoAwaitPromiseChainRule = void 0;
|
|
4
|
+
const await_support_1 = require("./await-support");
|
|
5
|
+
const common_1 = require("./common");
|
|
6
|
+
const promise_chain_support_1 = require("./promise-chain-support");
|
|
7
|
+
const STATEMENT_LISTS = new Set(['BlockStatement', 'Program', 'StaticBlock', 'SwitchCase']);
|
|
8
|
+
/** The catch variable the fix writes when the handler has no parameter. */
|
|
9
|
+
const CATCH_NAME = 'error';
|
|
10
|
+
/** The identifiers a fallback value reads, and whether every node of it is a value the fix may move. */
|
|
11
|
+
const collectValue = (opts) => {
|
|
12
|
+
const { node, parameter, identifiers } = opts;
|
|
13
|
+
const type = common_1.AstNode.getType({ node }) ?? '';
|
|
14
|
+
const value = (child) => collectValue({ node: child, parameter, identifiers });
|
|
15
|
+
switch (type) {
|
|
16
|
+
case 'Literal': {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
case 'Identifier': {
|
|
20
|
+
identifiers.push(node);
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
case 'TemplateLiteral': {
|
|
24
|
+
return promise_chain_support_1.PromiseChainSupport.isConstant({ node });
|
|
25
|
+
}
|
|
26
|
+
case 'UnaryExpression': {
|
|
27
|
+
return promise_chain_support_1.PromiseChainSupport.isConstant({ node });
|
|
28
|
+
}
|
|
29
|
+
case 'ChainExpression': {
|
|
30
|
+
return value(common_1.AstNode.readField({ node, key: 'expression' }));
|
|
31
|
+
}
|
|
32
|
+
case 'MemberExpression': {
|
|
33
|
+
// Only a read of the error itself: `error.message`, `error?.response.status`.
|
|
34
|
+
const object = common_1.AstNode.readField({ node, key: 'object' });
|
|
35
|
+
const isRead = common_1.AstNode.readField({ node, key: 'computed' }) !== true ||
|
|
36
|
+
common_1.AstNode.getType({ node: common_1.AstNode.readField({ node, key: 'property' }) }) === 'Literal';
|
|
37
|
+
const isRooted = common_1.AstNode.getType({ node: object }) === 'MemberExpression' ||
|
|
38
|
+
(common_1.AstNode.getType({ node: object }) === 'Identifier' &&
|
|
39
|
+
common_1.AstNode.readField({ node: object, key: 'name' }) === parameter);
|
|
40
|
+
return isRead && isRooted && parameter !== undefined && value(object);
|
|
41
|
+
}
|
|
42
|
+
case 'ArrayExpression': {
|
|
43
|
+
const elements = common_1.AstNode.readField({ node, key: 'elements' });
|
|
44
|
+
return (Array.isArray(elements) && elements.every((element) => element === null || value(element)));
|
|
45
|
+
}
|
|
46
|
+
case 'ObjectExpression': {
|
|
47
|
+
const properties = common_1.AstNode.readField({ node, key: 'properties' });
|
|
48
|
+
return (Array.isArray(properties) &&
|
|
49
|
+
properties.every((property) => common_1.AstNode.getType({ node: property }) === 'Property' &&
|
|
50
|
+
common_1.AstNode.readField({ node: property, key: 'computed' }) !== true &&
|
|
51
|
+
common_1.AstNode.readField({ node: property, key: 'method' }) !== true &&
|
|
52
|
+
common_1.AstNode.readField({ node: property, key: 'kind' }) === 'init' &&
|
|
53
|
+
value(common_1.AstNode.readField({ node: property, key: 'value' }))));
|
|
54
|
+
}
|
|
55
|
+
default: {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/** Whether `name` is declared in `scope` or any scope around it: a catch variable of that name would shadow it. */
|
|
61
|
+
const isDeclaredAround = (opts) => {
|
|
62
|
+
for (let scope = opts.scope; scope; scope = scope.upper) {
|
|
63
|
+
if (scope.set.has(opts.name)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* The plan of the fix for `awaitNode`, or `undefined` when the strict safe shape does not hold:
|
|
71
|
+
* `const|let x[: T] = await f().catch(() => <value>)` or `.catch((error) => <value>)`, alone in its declaration,
|
|
72
|
+
* in a statement list; `x` never written again and, without a type annotation, read only in its own function;
|
|
73
|
+
* the handler an expression-bodied, non-async arrow with at most one plain parameter; `<value>` a literal, an
|
|
74
|
+
* identifier, a read of the error (`error.message`) or an array or object literal of those; `f()` with no
|
|
75
|
+
* `.then`, `.catch` or `.finally` before and no `await` inside; no comment the fix would drop.
|
|
76
|
+
*/
|
|
77
|
+
const planFix = (opts) => {
|
|
78
|
+
const { awaitNode, sourceCode } = opts;
|
|
79
|
+
const call = common_1.AstNode.readField({ node: awaitNode, key: 'argument' });
|
|
80
|
+
const callee = common_1.AstNode.readField({ node: call, key: 'callee' });
|
|
81
|
+
const args = common_1.AstNode.readField({ node: call, key: 'arguments' });
|
|
82
|
+
if (common_1.AstNode.getType({ node: call }) !== 'CallExpression' ||
|
|
83
|
+
common_1.AstNode.readField({ node: call, key: 'optional' }) === true ||
|
|
84
|
+
common_1.AstNode.getType({ node: callee }) !== 'MemberExpression' ||
|
|
85
|
+
common_1.AstNode.readField({ node: callee, key: 'computed' }) === true ||
|
|
86
|
+
common_1.AstNode.readField({ node: callee, key: 'optional' }) === true ||
|
|
87
|
+
promise_chain_support_1.PromiseChainSupport.memberName({ member: callee }) !== 'catch' ||
|
|
88
|
+
!Array.isArray(args) ||
|
|
89
|
+
args.length !== 1) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const handler = args[0];
|
|
93
|
+
const params = common_1.AstNode.readField({ node: handler, key: 'params' });
|
|
94
|
+
const body = common_1.AstNode.readField({ node: handler, key: 'body' });
|
|
95
|
+
const parameter = Array.isArray(params) ? params[0] : undefined;
|
|
96
|
+
if (common_1.AstNode.getType({ node: handler }) !== 'ArrowFunctionExpression' ||
|
|
97
|
+
common_1.AstNode.readField({ node: handler, key: 'async' }) === true ||
|
|
98
|
+
common_1.AstNode.getType({ node: body }) === 'BlockStatement' ||
|
|
99
|
+
common_1.AstNode.readField({ node: handler, key: 'returnType' }) ||
|
|
100
|
+
common_1.AstNode.readField({ node: handler, key: 'typeParameters' }) ||
|
|
101
|
+
!Array.isArray(params) ||
|
|
102
|
+
params.length > 1 ||
|
|
103
|
+
(parameter !== undefined &&
|
|
104
|
+
(common_1.AstNode.getType({ node: parameter }) !== 'Identifier' ||
|
|
105
|
+
common_1.AstNode.readField({ node: parameter, key: 'typeAnnotation' }) ||
|
|
106
|
+
common_1.AstNode.readField({ node: parameter, key: 'optional' }) === true))) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const receiver = common_1.AstNode.readField({ node: callee, key: 'object' });
|
|
110
|
+
if (!await_support_1.AwaitSupport.isNode(receiver) ||
|
|
111
|
+
!await_support_1.AwaitSupport.isNode(body) ||
|
|
112
|
+
!await_support_1.AwaitSupport.isNode(handler) ||
|
|
113
|
+
promise_chain_support_1.PromiseChainSupport.hasChainOnSpine({ node: receiver }) ||
|
|
114
|
+
/\b(await|yield)\b/.test(sourceCode.getText(receiver))) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
const declarator = await_support_1.AwaitSupport.parentOf({ node: awaitNode });
|
|
118
|
+
const declaration = await_support_1.AwaitSupport.parentOf({ node: declarator });
|
|
119
|
+
const id = common_1.AstNode.readField({ node: declarator, key: 'id' });
|
|
120
|
+
const declarations = common_1.AstNode.readField({ node: declaration, key: 'declarations' });
|
|
121
|
+
const kind = String(common_1.AstNode.readField({ node: declaration, key: 'kind' }));
|
|
122
|
+
if (common_1.AstNode.getType({ node: declarator }) !== 'VariableDeclarator' ||
|
|
123
|
+
common_1.AstNode.readField({ node: declarator, key: 'init' }) !== awaitNode ||
|
|
124
|
+
common_1.AstNode.readField({ node: declarator, key: 'definite' }) === true ||
|
|
125
|
+
common_1.AstNode.getType({ node: id }) !== 'Identifier' ||
|
|
126
|
+
!await_support_1.AwaitSupport.isNode(declarator) ||
|
|
127
|
+
!await_support_1.AwaitSupport.isNode(declaration) ||
|
|
128
|
+
!Array.isArray(declarations) ||
|
|
129
|
+
declarations.length !== 1 ||
|
|
130
|
+
!['const', 'let'].includes(kind) ||
|
|
131
|
+
!STATEMENT_LISTS.has(common_1.AstNode.getType({ node: await_support_1.AwaitSupport.parentOf({ node: declaration }) }) ?? '')) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
const name = String(common_1.AstNode.readField({ node: id, key: 'name' }));
|
|
135
|
+
const typeAnnotation = common_1.AstNode.readField({ node: id, key: 'typeAnnotation' });
|
|
136
|
+
const annotation = await_support_1.AwaitSupport.isNode(typeAnnotation) ? sourceCode.getText(typeAnnotation) : '';
|
|
137
|
+
// `let x;` with no annotation takes its type from the assignments that follow, so TypeScript must see every
|
|
138
|
+
// read in the same function: a read from a nested function, an export or a `typeof` makes it an implicit any.
|
|
139
|
+
const variable = sourceCode.getDeclaredVariables(declarator).find((entry) => entry.name === name);
|
|
140
|
+
if (!variable) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
for (const reference of variable.references) {
|
|
144
|
+
if (reference.identifier === id) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const parentType = common_1.AstNode.getType({
|
|
148
|
+
node: await_support_1.AwaitSupport.parentOf({ node: reference.identifier }),
|
|
149
|
+
});
|
|
150
|
+
if (reference.isWrite() ||
|
|
151
|
+
(annotation === '' &&
|
|
152
|
+
(reference.from.variableScope !== variable.scope.variableScope ||
|
|
153
|
+
parentType === 'ExportSpecifier' ||
|
|
154
|
+
parentType === 'TSTypeQuery'))) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const parameterName = parameter === undefined
|
|
159
|
+
? undefined
|
|
160
|
+
: String(common_1.AstNode.readField({ node: parameter, key: 'name' }));
|
|
161
|
+
const identifiers = [];
|
|
162
|
+
if (!collectValue({ node: body, parameter: parameterName, identifiers })) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
// `x = []` on a `let x;` with no annotation makes an evolving `any[]`, looser than the `never[]` it was.
|
|
166
|
+
const elements = common_1.AstNode.readField({ node: body, key: 'elements' });
|
|
167
|
+
if (annotation === '' &&
|
|
168
|
+
common_1.AstNode.getType({ node: body }) === 'ArrayExpression' &&
|
|
169
|
+
Array.isArray(elements) &&
|
|
170
|
+
elements.length === 0) {
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
const parameterVariable = sourceCode
|
|
174
|
+
.getDeclaredVariables(handler)
|
|
175
|
+
.find((entry) => entry.name === parameterName);
|
|
176
|
+
const errorReads = new Set(parameterVariable?.references.map((reference) => reference.identifier) ?? []);
|
|
177
|
+
const freeNames = new Set(identifiers
|
|
178
|
+
.filter((identifier) => !errorReads.has(identifier))
|
|
179
|
+
.map((identifier) => String(common_1.AstNode.readField({ node: identifier, key: 'name' }))));
|
|
180
|
+
// The fallback is evaluated after the `let`: reading the declared name there would read `undefined`.
|
|
181
|
+
if (freeNames.has(name)) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
const scope = sourceCode.getScope(declaration);
|
|
185
|
+
const isTaken = (candidate) => candidate === name ||
|
|
186
|
+
freeNames.has(candidate) ||
|
|
187
|
+
(candidate !== parameterName && isDeclaredAround({ scope, name: candidate }));
|
|
188
|
+
const base = parameterName ?? CATCH_NAME;
|
|
189
|
+
let catchName = base;
|
|
190
|
+
for (let index = 2; isTaken(catchName); index += 1) {
|
|
191
|
+
catchName = `${base}${index}`;
|
|
192
|
+
}
|
|
193
|
+
const comments = sourceCode.getCommentsInside(declaration);
|
|
194
|
+
const isInside = (range, inner) => range !== undefined && inner !== undefined && range[0] <= inner[0] && inner[1] <= range[1];
|
|
195
|
+
if (comments.some((comment) => !isInside(receiver.range, comment.range) && !isInside(body.range, comment.range))) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
const bodyStart = body.range?.[0] ?? 0;
|
|
199
|
+
let valueText = sourceCode.getText(body);
|
|
200
|
+
if (catchName !== parameterName && errorReads.size > 0) {
|
|
201
|
+
const edits = [...errorReads]
|
|
202
|
+
.filter(await_support_1.AwaitSupport.isNode)
|
|
203
|
+
.map((identifier) => {
|
|
204
|
+
const parent = await_support_1.AwaitSupport.parentOf({ node: identifier });
|
|
205
|
+
const isShorthand = common_1.AstNode.getType({ node: parent }) === 'Property' &&
|
|
206
|
+
common_1.AstNode.readField({ node: parent, key: 'shorthand' }) === true;
|
|
207
|
+
return {
|
|
208
|
+
range: identifier.range ?? [0, 0],
|
|
209
|
+
text: isShorthand ? `${parameterName}: ${catchName}` : catchName,
|
|
210
|
+
};
|
|
211
|
+
})
|
|
212
|
+
.sort((left, right) => right.range[0] - left.range[0]);
|
|
213
|
+
for (const edit of edits) {
|
|
214
|
+
valueText =
|
|
215
|
+
valueText.slice(0, edit.range[0] - bodyStart) +
|
|
216
|
+
edit.text +
|
|
217
|
+
valueText.slice(edit.range[1] - bodyStart);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
declaration,
|
|
222
|
+
kind,
|
|
223
|
+
name,
|
|
224
|
+
annotation,
|
|
225
|
+
receiverText: sourceCode.getText(receiver),
|
|
226
|
+
catchName,
|
|
227
|
+
isErrorRead: errorReads.size > 0,
|
|
228
|
+
valueText,
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* `venizia/no-await-promise-chain`. One expression uses one async style: `await` (in a try/catch when it can
|
|
233
|
+
* fail), or a promise chain that is not awaited. An `await` whose operand - after parentheses, `!`, `as`,
|
|
234
|
+
* `satisfies` and an optional chain - is a call of `.then`, `.catch` or `.finally` (`?.then(`, `['then'](`, a
|
|
235
|
+
* thenable such as a query builder) is reported once, however long the chain. A chain that is returned or stands
|
|
236
|
+
* as a statement is not awaited and is not reported, nor is `await Promise.all(xs.map(f))`, `await new Promise(...)`,
|
|
237
|
+
* `await x.then` (no call), or an `await` inside a callback of a chain.
|
|
238
|
+
*
|
|
239
|
+
* The fix is made for one shape only, `const|let x = await f().catch(() => <value>)` (see `planFix` for every
|
|
240
|
+
* condition), and writes `let x; try { x = await f(); } catch (error) { x = <value>; }` (`catch { ... }` when the value does not read the error): the `const` becomes a
|
|
241
|
+
* `let`, a type annotation stays, the catch variable takes the handler's parameter name (`error` without one,
|
|
242
|
+
* renamed when the name is taken), and is typed `any` when the value reads it, as the handler's parameter was.
|
|
243
|
+
* The try also catches a synchronous throw of `f()`, which the chain let through. Everything else is reported.
|
|
244
|
+
*/
|
|
245
|
+
class NoAwaitPromiseChainRule {
|
|
246
|
+
static { this.NAME = `${common_1.HOUSE_PLUGIN_NAMESPACE}/no-await-promise-chain`; }
|
|
247
|
+
static { this.RULE = {
|
|
248
|
+
meta: {
|
|
249
|
+
type: 'suggestion',
|
|
250
|
+
fixable: 'code',
|
|
251
|
+
schema: [],
|
|
252
|
+
messages: {
|
|
253
|
+
mixed: 'Do not `await` a promise chain (`.{{method}}(...)`): wrap `await f()` in try/catch, or drop the `await` and keep the chain.',
|
|
254
|
+
mixedConst: 'Do not `await` a promise chain (`.{{method}}(...)`): wrap `await f()` in try/catch, or drop the `await` and keep the chain. The fix writes the try/catch and turns the `const` into a `let`.',
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
create: (context) => {
|
|
258
|
+
const { sourceCode } = context;
|
|
259
|
+
return {
|
|
260
|
+
[common_1.AstNodeTypes.AWAIT_EXPRESSION]: (node) => {
|
|
261
|
+
const method = promise_chain_support_1.PromiseChainSupport.chainMethodOf({
|
|
262
|
+
node: common_1.AstNode.readField({ node, key: 'argument' }),
|
|
263
|
+
});
|
|
264
|
+
if (method === undefined) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const plan = planFix({ awaitNode: node, sourceCode });
|
|
268
|
+
context.report({
|
|
269
|
+
node,
|
|
270
|
+
messageId: plan?.kind === 'const' ? 'mixedConst' : 'mixed',
|
|
271
|
+
data: { method },
|
|
272
|
+
fix: plan
|
|
273
|
+
? (fixer) => {
|
|
274
|
+
const indent = await_support_1.AwaitSupport.indentOf({ node: plan.declaration, sourceCode });
|
|
275
|
+
const inner = `${indent} `;
|
|
276
|
+
// The optional catch binding when nothing reads the error: a named one would be an unused variable.
|
|
277
|
+
const head = plan.isErrorRead ? `catch (${plan.catchName}: any)` : 'catch';
|
|
278
|
+
return fixer.replaceText(plan.declaration, [
|
|
279
|
+
`let ${plan.name}${plan.annotation};`,
|
|
280
|
+
`${indent}try {`,
|
|
281
|
+
`${inner}${plan.name} = await ${plan.receiverText};`,
|
|
282
|
+
`${indent}} ${head} {`,
|
|
283
|
+
`${inner}${plan.name} = ${plan.valueText};`,
|
|
284
|
+
`${indent}}`,
|
|
285
|
+
].join('\n'));
|
|
286
|
+
}
|
|
287
|
+
: null,
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
},
|
|
292
|
+
}; }
|
|
293
|
+
static build(opts = {}) {
|
|
294
|
+
return opts.severity ?? 'error';
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
exports.NoAwaitPromiseChainRule = NoAwaitPromiseChainRule;
|
|
298
|
+
//# sourceMappingURL=no-await-promise-chain.rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-await-promise-chain.rule.js","sourceRoot":"","sources":["../../../src/linters/house/no-await-promise-chain.rule.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Rule, Scope, SourceCode } from 'eslint';\nimport type { TRuleSeverity } from '../rules/common';\nimport { AwaitSupport } from './await-support';\nimport { AstNode, AstNodeTypes, HOUSE_PLUGIN_NAMESPACE } from './common';\nimport { PromiseChainSupport } from './promise-chain-support';\n\nconst STATEMENT_LISTS = new Set(['BlockStatement', 'Program', 'StaticBlock', 'SwitchCase']);\n/** The catch variable the fix writes when the handler has no parameter. */\nconst CATCH_NAME = 'error';\n\n/** What the fix needs, read once from a `const x = await f().catch(h)` that passed every check. */\ninterface IFixPlan {\n declaration: Rule.Node;\n kind: string;\n name: string;\n annotation: string;\n receiverText: string;\n catchName: string;\n isErrorRead: boolean;\n valueText: string;\n}\n\n/** The identifiers a fallback value reads, and whether every node of it is a value the fix may move. */\nconst collectValue = (opts: {\n node: unknown;\n parameter: string | undefined;\n identifiers: unknown[];\n}): boolean => {\n const { node, parameter, identifiers } = opts;\n const type = AstNode.getType({ node }) ?? '';\n const value = (child: unknown) => collectValue({ node: child, parameter, identifiers });\n\n switch (type) {\n case 'Literal': {\n return true;\n }\n case 'Identifier': {\n identifiers.push(node);\n return true;\n }\n case 'TemplateLiteral': {\n return PromiseChainSupport.isConstant({ node });\n }\n case 'UnaryExpression': {\n return PromiseChainSupport.isConstant({ node });\n }\n case 'ChainExpression': {\n return value(AstNode.readField({ node, key: 'expression' }));\n }\n case 'MemberExpression': {\n // Only a read of the error itself: `error.message`, `error?.response.status`.\n const object = AstNode.readField({ node, key: 'object' });\n const isRead =\n AstNode.readField({ node, key: 'computed' }) !== true ||\n AstNode.getType({ node: AstNode.readField({ node, key: 'property' }) }) === 'Literal';\n const isRooted =\n AstNode.getType({ node: object }) === 'MemberExpression' ||\n (AstNode.getType({ node: object }) === 'Identifier' &&\n AstNode.readField({ node: object, key: 'name' }) === parameter);\n\n return isRead && isRooted && parameter !== undefined && value(object);\n }\n case 'ArrayExpression': {\n const elements = AstNode.readField({ node, key: 'elements' });\n return (\n Array.isArray(elements) && elements.every((element) => element === null || value(element))\n );\n }\n case 'ObjectExpression': {\n const properties = AstNode.readField({ node, key: 'properties' });\n return (\n Array.isArray(properties) &&\n properties.every(\n (property) =>\n AstNode.getType({ node: property }) === 'Property' &&\n AstNode.readField({ node: property, key: 'computed' }) !== true &&\n AstNode.readField({ node: property, key: 'method' }) !== true &&\n AstNode.readField({ node: property, key: 'kind' }) === 'init' &&\n value(AstNode.readField({ node: property, key: 'value' })),\n )\n );\n }\n default: {\n return false;\n }\n }\n};\n\n/** Whether `name` is declared in `scope` or any scope around it: a catch variable of that name would shadow it. */\nconst isDeclaredAround = (opts: { scope: Scope.Scope | null; name: string }): boolean => {\n for (let scope = opts.scope; scope; scope = scope.upper) {\n if (scope.set.has(opts.name)) {\n return true;\n }\n }\n\n return false;\n};\n\n/**\n * The plan of the fix for `awaitNode`, or `undefined` when the strict safe shape does not hold:\n * `const|let x[: T] = await f().catch(() => <value>)` or `.catch((error) => <value>)`, alone in its declaration,\n * in a statement list; `x` never written again and, without a type annotation, read only in its own function;\n * the handler an expression-bodied, non-async arrow with at most one plain parameter; `<value>` a literal, an\n * identifier, a read of the error (`error.message`) or an array or object literal of those; `f()` with no\n * `.then`, `.catch` or `.finally` before and no `await` inside; no comment the fix would drop.\n */\nconst planFix = (opts: { awaitNode: Rule.Node; sourceCode: SourceCode }): IFixPlan | undefined => {\n const { awaitNode, sourceCode } = opts;\n const call = AstNode.readField({ node: awaitNode, key: 'argument' });\n const callee = AstNode.readField({ node: call, key: 'callee' });\n const args = AstNode.readField({ node: call, key: 'arguments' });\n\n if (\n AstNode.getType({ node: call }) !== 'CallExpression' ||\n AstNode.readField({ node: call, key: 'optional' }) === true ||\n AstNode.getType({ node: callee }) !== 'MemberExpression' ||\n AstNode.readField({ node: callee, key: 'computed' }) === true ||\n AstNode.readField({ node: callee, key: 'optional' }) === true ||\n PromiseChainSupport.memberName({ member: callee }) !== 'catch' ||\n !Array.isArray(args) ||\n args.length !== 1\n ) {\n return undefined;\n }\n\n const handler: unknown = args[0];\n const params = AstNode.readField({ node: handler, key: 'params' });\n const body = AstNode.readField({ node: handler, key: 'body' });\n const parameter: unknown = Array.isArray(params) ? params[0] : undefined;\n\n if (\n AstNode.getType({ node: handler }) !== 'ArrowFunctionExpression' ||\n AstNode.readField({ node: handler, key: 'async' }) === true ||\n AstNode.getType({ node: body }) === 'BlockStatement' ||\n AstNode.readField({ node: handler, key: 'returnType' }) ||\n AstNode.readField({ node: handler, key: 'typeParameters' }) ||\n !Array.isArray(params) ||\n params.length > 1 ||\n (parameter !== undefined &&\n (AstNode.getType({ node: parameter }) !== 'Identifier' ||\n AstNode.readField({ node: parameter, key: 'typeAnnotation' }) ||\n AstNode.readField({ node: parameter, key: 'optional' }) === true))\n ) {\n return undefined;\n }\n\n const receiver = AstNode.readField({ node: callee, key: 'object' });\n if (\n !AwaitSupport.isNode(receiver) ||\n !AwaitSupport.isNode(body) ||\n !AwaitSupport.isNode(handler) ||\n PromiseChainSupport.hasChainOnSpine({ node: receiver }) ||\n /\\b(await|yield)\\b/.test(sourceCode.getText(receiver))\n ) {\n return undefined;\n }\n\n const declarator = AwaitSupport.parentOf({ node: awaitNode });\n const declaration = AwaitSupport.parentOf({ node: declarator });\n const id = AstNode.readField({ node: declarator, key: 'id' });\n const declarations = AstNode.readField({ node: declaration, key: 'declarations' });\n const kind = String(AstNode.readField({ node: declaration, key: 'kind' }));\n\n if (\n AstNode.getType({ node: declarator }) !== 'VariableDeclarator' ||\n AstNode.readField({ node: declarator, key: 'init' }) !== awaitNode ||\n AstNode.readField({ node: declarator, key: 'definite' }) === true ||\n AstNode.getType({ node: id }) !== 'Identifier' ||\n !AwaitSupport.isNode(declarator) ||\n !AwaitSupport.isNode(declaration) ||\n !Array.isArray(declarations) ||\n declarations.length !== 1 ||\n !['const', 'let'].includes(kind) ||\n !STATEMENT_LISTS.has(\n AstNode.getType({ node: AwaitSupport.parentOf({ node: declaration }) }) ?? '',\n )\n ) {\n return undefined;\n }\n\n const name = String(AstNode.readField({ node: id, key: 'name' }));\n const typeAnnotation = AstNode.readField({ node: id, key: 'typeAnnotation' });\n const annotation = AwaitSupport.isNode(typeAnnotation) ? sourceCode.getText(typeAnnotation) : '';\n\n // `let x;` with no annotation takes its type from the assignments that follow, so TypeScript must see every\n // read in the same function: a read from a nested function, an export or a `typeof` makes it an implicit any.\n const variable = sourceCode.getDeclaredVariables(declarator).find((entry) => entry.name === name);\n if (!variable) {\n return undefined;\n }\n for (const reference of variable.references) {\n if (reference.identifier === id) {\n continue;\n }\n const parentType = AstNode.getType({\n node: AwaitSupport.parentOf({ node: reference.identifier }),\n });\n if (\n reference.isWrite() ||\n (annotation === '' &&\n (reference.from.variableScope !== variable.scope.variableScope ||\n parentType === 'ExportSpecifier' ||\n parentType === 'TSTypeQuery'))\n ) {\n return undefined;\n }\n }\n\n const parameterName =\n parameter === undefined\n ? undefined\n : String(AstNode.readField({ node: parameter, key: 'name' }));\n const identifiers: unknown[] = [];\n if (!collectValue({ node: body, parameter: parameterName, identifiers })) {\n return undefined;\n }\n\n // `x = []` on a `let x;` with no annotation makes an evolving `any[]`, looser than the `never[]` it was.\n const elements = AstNode.readField({ node: body, key: 'elements' });\n if (\n annotation === '' &&\n AstNode.getType({ node: body }) === 'ArrayExpression' &&\n Array.isArray(elements) &&\n elements.length === 0\n ) {\n return undefined;\n }\n\n const parameterVariable = sourceCode\n .getDeclaredVariables(handler)\n .find((entry) => entry.name === parameterName);\n const errorReads = new Set<unknown>(\n parameterVariable?.references.map((reference) => reference.identifier) ?? [],\n );\n const freeNames = new Set(\n identifiers\n .filter((identifier) => !errorReads.has(identifier))\n .map((identifier) => String(AstNode.readField({ node: identifier, key: 'name' }))),\n );\n\n // The fallback is evaluated after the `let`: reading the declared name there would read `undefined`.\n if (freeNames.has(name)) {\n return undefined;\n }\n\n const scope = sourceCode.getScope(declaration);\n const isTaken = (candidate: string) =>\n candidate === name ||\n freeNames.has(candidate) ||\n (candidate !== parameterName && isDeclaredAround({ scope, name: candidate }));\n const base = parameterName ?? CATCH_NAME;\n let catchName = base;\n for (let index = 2; isTaken(catchName); index += 1) {\n catchName = `${base}${index}`;\n }\n\n const comments = sourceCode.getCommentsInside(declaration);\n const isInside = (range: [number, number] | undefined, inner: [number, number] | undefined) =>\n range !== undefined && inner !== undefined && range[0] <= inner[0] && inner[1] <= range[1];\n if (\n comments.some(\n (comment) => !isInside(receiver.range, comment.range) && !isInside(body.range, comment.range),\n )\n ) {\n return undefined;\n }\n\n const bodyStart = body.range?.[0] ?? 0;\n let valueText = sourceCode.getText(body);\n if (catchName !== parameterName && errorReads.size > 0) {\n const edits = [...errorReads]\n .filter(AwaitSupport.isNode)\n .map((identifier) => {\n const parent = AwaitSupport.parentOf({ node: identifier });\n const isShorthand =\n AstNode.getType({ node: parent }) === 'Property' &&\n AstNode.readField({ node: parent, key: 'shorthand' }) === true;\n\n return {\n range: identifier.range ?? [0, 0],\n text: isShorthand ? `${parameterName}: ${catchName}` : catchName,\n };\n })\n .sort((left, right) => right.range[0] - left.range[0]);\n\n for (const edit of edits) {\n valueText =\n valueText.slice(0, edit.range[0] - bodyStart) +\n edit.text +\n valueText.slice(edit.range[1] - bodyStart);\n }\n }\n\n return {\n declaration,\n kind,\n name,\n annotation,\n receiverText: sourceCode.getText(receiver),\n catchName,\n isErrorRead: errorReads.size > 0,\n valueText,\n };\n};\n\n/**\n * `venizia/no-await-promise-chain`. One expression uses one async style: `await` (in a try/catch when it can\n * fail), or a promise chain that is not awaited. An `await` whose operand - after parentheses, `!`, `as`,\n * `satisfies` and an optional chain - is a call of `.then`, `.catch` or `.finally` (`?.then(`, `['then'](`, a\n * thenable such as a query builder) is reported once, however long the chain. A chain that is returned or stands\n * as a statement is not awaited and is not reported, nor is `await Promise.all(xs.map(f))`, `await new Promise(...)`,\n * `await x.then` (no call), or an `await` inside a callback of a chain.\n *\n * The fix is made for one shape only, `const|let x = await f().catch(() => <value>)` (see `planFix` for every\n * condition), and writes `let x; try { x = await f(); } catch (error) { x = <value>; }` (`catch { ... }` when the value does not read the error): the `const` becomes a\n * `let`, a type annotation stays, the catch variable takes the handler's parameter name (`error` without one,\n * renamed when the name is taken), and is typed `any` when the value reads it, as the handler's parameter was.\n * The try also catches a synchronous throw of `f()`, which the chain let through. Everything else is reported.\n */\nexport class NoAwaitPromiseChainRule {\n static readonly NAME = `${HOUSE_PLUGIN_NAMESPACE}/no-await-promise-chain`;\n\n static readonly RULE: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n fixable: 'code',\n schema: [],\n messages: {\n mixed:\n 'Do not `await` a promise chain (`.{{method}}(...)`): wrap `await f()` in try/catch, or drop the `await` and keep the chain.',\n mixedConst:\n 'Do not `await` a promise chain (`.{{method}}(...)`): wrap `await f()` in try/catch, or drop the `await` and keep the chain. The fix writes the try/catch and turns the `const` into a `let`.',\n },\n },\n create: (context) => {\n const { sourceCode } = context;\n\n return {\n [AstNodeTypes.AWAIT_EXPRESSION]: (node: Rule.Node) => {\n const method = PromiseChainSupport.chainMethodOf({\n node: AstNode.readField({ node, key: 'argument' }),\n });\n if (method === undefined) {\n return;\n }\n\n const plan = planFix({ awaitNode: node, sourceCode });\n\n context.report({\n node,\n messageId: plan?.kind === 'const' ? 'mixedConst' : 'mixed',\n data: { method },\n fix: plan\n ? (fixer) => {\n const indent = AwaitSupport.indentOf({ node: plan.declaration, sourceCode });\n const inner = `${indent} `;\n // The optional catch binding when nothing reads the error: a named one would be an unused variable.\n const head = plan.isErrorRead ? `catch (${plan.catchName}: any)` : 'catch';\n\n return fixer.replaceText(\n plan.declaration,\n [\n `let ${plan.name}${plan.annotation};`,\n `${indent}try {`,\n `${inner}${plan.name} = await ${plan.receiverText};`,\n `${indent}} ${head} {`,\n `${inner}${plan.name} = ${plan.valueText};`,\n `${indent}}`,\n ].join('\\n'),\n );\n }\n : null,\n });\n },\n };\n },\n };\n\n static build(opts: { severity?: TRuleSeverity } = {}): TRuleSeverity {\n return opts.severity ?? 'error';\n }\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
import type { TRuleSeverity } from '../rules/common';
|
|
3
|
+
/**
|
|
4
|
+
* `venizia/no-inline-await`. `await` stands alone: as a whole statement (`await f();`) or as the whole initializer
|
|
5
|
+
* of a declaration (`const result = await f();`). Inside any other expression - a call argument, a spread, an
|
|
6
|
+
* array or object, a ternary, a logical operator, a template literal, a member access, an assignment - it is
|
|
7
|
+
* reported, and `return g(await f())` is the same report. `return await f()` is `no-return-await`'s.
|
|
8
|
+
*
|
|
9
|
+
* The fix moves the `await` into a `const` in front of the statement. It is made only when nothing that does more
|
|
10
|
+
* than read a value runs before the `await` inside the statement, and when the `await` is not in a branch, a loop
|
|
11
|
+
* test, an unbraced `if` branch or an arrow expression body; those cases are reported and left alone.
|
|
12
|
+
*/
|
|
13
|
+
export declare class NoInlineAwaitRule {
|
|
14
|
+
static readonly NAME = "venizia/no-inline-await";
|
|
15
|
+
static readonly RULE: Rule.RuleModule;
|
|
16
|
+
static build(opts?: {
|
|
17
|
+
severity?: TRuleSeverity;
|
|
18
|
+
}): TRuleSeverity;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=no-inline-await.rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-inline-await.rule.d.ts","sourceRoot":"","sources":["../../../src/linters/house/no-inline-await.rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIrD;;;;;;;;;GASG;AACH,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,QAAQ,CAAC,IAAI,6BAA+C;IAEnE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CA0DnC;IAEF,MAAM,CAAC,KAAK,CAAC,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,aAAa,CAAA;KAAO,GAAG,aAAa;CAGrE"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoInlineAwaitRule = void 0;
|
|
4
|
+
const await_support_1 = require("./await-support");
|
|
5
|
+
const common_1 = require("./common");
|
|
6
|
+
/**
|
|
7
|
+
* `venizia/no-inline-await`. `await` stands alone: as a whole statement (`await f();`) or as the whole initializer
|
|
8
|
+
* of a declaration (`const result = await f();`). Inside any other expression - a call argument, a spread, an
|
|
9
|
+
* array or object, a ternary, a logical operator, a template literal, a member access, an assignment - it is
|
|
10
|
+
* reported, and `return g(await f())` is the same report. `return await f()` is `no-return-await`'s.
|
|
11
|
+
*
|
|
12
|
+
* The fix moves the `await` into a `const` in front of the statement. It is made only when nothing that does more
|
|
13
|
+
* than read a value runs before the `await` inside the statement, and when the `await` is not in a branch, a loop
|
|
14
|
+
* test, an unbraced `if` branch or an arrow expression body; those cases are reported and left alone.
|
|
15
|
+
*/
|
|
16
|
+
class NoInlineAwaitRule {
|
|
17
|
+
static { this.NAME = `${common_1.HOUSE_PLUGIN_NAMESPACE}/no-inline-await`; }
|
|
18
|
+
static { this.RULE = {
|
|
19
|
+
meta: {
|
|
20
|
+
type: 'suggestion',
|
|
21
|
+
fixable: 'code',
|
|
22
|
+
schema: [],
|
|
23
|
+
messages: {
|
|
24
|
+
inline: '`await` stands alone as a statement or as the whole initializer of a declaration (`const result = await f();`), not inside an expression.',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
create: (context) => {
|
|
28
|
+
const { sourceCode } = context;
|
|
29
|
+
return {
|
|
30
|
+
[common_1.AstNodeTypes.AWAIT_EXPRESSION]: (node) => {
|
|
31
|
+
const parent = await_support_1.AwaitSupport.parentOf({ node });
|
|
32
|
+
const parentType = common_1.AstNode.getType({ node: parent });
|
|
33
|
+
const isWhole = (parentType === 'ExpressionStatement' &&
|
|
34
|
+
common_1.AstNode.readField({ node: parent, key: 'expression' }) === node) ||
|
|
35
|
+
(parentType === 'VariableDeclarator' &&
|
|
36
|
+
common_1.AstNode.readField({ node: parent, key: 'init' }) === node);
|
|
37
|
+
const isReturned = (parentType === 'ReturnStatement' || parentType === 'ArrowFunctionExpression') &&
|
|
38
|
+
(common_1.AstNode.readField({ node: parent, key: 'argument' }) === node ||
|
|
39
|
+
common_1.AstNode.readField({ node: parent, key: 'body' }) === node);
|
|
40
|
+
if (isWhole || isReturned) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const statement = await_support_1.AwaitSupport.hoistTarget({ awaitNode: node });
|
|
44
|
+
const canFix = statement !== undefined &&
|
|
45
|
+
!await_support_1.AwaitSupport.hasEffectBefore({ statement, awaitNode: node, sourceCode });
|
|
46
|
+
context.report({
|
|
47
|
+
node,
|
|
48
|
+
messageId: 'inline',
|
|
49
|
+
fix: canFix
|
|
50
|
+
? (fixer) => {
|
|
51
|
+
const name = await_support_1.AwaitSupport.freshName({ node, base: 'result', sourceCode });
|
|
52
|
+
const [start, end] = await_support_1.AwaitSupport.replaceRangeOf({ awaitNode: node, sourceCode });
|
|
53
|
+
const indent = await_support_1.AwaitSupport.indentOf({ node: statement, sourceCode });
|
|
54
|
+
return [
|
|
55
|
+
fixer.insertTextBefore(statement, `const ${name} = ${sourceCode.getText(node)};\n${indent}`),
|
|
56
|
+
fixer.replaceTextRange([start, end], name),
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
: null,
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
}; }
|
|
65
|
+
static build(opts = {}) {
|
|
66
|
+
return opts.severity ?? 'error';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.NoInlineAwaitRule = NoInlineAwaitRule;
|
|
70
|
+
//# sourceMappingURL=no-inline-await.rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-inline-await.rule.js","sourceRoot":"","sources":["../../../src/linters/house/no-inline-await.rule.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Rule } from 'eslint';\nimport type { TRuleSeverity } from '../rules/common';\nimport { AwaitSupport } from './await-support';\nimport { AstNode, AstNodeTypes, HOUSE_PLUGIN_NAMESPACE } from './common';\n\n/**\n * `venizia/no-inline-await`. `await` stands alone: as a whole statement (`await f();`) or as the whole initializer\n * of a declaration (`const result = await f();`). Inside any other expression - a call argument, a spread, an\n * array or object, a ternary, a logical operator, a template literal, a member access, an assignment - it is\n * reported, and `return g(await f())` is the same report. `return await f()` is `no-return-await`'s.\n *\n * The fix moves the `await` into a `const` in front of the statement. It is made only when nothing that does more\n * than read a value runs before the `await` inside the statement, and when the `await` is not in a branch, a loop\n * test, an unbraced `if` branch or an arrow expression body; those cases are reported and left alone.\n */\nexport class NoInlineAwaitRule {\n static readonly NAME = `${HOUSE_PLUGIN_NAMESPACE}/no-inline-await`;\n\n static readonly RULE: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n fixable: 'code',\n schema: [],\n messages: {\n inline:\n '`await` stands alone as a statement or as the whole initializer of a declaration (`const result = await f();`), not inside an expression.',\n },\n },\n create: (context) => {\n const { sourceCode } = context;\n\n return {\n [AstNodeTypes.AWAIT_EXPRESSION]: (node: Rule.Node) => {\n const parent = AwaitSupport.parentOf({ node });\n const parentType = AstNode.getType({ node: parent });\n const isWhole =\n (parentType === 'ExpressionStatement' &&\n AstNode.readField({ node: parent, key: 'expression' }) === node) ||\n (parentType === 'VariableDeclarator' &&\n AstNode.readField({ node: parent, key: 'init' }) === node);\n const isReturned =\n (parentType === 'ReturnStatement' || parentType === 'ArrowFunctionExpression') &&\n (AstNode.readField({ node: parent, key: 'argument' }) === node ||\n AstNode.readField({ node: parent, key: 'body' }) === node);\n\n if (isWhole || isReturned) {\n return;\n }\n\n const statement = AwaitSupport.hoistTarget({ awaitNode: node });\n const canFix =\n statement !== undefined &&\n !AwaitSupport.hasEffectBefore({ statement, awaitNode: node, sourceCode });\n\n context.report({\n node,\n messageId: 'inline',\n fix: canFix\n ? (fixer) => {\n const name = AwaitSupport.freshName({ node, base: 'result', sourceCode });\n const [start, end] = AwaitSupport.replaceRangeOf({ awaitNode: node, sourceCode });\n const indent = AwaitSupport.indentOf({ node: statement, sourceCode });\n\n return [\n fixer.insertTextBefore(\n statement,\n `const ${name} = ${sourceCode.getText(node)};\\n${indent}`,\n ),\n fixer.replaceTextRange([start, end], name),\n ];\n }\n : null,\n });\n },\n };\n },\n };\n\n static build(opts: { severity?: TRuleSeverity } = {}): TRuleSeverity {\n return opts.severity ?? 'error';\n }\n}\n"]}
|
|
@@ -19,16 +19,16 @@ class NoNewErrorRule {
|
|
|
19
19
|
},
|
|
20
20
|
create: (context) => {
|
|
21
21
|
const report = (opts) => context.report({ node: opts.node, messageId: 'newError', data: { form: opts.form } });
|
|
22
|
-
const isErrorIdentifier = (callee) =>
|
|
23
|
-
|
|
22
|
+
const isErrorIdentifier = (callee) => common_1.AstNode.getType({ node: callee }) === 'Identifier' &&
|
|
23
|
+
common_1.AstNode.readField({ node: callee, key: 'name' }) === 'Error';
|
|
24
24
|
return {
|
|
25
25
|
[common_1.AstNodeTypes.NEW_EXPRESSION]: (node) => {
|
|
26
|
-
if (isErrorIdentifier(
|
|
26
|
+
if (isErrorIdentifier(common_1.AstNode.readField({ node, key: 'callee' }))) {
|
|
27
27
|
report({ node, form: 'new Error(...)' });
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
[common_1.AstNodeTypes.CALL_EXPRESSION]: (node) => {
|
|
31
|
-
if (isErrorIdentifier(
|
|
31
|
+
if (isErrorIdentifier(common_1.AstNode.readField({ node, key: 'callee' }))) {
|
|
32
32
|
report({ node, form: 'Error(...)' });
|
|
33
33
|
}
|
|
34
34
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-new-error.rule.js","sourceRoot":"","sources":["../../../src/linters/house/no-new-error.rule.ts"],"names":[],"mappings":";;;AAEA,
|
|
1
|
+
{"version":3,"file":"no-new-error.rule.js","sourceRoot":"","sources":["../../../src/linters/house/no-new-error.rule.ts"],"names":[],"mappings":";;;AAEA,qCAAyE;AAEzE;;;;GAIG;AACH,MAAa,cAAc;aACT,SAAI,GAAG,GAAG,+BAAsB,eAAe,CAAC;aAEhD,SAAI,GAAoB;QACtC,IAAI,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,EAAE;YACV,QAAQ,EAAE;gBACR,QAAQ,EACN,2IAA2I;aAC9I;SACF;QACD,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE;YAClB,MAAM,MAAM,GAAG,CAAC,IAAuC,EAAE,EAAE,CACzD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAExF,MAAM,iBAAiB,GAAG,CAAC,MAAe,EAAE,EAAE,CAC5C,gBAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,YAAY;gBAClD,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC;YAE/D,OAAO;gBACL,CAAC,qBAAY,CAAC,cAAc,CAAC,EAAE,CAAC,IAAe,EAAE,EAAE;oBACjD,IAAI,iBAAiB,CAAC,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC;wBAClE,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;oBAC3C,CAAC;gBACH,CAAC;gBACD,CAAC,qBAAY,CAAC,eAAe,CAAC,EAAE,CAAC,IAAe,EAAE,EAAE;oBAClD,IAAI,iBAAiB,CAAC,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC;wBAClE,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;oBACvC,CAAC;gBACH,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC;IAEF,MAAM,CAAC,KAAK,CAAC,OAAqC,EAAE;QAClD,OAAO,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC;IAClC,CAAC;;AArCH,wCAsCC","sourcesContent":["import type { Rule } from 'eslint';\nimport type { TRuleSeverity } from '../rules/common';\nimport { AstNode, AstNodeTypes, HOUSE_PLUGIN_NAMESPACE } from './common';\n\n/**\n * `venizia/no-new-error`. An error is thrown through `getError({ message })` (or an `ApplicationError`),\n * never `new Error(...)` or `Error(...)`: only an ApplicationError carries the status, the message code and\n * args, and the `extra` every IGNIS layer reads.\n */\nexport class NoNewErrorRule {\n static readonly NAME = `${HOUSE_PLUGIN_NAMESPACE}/no-new-error`;\n\n static readonly RULE: Rule.RuleModule = {\n meta: {\n type: 'problem',\n schema: [],\n messages: {\n newError:\n 'Throw through getError({ message }) (or an ApplicationError), not `{{form}}`: only an ApplicationError carries the status, code and args.',\n },\n },\n create: (context) => {\n const report = (opts: { node: Rule.Node; form: string }) =>\n context.report({ node: opts.node, messageId: 'newError', data: { form: opts.form } });\n\n const isErrorIdentifier = (callee: unknown) =>\n AstNode.getType({ node: callee }) === 'Identifier' &&\n AstNode.readField({ node: callee, key: 'name' }) === 'Error';\n\n return {\n [AstNodeTypes.NEW_EXPRESSION]: (node: Rule.Node) => {\n if (isErrorIdentifier(AstNode.readField({ node, key: 'callee' }))) {\n report({ node, form: 'new Error(...)' });\n }\n },\n [AstNodeTypes.CALL_EXPRESSION]: (node: Rule.Node) => {\n if (isErrorIdentifier(AstNode.readField({ node, key: 'callee' }))) {\n report({ node, form: 'Error(...)' });\n }\n },\n };\n },\n };\n\n static build(opts: { severity?: TRuleSeverity } = {}): TRuleSeverity {\n return opts.severity ?? 'error';\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
import type { TRuleSeverity } from '../rules/common';
|
|
3
|
+
/**
|
|
4
|
+
* `venizia/no-return-await`. A function returns the promise (`return f()`), never `return await f()`. Where the
|
|
5
|
+
* `await` matters (inside `try`, `catch` or `finally`) it is written on its own line:
|
|
6
|
+
* `const result = await f(); return result;`. An arrow whose body is an `await` is the same: `() => f()`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class NoReturnAwaitRule {
|
|
9
|
+
static readonly NAME = "venizia/no-return-await";
|
|
10
|
+
static readonly RULE: Rule.RuleModule;
|
|
11
|
+
static build(opts?: {
|
|
12
|
+
severity?: TRuleSeverity;
|
|
13
|
+
}): TRuleSeverity;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=no-return-await.rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-return-await.rule.d.ts","sourceRoot":"","sources":["../../../src/linters/house/no-return-await.rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAyBrD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,QAAQ,CAAC,IAAI,6BAA+C;IAEnE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CA0EnC;IAEF,MAAM,CAAC,KAAK,CAAC,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,aAAa,CAAA;KAAO,GAAG,aAAa;CAGrE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoReturnAwaitRule = void 0;
|
|
4
|
+
const await_support_1 = require("./await-support");
|
|
5
|
+
const common_1 = require("./common");
|
|
6
|
+
const FUNCTION_TYPES = new Set([
|
|
7
|
+
'FunctionDeclaration',
|
|
8
|
+
'FunctionExpression',
|
|
9
|
+
'ArrowFunctionExpression',
|
|
10
|
+
]);
|
|
11
|
+
const STATEMENT_LISTS = new Set(['BlockStatement', 'Program', 'StaticBlock', 'SwitchCase']);
|
|
12
|
+
/** `node` sits inside a `try`, `catch` or `finally` of its own function. */
|
|
13
|
+
const isInTry = (node) => {
|
|
14
|
+
let current = await_support_1.AwaitSupport.parentOf({ node });
|
|
15
|
+
while (current && !FUNCTION_TYPES.has(common_1.AstNode.getType({ node: current }) ?? '')) {
|
|
16
|
+
if (common_1.AstNode.getType({ node: current }) === 'TryStatement') {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
current = await_support_1.AwaitSupport.parentOf({ node: current });
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* `venizia/no-return-await`. A function returns the promise (`return f()`), never `return await f()`. Where the
|
|
25
|
+
* `await` matters (inside `try`, `catch` or `finally`) it is written on its own line:
|
|
26
|
+
* `const result = await f(); return result;`. An arrow whose body is an `await` is the same: `() => f()`.
|
|
27
|
+
*/
|
|
28
|
+
class NoReturnAwaitRule {
|
|
29
|
+
static { this.NAME = `${common_1.HOUSE_PLUGIN_NAMESPACE}/no-return-await`; }
|
|
30
|
+
static { this.RULE = {
|
|
31
|
+
meta: {
|
|
32
|
+
type: 'suggestion',
|
|
33
|
+
fixable: 'code',
|
|
34
|
+
schema: [],
|
|
35
|
+
messages: {
|
|
36
|
+
returnAwait: 'Return the promise (`return f()`), not `return await f()`; where the await matters (inside try, catch or finally) write `const result = await f(); return result;`.',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
create: (context) => {
|
|
40
|
+
const { sourceCode } = context;
|
|
41
|
+
const reportAwait = (opts) => {
|
|
42
|
+
const argument = common_1.AstNode.readField({ node: opts.awaitNode, key: 'argument' });
|
|
43
|
+
const argumentText = await_support_1.AwaitSupport.isNode(argument) &&
|
|
44
|
+
common_1.AstNode.getType({ node: argument }) !== 'SequenceExpression'
|
|
45
|
+
? sourceCode.getText(argument)
|
|
46
|
+
: undefined;
|
|
47
|
+
context.report({
|
|
48
|
+
node: opts.reported,
|
|
49
|
+
messageId: 'returnAwait',
|
|
50
|
+
fix: argumentText === undefined
|
|
51
|
+
? null
|
|
52
|
+
: (fixer) => {
|
|
53
|
+
if (!opts.statement || !isInTry(opts.statement)) {
|
|
54
|
+
return fixer.replaceText(opts.awaitNode, argumentText);
|
|
55
|
+
}
|
|
56
|
+
const outer = await_support_1.AwaitSupport.parentOf({ node: opts.statement });
|
|
57
|
+
if (!STATEMENT_LISTS.has(common_1.AstNode.getType({ node: outer }) ?? '')) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const name = await_support_1.AwaitSupport.freshName({
|
|
61
|
+
node: opts.statement,
|
|
62
|
+
base: 'result',
|
|
63
|
+
sourceCode,
|
|
64
|
+
});
|
|
65
|
+
const indent = await_support_1.AwaitSupport.indentOf({ node: opts.statement, sourceCode });
|
|
66
|
+
return fixer.replaceText(opts.statement, `const ${name} = await ${argumentText};\n${indent}return ${name};`);
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
[common_1.AstNodeTypes.RETURN_STATEMENT]: (node) => {
|
|
72
|
+
const argument = common_1.AstNode.readField({ node, key: 'argument' });
|
|
73
|
+
if (await_support_1.AwaitSupport.isNode(argument) &&
|
|
74
|
+
common_1.AstNode.getType({ node: argument }) === 'AwaitExpression') {
|
|
75
|
+
reportAwait({ reported: node, awaitNode: argument, statement: node });
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[common_1.AstNodeTypes.ARROW_FUNCTION_EXPRESSION]: (node) => {
|
|
79
|
+
const body = common_1.AstNode.readField({ node, key: 'body' });
|
|
80
|
+
if (await_support_1.AwaitSupport.isNode(body) && common_1.AstNode.getType({ node: body }) === 'AwaitExpression') {
|
|
81
|
+
reportAwait({ reported: node, awaitNode: body });
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
}; }
|
|
87
|
+
static build(opts = {}) {
|
|
88
|
+
return opts.severity ?? 'error';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.NoReturnAwaitRule = NoReturnAwaitRule;
|
|
92
|
+
//# sourceMappingURL=no-return-await.rule.js.map
|