@venizia/dev-configs 0.4.7-0 → 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 -70
- 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 +99 -98
- 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 -70
- 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 +29 -8
- package/dist/cli/codemod/async-style.d.ts.map +1 -1
- package/dist/cli/codemod/async-style.js +102 -20
- package/dist/cli/codemod/async-style.js.map +1 -1
- package/dist/cli/codemod/index.d.ts.map +1 -1
- package/dist/cli/codemod/index.js +6 -2
- 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 -1
- 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 +17 -3
- package/dist/linters/house/await-support.d.ts.map +1 -1
- package/dist/linters/house/await-support.js +52 -23
- package/dist/linters/house/await-support.js.map +1 -1
- package/dist/linters/house/common.d.ts +13 -9
- package/dist/linters/house/common.d.ts.map +1 -1
- package/dist/linters/house/common.js +18 -15
- package/dist/linters/house/common.js.map +1 -1
- package/dist/linters/house/configs.d.ts +5 -2
- package/dist/linters/house/configs.d.ts.map +1 -1
- package/dist/linters/house/configs.js +11 -2
- 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.js +3 -3
- package/dist/linters/house/effect-cleanup-consistency.rule.js.map +1 -1
- package/dist/linters/house/hook-support.d.ts.map +1 -1
- package/dist/linters/house/hook-support.js +17 -14
- package/dist/linters/house/hook-support.js.map +1 -1
- package/dist/linters/house/index.d.ts +3 -0
- package/dist/linters/house/index.d.ts.map +1 -1
- package/dist/linters/house/index.js +3 -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.js +1 -1
- package/dist/linters/house/no-async-hook-callback.rule.js.map +1 -1
- 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.map +1 -1
- package/dist/linters/house/no-inline-await.rule.js +6 -12
- package/dist/linters/house/no-inline-await.rule.js.map +1 -1
- 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.map +1 -1
- package/dist/linters/house/no-return-await.rule.js +11 -11
- package/dist/linters/house/no-return-await.rule.js.map +1 -1
- 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 +4 -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
|
@@ -7,14 +7,16 @@ const CALL_TYPES = new Set(['CallExpression', 'NewExpression']);
|
|
|
7
7
|
const isArrayContaining = (opts) => Array.isArray(opts.list) && opts.list.includes(opts.item);
|
|
8
8
|
/** `useX(...)`: a hook's callback is ours, so it counts. */
|
|
9
9
|
const isHookCall = (call) => {
|
|
10
|
-
const callee =
|
|
11
|
-
const name =
|
|
12
|
-
return (
|
|
10
|
+
const callee = common_1.AstNode.readField({ node: call, key: 'callee' });
|
|
11
|
+
const name = common_1.AstNode.readField({ node: callee, key: 'name' });
|
|
12
|
+
return (common_1.AstNode.getType({ node: callee }) === 'Identifier' &&
|
|
13
|
+
typeof name === 'string' &&
|
|
14
|
+
HOOK_NAME.test(name));
|
|
13
15
|
};
|
|
14
16
|
/** `node` is an argument of a call or `new` that is not a hook. */
|
|
15
|
-
const isLibraryArgument = (opts) => CALL_TYPES.has(
|
|
17
|
+
const isLibraryArgument = (opts) => CALL_TYPES.has(common_1.AstNode.getType({ node: opts.parent }) ?? '') &&
|
|
16
18
|
isArrayContaining({
|
|
17
|
-
list:
|
|
19
|
+
list: common_1.AstNode.readField({ node: opts.parent, key: 'arguments' }),
|
|
18
20
|
item: opts.node,
|
|
19
21
|
}) &&
|
|
20
22
|
!isHookCall(opts.parent);
|
|
@@ -25,17 +27,20 @@ const isLibraryArgument = (opts) => CALL_TYPES.has((0, common_1.nodeType)({ node
|
|
|
25
27
|
const hasDeclaredShape = (object) => {
|
|
26
28
|
let current = object;
|
|
27
29
|
while (current) {
|
|
28
|
-
const parent =
|
|
29
|
-
const parentType =
|
|
30
|
+
const parent = common_1.AstNode.readField({ node: current, key: 'parent' });
|
|
31
|
+
const parentType = common_1.AstNode.getType({ node: parent });
|
|
30
32
|
const isTypedVariable = parentType === 'VariableDeclarator' &&
|
|
31
|
-
Boolean(
|
|
33
|
+
Boolean(common_1.AstNode.readField({
|
|
34
|
+
node: common_1.AstNode.readField({ node: parent, key: 'id' }),
|
|
35
|
+
key: 'typeAnnotation',
|
|
36
|
+
}));
|
|
32
37
|
if (isTypedVariable ||
|
|
33
38
|
parentType === 'TSSatisfiesExpression' ||
|
|
34
39
|
isLibraryArgument({ node: current, parent })) {
|
|
35
40
|
return true;
|
|
36
41
|
}
|
|
37
|
-
const isNestedValue = parentType === 'Property' &&
|
|
38
|
-
current = isNestedValue ?
|
|
42
|
+
const isNestedValue = parentType === 'Property' && common_1.AstNode.readField({ node: parent, key: 'value' }) === current;
|
|
43
|
+
current = isNestedValue ? common_1.AstNode.readField({ node: parent, key: 'parent' }) : undefined;
|
|
39
44
|
}
|
|
40
45
|
return false;
|
|
41
46
|
};
|
|
@@ -61,39 +66,43 @@ class MaxParamsOwnRule {
|
|
|
61
66
|
const max = typeof context.options[0] === 'number' ? context.options[0] : MaxParamsOwnRule.DEFAULT_MAX;
|
|
62
67
|
const isCallback = (node) => {
|
|
63
68
|
const parent = node.parent;
|
|
64
|
-
const isJsxProp =
|
|
65
|
-
|
|
69
|
+
const isJsxProp = common_1.AstNode.getType({ node: parent }) === 'JSXExpressionContainer' &&
|
|
70
|
+
common_1.AstNode.getType({ node: common_1.AstNode.readField({ node: parent, key: 'parent' }) }) ===
|
|
71
|
+
'JSXAttribute';
|
|
66
72
|
return isJsxProp || isLibraryArgument({ node, parent });
|
|
67
73
|
};
|
|
68
74
|
const isInjectedConstructor = (opts) => {
|
|
69
75
|
const parent = opts.node.parent;
|
|
70
76
|
const isDecorated = (param) => {
|
|
71
|
-
const own =
|
|
72
|
-
const property =
|
|
73
|
-
node:
|
|
77
|
+
const own = common_1.AstNode.readField({ node: param, key: 'decorators' });
|
|
78
|
+
const property = common_1.AstNode.readField({
|
|
79
|
+
node: common_1.AstNode.readField({ node: param, key: 'parameter' }),
|
|
74
80
|
key: 'decorators',
|
|
75
81
|
});
|
|
76
82
|
return ((Array.isArray(own) && own.length > 0) ||
|
|
77
83
|
(Array.isArray(property) && property.length > 0));
|
|
78
84
|
};
|
|
79
|
-
return (
|
|
80
|
-
|
|
85
|
+
return (common_1.AstNode.getType({ node: parent }) === 'MethodDefinition' &&
|
|
86
|
+
common_1.AstNode.readField({ node: parent, key: 'kind' }) === 'constructor' &&
|
|
81
87
|
opts.params.every(isDecorated));
|
|
82
88
|
};
|
|
83
89
|
const isTypedByContext = (node) => {
|
|
84
90
|
const parent = node.parent;
|
|
85
|
-
const parentType =
|
|
91
|
+
const parentType = common_1.AstNode.getType({ node: parent });
|
|
86
92
|
const isTypedInit = parentType === 'VariableDeclarator' &&
|
|
87
|
-
|
|
88
|
-
Boolean(
|
|
89
|
-
|
|
93
|
+
common_1.AstNode.readField({ node: parent, key: 'init' }) === node &&
|
|
94
|
+
Boolean(common_1.AstNode.readField({
|
|
95
|
+
node: common_1.AstNode.readField({ node: parent, key: 'id' }),
|
|
96
|
+
key: 'typeAnnotation',
|
|
97
|
+
}));
|
|
98
|
+
const grandParent = common_1.AstNode.readField({ node: parent, key: 'parent' });
|
|
90
99
|
const isMember = parentType === 'Property' &&
|
|
91
|
-
|
|
92
|
-
|
|
100
|
+
common_1.AstNode.readField({ node: parent, key: 'value' }) === node &&
|
|
101
|
+
common_1.AstNode.getType({ node: grandParent }) === 'ObjectExpression';
|
|
93
102
|
return isTypedInit || (isMember && hasDeclaredShape(grandParent));
|
|
94
103
|
};
|
|
95
104
|
const check = (node) => {
|
|
96
|
-
const params =
|
|
105
|
+
const params = common_1.AstNode.readField({ node, key: 'params' });
|
|
97
106
|
if (!Array.isArray(params) || params.length <= max) {
|
|
98
107
|
return;
|
|
99
108
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"max-params-own.rule.js","sourceRoot":"","sources":["../../../src/linters/house/max-params-own.rule.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Linter, Rule } from 'eslint';\nimport type { TRuleSeverity } from '../rules/common';\nimport { AstNodeTypes, HOUSE_PLUGIN_NAMESPACE, nodeType, readField } from './common';\n\nconst HOOK_NAME = /^use[A-Z]/;\nconst CALL_TYPES = new Set(['CallExpression', 'NewExpression']);\n\nconst isArrayContaining = (opts: { list: unknown; item: unknown }) =>\n Array.isArray(opts.list) && opts.list.includes(opts.item);\n\n/** `useX(...)`: a hook's callback is ours, so it counts. */\nconst isHookCall = (call: unknown) => {\n const callee = readField({ node: call, key: 'callee' });\n const name = readField({ node: callee, key: 'name' });\n return (\n nodeType({ node: callee }) === 'Identifier' && typeof name === 'string' && HOOK_NAME.test(name)\n );\n};\n\n/** `node` is an argument of a call or `new` that is not a hook. */\nconst isLibraryArgument = (opts: { node: unknown; parent: unknown }) =>\n CALL_TYPES.has(nodeType({ node: opts.parent }) ?? '') &&\n isArrayContaining({\n list: readField({ node: opts.parent, key: 'arguments' }),\n item: opts.node,\n }) &&\n !isHookCall(opts.parent);\n\n/**\n * An object literal whose shape something else declares: a typed variable, `satisfies`, or an argument\n * of a non-hook call (a library option bag). A nested literal inherits it.\n */\nconst hasDeclaredShape = (object: unknown): boolean => {\n let current = object;\n\n while (current) {\n const parent = readField({ node: current, key: 'parent' });\n const parentType = nodeType({ node: parent });\n const isTypedVariable =\n parentType === 'VariableDeclarator' &&\n Boolean(readField({ node: readField({ node: parent, key: 'id' }), key: 'typeAnnotation' }));\n\n if (\n isTypedVariable ||\n parentType === 'TSSatisfiesExpression' ||\n isLibraryArgument({ node: current, parent })\n ) {\n return true;\n }\n\n const isNestedValue =\n parentType === 'Property' && readField({ node: parent, key: 'value' }) === current;\n current = isNestedValue ? readField({ node: parent, key: 'parent' }) : undefined;\n }\n\n return false;\n};\n\n/**\n * `venizia/max-params-own`. A function we write takes at most `max` (default 5) positional parameters;\n * beyond that it takes one options object. A signature that belongs to someone else is not counted: an\n * inline callback handed to a call (map, reduce, sort, a library handler) or to a JSX prop, a function a\n * declared type fixes (`const x: TFn = (...)`, a member of a typed or `satisfies` object), and a\n * constructor whose every parameter is injected (`@inject`). A hook's callback (`useCallback(...)`) is ours.\n */\nexport class MaxParamsOwnRule {\n static readonly NAME = `${HOUSE_PLUGIN_NAMESPACE}/max-params-own`;\n\n static readonly DEFAULT_MAX = 5;\n\n static readonly RULE: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n schema: [{ type: 'integer', minimum: 0 }],\n messages: {\n tooMany:\n 'A function we write takes up to {{max}} positional parameters, not {{count}}: take one options object.',\n },\n },\n create: (context) => {\n const max =\n typeof context.options[0] === 'number' ? context.options[0] : MaxParamsOwnRule.DEFAULT_MAX;\n\n const isCallback = (node: Rule.Node) => {\n const parent = node.parent;\n const isJsxProp =\n nodeType({ node: parent }) === 'JSXExpressionContainer' &&\n nodeType({ node: readField({ node: parent, key: 'parent' }) }) === 'JSXAttribute';\n return isJsxProp || isLibraryArgument({ node, parent });\n };\n\n const isInjectedConstructor = (opts: { node: Rule.Node; params: unknown[] }) => {\n const parent = opts.node.parent;\n const isDecorated = (param: unknown) => {\n const own = readField({ node: param, key: 'decorators' });\n const property = readField({\n node: readField({ node: param, key: 'parameter' }),\n key: 'decorators',\n });\n return (\n (Array.isArray(own) && own.length > 0) ||\n (Array.isArray(property) && property.length > 0)\n );\n };\n\n return (\n nodeType({ node: parent }) === 'MethodDefinition' &&\n readField({ node: parent, key: 'kind' }) === 'constructor' &&\n opts.params.every(isDecorated)\n );\n };\n\n const isTypedByContext = (node: Rule.Node) => {\n const parent = node.parent;\n const parentType = nodeType({ node: parent });\n const isTypedInit =\n parentType === 'VariableDeclarator' &&\n readField({ node: parent, key: 'init' }) === node &&\n Boolean(\n readField({ node: readField({ node: parent, key: 'id' }), key: 'typeAnnotation' }),\n );\n const grandParent = readField({ node: parent, key: 'parent' });\n const isMember =\n parentType === 'Property' &&\n readField({ node: parent, key: 'value' }) === node &&\n nodeType({ node: grandParent }) === 'ObjectExpression';\n\n return isTypedInit || (isMember && hasDeclaredShape(grandParent));\n };\n\n const check = (node: Rule.Node) => {\n const params = readField({ node, key: 'params' });\n if (!Array.isArray(params) || params.length <= max) {\n return;\n }\n\n if (isCallback(node) || isInjectedConstructor({ node, params }) || isTypedByContext(node)) {\n return;\n }\n\n context.report({\n node,\n messageId: 'tooMany',\n data: { count: String(params.length), max: String(max) },\n });\n };\n\n return {\n [AstNodeTypes.FUNCTION_DECLARATION]: check,\n [AstNodeTypes.FUNCTION_EXPRESSION]: check,\n [AstNodeTypes.ARROW_FUNCTION_EXPRESSION]: check,\n };\n },\n };\n\n static build(opts: { severity?: TRuleSeverity; max?: number } = {}): Linter.RuleEntry {\n const { severity = 'error', max = MaxParamsOwnRule.DEFAULT_MAX } = opts;\n return [severity, max];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"max-params-own.rule.js","sourceRoot":"","sources":["../../../src/linters/house/max-params-own.rule.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Linter, Rule } from 'eslint';\nimport type { TRuleSeverity } from '../rules/common';\nimport { AstNode, AstNodeTypes, HOUSE_PLUGIN_NAMESPACE } from './common';\n\nconst HOOK_NAME = /^use[A-Z]/;\nconst CALL_TYPES = new Set(['CallExpression', 'NewExpression']);\n\nconst isArrayContaining = (opts: { list: unknown; item: unknown }) =>\n Array.isArray(opts.list) && opts.list.includes(opts.item);\n\n/** `useX(...)`: a hook's callback is ours, so it counts. */\nconst isHookCall = (call: unknown) => {\n const callee = AstNode.readField({ node: call, key: 'callee' });\n const name = AstNode.readField({ node: callee, key: 'name' });\n return (\n AstNode.getType({ node: callee }) === 'Identifier' &&\n typeof name === 'string' &&\n HOOK_NAME.test(name)\n );\n};\n\n/** `node` is an argument of a call or `new` that is not a hook. */\nconst isLibraryArgument = (opts: { node: unknown; parent: unknown }) =>\n CALL_TYPES.has(AstNode.getType({ node: opts.parent }) ?? '') &&\n isArrayContaining({\n list: AstNode.readField({ node: opts.parent, key: 'arguments' }),\n item: opts.node,\n }) &&\n !isHookCall(opts.parent);\n\n/**\n * An object literal whose shape something else declares: a typed variable, `satisfies`, or an argument\n * of a non-hook call (a library option bag). A nested literal inherits it.\n */\nconst hasDeclaredShape = (object: unknown): boolean => {\n let current = object;\n\n while (current) {\n const parent = AstNode.readField({ node: current, key: 'parent' });\n const parentType = AstNode.getType({ node: parent });\n const isTypedVariable =\n parentType === 'VariableDeclarator' &&\n Boolean(\n AstNode.readField({\n node: AstNode.readField({ node: parent, key: 'id' }),\n key: 'typeAnnotation',\n }),\n );\n\n if (\n isTypedVariable ||\n parentType === 'TSSatisfiesExpression' ||\n isLibraryArgument({ node: current, parent })\n ) {\n return true;\n }\n\n const isNestedValue =\n parentType === 'Property' && AstNode.readField({ node: parent, key: 'value' }) === current;\n current = isNestedValue ? AstNode.readField({ node: parent, key: 'parent' }) : undefined;\n }\n\n return false;\n};\n\n/**\n * `venizia/max-params-own`. A function we write takes at most `max` (default 5) positional parameters;\n * beyond that it takes one options object. A signature that belongs to someone else is not counted: an\n * inline callback handed to a call (map, reduce, sort, a library handler) or to a JSX prop, a function a\n * declared type fixes (`const x: TFn = (...)`, a member of a typed or `satisfies` object), and a\n * constructor whose every parameter is injected (`@inject`). A hook's callback (`useCallback(...)`) is ours.\n */\nexport class MaxParamsOwnRule {\n static readonly NAME = `${HOUSE_PLUGIN_NAMESPACE}/max-params-own`;\n\n static readonly DEFAULT_MAX = 5;\n\n static readonly RULE: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n schema: [{ type: 'integer', minimum: 0 }],\n messages: {\n tooMany:\n 'A function we write takes up to {{max}} positional parameters, not {{count}}: take one options object.',\n },\n },\n create: (context) => {\n const max =\n typeof context.options[0] === 'number' ? context.options[0] : MaxParamsOwnRule.DEFAULT_MAX;\n\n const isCallback = (node: Rule.Node) => {\n const parent = node.parent;\n const isJsxProp =\n AstNode.getType({ node: parent }) === 'JSXExpressionContainer' &&\n AstNode.getType({ node: AstNode.readField({ node: parent, key: 'parent' }) }) ===\n 'JSXAttribute';\n return isJsxProp || isLibraryArgument({ node, parent });\n };\n\n const isInjectedConstructor = (opts: { node: Rule.Node; params: unknown[] }) => {\n const parent = opts.node.parent;\n const isDecorated = (param: unknown) => {\n const own = AstNode.readField({ node: param, key: 'decorators' });\n const property = AstNode.readField({\n node: AstNode.readField({ node: param, key: 'parameter' }),\n key: 'decorators',\n });\n return (\n (Array.isArray(own) && own.length > 0) ||\n (Array.isArray(property) && property.length > 0)\n );\n };\n\n return (\n AstNode.getType({ node: parent }) === 'MethodDefinition' &&\n AstNode.readField({ node: parent, key: 'kind' }) === 'constructor' &&\n opts.params.every(isDecorated)\n );\n };\n\n const isTypedByContext = (node: Rule.Node) => {\n const parent = node.parent;\n const parentType = AstNode.getType({ node: parent });\n const isTypedInit =\n parentType === 'VariableDeclarator' &&\n AstNode.readField({ node: parent, key: 'init' }) === node &&\n Boolean(\n AstNode.readField({\n node: AstNode.readField({ node: parent, key: 'id' }),\n key: 'typeAnnotation',\n }),\n );\n const grandParent = AstNode.readField({ node: parent, key: 'parent' });\n const isMember =\n parentType === 'Property' &&\n AstNode.readField({ node: parent, key: 'value' }) === node &&\n AstNode.getType({ node: grandParent }) === 'ObjectExpression';\n\n return isTypedInit || (isMember && hasDeclaredShape(grandParent));\n };\n\n const check = (node: Rule.Node) => {\n const params = AstNode.readField({ node, key: 'params' });\n if (!Array.isArray(params) || params.length <= max) {\n return;\n }\n\n if (isCallback(node) || isInjectedConstructor({ node, params }) || isTypedByContext(node)) {\n return;\n }\n\n context.report({\n node,\n messageId: 'tooMany',\n data: { count: String(params.length), max: String(max) },\n });\n };\n\n return {\n [AstNodeTypes.FUNCTION_DECLARATION]: check,\n [AstNodeTypes.FUNCTION_EXPRESSION]: check,\n [AstNodeTypes.ARROW_FUNCTION_EXPRESSION]: check,\n };\n },\n };\n\n static build(opts: { severity?: TRuleSeverity; max?: number } = {}): Linter.RuleEntry {\n const { severity = 'error', max = MaxParamsOwnRule.DEFAULT_MAX } = opts;\n return [severity, max];\n }\n}\n"]}
|
|
@@ -28,7 +28,7 @@ class NoAsyncHookCallbackRule {
|
|
|
28
28
|
if (hook === undefined || !hook_support_1.HookSupport.isHook({ name: hook })) {
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
-
const args =
|
|
31
|
+
const args = common_1.AstNode.readField({ node, key: 'arguments' });
|
|
32
32
|
if (!Array.isArray(args)) {
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-async-hook-callback.rule.js","sourceRoot":"","sources":["../../../src/linters/house/no-async-hook-callback.rule.ts"],"names":[],"mappings":";;;AAEA,mDAA+C;AAC/C,
|
|
1
|
+
{"version":3,"file":"no-async-hook-callback.rule.js","sourceRoot":"","sources":["../../../src/linters/house/no-async-hook-callback.rule.ts"],"names":[],"mappings":";;;AAEA,mDAA+C;AAC/C,qCAAyE;AACzE,iDAA6C;AAE7C;;;;;;;GAOG;AACH,MAAa,uBAAuB;aAClB,SAAI,GAAG,GAAG,+BAAsB,yBAAyB,CAAC;aAE1D,SAAI,GAAoB;QACtC,IAAI,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,EAAE;YACV,QAAQ,EAAE;gBACR,aAAa,EACX,gIAAgI;aACnI;SACF;QACD,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACpB,CAAC,qBAAY,CAAC,eAAe,CAAC,EAAE,CAAC,IAAe,EAAE,EAAE;gBAClD,MAAM,IAAI,GAAG,0BAAW,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,0BAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;oBAC9D,OAAO;gBACT,CAAC;gBAED,MAAM,IAAI,GAAG,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;gBAC3D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;oBACzB,OAAO;gBACT,CAAC;gBAED,KAAK,MAAM,QAAQ,IAAI,IAAI,EAAE,CAAC;oBAC5B,KAAK,MAAM,KAAK,IAAI,0BAAW,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;wBACrE,IAAI,4BAAY,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;4BAC/B,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;wBAC9E,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;KACH,CAAC;IAEF,MAAM,CAAC,KAAK,CAAC,OAAqC,EAAE;QAClD,OAAO,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC;IAClC,CAAC;;AArCH,0DAsCC","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';\nimport { HookSupport } from './hook-support';\n\n/**\n * `venizia/no-async-hook-callback`. A hook never gets an async function: `useEffect(async () => {})` makes the\n * callback return a promise where React expects a cleanup function or nothing. Pass a sync callback and handle\n * the promise inside it with `.then`, `.catch` and `.finally`. Every argument of a `use[A-Z]...` call is checked,\n * `React.useX` too: an async function as the argument, as the value of a property of an object literal\n * (`useMutation({ mutationFn: async () => {} })`) and as an element of an array literal. A sync callback that\n * builds an async function inside is not an argument and is left alone.\n */\nexport class NoAsyncHookCallbackRule {\n static readonly NAME = `${HOUSE_PLUGIN_NAMESPACE}/no-async-hook-callback`;\n\n static readonly RULE: Rule.RuleModule = {\n meta: {\n type: 'problem',\n schema: [],\n messages: {\n asyncCallback:\n 'Do not pass an async function to `{{hook}}`; pass a sync callback and handle the promise inside it with .then/.catch/.finally.',\n },\n },\n create: (context) => ({\n [AstNodeTypes.CALL_EXPRESSION]: (node: Rule.Node) => {\n const hook = HookSupport.calleeName({ call: node });\n if (hook === undefined || !HookSupport.isHook({ name: hook })) {\n return;\n }\n\n const args = AstNode.readField({ node, key: 'arguments' });\n if (!Array.isArray(args)) {\n return;\n }\n\n for (const argument of args) {\n for (const found of HookSupport.asyncFunctionsIn({ node: argument })) {\n if (AwaitSupport.isNode(found)) {\n context.report({ node: found, messageId: 'asyncCallback', data: { hook } });\n }\n }\n }\n },\n }),\n };\n\n static build(opts: { severity?: TRuleSeverity } = {}): TRuleSeverity {\n return opts.severity ?? 'error';\n }\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
import type { TRuleSeverity } from '../rules/common';
|
|
3
|
+
/**
|
|
4
|
+
* `venizia/no-await-promise-chain`. One expression uses one async style: `await` (in a try/catch when it can
|
|
5
|
+
* fail), or a promise chain that is not awaited. An `await` whose operand - after parentheses, `!`, `as`,
|
|
6
|
+
* `satisfies` and an optional chain - is a call of `.then`, `.catch` or `.finally` (`?.then(`, `['then'](`, a
|
|
7
|
+
* thenable such as a query builder) is reported once, however long the chain. A chain that is returned or stands
|
|
8
|
+
* as a statement is not awaited and is not reported, nor is `await Promise.all(xs.map(f))`, `await new Promise(...)`,
|
|
9
|
+
* `await x.then` (no call), or an `await` inside a callback of a chain.
|
|
10
|
+
*
|
|
11
|
+
* The fix is made for one shape only, `const|let x = await f().catch(() => <value>)` (see `planFix` for every
|
|
12
|
+
* 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
|
|
13
|
+
* `let`, a type annotation stays, the catch variable takes the handler's parameter name (`error` without one,
|
|
14
|
+
* renamed when the name is taken), and is typed `any` when the value reads it, as the handler's parameter was.
|
|
15
|
+
* The try also catches a synchronous throw of `f()`, which the chain let through. Everything else is reported.
|
|
16
|
+
*/
|
|
17
|
+
export declare class NoAwaitPromiseChainRule {
|
|
18
|
+
static readonly NAME = "venizia/no-await-promise-chain";
|
|
19
|
+
static readonly RULE: Rule.RuleModule;
|
|
20
|
+
static build(opts?: {
|
|
21
|
+
severity?: TRuleSeverity;
|
|
22
|
+
}): TRuleSeverity;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=no-await-promise-chain.rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-await-promise-chain.rule.d.ts","sourceRoot":"","sources":["../../../src/linters/house/no-await-promise-chain.rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,QAAQ,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAiTrD;;;;;;;;;;;;;GAaG;AACH,qBAAa,uBAAuB;IAClC,MAAM,CAAC,QAAQ,CAAC,IAAI,oCAAsD;IAE1E,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAsDnC;IAEF,MAAM,CAAC,KAAK,CAAC,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,aAAa,CAAA;KAAO,GAAG,aAAa;CAGrE"}
|
|
@@ -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"]}
|
|
@@ -1 +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,
|
|
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"}
|