@blumintinc/eslint-plugin-blumint 1.13.0 → 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 +170 -139
- package/lib/index.js +190 -1
- package/lib/rules/array-methods-this-context.js +1 -1
- 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.js +3 -4
- package/lib/rules/enforce-assert-safe-object-key.js +11 -11
- package/lib/rules/enforce-boolean-naming-prefixes.js +11 -23
- package/lib/rules/enforce-callback-memo.js +1 -1
- package/lib/rules/enforce-centralized-mock-firestore.js +1 -1
- package/lib/rules/enforce-console-error.js +35 -7
- 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-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 +1 -1
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +100 -50
- package/lib/rules/enforce-firestore-doc-ref-generic.js +15 -3
- package/lib/rules/enforce-firestore-facade.js +4 -2
- package/lib/rules/enforce-firestore-set-merge.js +3 -4
- package/lib/rules/enforce-global-constants.js +1 -1
- package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
- package/lib/rules/enforce-memoize-async.js +2 -6
- package/lib/rules/enforce-memoize-getters.js +39 -15
- package/lib/rules/enforce-microdiff.js +1 -1
- package/lib/rules/enforce-object-literal-as-const.js +4 -4
- package/lib/rules/enforce-positive-naming.js +4 -0
- package/lib/rules/enforce-props-argument-name.js +2 -2
- 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 +1 -1
- 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.js +7 -3
- 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 +5 -4
- 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 +40 -13
- package/lib/rules/fast-deep-equal-over-microdiff.js +29 -39
- package/lib/rules/firestore-transaction-reads-before-writes.js +50 -4
- package/lib/rules/flatten-push-calls.d.ts +2 -0
- package/lib/rules/flatten-push-calls.js +428 -0
- 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/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.js +9 -3
- 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 +7 -3
- package/lib/rules/no-compositing-layer-props.js +2 -2
- 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.js +4 -6
- package/lib/rules/no-excessive-parent-chain.js +1 -1
- 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 +67 -12
- 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 +1 -1
- 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.js +6 -6
- package/lib/rules/no-memoize-on-static.js +9 -1
- package/lib/rules/no-misleading-boolean-prefixes.js +7 -3
- 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.js +29 -17
- package/lib/rules/no-passthrough-getters.js +38 -2
- 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.js +16 -3
- 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.js +11 -10
- package/lib/rules/no-separate-loading-state.js +7 -16
- package/lib/rules/no-stale-state-across-await.js +1 -1
- package/lib/rules/no-type-assertion-returns.js +43 -49
- package/lib/rules/no-undefined-null-passthrough.js +61 -31
- 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 -1
- package/lib/rules/no-unpinned-dependencies.js +36 -5
- package/lib/rules/no-unused-props.d.ts +2 -2
- package/lib/rules/no-unused-props.js +295 -91
- 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.js +6 -4
- package/lib/rules/parallelize-async-operations.js +24 -5
- package/lib/rules/prefer-batch-operations.d.ts +1 -3
- package/lib/rules/prefer-batch-operations.js +32 -5
- package/lib/rules/prefer-block-comments-for-declarations.js +9 -4
- 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-field-paths-in-transforms.js +4 -3
- package/lib/rules/prefer-fragment-component.js +1 -1
- 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.js +88 -13
- 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.js +60 -69
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +9 -4
- package/lib/rules/prefer-nullish-coalescing-override.d.ts +2 -1
- package/lib/rules/prefer-nullish-coalescing-override.js +11 -5
- package/lib/rules/prefer-params-over-parent-id.js +220 -171
- package/lib/rules/prefer-settings-object.js +2 -2
- package/lib/rules/prefer-type-over-interface.js +7 -2
- package/lib/rules/prefer-url-tostring-over-tojson.js +6 -3
- package/lib/rules/prefer-use-deep-compare-memo.js +8 -5
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +1 -1
- 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.js +2 -2
- 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 +1 -1
- package/lib/rules/require-memo.js +1 -1
- 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 +2 -3
- package/lib/rules/test-file-location-enforcement.js +1 -1
- package/lib/rules/use-latest-callback.js +6 -5
- 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 +24 -0
- package/lib/utils/getMethodName.d.ts +27 -0
- package/lib/utils/getMethodName.js +35 -0
- package/lib/utils/tsTypeClassifier.d.ts +30 -0
- package/lib/utils/tsTypeClassifier.js +149 -0
- package/package.json +3 -1
|
@@ -12,19 +12,40 @@ exports.noMisusedSwitchCase = (0, createRule_1.createRule)({
|
|
|
12
12
|
},
|
|
13
13
|
schema: [],
|
|
14
14
|
messages: {
|
|
15
|
-
noMisusedSwitchCase: '
|
|
15
|
+
noMisusedSwitchCase: 'Case expression uses logical OR "{{expressionText}}", but "||" collapses to a single operand so only one branch is reachable. Move each operand into its own case (e.g., "{{cascadingCases}}") so every value is matched and the switch intent stays clear.',
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
defaultOptions: [],
|
|
19
19
|
create(context) {
|
|
20
|
+
const sourceCode = context.getSourceCode();
|
|
21
|
+
const flattenLogicalOrOperands = (expression) => {
|
|
22
|
+
const operands = [];
|
|
23
|
+
const visit = (node) => {
|
|
24
|
+
if (node.type === 'LogicalExpression' && node.operator === '||') {
|
|
25
|
+
visit(node.left);
|
|
26
|
+
visit(node.right);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
operands.push(sourceCode.getText(node));
|
|
30
|
+
};
|
|
31
|
+
visit(expression);
|
|
32
|
+
return operands;
|
|
33
|
+
};
|
|
20
34
|
return {
|
|
21
35
|
SwitchStatement(node) {
|
|
22
36
|
for (const switchCase of node.cases) {
|
|
23
37
|
if (switchCase.test?.type === 'LogicalExpression' &&
|
|
24
38
|
switchCase.test.operator === '||') {
|
|
39
|
+
const operands = flattenLogicalOrOperands(switchCase.test);
|
|
25
40
|
context.report({
|
|
26
41
|
node: switchCase,
|
|
27
42
|
messageId: 'noMisusedSwitchCase',
|
|
43
|
+
data: {
|
|
44
|
+
expressionText: sourceCode.getText(switchCase.test),
|
|
45
|
+
cascadingCases: operands
|
|
46
|
+
.map((operand) => `case ${operand}:`)
|
|
47
|
+
.join(' '),
|
|
48
|
+
},
|
|
28
49
|
});
|
|
29
50
|
}
|
|
30
51
|
}
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
type MessageIds = 'noMixedTransactions' | 'noMixedTransactionsFetcher';
|
|
2
|
+
export declare const noMixedFirestoreTransactions: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
3
|
+
export {};
|
|
@@ -8,6 +8,7 @@ const NON_TRANSACTIONAL_CLASSES = new Set([
|
|
|
8
8
|
'FirestoreDocFetcher',
|
|
9
9
|
'FirestoreFetcher',
|
|
10
10
|
]);
|
|
11
|
+
const FETCHER_CLASSES = new Set(['FirestoreDocFetcher', 'FirestoreFetcher']);
|
|
11
12
|
exports.noMixedFirestoreTransactions = (0, createRule_1.createRule)({
|
|
12
13
|
name: 'no-mixed-firestore-transactions',
|
|
13
14
|
meta: {
|
|
@@ -18,19 +19,26 @@ exports.noMixedFirestoreTransactions = (0, createRule_1.createRule)({
|
|
|
18
19
|
},
|
|
19
20
|
schema: [],
|
|
20
21
|
messages: {
|
|
21
|
-
noMixedTransactions: '
|
|
22
|
+
noMixedTransactions: 'Non-transactional Firestore helper "{{ className }}" is instantiated inside a transaction callback, so its reads and writes bypass the transaction context. That breaks Firestore\'s atomicity guarantees and can commit partial updates. Use the transaction-safe "{{ transactionalClass }}" and pass the provided transaction so every operation participates in the same commit.',
|
|
23
|
+
noMixedTransactionsFetcher: 'Non-transactional Firestore helper "{{ className }}" is instantiated inside a transaction callback, so its reads and writes bypass the transaction context. That breaks Firestore\'s atomicity guarantees and can commit partial updates. Pass the provided transaction to the constructor (e.g., new {{ className }}(ref, { transaction: tx })) or to fetch() (e.g., fetch({ transaction: tx })) so the operation participates in the transaction.',
|
|
22
24
|
},
|
|
23
25
|
},
|
|
24
26
|
defaultOptions: [],
|
|
25
27
|
create(context) {
|
|
26
28
|
const transactionScopes = new Set();
|
|
29
|
+
/**
|
|
30
|
+
* Tracks fetcher lifetimes per transaction scope and binding name.
|
|
31
|
+
* The outer map keys by the transaction scope node so nested transactions keep
|
|
32
|
+
* independent state. Each scope maps variable names to an ordered list of
|
|
33
|
+
* instances, preserving reassignment history. Arrays keep the latest instance
|
|
34
|
+
* easy to read for fetch calls while still retaining older instances for
|
|
35
|
+
* Program:exit validation, which must flag any overwritten fetcher that never
|
|
36
|
+
* received a transaction.
|
|
37
|
+
*/
|
|
38
|
+
const fetcherInstances = new Map();
|
|
27
39
|
function getTransactionalClassName(className) {
|
|
28
40
|
if (className === 'DocSetter')
|
|
29
41
|
return 'DocSetterTransaction';
|
|
30
|
-
if (className === 'FirestoreDocFetcher')
|
|
31
|
-
return 'FirestoreDocFetcherTransaction';
|
|
32
|
-
if (className === 'FirestoreFetcher')
|
|
33
|
-
return 'FirestoreFetcherTransaction';
|
|
34
42
|
return className;
|
|
35
43
|
}
|
|
36
44
|
function isFirestoreTransaction(node) {
|
|
@@ -65,53 +73,307 @@ exports.noMixedFirestoreTransactions = (0, createRule_1.createRule)({
|
|
|
65
73
|
typeName.right.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
66
74
|
typeName.right.name === 'Transaction');
|
|
67
75
|
}
|
|
68
|
-
function
|
|
76
|
+
function isFunctionLike(node) {
|
|
77
|
+
return (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
78
|
+
node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
79
|
+
node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression);
|
|
80
|
+
}
|
|
81
|
+
function identifyTransactionScope(node) {
|
|
82
|
+
if (transactionScopes.has(node)) {
|
|
83
|
+
return node;
|
|
84
|
+
}
|
|
85
|
+
if (isFunctionLike(node) && node.params.some(isTransactionParameter)) {
|
|
86
|
+
return node.body;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
function getTransactionScopeChain(node) {
|
|
91
|
+
const scopes = [];
|
|
69
92
|
let current = node;
|
|
70
93
|
while (current) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
75
|
-
current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
76
|
-
current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
77
|
-
const params = current.params;
|
|
78
|
-
if (params.some(isTransactionParameter)) {
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
94
|
+
const scope = identifyTransactionScope(current);
|
|
95
|
+
if (scope && scopes[scopes.length - 1] !== scope) {
|
|
96
|
+
scopes.push(scope);
|
|
81
97
|
}
|
|
82
98
|
current = current.parent;
|
|
83
99
|
}
|
|
100
|
+
return scopes;
|
|
101
|
+
}
|
|
102
|
+
function getTransactionScope(node) {
|
|
103
|
+
return getTransactionScopeChain(node)[0] ?? null;
|
|
104
|
+
}
|
|
105
|
+
function getVariableName(node) {
|
|
106
|
+
const parent = node.parent;
|
|
107
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
108
|
+
parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
109
|
+
return parent.id.name;
|
|
110
|
+
}
|
|
111
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
|
112
|
+
parent.left.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
113
|
+
return parent.left.name;
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
function isFetcherClass(className) {
|
|
118
|
+
return FETCHER_CLASSES.has(className);
|
|
119
|
+
}
|
|
120
|
+
function isTransactionKey(key) {
|
|
121
|
+
if (key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
122
|
+
return key.name === 'transaction';
|
|
123
|
+
}
|
|
124
|
+
if (key.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
125
|
+
return key.value === 'transaction';
|
|
126
|
+
}
|
|
127
|
+
if (key.type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
|
|
128
|
+
return (key.expressions.length === 0 &&
|
|
129
|
+
key.quasis.length === 1 &&
|
|
130
|
+
key.quasis[0].value.cooked === 'transaction');
|
|
131
|
+
}
|
|
84
132
|
return false;
|
|
85
133
|
}
|
|
134
|
+
function isTransactionOptionObject(arg) {
|
|
135
|
+
if (arg.type !== utils_1.AST_NODE_TYPES.ObjectExpression)
|
|
136
|
+
return false;
|
|
137
|
+
return arg.properties.some((property) => {
|
|
138
|
+
if (property.type !== utils_1.AST_NODE_TYPES.Property) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
const key = property.key;
|
|
142
|
+
return isTransactionKey(key);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function hasTransactionOption(args) {
|
|
146
|
+
return args.some(isTransactionOptionObject);
|
|
147
|
+
}
|
|
148
|
+
function getFetchCallContext(node) {
|
|
149
|
+
const transactionScopesForCall = getTransactionScopeChain(node);
|
|
150
|
+
const transactionScope = transactionScopesForCall[0];
|
|
151
|
+
if (!transactionScope)
|
|
152
|
+
return null;
|
|
153
|
+
return {
|
|
154
|
+
transactionScope,
|
|
155
|
+
transactionScopesForCall,
|
|
156
|
+
callHasTransaction: hasTransactionOption(node.arguments),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function ensureFetcherScope(scope) {
|
|
160
|
+
if (!fetcherInstances.has(scope)) {
|
|
161
|
+
fetcherInstances.set(scope, new Map());
|
|
162
|
+
}
|
|
163
|
+
return fetcherInstances.get(scope);
|
|
164
|
+
}
|
|
165
|
+
function addFetcherInstance(scope, variableName, instance) {
|
|
166
|
+
const scopeMap = ensureFetcherScope(scope);
|
|
167
|
+
if (!scopeMap.has(variableName)) {
|
|
168
|
+
scopeMap.set(variableName, []);
|
|
169
|
+
}
|
|
170
|
+
scopeMap.get(variableName).push(instance);
|
|
171
|
+
}
|
|
172
|
+
function findFetcherInstance(scopes, variableName) {
|
|
173
|
+
for (const scope of scopes) {
|
|
174
|
+
const scopeInstances = fetcherInstances.get(scope);
|
|
175
|
+
const instances = scopeInstances?.get(variableName);
|
|
176
|
+
if (instances?.length) {
|
|
177
|
+
return instances[instances.length - 1];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Determines whether a fetcher instance should be reported for mixed or unsafe
|
|
184
|
+
* usage.
|
|
185
|
+
*
|
|
186
|
+
* Reporting occurs in three scenarios:
|
|
187
|
+
* 1. The constructor lacks a transaction AND any call occurs without a transaction.
|
|
188
|
+
* 2. The constructor lacks a transaction AND no calls occur at all (suspicious
|
|
189
|
+
* unused instance in a transaction scope).
|
|
190
|
+
* 3. Constructor-level transactions make the instance always safe.
|
|
191
|
+
*
|
|
192
|
+
* If the constructor lacked a transaction but at least one call provided it, we
|
|
193
|
+
* skip reporting because the caller opted into explicit transactional use.
|
|
194
|
+
*/
|
|
195
|
+
function shouldReportFetcher(instance) {
|
|
196
|
+
if (instance.constructorHasTransaction)
|
|
197
|
+
return false;
|
|
198
|
+
if (instance.hasCallWithoutTransaction)
|
|
199
|
+
return true;
|
|
200
|
+
if (instance.hasTransactionCall)
|
|
201
|
+
return false;
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
function isFetchCall(callee) {
|
|
205
|
+
return (callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
206
|
+
callee.property.name === 'fetch');
|
|
207
|
+
}
|
|
208
|
+
function trackTransactionScope(node) {
|
|
209
|
+
if (!isFirestoreTransaction(node))
|
|
210
|
+
return;
|
|
211
|
+
const callback = node.arguments[0];
|
|
212
|
+
if (!callback)
|
|
213
|
+
return;
|
|
214
|
+
if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
215
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
216
|
+
transactionScopes.add(callback.body);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function updateInstanceTransactionState(instance, callHasTransaction) {
|
|
220
|
+
if (callHasTransaction || instance.constructorHasTransaction) {
|
|
221
|
+
instance.hasTransactionCall =
|
|
222
|
+
instance.hasTransactionCall || callHasTransaction;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
instance.hasCallWithoutTransaction = true;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function handleIdentifierFetchCall(identifier, transactionScopesForCall, callHasTransaction) {
|
|
229
|
+
const instance = findFetcherInstance(transactionScopesForCall, identifier.name);
|
|
230
|
+
if (!instance)
|
|
231
|
+
return;
|
|
232
|
+
updateInstanceTransactionState(instance, callHasTransaction);
|
|
233
|
+
}
|
|
234
|
+
function isInlineFetcherNew(object) {
|
|
235
|
+
return (object.type === utils_1.AST_NODE_TYPES.NewExpression &&
|
|
236
|
+
object.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
237
|
+
isFetcherClass(object.callee.name));
|
|
238
|
+
}
|
|
239
|
+
function handleInlineNewFetchCall(object, callHasTransaction) {
|
|
240
|
+
const constructorHasTransaction = hasTransactionOption(object.arguments);
|
|
241
|
+
if (constructorHasTransaction || callHasTransaction) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const className = object.callee.name;
|
|
245
|
+
context.report({
|
|
246
|
+
node: object,
|
|
247
|
+
messageId: 'noMixedTransactionsFetcher',
|
|
248
|
+
data: {
|
|
249
|
+
className,
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function handleFetchCall(node, callee) {
|
|
254
|
+
const contextForCall = getFetchCallContext(node);
|
|
255
|
+
if (!contextForCall)
|
|
256
|
+
return;
|
|
257
|
+
const { transactionScopesForCall, callHasTransaction } = contextForCall;
|
|
258
|
+
const object = callee.object;
|
|
259
|
+
if (object.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
260
|
+
handleIdentifierFetchCall(object, transactionScopesForCall, callHasTransaction);
|
|
261
|
+
}
|
|
262
|
+
else if (isInlineFetcherNew(object)) {
|
|
263
|
+
handleInlineNewFetchCall(object, callHasTransaction);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function reportNonFetcherInTransaction(node, className) {
|
|
267
|
+
context.report({
|
|
268
|
+
node,
|
|
269
|
+
messageId: 'noMixedTransactions',
|
|
270
|
+
data: {
|
|
271
|
+
className,
|
|
272
|
+
transactionalClass: getTransactionalClassName(className),
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
function isNewExpressionUsedForFetchCall(node) {
|
|
277
|
+
return (node.parent?.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
278
|
+
node.parent.object === node &&
|
|
279
|
+
isFetchCall(node.parent) &&
|
|
280
|
+
node.parent.parent?.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
281
|
+
node.parent.parent.callee === node.parent);
|
|
282
|
+
}
|
|
283
|
+
function handleFetcherWithoutVariable(node, className, constructorHasTransaction) {
|
|
284
|
+
if (constructorHasTransaction)
|
|
285
|
+
return;
|
|
286
|
+
if (isNewExpressionUsedForFetchCall(node))
|
|
287
|
+
return;
|
|
288
|
+
context.report({
|
|
289
|
+
node,
|
|
290
|
+
messageId: 'noMixedTransactionsFetcher',
|
|
291
|
+
data: {
|
|
292
|
+
className,
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
function handleFetcherWithVariable(node, className, constructorHasTransaction, variableName, transactionScope) {
|
|
297
|
+
addFetcherInstance(transactionScope, variableName, {
|
|
298
|
+
node,
|
|
299
|
+
className,
|
|
300
|
+
constructorHasTransaction,
|
|
301
|
+
hasTransactionCall: constructorHasTransaction,
|
|
302
|
+
hasCallWithoutTransaction: false,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
function handleFetcherClass(node, className, transactionScope) {
|
|
306
|
+
const constructorHasTransaction = hasTransactionOption(node.arguments);
|
|
307
|
+
const variableName = getVariableName(node);
|
|
308
|
+
if (!variableName) {
|
|
309
|
+
handleFetcherWithoutVariable(node, className, constructorHasTransaction);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
handleFetcherWithVariable(node, className, constructorHasTransaction, variableName, transactionScope);
|
|
313
|
+
}
|
|
314
|
+
function handleNewExpression(node) {
|
|
315
|
+
if (!isNonTransactionalClass(node))
|
|
316
|
+
return;
|
|
317
|
+
const transactionScope = getTransactionScope(node);
|
|
318
|
+
if (!transactionScope)
|
|
319
|
+
return;
|
|
320
|
+
const className = node.callee.name;
|
|
321
|
+
if (!isFetcherClass(className)) {
|
|
322
|
+
reportNonFetcherInTransaction(node, className);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
handleFetcherClass(node, className, transactionScope);
|
|
326
|
+
}
|
|
327
|
+
function reportFetcherInstance(instance) {
|
|
328
|
+
if (!shouldReportFetcher(instance))
|
|
329
|
+
return;
|
|
330
|
+
context.report({
|
|
331
|
+
node: instance.node,
|
|
332
|
+
messageId: 'noMixedTransactionsFetcher',
|
|
333
|
+
data: {
|
|
334
|
+
className: instance.className,
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
function reportFetcherInstances(instances) {
|
|
339
|
+
for (const instance of instances) {
|
|
340
|
+
reportFetcherInstance(instance);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function reportFetcherScope(scope) {
|
|
344
|
+
for (const instances of scope.values()) {
|
|
345
|
+
reportFetcherInstances(instances);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function reportAllFetcherScopes() {
|
|
349
|
+
for (const scope of fetcherInstances.values()) {
|
|
350
|
+
reportFetcherScope(scope);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
86
353
|
return {
|
|
87
|
-
|
|
88
|
-
|
|
354
|
+
NewExpression: handleNewExpression,
|
|
355
|
+
CallExpression(node) {
|
|
356
|
+
const callee = node.callee;
|
|
357
|
+
if (callee.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
89
358
|
return;
|
|
90
|
-
const callback = node.arguments[0];
|
|
91
|
-
if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
92
|
-
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
93
|
-
transactionScopes.add(callback.body);
|
|
94
359
|
}
|
|
360
|
+
if (!isFetchCall(callee)) {
|
|
361
|
+
trackTransactionScope(node);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
handleFetchCall(node, callee);
|
|
95
365
|
},
|
|
96
366
|
'CallExpression[callee.property.name="runTransaction"]:exit'(node) {
|
|
97
367
|
const callback = node.arguments[0];
|
|
368
|
+
if (!callback)
|
|
369
|
+
return;
|
|
98
370
|
if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
99
371
|
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
100
372
|
transactionScopes.delete(callback.body);
|
|
101
373
|
}
|
|
102
374
|
},
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return;
|
|
106
|
-
const className = node.callee.name;
|
|
107
|
-
context.report({
|
|
108
|
-
node,
|
|
109
|
-
messageId: 'noMixedTransactions',
|
|
110
|
-
data: {
|
|
111
|
-
className,
|
|
112
|
-
transactionalClass: getTransactionalClassName(className),
|
|
113
|
-
},
|
|
114
|
-
});
|
|
375
|
+
'Program:exit'() {
|
|
376
|
+
reportAllFetcherScopes();
|
|
115
377
|
},
|
|
116
378
|
};
|
|
117
379
|
},
|
|
@@ -14,12 +14,12 @@ exports.noMockFirebaseAdmin = (0, createRule_1.createRule)({
|
|
|
14
14
|
meta: {
|
|
15
15
|
type: 'problem',
|
|
16
16
|
docs: {
|
|
17
|
-
description: 'Prevent mocking of
|
|
17
|
+
description: 'Prevent direct mocking of firebaseAdmin; use shared test helpers instead',
|
|
18
18
|
recommended: 'error',
|
|
19
19
|
},
|
|
20
20
|
schema: [],
|
|
21
21
|
messages: {
|
|
22
|
-
noMockFirebaseAdmin: 'Do not mock firebaseAdmin
|
|
22
|
+
noMockFirebaseAdmin: 'Do not mock firebaseAdmin module "{{modulePath}}". The project already ships a stable mock in jest.setup.node.js; overriding it creates divergent Firestore/Auth state and brittle test fixtures. Keep the shared mock and use __test-utils__/mockFirestore to seed data instead of replacing the module.',
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
defaultOptions: [],
|
|
@@ -58,6 +58,9 @@ exports.noMockFirebaseAdmin = (0, createRule_1.createRule)({
|
|
|
58
58
|
context.report({
|
|
59
59
|
node,
|
|
60
60
|
messageId: 'noMockFirebaseAdmin',
|
|
61
|
+
data: {
|
|
62
|
+
modulePath: mockPath,
|
|
63
|
+
},
|
|
61
64
|
});
|
|
62
65
|
}
|
|
63
66
|
}
|
|
@@ -29,7 +29,7 @@ const utils_1 = require("@typescript-eslint/utils");
|
|
|
29
29
|
const ts = __importStar(require("typescript"));
|
|
30
30
|
exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
|
|
31
31
|
create(context) {
|
|
32
|
-
const sourceCode = context.
|
|
32
|
+
const sourceCode = context.sourceCode;
|
|
33
33
|
const parserServices = sourceCode.parserServices;
|
|
34
34
|
// If TypeScript parser services are not available, return an empty object
|
|
35
35
|
if (!parserServices ||
|
|
@@ -191,11 +191,13 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
|
|
|
191
191
|
// Check if the call is Object.values()
|
|
192
192
|
if (isObjectValuesCall(node)) {
|
|
193
193
|
const argument = node.arguments[0];
|
|
194
|
+
const argumentText = sourceCode.getText(argument);
|
|
194
195
|
// Quick check for string literals and template literals
|
|
195
196
|
if (isStringLiteral(argument)) {
|
|
196
197
|
context.report({
|
|
197
198
|
node,
|
|
198
199
|
messageId: 'unexpected',
|
|
200
|
+
data: { argument: argumentText },
|
|
199
201
|
});
|
|
200
202
|
return;
|
|
201
203
|
}
|
|
@@ -204,6 +206,7 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
|
|
|
204
206
|
context.report({
|
|
205
207
|
node,
|
|
206
208
|
messageId: 'unexpected',
|
|
209
|
+
data: { argument: argumentText },
|
|
207
210
|
});
|
|
208
211
|
return;
|
|
209
212
|
}
|
|
@@ -220,6 +223,7 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
|
|
|
220
223
|
context.report({
|
|
221
224
|
node,
|
|
222
225
|
messageId: 'unexpected',
|
|
226
|
+
data: { argument: argumentText },
|
|
223
227
|
});
|
|
224
228
|
return;
|
|
225
229
|
}
|
|
@@ -234,6 +238,7 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
|
|
|
234
238
|
context.report({
|
|
235
239
|
node,
|
|
236
240
|
messageId: 'unexpected',
|
|
241
|
+
data: { argument: argumentText },
|
|
237
242
|
});
|
|
238
243
|
return;
|
|
239
244
|
}
|
|
@@ -243,6 +248,7 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
|
|
|
243
248
|
context.report({
|
|
244
249
|
node,
|
|
245
250
|
messageId: 'unexpected',
|
|
251
|
+
data: { argument: argumentText },
|
|
246
252
|
});
|
|
247
253
|
return;
|
|
248
254
|
}
|
|
@@ -284,9 +290,11 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
|
|
|
284
290
|
.filter((p) => p.type === utils_1.AST_NODE_TYPES.Identifier)
|
|
285
291
|
.map((p) => p.name);
|
|
286
292
|
if (paramNames.includes(argument.name)) {
|
|
293
|
+
const argumentText = sourceCode.getText(argument);
|
|
287
294
|
context.report({
|
|
288
295
|
node: call,
|
|
289
296
|
messageId: 'unexpected',
|
|
297
|
+
data: { argument: argumentText },
|
|
290
298
|
});
|
|
291
299
|
}
|
|
292
300
|
}
|
|
@@ -304,7 +312,7 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
|
|
|
304
312
|
},
|
|
305
313
|
schema: [],
|
|
306
314
|
messages: {
|
|
307
|
-
unexpected: 'Object.values()
|
|
315
|
+
unexpected: 'Object.values() receives string-like argument {{argument}}. Strings are iterable, so Object.values returns one entry per character instead of object properties, which signals the argument is not an object. Pass an object (for example, a Record or Object.fromEntries(map)) to Object.values or use string helpers (split, spread, Array.from) to work with text.',
|
|
308
316
|
},
|
|
309
317
|
},
|
|
310
318
|
defaultOptions: [],
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.noOverridableMethodCallsInConstructor = void 0;
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
5
|
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const getMethodName_1 = require("../utils/getMethodName");
|
|
6
7
|
exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
7
8
|
name: 'no-overridable-method-calls-in-constructor',
|
|
8
9
|
meta: {
|
|
@@ -14,11 +15,12 @@ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
|
14
15
|
fixable: undefined,
|
|
15
16
|
schema: [],
|
|
16
17
|
messages: {
|
|
17
|
-
noOverridableMethodCallsInConstructor: '
|
|
18
|
+
noOverridableMethodCallsInConstructor: 'What\'s wrong: Constructor calls overridable or abstract {{target}} member "{{methodName}}". \u2192 Why it matters: This executes subclass overrides before the subclass constructor finishes initializing its fields, which can read undefined state or run side effects on a partially constructed instance. \u2192 How to fix: Move the call to a post-construction initializer, or make "{{methodName}}" private or static so construction never executes overridable code.',
|
|
18
19
|
},
|
|
19
20
|
},
|
|
20
21
|
defaultOptions: [],
|
|
21
22
|
create(context) {
|
|
23
|
+
const sourceCode = context.getSourceCode();
|
|
22
24
|
/**
|
|
23
25
|
* Checks if a method is overridable (not private and not static)
|
|
24
26
|
*/
|
|
@@ -40,8 +42,8 @@ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
|
40
42
|
/**
|
|
41
43
|
* Checks if a method is abstract
|
|
42
44
|
*/
|
|
43
|
-
function isAbstractMethod(
|
|
44
|
-
return
|
|
45
|
+
function isAbstractMethod(node) {
|
|
46
|
+
return node.abstract === true;
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
47
49
|
* Collects all method names from a class, categorized by whether they are overridable
|
|
@@ -51,7 +53,7 @@ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
|
51
53
|
const abstractMethods = new Set();
|
|
52
54
|
for (const member of classNode.body.body) {
|
|
53
55
|
if (member.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
|
|
54
|
-
const methodName =
|
|
56
|
+
const methodName = getMethodNameFromDefinition(member);
|
|
55
57
|
if (methodName) {
|
|
56
58
|
if (isAbstractMethod(member)) {
|
|
57
59
|
abstractMethods.add(methodName);
|
|
@@ -62,7 +64,7 @@ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
else if (member.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition) {
|
|
65
|
-
const methodName =
|
|
67
|
+
const methodName = getMethodNameFromDefinition(member);
|
|
66
68
|
if (methodName) {
|
|
67
69
|
abstractMethods.add(methodName);
|
|
68
70
|
}
|
|
@@ -71,19 +73,13 @@ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
|
71
73
|
return { overridableMethods, abstractMethods };
|
|
72
74
|
}
|
|
73
75
|
/**
|
|
74
|
-
* Gets the method name from a
|
|
76
|
+
* Gets the method name from a method definition
|
|
75
77
|
*/
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return typeof key.value === 'string'
|
|
82
|
-
? key.value
|
|
83
|
-
: String(key.value);
|
|
84
|
-
default:
|
|
85
|
-
return null; // Computed property names are not handled
|
|
86
|
-
}
|
|
78
|
+
function getMethodNameFromDefinition(method) {
|
|
79
|
+
const name = (0, getMethodName_1.getMethodName)(method, sourceCode, {
|
|
80
|
+
computedFallbackToText: false,
|
|
81
|
+
});
|
|
82
|
+
return name || null; // Computed property names are not handled
|
|
87
83
|
}
|
|
88
84
|
/**
|
|
89
85
|
* Checks if a call expression is calling a method on 'this' or 'super'
|
|
@@ -185,6 +181,10 @@ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
|
185
181
|
context.report({
|
|
186
182
|
node: bodyNode,
|
|
187
183
|
messageId: 'noOverridableMethodCallsInConstructor',
|
|
184
|
+
data: {
|
|
185
|
+
methodName,
|
|
186
|
+
target: isSuper ? 'super' : 'this',
|
|
187
|
+
},
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
190
|
// Overridable methods are a problem when called on 'this'
|
|
@@ -192,6 +192,10 @@ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
|
192
192
|
context.report({
|
|
193
193
|
node: bodyNode,
|
|
194
194
|
messageId: 'noOverridableMethodCallsInConstructor',
|
|
195
|
+
data: {
|
|
196
|
+
methodName,
|
|
197
|
+
target: 'this',
|
|
198
|
+
},
|
|
195
199
|
});
|
|
196
200
|
}
|
|
197
201
|
}
|
|
@@ -213,6 +217,10 @@ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
|
213
217
|
context.report({
|
|
214
218
|
node: bodyNode,
|
|
215
219
|
messageId: 'noOverridableMethodCallsInConstructor',
|
|
220
|
+
data: {
|
|
221
|
+
methodName: propertyName,
|
|
222
|
+
target: isSuper ? 'super' : 'this',
|
|
223
|
+
},
|
|
216
224
|
});
|
|
217
225
|
}
|
|
218
226
|
// Overridable methods are a problem when accessed on 'this'
|
|
@@ -220,6 +228,10 @@ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
|
220
228
|
context.report({
|
|
221
229
|
node: bodyNode,
|
|
222
230
|
messageId: 'noOverridableMethodCallsInConstructor',
|
|
231
|
+
data: {
|
|
232
|
+
methodName: propertyName,
|
|
233
|
+
target: 'this',
|
|
234
|
+
},
|
|
223
235
|
});
|
|
224
236
|
}
|
|
225
237
|
}
|