@blumintinc/eslint-plugin-blumint 1.12.6 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -99
- package/lib/index.js +300 -17
- package/lib/rules/array-methods-this-context.js +20 -2
- package/lib/rules/avoid-utils-directory.js +11 -2
- package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
- package/lib/rules/consistent-callback-naming.js +1 -1
- package/lib/rules/dynamic-https-errors.d.ts +3 -1
- package/lib/rules/dynamic-https-errors.js +48 -11
- package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
- package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +52 -96
- package/lib/rules/enforce-assert-throws.d.ts +3 -1
- package/lib/rules/enforce-assert-throws.js +493 -51
- package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
- package/lib/rules/enforce-boolean-naming-prefixes.js +820 -39
- package/lib/rules/enforce-callable-types.js +4 -4
- package/lib/rules/enforce-callback-memo.js +143 -18
- package/lib/rules/enforce-centralized-mock-firestore.js +5 -2
- package/lib/rules/enforce-console-error.d.ts +3 -0
- package/lib/rules/enforce-console-error.js +278 -0
- package/lib/rules/enforce-css-media-queries.js +24 -46
- package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
- package/lib/rules/enforce-dynamic-file-naming.js +53 -28
- package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
- package/lib/rules/enforce-dynamic-imports.js +1 -1
- package/lib/rules/enforce-early-destructuring.d.ts +2 -0
- package/lib/rules/enforce-early-destructuring.js +980 -0
- package/lib/rules/enforce-empty-object-check.d.ts +11 -0
- package/lib/rules/enforce-empty-object-check.js +502 -0
- package/lib/rules/enforce-exported-function-types.js +4 -4
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +262 -0
- package/lib/rules/enforce-firestore-doc-ref-generic.js +350 -3
- package/lib/rules/enforce-firestore-facade.js +349 -157
- package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
- package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
- package/lib/rules/enforce-firestore-set-merge.js +3 -4
- package/lib/rules/enforce-global-constants.d.ts +4 -1
- package/lib/rules/enforce-global-constants.js +240 -12
- package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
- package/lib/rules/enforce-memoize-async.d.ts +2 -1
- package/lib/rules/enforce-memoize-async.js +92 -27
- package/lib/rules/enforce-memoize-getters.d.ts +3 -0
- package/lib/rules/enforce-memoize-getters.js +147 -0
- package/lib/rules/enforce-microdiff.js +3 -2
- package/lib/rules/enforce-object-literal-as-const.js +4 -4
- package/lib/rules/enforce-positive-naming.js +9 -7
- package/lib/rules/enforce-props-argument-name.d.ts +2 -7
- package/lib/rules/enforce-props-argument-name.js +165 -151
- package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
- package/lib/rules/enforce-props-naming-consistency.js +145 -0
- package/lib/rules/enforce-querykey-ts.d.ts +9 -0
- package/lib/rules/enforce-querykey-ts.js +274 -0
- package/lib/rules/enforce-safe-stringify.js +1 -1
- package/lib/rules/enforce-serializable-params.js +3 -3
- package/lib/rules/enforce-singular-type-names.js +10 -6
- package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
- package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
- package/lib/rules/enforce-storage-context.d.ts +9 -0
- package/lib/rules/enforce-storage-context.js +646 -0
- package/lib/rules/enforce-timestamp-now.js +23 -7
- package/lib/rules/enforce-transform-memoization.d.ts +4 -0
- package/lib/rules/enforce-transform-memoization.js +416 -0
- package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
- package/lib/rules/enforce-typescript-markdown-code-blocks.js +100 -0
- package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
- package/lib/rules/enforce-unique-cursor-headers.js +365 -0
- package/lib/rules/enforce-verb-noun-naming.js +90 -14
- package/lib/rules/ensure-pointer-events-none.js +28 -2
- package/lib/rules/export-if-in-doubt.js +27 -4
- package/lib/rules/extract-global-constants.js +92 -53
- package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
- package/lib/rules/fast-deep-equal-over-microdiff.js +351 -83
- package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
- package/lib/rules/firestore-transaction-reads-before-writes.js +277 -0
- package/lib/rules/flatten-push-calls.d.ts +2 -0
- package/lib/rules/flatten-push-calls.js +428 -0
- package/lib/rules/generic-starts-with-t.js +8 -2
- package/lib/rules/global-const-style.js +50 -19
- package/lib/rules/jsdoc-above-field.d.ts +11 -0
- package/lib/rules/jsdoc-above-field.js +208 -0
- package/lib/rules/key-only-outermost-element.js +21 -3
- package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
- package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
- package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
- package/lib/rules/memo-compare-deeply-complex-props.js +788 -0
- package/lib/rules/memo-nested-react-components.d.ts +8 -0
- package/lib/rules/memo-nested-react-components.js +410 -0
- package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
- package/lib/rules/memoize-root-level-hocs.js +242 -0
- package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
- package/lib/rules/no-always-true-false-conditions.js +68 -8
- package/lib/rules/no-array-length-in-deps.d.ts +12 -0
- package/lib/rules/no-array-length-in-deps.js +314 -0
- package/lib/rules/no-async-array-filter.js +5 -2
- package/lib/rules/no-async-foreach.js +210 -12
- package/lib/rules/no-circular-references.js +18 -17
- package/lib/rules/no-class-instance-destructuring.js +55 -12
- package/lib/rules/no-complex-cloud-params.js +22 -5
- package/lib/rules/no-compositing-layer-props.js +6 -4
- package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
- package/lib/rules/no-console-error.d.ts +9 -0
- package/lib/rules/no-console-error.js +525 -0
- package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
- package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
- package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
- package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
- package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
- package/lib/rules/no-entire-object-hook-deps.js +320 -68
- package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
- package/lib/rules/no-excessive-parent-chain.js +273 -0
- package/lib/rules/no-explicit-return-type.d.ts +2 -1
- package/lib/rules/no-explicit-return-type.js +215 -26
- package/lib/rules/no-filter-without-return.js +5 -1
- package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
- package/lib/rules/no-firestore-jest-mock.js +126 -8
- package/lib/rules/no-firestore-object-arrays.js +398 -28
- package/lib/rules/no-handler-suffix.d.ts +12 -0
- package/lib/rules/no-handler-suffix.js +305 -0
- package/lib/rules/no-hungarian.js +14 -14
- package/lib/rules/no-inline-component-prop.d.ts +10 -0
- package/lib/rules/no-inline-component-prop.js +456 -0
- package/lib/rules/no-jsx-in-hooks.js +6 -1
- package/lib/rules/no-jsx-whitespace-literal.js +8 -2
- package/lib/rules/no-margin-properties.d.ts +7 -1
- package/lib/rules/no-margin-properties.js +22 -9
- package/lib/rules/no-memoize-on-static.js +9 -1
- package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
- package/lib/rules/no-misleading-boolean-prefixes.js +331 -0
- package/lib/rules/no-misused-switch-case.js +22 -1
- package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
- package/lib/rules/no-mixed-firestore-transactions.js +296 -34
- package/lib/rules/no-mock-firebase-admin.js +5 -2
- package/lib/rules/no-object-values-on-strings.js +10 -2
- package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
- package/lib/rules/no-overridable-method-calls-in-constructor.js +272 -0
- package/lib/rules/no-passthrough-getters.d.ts +4 -0
- package/lib/rules/no-passthrough-getters.js +206 -0
- package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
- package/lib/rules/no-redundant-annotation-assertion.js +402 -0
- package/lib/rules/no-redundant-param-types.js +16 -6
- package/lib/rules/no-redundant-this-params.d.ts +3 -0
- package/lib/rules/no-redundant-this-params.js +459 -0
- package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
- package/lib/rules/no-redundant-usecallback-wrapper.js +328 -0
- package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
- package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
- package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
- package/lib/rules/no-restricted-properties-fix.js +173 -0
- package/lib/rules/no-separate-loading-state.d.ts +8 -0
- package/lib/rules/no-separate-loading-state.js +133 -0
- package/lib/rules/no-stale-state-across-await.d.ts +1 -0
- package/lib/rules/no-stale-state-across-await.js +185 -0
- package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
- package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
- package/lib/rules/no-type-assertion-returns.js +49 -49
- package/lib/rules/no-undefined-null-passthrough.js +217 -40
- package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
- package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
- package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
- package/lib/rules/no-unnecessary-destructuring-rename.js +347 -0
- package/lib/rules/no-unnecessary-destructuring.js +14 -4
- package/lib/rules/no-unnecessary-verb-suffix.js +2 -3
- package/lib/rules/no-unpinned-dependencies.js +36 -5
- package/lib/rules/no-unsafe-firestore-spread.js +8 -5
- package/lib/rules/no-unused-props.d.ts +5 -2
- package/lib/rules/no-unused-props.js +451 -112
- package/lib/rules/no-unused-usestate.js +6 -2
- package/lib/rules/no-useless-fragment.js +28 -2
- package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
- package/lib/rules/no-useless-usememo-primitives.js +393 -0
- package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
- package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
- package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
- package/lib/rules/omit-index-html.d.ts +2 -1
- package/lib/rules/omit-index-html.js +62 -7
- package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
- package/lib/rules/optimize-object-boolean-conditions.js +236 -0
- package/lib/rules/parallelize-async-operations.d.ts +8 -0
- package/lib/rules/parallelize-async-operations.js +394 -0
- package/lib/rules/prefer-batch-operations.d.ts +1 -3
- package/lib/rules/prefer-batch-operations.js +228 -35
- package/lib/rules/prefer-block-comments-for-declarations.js +22 -7
- package/lib/rules/prefer-clone-deep.js +3 -3
- package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
- package/lib/rules/prefer-destructuring-no-class.js +244 -59
- package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
- package/lib/rules/prefer-docsetter-setall.js +243 -0
- package/lib/rules/prefer-document-flattening.d.ts +4 -0
- package/lib/rules/prefer-document-flattening.js +239 -0
- package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
- package/lib/rules/prefer-field-paths-in-transforms.js +251 -0
- package/lib/rules/prefer-fragment-component.js +4 -4
- package/lib/rules/prefer-fragment-shorthand.js +2 -1
- package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
- package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
- package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
- package/lib/rules/prefer-global-router-state-key.js +305 -31
- package/lib/rules/prefer-memoized-props.d.ts +3 -0
- package/lib/rules/prefer-memoized-props.js +445 -0
- package/lib/rules/prefer-next-dynamic.d.ts +9 -0
- package/lib/rules/prefer-next-dynamic.js +357 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +435 -0
- package/lib/rules/prefer-nullish-coalescing-override.d.ts +7 -0
- package/lib/rules/prefer-nullish-coalescing-override.js +188 -0
- package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
- package/lib/rules/prefer-params-over-parent-id.js +277 -0
- package/lib/rules/prefer-settings-object.js +23 -15
- package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
- package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
- package/lib/rules/prefer-type-over-interface.js +7 -2
- package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
- package/lib/rules/prefer-url-tostring-over-tojson.js +169 -0
- package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
- package/lib/rules/prefer-use-deep-compare-memo.js +431 -0
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +25 -2
- package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
- package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
- package/lib/rules/prevent-children-clobber.d.ts +2 -0
- package/lib/rules/prevent-children-clobber.js +536 -0
- package/lib/rules/react-memoize-literals.d.ts +4 -0
- package/lib/rules/react-memoize-literals.js +495 -0
- package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
- package/lib/rules/react-usememo-should-be-component.js +144 -78
- package/lib/rules/require-dynamic-firebase-imports.js +2 -1
- package/lib/rules/require-hooks-default-params.js +17 -16
- package/lib/rules/require-https-error-cause.d.ts +4 -0
- package/lib/rules/require-https-error-cause.js +136 -0
- package/lib/rules/require-https-error.js +43 -21
- package/lib/rules/require-image-optimized.js +2 -2
- package/lib/rules/require-memo.js +12 -2
- package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
- package/lib/rules/require-memoize-jsx-returners.js +485 -0
- package/lib/rules/require-usememo-object-literals.js +9 -4
- package/lib/rules/semantic-function-prefixes.js +38 -18
- package/lib/rules/sync-onwrite-name-func.js +5 -1
- package/lib/rules/test-file-location-enforcement.d.ts +1 -0
- package/lib/rules/test-file-location-enforcement.js +57 -0
- package/lib/rules/use-custom-link.js +6 -1
- package/lib/rules/use-custom-router.js +6 -1
- package/lib/rules/use-latest-callback.d.ts +3 -0
- package/lib/rules/use-latest-callback.js +271 -0
- package/lib/rules/vertically-group-related-functions.d.ts +16 -0
- package/lib/rules/vertically-group-related-functions.js +480 -0
- package/lib/utils/ASTHelpers.d.ts +13 -1
- package/lib/utils/ASTHelpers.js +34 -0
- package/lib/utils/createRule.js +1 -1
- package/lib/utils/getMethodName.d.ts +27 -0
- package/lib/utils/getMethodName.js +35 -0
- package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
- package/lib/utils/ruleTester.d.ts +1 -0
- package/lib/utils/ruleTester.js +4 -1
- package/lib/utils/tsTypeClassifier.d.ts +30 -0
- package/lib/utils/tsTypeClassifier.js +149 -0
- package/package.json +23 -14
- package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parallelizeAsyncOperations = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const defaultOptions = [
|
|
7
|
+
{
|
|
8
|
+
sideEffectPatterns: [
|
|
9
|
+
'updatecounter',
|
|
10
|
+
'setcounter',
|
|
11
|
+
'incrementcounter',
|
|
12
|
+
'decrementcounter',
|
|
13
|
+
'updatethreshold',
|
|
14
|
+
'setthreshold',
|
|
15
|
+
'checkthreshold',
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
exports.parallelizeAsyncOperations = (0, createRule_1.createRule)({
|
|
20
|
+
name: 'parallelize-async-operations',
|
|
21
|
+
meta: {
|
|
22
|
+
type: 'suggestion',
|
|
23
|
+
docs: {
|
|
24
|
+
description: 'Enforce the use of Promise.all() when multiple independent asynchronous operations are awaited sequentially',
|
|
25
|
+
recommended: 'error',
|
|
26
|
+
},
|
|
27
|
+
fixable: 'code',
|
|
28
|
+
schema: [
|
|
29
|
+
{
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
sideEffectPatterns: {
|
|
33
|
+
type: 'array',
|
|
34
|
+
items: {
|
|
35
|
+
anyOf: [
|
|
36
|
+
{ type: 'string' },
|
|
37
|
+
{ type: 'object', instanceof: 'RegExp' },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
default: [],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
messages: {
|
|
47
|
+
parallelizeAsyncOperations: 'Awaiting {{awaitCount}} independent async operations sequentially makes their network and I/O latency add up, which slows responses and wastes compute. These awaits have no data dependency or per-call error handling, so run them together with Promise.all([...]) and destructure the results when you need individual values.',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
defaultOptions,
|
|
51
|
+
create(context, [options]) {
|
|
52
|
+
const sourceCode = context.sourceCode;
|
|
53
|
+
const sideEffectMatchers = (options?.sideEffectPatterns ?? []).map((pattern) => typeof pattern === 'string' ? new RegExp(pattern, 'i') : pattern);
|
|
54
|
+
const reportedRanges = new Set();
|
|
55
|
+
/**
|
|
56
|
+
* Checks if a node is an await expression
|
|
57
|
+
*/
|
|
58
|
+
function isAwaitExpression(node) {
|
|
59
|
+
return node.type === utils_1.AST_NODE_TYPES.AwaitExpression;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Checks if a node is a variable declaration with an await expression initializer
|
|
63
|
+
*/
|
|
64
|
+
function isVariableDeclarationWithAwait(node) {
|
|
65
|
+
if (node.type !== utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return node.declarations.some((declaration) => declaration.init && isAwaitExpression(declaration.init));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Checks if a node is an expression statement with an await expression
|
|
72
|
+
*/
|
|
73
|
+
function isExpressionStatementWithAwait(node) {
|
|
74
|
+
return (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
75
|
+
node.expression.type === utils_1.AST_NODE_TYPES.AwaitExpression);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Extracts the await expression from a node
|
|
79
|
+
*/
|
|
80
|
+
function getAwaitExpression(node) {
|
|
81
|
+
if (isAwaitExpression(node)) {
|
|
82
|
+
return node;
|
|
83
|
+
}
|
|
84
|
+
if (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
85
|
+
isAwaitExpression(node.expression)) {
|
|
86
|
+
return node.expression;
|
|
87
|
+
}
|
|
88
|
+
if (node.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
89
|
+
for (const declaration of node.declarations) {
|
|
90
|
+
if (declaration.init && isAwaitExpression(declaration.init)) {
|
|
91
|
+
return declaration.init;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Checks if an identifier is used in a node
|
|
99
|
+
*/
|
|
100
|
+
function isIdentifierUsedInNode(identifier, node) {
|
|
101
|
+
let isUsed = false;
|
|
102
|
+
function visit(node) {
|
|
103
|
+
if (node.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
104
|
+
node.name === identifier) {
|
|
105
|
+
isUsed = true;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Recursively visit all child nodes
|
|
109
|
+
for (const key in node) {
|
|
110
|
+
if (key === 'parent' || key === 'range' || key === 'loc')
|
|
111
|
+
continue;
|
|
112
|
+
const child = node[key];
|
|
113
|
+
if (child && typeof child === 'object') {
|
|
114
|
+
if (Array.isArray(child)) {
|
|
115
|
+
for (const item of child) {
|
|
116
|
+
if (item && typeof item === 'object' && 'type' in item) {
|
|
117
|
+
visit(item);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if ('type' in child) {
|
|
122
|
+
visit(child);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
visit(node);
|
|
128
|
+
return isUsed;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Checks if there are dependencies between await expressions
|
|
132
|
+
*/
|
|
133
|
+
function hasDependencies(awaitNodes, variableNames, sideEffectPatterns) {
|
|
134
|
+
// If we have fewer than 2 nodes, there are no dependencies to check
|
|
135
|
+
if (awaitNodes.length < 2) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
// For each await node (except the first), check if it depends on previous variables
|
|
139
|
+
for (let i = 1; i < awaitNodes.length; i++) {
|
|
140
|
+
const currentNode = awaitNodes[i];
|
|
141
|
+
// Check if any previous variable is used in the current await expression
|
|
142
|
+
for (const varName of variableNames) {
|
|
143
|
+
const awaitExpr = getAwaitExpression(currentNode);
|
|
144
|
+
if (awaitExpr &&
|
|
145
|
+
isIdentifierUsedInNode(varName, awaitExpr.argument)) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Check for operations that might have side effects that affect subsequent operations
|
|
150
|
+
// This is a conservative heuristic - we only flag very specific patterns
|
|
151
|
+
const awaitExpr = getAwaitExpression(currentNode);
|
|
152
|
+
if (awaitExpr &&
|
|
153
|
+
awaitExpr.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
154
|
+
const callee = awaitExpr.argument.callee;
|
|
155
|
+
// Check for method calls that might indicate side effects
|
|
156
|
+
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
157
|
+
callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
158
|
+
const methodName = callee.property.name;
|
|
159
|
+
if (sideEffectPatterns.some((pattern) => pattern.test(methodName))) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
164
|
+
const functionName = callee.name;
|
|
165
|
+
if (sideEffectPatterns.some((pattern) => pattern.test(functionName))) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// If any node is a variable declaration with destructuring, consider it as having dependencies
|
|
172
|
+
// This is because destructuring often creates variables that are used later
|
|
173
|
+
for (const node of awaitNodes) {
|
|
174
|
+
if (node.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
175
|
+
for (const declaration of node.declarations) {
|
|
176
|
+
if (declaration.id.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Extracts variable names from variable declarations
|
|
186
|
+
*/
|
|
187
|
+
function extractVariableNames(nodes) {
|
|
188
|
+
const variableNames = new Set();
|
|
189
|
+
/**
|
|
190
|
+
* Recursively extract identifiers from patterns
|
|
191
|
+
*/
|
|
192
|
+
function extractIdentifiersFromPattern(pattern) {
|
|
193
|
+
switch (pattern.type) {
|
|
194
|
+
case utils_1.AST_NODE_TYPES.Identifier:
|
|
195
|
+
variableNames.add(pattern.name);
|
|
196
|
+
break;
|
|
197
|
+
case utils_1.AST_NODE_TYPES.ObjectPattern:
|
|
198
|
+
for (const property of pattern.properties) {
|
|
199
|
+
if (property.type === utils_1.AST_NODE_TYPES.Property) {
|
|
200
|
+
extractIdentifiersFromPattern(property.value);
|
|
201
|
+
}
|
|
202
|
+
else if (property.type === utils_1.AST_NODE_TYPES.RestElement) {
|
|
203
|
+
extractIdentifiersFromPattern(property.argument);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
case utils_1.AST_NODE_TYPES.ArrayPattern:
|
|
208
|
+
for (const element of pattern.elements) {
|
|
209
|
+
if (element) {
|
|
210
|
+
extractIdentifiersFromPattern(element);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
break;
|
|
214
|
+
case utils_1.AST_NODE_TYPES.RestElement:
|
|
215
|
+
extractIdentifiersFromPattern(pattern.argument);
|
|
216
|
+
break;
|
|
217
|
+
case utils_1.AST_NODE_TYPES.AssignmentPattern:
|
|
218
|
+
extractIdentifiersFromPattern(pattern.left);
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
for (const node of nodes) {
|
|
223
|
+
if (node.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
224
|
+
for (const declaration of node.declarations) {
|
|
225
|
+
extractIdentifiersFromPattern(declaration.id);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return variableNames;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Checks if nodes are in try-catch blocks (either individual or shared)
|
|
233
|
+
*/
|
|
234
|
+
function areInTryCatchBlocks(nodes) {
|
|
235
|
+
for (const node of nodes) {
|
|
236
|
+
let current = node;
|
|
237
|
+
// Traverse up to find if the node is in a try block
|
|
238
|
+
while (current && current.parent) {
|
|
239
|
+
if (current.parent.type === utils_1.AST_NODE_TYPES.TryStatement &&
|
|
240
|
+
current.parent.block === current) {
|
|
241
|
+
// If we find a try block, we should not parallelize
|
|
242
|
+
// This applies to both individual and shared try-catch blocks
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
current = current.parent;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Checks if nodes are in a loop
|
|
252
|
+
*/
|
|
253
|
+
function areInLoop(nodes) {
|
|
254
|
+
for (const node of nodes) {
|
|
255
|
+
let current = node;
|
|
256
|
+
// Traverse up to find if the node is in a loop
|
|
257
|
+
while (current && current.parent) {
|
|
258
|
+
if (current.parent.type === utils_1.AST_NODE_TYPES.ForStatement ||
|
|
259
|
+
current.parent.type === utils_1.AST_NODE_TYPES.ForInStatement ||
|
|
260
|
+
current.parent.type === utils_1.AST_NODE_TYPES.ForOfStatement ||
|
|
261
|
+
current.parent.type === utils_1.AST_NODE_TYPES.WhileStatement ||
|
|
262
|
+
current.parent.type === utils_1.AST_NODE_TYPES.DoWhileStatement) {
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
current = current.parent;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Generates a fix for sequential awaits
|
|
272
|
+
*
|
|
273
|
+
* Returns null when the sequential awaits cannot be safely rewritten as a Promise.all.
|
|
274
|
+
*/
|
|
275
|
+
function generateFix(fixer, awaitNodes) {
|
|
276
|
+
if (awaitNodes.length < 2) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
// Extract the await expressions
|
|
280
|
+
const awaitExpressions = awaitNodes
|
|
281
|
+
.map((node) => getAwaitExpression(node))
|
|
282
|
+
.filter((node) => node !== null);
|
|
283
|
+
if (awaitExpressions.length < 2) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
// Get the text of each await argument
|
|
287
|
+
const awaitArguments = awaitExpressions.map((expr) => sourceCode.getText(expr.argument));
|
|
288
|
+
const idsText = [];
|
|
289
|
+
const declKinds = new Set();
|
|
290
|
+
let hasVariableDeclarations = false;
|
|
291
|
+
for (const node of awaitNodes) {
|
|
292
|
+
if (node.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
293
|
+
hasVariableDeclarations = true;
|
|
294
|
+
declKinds.add(node.kind);
|
|
295
|
+
for (const declarator of node.declarations) {
|
|
296
|
+
idsText.push(sourceCode.getText(declarator.id));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement) {
|
|
300
|
+
idsText.push('');
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
let promiseAllText;
|
|
304
|
+
if (hasVariableDeclarations) {
|
|
305
|
+
if (declKinds.size !== 1) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
if (idsText.length !== awaitArguments.length) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
const destructuringPattern = idsText.join(', ');
|
|
312
|
+
const declKind = Array.from(declKinds)[0];
|
|
313
|
+
promiseAllText = `${declKind} [${destructuringPattern}] = await Promise.all([\n ${awaitArguments.join(',\n ')}\n]);`;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
// Simple Promise.all without variable assignments
|
|
317
|
+
promiseAllText = `await Promise.all([\n ${awaitArguments.join(',\n ')}\n]);`;
|
|
318
|
+
}
|
|
319
|
+
// Find the start position, accounting for leading comments
|
|
320
|
+
const startPos = awaitNodes[0].range[0];
|
|
321
|
+
// Replace the range from the start of the first await to the end of the last await
|
|
322
|
+
const endPos = awaitNodes[awaitNodes.length - 1].range[1];
|
|
323
|
+
return fixer.replaceTextRange([startPos, endPos], promiseAllText);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Generates a deduplication key from await nodes' range metadata.
|
|
327
|
+
*/
|
|
328
|
+
function getDeduplicationKey(awaitNodes) {
|
|
329
|
+
const rangeStart = awaitNodes[0].range[0];
|
|
330
|
+
const rangeEnd = awaitNodes[awaitNodes.length - 1].range[1];
|
|
331
|
+
return `${rangeStart}-${rangeEnd}`;
|
|
332
|
+
}
|
|
333
|
+
const processStatementList = (statements) => {
|
|
334
|
+
const awaitNodes = [];
|
|
335
|
+
for (const statement of statements) {
|
|
336
|
+
if (isExpressionStatementWithAwait(statement) ||
|
|
337
|
+
isVariableDeclarationWithAwait(statement)) {
|
|
338
|
+
awaitNodes.push(statement);
|
|
339
|
+
}
|
|
340
|
+
else if (awaitNodes.length >= 2) {
|
|
341
|
+
const variableNames = extractVariableNames(awaitNodes);
|
|
342
|
+
if (!hasDependencies(awaitNodes, variableNames, sideEffectMatchers) &&
|
|
343
|
+
!areInTryCatchBlocks(awaitNodes) &&
|
|
344
|
+
!areInLoop(awaitNodes)) {
|
|
345
|
+
const key = getDeduplicationKey(awaitNodes);
|
|
346
|
+
if (!reportedRanges.has(key)) {
|
|
347
|
+
reportedRanges.add(key);
|
|
348
|
+
context.report({
|
|
349
|
+
node: awaitNodes[0],
|
|
350
|
+
messageId: 'parallelizeAsyncOperations',
|
|
351
|
+
data: {
|
|
352
|
+
awaitCount: awaitNodes.length.toString(),
|
|
353
|
+
},
|
|
354
|
+
fix: (fixer) => generateFix(fixer, awaitNodes),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
awaitNodes.length = 0;
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
awaitNodes.length = 0;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (awaitNodes.length >= 2) {
|
|
365
|
+
const variableNames = extractVariableNames(awaitNodes);
|
|
366
|
+
if (!hasDependencies(awaitNodes, variableNames, sideEffectMatchers) &&
|
|
367
|
+
!areInTryCatchBlocks(awaitNodes) &&
|
|
368
|
+
!areInLoop(awaitNodes)) {
|
|
369
|
+
const key = getDeduplicationKey(awaitNodes);
|
|
370
|
+
if (!reportedRanges.has(key)) {
|
|
371
|
+
reportedRanges.add(key);
|
|
372
|
+
context.report({
|
|
373
|
+
node: awaitNodes[0],
|
|
374
|
+
messageId: 'parallelizeAsyncOperations',
|
|
375
|
+
data: {
|
|
376
|
+
awaitCount: awaitNodes.length.toString(),
|
|
377
|
+
},
|
|
378
|
+
fix: (fixer) => generateFix(fixer, awaitNodes),
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
return {
|
|
385
|
+
Program(node) {
|
|
386
|
+
processStatementList(node.body);
|
|
387
|
+
},
|
|
388
|
+
BlockStatement(node) {
|
|
389
|
+
processStatementList(node.body);
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
//# sourceMappingURL=parallelize-async-operations.js.map
|
|
@@ -1,3 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const preferBatchOperations: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
3
|
-
export {};
|
|
1
|
+
export declare const preferBatchOperations: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"preferBatch", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|