@venizia/dev-configs 0.4.8-2 → 0.4.8-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/cli/codemod/async-style.d.ts +2 -2
- package/dist/cli/codemod/async-style.js +4 -2
- package/dist/cli/codemod/async-style.js.map +1 -1
- package/dist/cli/codemod/namespace-functions/rewriter.js +1 -1
- package/dist/cli/codemod/namespace-functions/rewriter.js.map +1 -1
- package/dist/linters/eslint.js +6 -1
- package/dist/linters/eslint.js.map +1 -1
- package/dist/linters/house/async-rules.d.ts +1 -1
- package/dist/linters/house/async-rules.js +3 -1
- package/dist/linters/house/async-rules.js.map +1 -1
- package/dist/linters/house/await-support.js +45 -38
- package/dist/linters/house/await-support.js.map +1 -1
- package/dist/linters/house/common.d.ts +43 -0
- package/dist/linters/house/common.js +43 -0
- package/dist/linters/house/common.js.map +1 -1
- package/dist/linters/house/const-class-values.rule.js +9 -8
- package/dist/linters/house/const-class-values.rule.js.map +1 -1
- package/dist/linters/house/hook-support.js +11 -7
- package/dist/linters/house/hook-support.js.map +1 -1
- package/dist/linters/house/index.d.ts +1 -0
- package/dist/linters/house/index.js +1 -0
- package/dist/linters/house/index.js.map +1 -1
- package/dist/linters/house/max-params-own.rule.js +12 -11
- package/dist/linters/house/max-params-own.rule.js.map +1 -1
- package/dist/linters/house/no-await-new-chain.rule.d.ts +37 -0
- package/dist/linters/house/no-await-new-chain.rule.js +179 -0
- package/dist/linters/house/no-await-new-chain.rule.js.map +1 -0
- package/dist/linters/house/no-await-promise-chain.rule.js +23 -22
- package/dist/linters/house/no-await-promise-chain.rule.js.map +1 -1
- package/dist/linters/house/no-inline-await.rule.js +4 -3
- package/dist/linters/house/no-inline-await.rule.js.map +1 -1
- package/dist/linters/house/no-leaked-render.rule.js +3 -3
- package/dist/linters/house/no-leaked-render.rule.js.map +1 -1
- package/dist/linters/house/no-new-error.rule.js +1 -1
- package/dist/linters/house/no-new-error.rule.js.map +1 -1
- package/dist/linters/house/no-return-await.rule.js +5 -4
- package/dist/linters/house/no-return-await.rule.js.map +1 -1
- package/dist/linters/house/no-silent-catch.rule.js +4 -3
- package/dist/linters/house/no-silent-catch.rule.js.map +1 -1
- package/dist/linters/house/plugin.js +2 -0
- package/dist/linters/house/plugin.js.map +1 -1
- package/dist/linters/house/promise-chain-support.js +25 -25
- package/dist/linters/house/promise-chain-support.js.map +1 -1
- package/dist/linters/oxlint.d.ts +6 -5
- package/dist/linters/oxlint.js +8 -3
- package/dist/linters/oxlint.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MaxParamsOwnRule = void 0;
|
|
4
4
|
const common_1 = require("./common");
|
|
5
5
|
const HOOK_NAME = /^use[A-Z]/;
|
|
6
|
-
const CALL_TYPES = new Set([
|
|
6
|
+
const CALL_TYPES = new Set([common_1.AstNodeTypes.CALL_EXPRESSION, common_1.AstNodeTypes.NEW_EXPRESSION]);
|
|
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
10
|
const callee = common_1.AstNode.readField({ node: call, key: 'callee' });
|
|
11
11
|
const name = common_1.AstNode.readField({ node: callee, key: 'name' });
|
|
12
|
-
return (common_1.AstNode.getType({ node: callee }) ===
|
|
12
|
+
return (common_1.AstNode.getType({ node: callee }) === common_1.AstNodeTypes.IDENTIFIER &&
|
|
13
13
|
typeof name === 'string' &&
|
|
14
14
|
HOOK_NAME.test(name));
|
|
15
15
|
};
|
|
@@ -29,17 +29,18 @@ const hasDeclaredShape = (object) => {
|
|
|
29
29
|
while (current) {
|
|
30
30
|
const parent = common_1.AstNode.readField({ node: current, key: 'parent' });
|
|
31
31
|
const parentType = common_1.AstNode.getType({ node: parent });
|
|
32
|
-
const isTypedVariable = parentType ===
|
|
32
|
+
const isTypedVariable = parentType === common_1.AstNodeTypes.VARIABLE_DECLARATOR &&
|
|
33
33
|
Boolean(common_1.AstNode.readField({
|
|
34
34
|
node: common_1.AstNode.readField({ node: parent, key: 'id' }),
|
|
35
35
|
key: 'typeAnnotation',
|
|
36
36
|
}));
|
|
37
37
|
if (isTypedVariable ||
|
|
38
|
-
parentType ===
|
|
38
|
+
parentType === common_1.AstNodeTypes.SATISFIES_EXPRESSION ||
|
|
39
39
|
isLibraryArgument({ node: current, parent })) {
|
|
40
40
|
return true;
|
|
41
41
|
}
|
|
42
|
-
const isNestedValue = parentType ===
|
|
42
|
+
const isNestedValue = parentType === common_1.AstNodeTypes.PROPERTY &&
|
|
43
|
+
common_1.AstNode.readField({ node: parent, key: 'value' }) === current;
|
|
43
44
|
current = isNestedValue ? common_1.AstNode.readField({ node: parent, key: 'parent' }) : undefined;
|
|
44
45
|
}
|
|
45
46
|
return false;
|
|
@@ -66,9 +67,9 @@ class MaxParamsOwnRule {
|
|
|
66
67
|
const max = typeof context.options[0] === 'number' ? context.options[0] : MaxParamsOwnRule.DEFAULT_MAX;
|
|
67
68
|
const isCallback = (node) => {
|
|
68
69
|
const parent = node.parent;
|
|
69
|
-
const isJsxProp = common_1.AstNode.getType({ node: parent }) ===
|
|
70
|
+
const isJsxProp = common_1.AstNode.getType({ node: parent }) === common_1.AstNodeTypes.JSX_EXPRESSION_CONTAINER &&
|
|
70
71
|
common_1.AstNode.getType({ node: common_1.AstNode.readField({ node: parent, key: 'parent' }) }) ===
|
|
71
|
-
|
|
72
|
+
common_1.AstNodeTypes.JSX_ATTRIBUTE;
|
|
72
73
|
return isJsxProp || isLibraryArgument({ node, parent });
|
|
73
74
|
};
|
|
74
75
|
const isInjectedConstructor = (opts) => {
|
|
@@ -82,23 +83,23 @@ class MaxParamsOwnRule {
|
|
|
82
83
|
return ((Array.isArray(own) && own.length > 0) ||
|
|
83
84
|
(Array.isArray(property) && property.length > 0));
|
|
84
85
|
};
|
|
85
|
-
return (common_1.AstNode.getType({ node: parent }) ===
|
|
86
|
+
return (common_1.AstNode.getType({ node: parent }) === common_1.AstNodeTypes.METHOD_DEFINITION &&
|
|
86
87
|
common_1.AstNode.readField({ node: parent, key: 'kind' }) === 'constructor' &&
|
|
87
88
|
opts.params.every(isDecorated));
|
|
88
89
|
};
|
|
89
90
|
const isTypedByContext = (node) => {
|
|
90
91
|
const parent = node.parent;
|
|
91
92
|
const parentType = common_1.AstNode.getType({ node: parent });
|
|
92
|
-
const isTypedInit = parentType ===
|
|
93
|
+
const isTypedInit = parentType === common_1.AstNodeTypes.VARIABLE_DECLARATOR &&
|
|
93
94
|
common_1.AstNode.readField({ node: parent, key: 'init' }) === node &&
|
|
94
95
|
Boolean(common_1.AstNode.readField({
|
|
95
96
|
node: common_1.AstNode.readField({ node: parent, key: 'id' }),
|
|
96
97
|
key: 'typeAnnotation',
|
|
97
98
|
}));
|
|
98
99
|
const grandParent = common_1.AstNode.readField({ node: parent, key: 'parent' });
|
|
99
|
-
const isMember = parentType ===
|
|
100
|
+
const isMember = parentType === common_1.AstNodeTypes.PROPERTY &&
|
|
100
101
|
common_1.AstNode.readField({ node: parent, key: 'value' }) === node &&
|
|
101
|
-
common_1.AstNode.getType({ node: grandParent }) ===
|
|
102
|
+
common_1.AstNode.getType({ node: grandParent }) === common_1.AstNodeTypes.OBJECT_EXPRESSION;
|
|
102
103
|
return isTypedInit || (isMember && hasDeclaredShape(grandParent));
|
|
103
104
|
};
|
|
104
105
|
const check = (node) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"max-params-own.rule.js","sourceRoot":"","sources":["../../../src/linters/house/max-params-own.rule.ts"],"names":[],"mappings":";;;AAEA,qCAAyE;AAEzE,MAAM,SAAS,GAAG,WAAW,CAAC;AAC9B,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC,CAAC;AAEhE,MAAM,iBAAiB,GAAG,CAAC,IAAsC,EAAE,EAAE,CACnE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAE5D,4DAA4D;AAC5D,MAAM,UAAU,GAAG,CAAC,IAAa,EAAE,EAAE;IACnC,MAAM,MAAM,GAAG,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;IAE9D,OAAO,CACL,gBAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,YAAY;QAClD,OAAO,IAAI,KAAK,QAAQ;QACxB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CACrB,CAAC;AACJ,CAAC,CAAC;AAEF,mEAAmE;AACnE,MAAM,iBAAiB,GAAG,CAAC,IAAwC,EAAE,EAAE,CACrE,UAAU,CAAC,GAAG,CAAC,gBAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;IAC5D,iBAAiB,CAAC;QAChB,IAAI,EAAE,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC;QAChE,IAAI,EAAE,IAAI,CAAC,IAAI;KAChB,CAAC;IACF,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAE3B;;;GAGG;AACH,MAAM,gBAAgB,GAAG,CAAC,MAAe,EAAW,EAAE;IACpD,IAAI,OAAO,GAAG,MAAM,CAAC;IAErB,OAAO,OAAO,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnE,MAAM,UAAU,GAAG,gBAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACrD,MAAM,eAAe,GACnB,UAAU,KAAK,oBAAoB;YACnC,OAAO,CACL,gBAAO,CAAC,SAAS,CAAC;gBAChB,IAAI,EAAE,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;gBACpD,GAAG,EAAE,gBAAgB;aACtB,CAAC,CACH,CAAC;QAEJ,IACE,eAAe;YACf,UAAU,KAAK,uBAAuB;YACtC,iBAAiB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAC5C,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,aAAa,GACjB,UAAU,KAAK,UAAU,IAAI,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC;QAC7F,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3F,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAa,gBAAgB;aACX,SAAI,GAAG,GAAG,+BAAsB,iBAAiB,CAAC;aAElD,gBAAW,GAAG,CAAC,CAAC;aAEhB,SAAI,GAAoB;QACtC,IAAI,EAAE;YACJ,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;YACzC,QAAQ,EAAE;gBACR,OAAO,EACL,wGAAwG;aAC3G;SACF;QACD,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE;YAClB,MAAM,GAAG,GACP,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,WAAW,CAAC;YAE7F,MAAM,UAAU,GAAG,CAAC,IAAe,EAAE,EAAE;gBACrC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;gBAC3B,MAAM,SAAS,GACb,gBAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,wBAAwB;oBAC9D,gBAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;wBAC3E,cAAc,CAAC;gBAEnB,OAAO,SAAS,IAAI,iBAAiB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC1D,CAAC,CAAC;YAEF,MAAM,qBAAqB,GAAG,CAAC,IAA4C,EAAE,EAAE;gBAC7E,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;gBAEhC,MAAM,WAAW,GAAG,CAAC,KAAc,EAAE,EAAE;oBACrC,MAAM,GAAG,GAAG,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;oBAClE,MAAM,QAAQ,GAAG,gBAAO,CAAC,SAAS,CAAC;wBACjC,IAAI,EAAE,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC;wBAC1D,GAAG,EAAE,YAAY;qBAClB,CAAC,CAAC;oBAEH,OAAO,CACL,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;wBACtC,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CACjD,CAAC;gBACJ,CAAC,CAAC;gBAEF,OAAO,CACL,gBAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,kBAAkB;oBACxD,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,KAAK,aAAa;oBAClE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAC/B,CAAC;YACJ,CAAC,CAAC;YAEF,MAAM,gBAAgB,GAAG,CAAC,IAAe,EAAE,EAAE;gBAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;gBAC3B,MAAM,UAAU,GAAG,gBAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;gBACrD,MAAM,WAAW,GACf,UAAU,KAAK,oBAAoB;oBACnC,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,KAAK,IAAI;oBACzD,OAAO,CACL,gBAAO,CAAC,SAAS,CAAC;wBAChB,IAAI,EAAE,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;wBACpD,GAAG,EAAE,gBAAgB;qBACtB,CAAC,CACH,CAAC;gBACJ,MAAM,WAAW,GAAG,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACvE,MAAM,QAAQ,GACZ,UAAU,KAAK,UAAU;oBACzB,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,KAAK,IAAI;oBAC1D,gBAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,KAAK,kBAAkB,CAAC;gBAEhE,OAAO,WAAW,IAAI,CAAC,QAAQ,IAAI,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC;YACpE,CAAC,CAAC;YAEF,MAAM,KAAK,GAAG,CAAC,IAAe,EAAE,EAAE;gBAChC,MAAM,MAAM,GAAG,gBAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAE1D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;oBACnD,OAAO;gBACT,CAAC;gBAED,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC1F,OAAO;gBACT,CAAC;gBAED,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI;oBACJ,SAAS,EAAE,SAAS;oBACpB,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE;iBACzD,CAAC,CAAC;YACL,CAAC,CAAC;YAEF,OAAO;gBACL,CAAC,qBAAY,CAAC,oBAAoB,CAAC,EAAE,KAAK;gBAC1C,CAAC,qBAAY,CAAC,mBAAmB,CAAC,EAAE,KAAK;gBACzC,CAAC,qBAAY,CAAC,yBAAyB,CAAC,EAAE,KAAK;aAChD,CAAC;QACJ,CAAC;KACF,CAAC;IAEF,MAAM,CAAC,KAAK,CACV,OAKI,EAAE;QAEN,MAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,GAAG,GAAG,gBAAgB,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC;QAExE,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACzB,CAAC;;AA7GH,4CA8GC","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\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` 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\n return isJsxProp || isLibraryArgument({ node, parent });\n };\n\n const isInjectedConstructor = (opts: { node: Rule.Node; params: unknown[] }) => {\n const parent = opts.node.parent;\n\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\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\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(\n opts: {\n /** @default 'error' */\n severity?: TRuleSeverity;\n /** @default 5 */\n max?: number;\n } = {},\n ): Linter.RuleEntry {\n const { severity = 'error', max = MaxParamsOwnRule.DEFAULT_MAX } = opts;\n\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([AstNodeTypes.CALL_EXPRESSION, AstNodeTypes.NEW_EXPRESSION]);\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\n return (\n AstNode.getType({ node: callee }) === AstNodeTypes.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 === AstNodeTypes.VARIABLE_DECLARATOR &&\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 === AstNodeTypes.SATISFIES_EXPRESSION ||\n isLibraryArgument({ node: current, parent })\n ) {\n return true;\n }\n\n const isNestedValue =\n parentType === AstNodeTypes.PROPERTY &&\n 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` 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 }) === AstNodeTypes.JSX_EXPRESSION_CONTAINER &&\n AstNode.getType({ node: AstNode.readField({ node: parent, key: 'parent' }) }) ===\n AstNodeTypes.JSX_ATTRIBUTE;\n\n return isJsxProp || isLibraryArgument({ node, parent });\n };\n\n const isInjectedConstructor = (opts: { node: Rule.Node; params: unknown[] }) => {\n const parent = opts.node.parent;\n\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\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 }) === AstNodeTypes.METHOD_DEFINITION &&\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 === AstNodeTypes.VARIABLE_DECLARATOR &&\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 === AstNodeTypes.PROPERTY &&\n AstNode.readField({ node: parent, key: 'value' }) === node &&\n AstNode.getType({ node: grandParent }) === AstNodeTypes.OBJECT_EXPRESSION;\n\n return isTypedInit || (isMember && hasDeclaredShape(grandParent));\n };\n\n const check = (node: Rule.Node) => {\n const params = AstNode.readField({ node, key: 'params' });\n\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(\n opts: {\n /** @default 'error' */\n severity?: TRuleSeverity;\n /** @default 5 */\n max?: number;\n } = {},\n ): Linter.RuleEntry {\n const { severity = 'error', max = MaxParamsOwnRule.DEFAULT_MAX } = opts;\n\n return [severity, max];\n }\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
import type { TRuleSeverity } from '../rules/common';
|
|
3
|
+
/**
|
|
4
|
+
* `venizia/no-await-new-chain`. A method is never called on a fresh instance inside an `await`:
|
|
5
|
+
* `await new Runner({ ... }).run(...)` and `const text = await new Response(body).text()` declare the instance first,
|
|
6
|
+
* then await on its own line (`const runner = new Runner({ ... }); await runner.run(...);`). A builder chain
|
|
7
|
+
* (`await new SignJWT(claims).setIssuer(iss).sign(key)`) declares the builder, then awaits its last call.
|
|
8
|
+
*
|
|
9
|
+
* `await new Promise(...)` calls no method and is allowed. `await new X().prop` is not a call and is allowed.
|
|
10
|
+
*
|
|
11
|
+
* The fix moves the instance, or the chain up to the last call, into a `const` in front of the statement. It is made
|
|
12
|
+
* only when nothing that does more than read a value runs before the `await` inside the statement; otherwise the
|
|
13
|
+
* finding is reported and left alone.
|
|
14
|
+
*/
|
|
15
|
+
export declare class NoAwaitNewChainRule {
|
|
16
|
+
static readonly NAME = "venizia/no-await-new-chain";
|
|
17
|
+
/** The expression under any transparent wrapper. */
|
|
18
|
+
private static unwrap;
|
|
19
|
+
/**
|
|
20
|
+
* The receiver of the last call when the awaited value is a method call on a `new` (directly or through a chain of
|
|
21
|
+
* calls and member reads), else `undefined`.
|
|
22
|
+
*/
|
|
23
|
+
private static receiverOf;
|
|
24
|
+
/**
|
|
25
|
+
* Whether the `await` is the whole statement (`await f();`) or the whole initializer of a declaration with one
|
|
26
|
+
* declarator (`const { a } = await f();`). Nothing runs before it there: a binding pattern and its defaults are
|
|
27
|
+
* evaluated after the initializer.
|
|
28
|
+
*/
|
|
29
|
+
private static isFirstToRun;
|
|
30
|
+
/** `Response` becomes `response`, `ns.SignJWT` becomes `signJWT`; anything else is `instance`. */
|
|
31
|
+
private static nameOf;
|
|
32
|
+
static readonly RULE: Rule.RuleModule;
|
|
33
|
+
static build(opts?: {
|
|
34
|
+
severity?: TRuleSeverity;
|
|
35
|
+
}): TRuleSeverity;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=no-await-new-chain.rule.d.ts.map
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoAwaitNewChainRule = void 0;
|
|
4
|
+
const await_support_1 = require("./await-support");
|
|
5
|
+
const common_1 = require("./common");
|
|
6
|
+
/** Wrappers that carry no meaning of their own around the call or the instance: `(await new X().m()) as Y`, `new X()!.m()`, `new X()?.m()`. */
|
|
7
|
+
const TRANSPARENT_TYPES = new Set([
|
|
8
|
+
common_1.AstNodeTypes.CHAIN_EXPRESSION,
|
|
9
|
+
common_1.AstNodeTypes.AS_EXPRESSION,
|
|
10
|
+
common_1.AstNodeTypes.SATISFIES_EXPRESSION,
|
|
11
|
+
common_1.AstNodeTypes.NON_NULL_EXPRESSION,
|
|
12
|
+
]);
|
|
13
|
+
/** The classes whose `new` is awaited on its own account: `await new Promise(...)` calls no method, and `.then` on it is `no-await-promise-chain`'s. */
|
|
14
|
+
const EXEMPT_CLASSES = new Set(['Promise']);
|
|
15
|
+
/**
|
|
16
|
+
* `venizia/no-await-new-chain`. A method is never called on a fresh instance inside an `await`:
|
|
17
|
+
* `await new Runner({ ... }).run(...)` and `const text = await new Response(body).text()` declare the instance first,
|
|
18
|
+
* then await on its own line (`const runner = new Runner({ ... }); await runner.run(...);`). A builder chain
|
|
19
|
+
* (`await new SignJWT(claims).setIssuer(iss).sign(key)`) declares the builder, then awaits its last call.
|
|
20
|
+
*
|
|
21
|
+
* `await new Promise(...)` calls no method and is allowed. `await new X().prop` is not a call and is allowed.
|
|
22
|
+
*
|
|
23
|
+
* The fix moves the instance, or the chain up to the last call, into a `const` in front of the statement. It is made
|
|
24
|
+
* only when nothing that does more than read a value runs before the `await` inside the statement; otherwise the
|
|
25
|
+
* finding is reported and left alone.
|
|
26
|
+
*/
|
|
27
|
+
class NoAwaitNewChainRule {
|
|
28
|
+
static { this.NAME = `${common_1.HOUSE_PLUGIN_NAMESPACE}/no-await-new-chain`; }
|
|
29
|
+
/** The expression under any transparent wrapper. */
|
|
30
|
+
static unwrap(opts) {
|
|
31
|
+
let current = opts.node;
|
|
32
|
+
while (TRANSPARENT_TYPES.has(common_1.AstNode.getType({ node: current }) ?? '')) {
|
|
33
|
+
current = common_1.AstNode.readField({ node: current, key: 'expression' });
|
|
34
|
+
}
|
|
35
|
+
return current;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The receiver of the last call when the awaited value is a method call on a `new` (directly or through a chain of
|
|
39
|
+
* calls and member reads), else `undefined`.
|
|
40
|
+
*/
|
|
41
|
+
static receiverOf(opts) {
|
|
42
|
+
const operand = NoAwaitNewChainRule.unwrap({
|
|
43
|
+
node: common_1.AstNode.readField({ node: opts.awaitNode, key: 'argument' }),
|
|
44
|
+
});
|
|
45
|
+
if (common_1.AstNode.getType({ node: operand }) !== common_1.AstNodeTypes.CALL_EXPRESSION) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const callee = NoAwaitNewChainRule.unwrap({
|
|
49
|
+
node: common_1.AstNode.readField({ node: operand, key: 'callee' }),
|
|
50
|
+
});
|
|
51
|
+
if (common_1.AstNode.getType({ node: callee }) !== common_1.AstNodeTypes.MEMBER_EXPRESSION) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const receiver = common_1.AstNode.readField({ node: callee, key: 'object' });
|
|
55
|
+
let current = NoAwaitNewChainRule.unwrap({ node: receiver });
|
|
56
|
+
// Down the chain: `new X().a().b` is a call of a member of a call of a member of the `new`.
|
|
57
|
+
for (;;) {
|
|
58
|
+
const type = common_1.AstNode.getType({ node: current });
|
|
59
|
+
if (type === common_1.AstNodeTypes.CALL_EXPRESSION) {
|
|
60
|
+
current = NoAwaitNewChainRule.unwrap({
|
|
61
|
+
node: common_1.AstNode.readField({ node: current, key: 'callee' }),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else if (type === common_1.AstNodeTypes.MEMBER_EXPRESSION) {
|
|
65
|
+
current = NoAwaitNewChainRule.unwrap({
|
|
66
|
+
node: common_1.AstNode.readField({ node: current, key: 'object' }),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (common_1.AstNode.getType({ node: current }) !== common_1.AstNodeTypes.NEW_EXPRESSION) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
const constructed = common_1.AstNode.readField({ node: current, key: 'callee' });
|
|
77
|
+
if (common_1.AstNode.getType({ node: constructed }) === common_1.AstNodeTypes.IDENTIFIER &&
|
|
78
|
+
EXEMPT_CLASSES.has(String(common_1.AstNode.readField({ node: constructed, key: 'name' })))) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return await_support_1.AwaitSupport.isNode(receiver) ? receiver : undefined;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Whether the `await` is the whole statement (`await f();`) or the whole initializer of a declaration with one
|
|
85
|
+
* declarator (`const { a } = await f();`). Nothing runs before it there: a binding pattern and its defaults are
|
|
86
|
+
* evaluated after the initializer.
|
|
87
|
+
*/
|
|
88
|
+
static isFirstToRun(opts) {
|
|
89
|
+
const { awaitNode, statement } = opts;
|
|
90
|
+
const type = common_1.AstNode.getType({ node: statement });
|
|
91
|
+
if (type === common_1.AstNodeTypes.EXPRESSION_STATEMENT) {
|
|
92
|
+
return common_1.AstNode.readField({ node: statement, key: 'expression' }) === awaitNode;
|
|
93
|
+
}
|
|
94
|
+
if (type !== common_1.AstNodeTypes.VARIABLE_DECLARATION) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const declarations = common_1.AstNode.readField({ node: statement, key: 'declarations' });
|
|
98
|
+
return (Array.isArray(declarations) &&
|
|
99
|
+
declarations.length === 1 &&
|
|
100
|
+
common_1.AstNode.readField({ node: declarations[0], key: 'init' }) === awaitNode);
|
|
101
|
+
}
|
|
102
|
+
/** `Response` becomes `response`, `ns.SignJWT` becomes `signJWT`; anything else is `instance`. */
|
|
103
|
+
static nameOf(opts) {
|
|
104
|
+
let current = NoAwaitNewChainRule.unwrap({ node: opts.receiver });
|
|
105
|
+
while (common_1.AstNode.getType({ node: current }) === common_1.AstNodeTypes.CALL_EXPRESSION ||
|
|
106
|
+
common_1.AstNode.getType({ node: current }) === common_1.AstNodeTypes.MEMBER_EXPRESSION) {
|
|
107
|
+
current = NoAwaitNewChainRule.unwrap({
|
|
108
|
+
node: common_1.AstNode.readField({
|
|
109
|
+
node: current,
|
|
110
|
+
key: common_1.AstNode.getType({ node: current }) === common_1.AstNodeTypes.MEMBER_EXPRESSION
|
|
111
|
+
? 'object'
|
|
112
|
+
: 'callee',
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const constructed = common_1.AstNode.readField({ node: current, key: 'callee' });
|
|
117
|
+
const word = common_1.AstNode.getType({ node: constructed }) === common_1.AstNodeTypes.IDENTIFIER
|
|
118
|
+
? common_1.AstNode.readField({ node: constructed, key: 'name' })
|
|
119
|
+
: common_1.AstNode.getType({ node: constructed }) === common_1.AstNodeTypes.MEMBER_EXPRESSION
|
|
120
|
+
? common_1.AstNode.readField({
|
|
121
|
+
node: common_1.AstNode.readField({ node: constructed, key: 'property' }),
|
|
122
|
+
key: 'name',
|
|
123
|
+
})
|
|
124
|
+
: undefined;
|
|
125
|
+
return typeof word === 'string' && /^[A-Za-z_$][\w$]*$/.test(word)
|
|
126
|
+
? `${word[0]?.toLowerCase()}${word.slice(1)}`
|
|
127
|
+
: 'instance';
|
|
128
|
+
}
|
|
129
|
+
static { this.RULE = {
|
|
130
|
+
meta: {
|
|
131
|
+
type: 'suggestion',
|
|
132
|
+
fixable: 'code',
|
|
133
|
+
schema: [],
|
|
134
|
+
messages: {
|
|
135
|
+
chain: 'Do not call a method on a new instance inside an `await`: declare the instance first (`const runner = new Runner(options);`), then `await runner.run();` on its own line.',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
create: (context) => {
|
|
139
|
+
const { sourceCode } = context;
|
|
140
|
+
return {
|
|
141
|
+
[common_1.AstNodeTypes.AWAIT_EXPRESSION]: (node) => {
|
|
142
|
+
const receiver = NoAwaitNewChainRule.receiverOf({ awaitNode: node });
|
|
143
|
+
if (receiver === undefined) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const statement = await_support_1.AwaitSupport.hoistTarget({ awaitNode: node });
|
|
147
|
+
const range = await_support_1.AwaitSupport.rangeOf({ node: receiver });
|
|
148
|
+
const canFix = statement !== undefined &&
|
|
149
|
+
range !== undefined &&
|
|
150
|
+
(NoAwaitNewChainRule.isFirstToRun({ awaitNode: node, statement }) ||
|
|
151
|
+
!await_support_1.AwaitSupport.hasEffectBefore({ statement, awaitNode: node, sourceCode }));
|
|
152
|
+
context.report({
|
|
153
|
+
node,
|
|
154
|
+
messageId: 'chain',
|
|
155
|
+
fix: canFix && range !== undefined
|
|
156
|
+
? (fixer) => {
|
|
157
|
+
const name = await_support_1.AwaitSupport.freshName({
|
|
158
|
+
node,
|
|
159
|
+
base: NoAwaitNewChainRule.nameOf({ receiver }),
|
|
160
|
+
sourceCode,
|
|
161
|
+
});
|
|
162
|
+
const indent = await_support_1.AwaitSupport.indentOf({ node: statement, sourceCode });
|
|
163
|
+
return [
|
|
164
|
+
fixer.insertTextBefore(statement, `const ${name} = ${sourceCode.getText(receiver)};\n${indent}`),
|
|
165
|
+
fixer.replaceTextRange(range, name),
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
: null,
|
|
169
|
+
});
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
}; }
|
|
174
|
+
static build(opts = {}) {
|
|
175
|
+
return opts.severity ?? 'error';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
exports.NoAwaitNewChainRule = NoAwaitNewChainRule;
|
|
179
|
+
//# sourceMappingURL=no-await-new-chain.rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-await-new-chain.rule.js","sourceRoot":"","sources":["../../../src/linters/house/no-await-new-chain.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/** Wrappers that carry no meaning of their own around the call or the instance: `(await new X().m()) as Y`, `new X()!.m()`, `new X()?.m()`. */\nconst TRANSPARENT_TYPES = new Set([\n AstNodeTypes.CHAIN_EXPRESSION,\n AstNodeTypes.AS_EXPRESSION,\n AstNodeTypes.SATISFIES_EXPRESSION,\n AstNodeTypes.NON_NULL_EXPRESSION,\n]);\n\n/** The classes whose `new` is awaited on its own account: `await new Promise(...)` calls no method, and `.then` on it is `no-await-promise-chain`'s. */\nconst EXEMPT_CLASSES = new Set(['Promise']);\n\n/**\n * `venizia/no-await-new-chain`. A method is never called on a fresh instance inside an `await`:\n * `await new Runner({ ... }).run(...)` and `const text = await new Response(body).text()` declare the instance first,\n * then await on its own line (`const runner = new Runner({ ... }); await runner.run(...);`). A builder chain\n * (`await new SignJWT(claims).setIssuer(iss).sign(key)`) declares the builder, then awaits its last call.\n *\n * `await new Promise(...)` calls no method and is allowed. `await new X().prop` is not a call and is allowed.\n *\n * The fix moves the instance, or the chain up to the last call, into a `const` in front of the statement. It is made\n * only when nothing that does more than read a value runs before the `await` inside the statement; otherwise the\n * finding is reported and left alone.\n */\nexport class NoAwaitNewChainRule {\n static readonly NAME = `${HOUSE_PLUGIN_NAMESPACE}/no-await-new-chain`;\n\n /** The expression under any transparent wrapper. */\n private static unwrap(opts: { node: unknown }): unknown {\n let current = opts.node;\n\n while (TRANSPARENT_TYPES.has(AstNode.getType({ node: current }) ?? '')) {\n current = AstNode.readField({ node: current, key: 'expression' });\n }\n\n return current;\n }\n\n /**\n * The receiver of the last call when the awaited value is a method call on a `new` (directly or through a chain of\n * calls and member reads), else `undefined`.\n */\n private static receiverOf(opts: { awaitNode: unknown }): Rule.Node | undefined {\n const operand = NoAwaitNewChainRule.unwrap({\n node: AstNode.readField({ node: opts.awaitNode, key: 'argument' }),\n });\n\n if (AstNode.getType({ node: operand }) !== AstNodeTypes.CALL_EXPRESSION) {\n return undefined;\n }\n\n const callee = NoAwaitNewChainRule.unwrap({\n node: AstNode.readField({ node: operand, key: 'callee' }),\n });\n\n if (AstNode.getType({ node: callee }) !== AstNodeTypes.MEMBER_EXPRESSION) {\n return undefined;\n }\n\n const receiver = AstNode.readField({ node: callee, key: 'object' });\n let current = NoAwaitNewChainRule.unwrap({ node: receiver });\n\n // Down the chain: `new X().a().b` is a call of a member of a call of a member of the `new`.\n for (;;) {\n const type = AstNode.getType({ node: current });\n\n if (type === AstNodeTypes.CALL_EXPRESSION) {\n current = NoAwaitNewChainRule.unwrap({\n node: AstNode.readField({ node: current, key: 'callee' }),\n });\n } else if (type === AstNodeTypes.MEMBER_EXPRESSION) {\n current = NoAwaitNewChainRule.unwrap({\n node: AstNode.readField({ node: current, key: 'object' }),\n });\n } else {\n break;\n }\n }\n\n if (AstNode.getType({ node: current }) !== AstNodeTypes.NEW_EXPRESSION) {\n return undefined;\n }\n\n const constructed = AstNode.readField({ node: current, key: 'callee' });\n\n if (\n AstNode.getType({ node: constructed }) === AstNodeTypes.IDENTIFIER &&\n EXEMPT_CLASSES.has(String(AstNode.readField({ node: constructed, key: 'name' })))\n ) {\n return undefined;\n }\n\n return AwaitSupport.isNode(receiver) ? receiver : undefined;\n }\n\n /**\n * Whether the `await` is the whole statement (`await f();`) or the whole initializer of a declaration with one\n * declarator (`const { a } = await f();`). Nothing runs before it there: a binding pattern and its defaults are\n * evaluated after the initializer.\n */\n private static isFirstToRun(opts: { awaitNode: unknown; statement: unknown }): boolean {\n const { awaitNode, statement } = opts;\n const type = AstNode.getType({ node: statement });\n\n if (type === AstNodeTypes.EXPRESSION_STATEMENT) {\n return AstNode.readField({ node: statement, key: 'expression' }) === awaitNode;\n }\n\n if (type !== AstNodeTypes.VARIABLE_DECLARATION) {\n return false;\n }\n\n const declarations = AstNode.readField({ node: statement, key: 'declarations' });\n\n return (\n Array.isArray(declarations) &&\n declarations.length === 1 &&\n AstNode.readField({ node: declarations[0], key: 'init' }) === awaitNode\n );\n }\n\n /** `Response` becomes `response`, `ns.SignJWT` becomes `signJWT`; anything else is `instance`. */\n private static nameOf(opts: { receiver: unknown }): string {\n let current = NoAwaitNewChainRule.unwrap({ node: opts.receiver });\n\n while (\n AstNode.getType({ node: current }) === AstNodeTypes.CALL_EXPRESSION ||\n AstNode.getType({ node: current }) === AstNodeTypes.MEMBER_EXPRESSION\n ) {\n current = NoAwaitNewChainRule.unwrap({\n node: AstNode.readField({\n node: current,\n key:\n AstNode.getType({ node: current }) === AstNodeTypes.MEMBER_EXPRESSION\n ? 'object'\n : 'callee',\n }),\n });\n }\n\n const constructed = AstNode.readField({ node: current, key: 'callee' });\n const word =\n AstNode.getType({ node: constructed }) === AstNodeTypes.IDENTIFIER\n ? AstNode.readField({ node: constructed, key: 'name' })\n : AstNode.getType({ node: constructed }) === AstNodeTypes.MEMBER_EXPRESSION\n ? AstNode.readField({\n node: AstNode.readField({ node: constructed, key: 'property' }),\n key: 'name',\n })\n : undefined;\n\n return typeof word === 'string' && /^[A-Za-z_$][\\w$]*$/.test(word)\n ? `${word[0]?.toLowerCase()}${word.slice(1)}`\n : 'instance';\n }\n\n static readonly RULE: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n fixable: 'code',\n schema: [],\n messages: {\n chain:\n 'Do not call a method on a new instance inside an `await`: declare the instance first (`const runner = new Runner(options);`), then `await runner.run();` on its own line.',\n },\n },\n create: (context) => {\n const { sourceCode } = context;\n\n return {\n [AstNodeTypes.AWAIT_EXPRESSION]: (node: Rule.Node) => {\n const receiver = NoAwaitNewChainRule.receiverOf({ awaitNode: node });\n\n if (receiver === undefined) {\n return;\n }\n\n const statement = AwaitSupport.hoistTarget({ awaitNode: node });\n const range = AwaitSupport.rangeOf({ node: receiver });\n const canFix =\n statement !== undefined &&\n range !== undefined &&\n (NoAwaitNewChainRule.isFirstToRun({ awaitNode: node, statement }) ||\n !AwaitSupport.hasEffectBefore({ statement, awaitNode: node, sourceCode }));\n\n context.report({\n node,\n messageId: 'chain',\n fix:\n canFix && range !== undefined\n ? (fixer) => {\n const name = AwaitSupport.freshName({\n node,\n base: NoAwaitNewChainRule.nameOf({ receiver }),\n sourceCode,\n });\n const indent = AwaitSupport.indentOf({ node: statement, sourceCode });\n\n return [\n fixer.insertTextBefore(\n statement,\n `const ${name} = ${sourceCode.getText(receiver)};\\n${indent}`,\n ),\n fixer.replaceTextRange(range, 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"]}
|
|
@@ -12,40 +12,41 @@ const collectValue = (opts) => {
|
|
|
12
12
|
const type = common_1.AstNode.getType({ node }) ?? '';
|
|
13
13
|
const value = (child) => collectValue({ node: child, parameter, identifiers });
|
|
14
14
|
switch (type) {
|
|
15
|
-
case
|
|
15
|
+
case common_1.AstNodeTypes.LITERAL: {
|
|
16
16
|
return true;
|
|
17
17
|
}
|
|
18
|
-
case
|
|
18
|
+
case common_1.AstNodeTypes.IDENTIFIER: {
|
|
19
19
|
identifiers.push(node);
|
|
20
20
|
return true;
|
|
21
21
|
}
|
|
22
|
-
case
|
|
22
|
+
case common_1.AstNodeTypes.TEMPLATE_LITERAL: {
|
|
23
23
|
return promise_chain_support_1.PromiseChainSupport.isConstant({ node });
|
|
24
24
|
}
|
|
25
|
-
case
|
|
25
|
+
case common_1.AstNodeTypes.UNARY_EXPRESSION: {
|
|
26
26
|
return promise_chain_support_1.PromiseChainSupport.isConstant({ node });
|
|
27
27
|
}
|
|
28
|
-
case
|
|
28
|
+
case common_1.AstNodeTypes.CHAIN_EXPRESSION: {
|
|
29
29
|
return value(common_1.AstNode.readField({ node, key: 'expression' }));
|
|
30
30
|
}
|
|
31
|
-
case
|
|
31
|
+
case common_1.AstNodeTypes.MEMBER_EXPRESSION: {
|
|
32
32
|
// Only a read of the error itself: `error.message`, `error?.response.status`.
|
|
33
33
|
const object = common_1.AstNode.readField({ node, key: 'object' });
|
|
34
34
|
const isRead = common_1.AstNode.readField({ node, key: 'computed' }) !== true ||
|
|
35
|
-
common_1.AstNode.getType({ node: common_1.AstNode.readField({ node, key: 'property' }) }) ===
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
common_1.AstNode.getType({ node: common_1.AstNode.readField({ node, key: 'property' }) }) ===
|
|
36
|
+
common_1.AstNodeTypes.LITERAL;
|
|
37
|
+
const isRooted = common_1.AstNode.getType({ node: object }) === common_1.AstNodeTypes.MEMBER_EXPRESSION ||
|
|
38
|
+
(common_1.AstNode.getType({ node: object }) === common_1.AstNodeTypes.IDENTIFIER &&
|
|
38
39
|
common_1.AstNode.readField({ node: object, key: 'name' }) === parameter);
|
|
39
40
|
return isRead && isRooted && parameter !== undefined && value(object);
|
|
40
41
|
}
|
|
41
|
-
case
|
|
42
|
+
case common_1.AstNodeTypes.ARRAY_EXPRESSION: {
|
|
42
43
|
const elements = common_1.AstNode.readField({ node, key: 'elements' });
|
|
43
44
|
return (Array.isArray(elements) && elements.every((element) => element === null || value(element)));
|
|
44
45
|
}
|
|
45
|
-
case
|
|
46
|
+
case common_1.AstNodeTypes.OBJECT_EXPRESSION: {
|
|
46
47
|
const properties = common_1.AstNode.readField({ node, key: 'properties' });
|
|
47
48
|
return (Array.isArray(properties) &&
|
|
48
|
-
properties.every((property) => common_1.AstNode.getType({ node: property }) ===
|
|
49
|
+
properties.every((property) => common_1.AstNode.getType({ node: property }) === common_1.AstNodeTypes.PROPERTY &&
|
|
49
50
|
common_1.AstNode.readField({ node: property, key: 'computed' }) !== true &&
|
|
50
51
|
common_1.AstNode.readField({ node: property, key: 'method' }) !== true &&
|
|
51
52
|
common_1.AstNode.readField({ node: property, key: 'kind' }) === 'init' &&
|
|
@@ -78,9 +79,9 @@ const planFix = (opts) => {
|
|
|
78
79
|
const call = common_1.AstNode.readField({ node: awaitNode, key: 'argument' });
|
|
79
80
|
const callee = common_1.AstNode.readField({ node: call, key: 'callee' });
|
|
80
81
|
const args = common_1.AstNode.readField({ node: call, key: 'arguments' });
|
|
81
|
-
if (common_1.AstNode.getType({ node: call }) !==
|
|
82
|
+
if (common_1.AstNode.getType({ node: call }) !== common_1.AstNodeTypes.CALL_EXPRESSION ||
|
|
82
83
|
common_1.AstNode.readField({ node: call, key: 'optional' }) === true ||
|
|
83
|
-
common_1.AstNode.getType({ node: callee }) !==
|
|
84
|
+
common_1.AstNode.getType({ node: callee }) !== common_1.AstNodeTypes.MEMBER_EXPRESSION ||
|
|
84
85
|
common_1.AstNode.readField({ node: callee, key: 'computed' }) === true ||
|
|
85
86
|
common_1.AstNode.readField({ node: callee, key: 'optional' }) === true ||
|
|
86
87
|
promise_chain_support_1.PromiseChainSupport.memberName({ member: callee }) !== 'catch' ||
|
|
@@ -92,7 +93,7 @@ const planFix = (opts) => {
|
|
|
92
93
|
const params = common_1.AstNode.readField({ node: handler, key: 'params' });
|
|
93
94
|
const body = common_1.AstNode.readField({ node: handler, key: 'body' });
|
|
94
95
|
const parameter = Array.isArray(params) ? params[0] : undefined;
|
|
95
|
-
if (common_1.AstNode.getType({ node: handler }) !==
|
|
96
|
+
if (common_1.AstNode.getType({ node: handler }) !== common_1.AstNodeTypes.ARROW_FUNCTION_EXPRESSION ||
|
|
96
97
|
common_1.AstNode.readField({ node: handler, key: 'async' }) === true ||
|
|
97
98
|
common_1.AstNode.getType({ node: body }) === common_1.AstNodeTypes.BLOCK_STATEMENT ||
|
|
98
99
|
common_1.AstNode.readField({ node: handler, key: 'returnType' }) ||
|
|
@@ -100,7 +101,7 @@ const planFix = (opts) => {
|
|
|
100
101
|
!Array.isArray(params) ||
|
|
101
102
|
params.length > 1 ||
|
|
102
103
|
(parameter !== undefined &&
|
|
103
|
-
(common_1.AstNode.getType({ node: parameter }) !==
|
|
104
|
+
(common_1.AstNode.getType({ node: parameter }) !== common_1.AstNodeTypes.IDENTIFIER ||
|
|
104
105
|
common_1.AstNode.readField({ node: parameter, key: 'typeAnnotation' }) ||
|
|
105
106
|
common_1.AstNode.readField({ node: parameter, key: 'optional' }) === true))) {
|
|
106
107
|
return undefined;
|
|
@@ -118,10 +119,10 @@ const planFix = (opts) => {
|
|
|
118
119
|
const id = common_1.AstNode.readField({ node: declarator, key: 'id' });
|
|
119
120
|
const declarations = common_1.AstNode.readField({ node: declaration, key: 'declarations' });
|
|
120
121
|
const kind = String(common_1.AstNode.readField({ node: declaration, key: 'kind' }));
|
|
121
|
-
if (common_1.AstNode.getType({ node: declarator }) !==
|
|
122
|
+
if (common_1.AstNode.getType({ node: declarator }) !== common_1.AstNodeTypes.VARIABLE_DECLARATOR ||
|
|
122
123
|
common_1.AstNode.readField({ node: declarator, key: 'init' }) !== awaitNode ||
|
|
123
124
|
common_1.AstNode.readField({ node: declarator, key: 'definite' }) === true ||
|
|
124
|
-
common_1.AstNode.getType({ node: id }) !==
|
|
125
|
+
common_1.AstNode.getType({ node: id }) !== common_1.AstNodeTypes.IDENTIFIER ||
|
|
125
126
|
!await_support_1.AwaitSupport.isNode(declarator) ||
|
|
126
127
|
!await_support_1.AwaitSupport.isNode(declaration) ||
|
|
127
128
|
!Array.isArray(declarations) ||
|
|
@@ -149,8 +150,8 @@ const planFix = (opts) => {
|
|
|
149
150
|
if (reference.isWrite() ||
|
|
150
151
|
(annotation === '' &&
|
|
151
152
|
(reference.from.variableScope !== variable.scope.variableScope ||
|
|
152
|
-
parentType ===
|
|
153
|
-
parentType ===
|
|
153
|
+
parentType === common_1.AstNodeTypes.EXPORT_SPECIFIER ||
|
|
154
|
+
parentType === common_1.AstNodeTypes.TYPE_QUERY))) {
|
|
154
155
|
return undefined;
|
|
155
156
|
}
|
|
156
157
|
}
|
|
@@ -164,7 +165,7 @@ const planFix = (opts) => {
|
|
|
164
165
|
// `x = []` on a `let x;` with no annotation makes an evolving `any[]`, looser than the `never[]` it was.
|
|
165
166
|
const elements = common_1.AstNode.readField({ node: body, key: 'elements' });
|
|
166
167
|
if (annotation === '' &&
|
|
167
|
-
common_1.AstNode.getType({ node: body }) ===
|
|
168
|
+
common_1.AstNode.getType({ node: body }) === common_1.AstNodeTypes.ARRAY_EXPRESSION &&
|
|
168
169
|
Array.isArray(elements) &&
|
|
169
170
|
elements.length === 0) {
|
|
170
171
|
return undefined;
|
|
@@ -201,7 +202,7 @@ const planFix = (opts) => {
|
|
|
201
202
|
.filter(await_support_1.AwaitSupport.isNode)
|
|
202
203
|
.map((identifier) => {
|
|
203
204
|
const parent = await_support_1.AwaitSupport.parentOf({ node: identifier });
|
|
204
|
-
const isShorthand = common_1.AstNode.getType({ node: parent }) ===
|
|
205
|
+
const isShorthand = common_1.AstNode.getType({ node: parent }) === common_1.AstNodeTypes.PROPERTY &&
|
|
205
206
|
common_1.AstNode.readField({ node: parent, key: 'shorthand' }) === true;
|
|
206
207
|
return {
|
|
207
208
|
range: identifier.range ?? [0, 0],
|