@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,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noOverridableMethodCallsInConstructor = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const getMethodName_1 = require("../utils/getMethodName");
|
|
7
|
+
exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
|
|
8
|
+
name: 'no-overridable-method-calls-in-constructor',
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'problem',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Disallow calling overridable methods in constructors to prevent unexpected behavior',
|
|
13
|
+
recommended: 'error',
|
|
14
|
+
},
|
|
15
|
+
fixable: undefined,
|
|
16
|
+
schema: [],
|
|
17
|
+
messages: {
|
|
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.',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
const sourceCode = context.getSourceCode();
|
|
24
|
+
/**
|
|
25
|
+
* Checks if a method is overridable (not private and not static)
|
|
26
|
+
*/
|
|
27
|
+
function isOverridableMethod(node) {
|
|
28
|
+
// Private methods are not overridable
|
|
29
|
+
if (node.accessibility === 'private') {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
// ES private methods (#foo) are non-overridable
|
|
33
|
+
if (node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// Static methods are not overridable in the same way as instance methods
|
|
37
|
+
if (node.static) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Checks if a method is abstract
|
|
44
|
+
*/
|
|
45
|
+
function isAbstractMethod(node) {
|
|
46
|
+
return node.abstract === true;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Collects all method names from a class, categorized by whether they are overridable
|
|
50
|
+
*/
|
|
51
|
+
function collectMethodNames(classNode) {
|
|
52
|
+
const overridableMethods = new Set();
|
|
53
|
+
const abstractMethods = new Set();
|
|
54
|
+
for (const member of classNode.body.body) {
|
|
55
|
+
if (member.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
|
|
56
|
+
const methodName = getMethodNameFromDefinition(member);
|
|
57
|
+
if (methodName) {
|
|
58
|
+
if (isAbstractMethod(member)) {
|
|
59
|
+
abstractMethods.add(methodName);
|
|
60
|
+
}
|
|
61
|
+
else if (isOverridableMethod(member)) {
|
|
62
|
+
overridableMethods.add(methodName);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (member.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition) {
|
|
67
|
+
const methodName = getMethodNameFromDefinition(member);
|
|
68
|
+
if (methodName) {
|
|
69
|
+
abstractMethods.add(methodName);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { overridableMethods, abstractMethods };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Gets the method name from a method definition
|
|
77
|
+
*/
|
|
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
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Checks if a call expression is calling a method on 'this' or 'super'
|
|
86
|
+
*/
|
|
87
|
+
function isThisOrSuperMethodCall(node) {
|
|
88
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
89
|
+
!node.callee.computed // Ignore computed properties like this[methodName]()
|
|
90
|
+
) {
|
|
91
|
+
const object = node.callee.object;
|
|
92
|
+
const property = node.callee.property;
|
|
93
|
+
// Check if it's a call on 'this'
|
|
94
|
+
if (object.type === utils_1.AST_NODE_TYPES.ThisExpression &&
|
|
95
|
+
property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
96
|
+
return {
|
|
97
|
+
isMethodCall: true,
|
|
98
|
+
methodName: property.name,
|
|
99
|
+
isSuper: false,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// Check if it's a call on 'super'
|
|
103
|
+
if (object.type === utils_1.AST_NODE_TYPES.Super &&
|
|
104
|
+
property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
105
|
+
return {
|
|
106
|
+
isMethodCall: true,
|
|
107
|
+
methodName: property.name,
|
|
108
|
+
isSuper: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// Check for method chaining: this.method().anotherMethod()
|
|
112
|
+
// In this case, object is a CallExpression
|
|
113
|
+
if (object.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
114
|
+
const nestedCall = isThisOrSuperMethodCall(object);
|
|
115
|
+
if (nestedCall.isMethodCall) {
|
|
116
|
+
return nestedCall; // Return the nested method call info
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { isMethodCall: false, methodName: null, isSuper: false };
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Checks if a member expression is accessing a property on 'this' or 'super'
|
|
124
|
+
*/
|
|
125
|
+
function isThisOrSuperPropertyAccess(node) {
|
|
126
|
+
if (!node.computed) {
|
|
127
|
+
// Ignore computed properties like this[propertyName]
|
|
128
|
+
const object = node.object;
|
|
129
|
+
const property = node.property;
|
|
130
|
+
// Check if it's access on 'this'
|
|
131
|
+
if (object.type === utils_1.AST_NODE_TYPES.ThisExpression &&
|
|
132
|
+
property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
133
|
+
// Skip 'constructor' property as it's not an overridable method
|
|
134
|
+
if (property.name === 'constructor') {
|
|
135
|
+
return {
|
|
136
|
+
isPropertyAccess: false,
|
|
137
|
+
propertyName: null,
|
|
138
|
+
isSuper: false,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
isPropertyAccess: true,
|
|
143
|
+
propertyName: property.name,
|
|
144
|
+
isSuper: false,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// Check if it's access on 'super'
|
|
148
|
+
if (object.type === utils_1.AST_NODE_TYPES.Super &&
|
|
149
|
+
property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
150
|
+
return {
|
|
151
|
+
isPropertyAccess: true,
|
|
152
|
+
propertyName: property.name,
|
|
153
|
+
isSuper: true,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return { isPropertyAccess: false, propertyName: null, isSuper: false };
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
// Process class declarations and expressions
|
|
161
|
+
'ClassDeclaration, ClassExpression'(node) {
|
|
162
|
+
const { overridableMethods, abstractMethods } = collectMethodNames(node);
|
|
163
|
+
// Find the constructor method
|
|
164
|
+
const ctorMethod = node.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
165
|
+
member.kind === 'constructor');
|
|
166
|
+
if (!ctorMethod || !ctorMethod.value.body) {
|
|
167
|
+
return; // No constructor or constructor has no body
|
|
168
|
+
}
|
|
169
|
+
// Function to recursively check for method calls in the constructor
|
|
170
|
+
function checkForMethodCalls(bodyNode) {
|
|
171
|
+
if (!bodyNode || typeof bodyNode !== 'object') {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Check for method calls
|
|
175
|
+
if (bodyNode.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
176
|
+
const { isMethodCall, methodName, isSuper } = isThisOrSuperMethodCall(bodyNode);
|
|
177
|
+
if (isMethodCall && methodName) {
|
|
178
|
+
// Abstract methods are always a problem when called from constructor
|
|
179
|
+
// This includes super calls to abstract methods
|
|
180
|
+
if (abstractMethods.has(methodName)) {
|
|
181
|
+
context.report({
|
|
182
|
+
node: bodyNode,
|
|
183
|
+
messageId: 'noOverridableMethodCallsInConstructor',
|
|
184
|
+
data: {
|
|
185
|
+
methodName,
|
|
186
|
+
target: isSuper ? 'super' : 'this',
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// Overridable methods are a problem when called on 'this'
|
|
191
|
+
else if (overridableMethods.has(methodName) && !isSuper) {
|
|
192
|
+
context.report({
|
|
193
|
+
node: bodyNode,
|
|
194
|
+
messageId: 'noOverridableMethodCallsInConstructor',
|
|
195
|
+
data: {
|
|
196
|
+
methodName,
|
|
197
|
+
target: 'this',
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Check for property access (getters/setters) - only if not part of a call expression
|
|
204
|
+
else if (bodyNode.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
205
|
+
// Check if this MemberExpression is the callee of a CallExpression
|
|
206
|
+
// If so, skip it as it will be handled by the CallExpression check
|
|
207
|
+
const parent = bodyNode.parent;
|
|
208
|
+
if (parent &&
|
|
209
|
+
parent.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
210
|
+
parent.callee === bodyNode) {
|
|
211
|
+
return; // Skip this MemberExpression as it's part of a method call
|
|
212
|
+
}
|
|
213
|
+
const { isPropertyAccess, propertyName, isSuper } = isThisOrSuperPropertyAccess(bodyNode);
|
|
214
|
+
if (isPropertyAccess && propertyName) {
|
|
215
|
+
// Abstract methods are always a problem when accessed from constructor
|
|
216
|
+
if (abstractMethods.has(propertyName)) {
|
|
217
|
+
context.report({
|
|
218
|
+
node: bodyNode,
|
|
219
|
+
messageId: 'noOverridableMethodCallsInConstructor',
|
|
220
|
+
data: {
|
|
221
|
+
methodName: propertyName,
|
|
222
|
+
target: isSuper ? 'super' : 'this',
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
// Overridable methods are a problem when accessed on 'this'
|
|
227
|
+
else if (overridableMethods.has(propertyName) && !isSuper) {
|
|
228
|
+
context.report({
|
|
229
|
+
node: bodyNode,
|
|
230
|
+
messageId: 'noOverridableMethodCallsInConstructor',
|
|
231
|
+
data: {
|
|
232
|
+
methodName: propertyName,
|
|
233
|
+
target: 'this',
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// Avoid circular references by not checking 'parent' property
|
|
240
|
+
const keysToSkip = new Set(['parent', 'loc', 'range']);
|
|
241
|
+
// Don't traverse into function expressions or arrow functions as they create new scopes
|
|
242
|
+
if (bodyNode.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
243
|
+
bodyNode.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
244
|
+
bodyNode.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
// Recursively check all child nodes
|
|
248
|
+
for (const key in bodyNode) {
|
|
249
|
+
if (keysToSkip.has(key))
|
|
250
|
+
continue;
|
|
251
|
+
const child = bodyNode[key];
|
|
252
|
+
if (child && typeof child === 'object') {
|
|
253
|
+
if ('type' in child) {
|
|
254
|
+
checkForMethodCalls(child);
|
|
255
|
+
}
|
|
256
|
+
else if (Array.isArray(child)) {
|
|
257
|
+
child.forEach((item) => {
|
|
258
|
+
if (item && typeof item === 'object' && 'type' in item) {
|
|
259
|
+
checkForMethodCalls(item);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Start checking from the constructor body
|
|
267
|
+
checkForMethodCalls(ctorMethod.value.body);
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
//# sourceMappingURL=no-overridable-method-calls-in-constructor.js.map
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noPassthroughGetters = void 0;
|
|
4
|
+
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
6
|
+
const getMethodName_1 = require("../utils/getMethodName");
|
|
7
|
+
const COMPLEX_EXPRESSION_TYPES = new Set([
|
|
8
|
+
utils_1.AST_NODE_TYPES.CallExpression,
|
|
9
|
+
utils_1.AST_NODE_TYPES.TemplateLiteral,
|
|
10
|
+
utils_1.AST_NODE_TYPES.NewExpression,
|
|
11
|
+
utils_1.AST_NODE_TYPES.BinaryExpression,
|
|
12
|
+
utils_1.AST_NODE_TYPES.ArrayExpression,
|
|
13
|
+
utils_1.AST_NODE_TYPES.ObjectExpression,
|
|
14
|
+
]);
|
|
15
|
+
exports.noPassthroughGetters = (0, createRule_1.createRule)({
|
|
16
|
+
create(context) {
|
|
17
|
+
const sourceCode = context.getSourceCode();
|
|
18
|
+
return {
|
|
19
|
+
// Target getter methods in classes
|
|
20
|
+
MethodDefinition(node) {
|
|
21
|
+
// Only check getter methods
|
|
22
|
+
if (node.kind !== 'get') {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// Skip if the getter has decorators (like @Memoize)
|
|
26
|
+
if (node.decorators && node.decorators.length > 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const methodBody = node.value.body;
|
|
30
|
+
if (!methodBody) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Check if the getter body is a simple return statement
|
|
34
|
+
if (methodBody.body.length === 1 &&
|
|
35
|
+
methodBody.body[0].type === 'ReturnStatement') {
|
|
36
|
+
const returnStatement = methodBody
|
|
37
|
+
.body[0];
|
|
38
|
+
// Skip if there's no return argument
|
|
39
|
+
if (!returnStatement.argument) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Skip if the return statement uses super
|
|
43
|
+
if (containsSuper(returnStatement.argument)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Skip if the return statement includes type assertions or casting
|
|
47
|
+
if (hasTypeAssertion(returnStatement.argument)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Skip if the return statement includes null/undefined handling
|
|
51
|
+
if (hasNullUndefinedHandling(returnStatement.argument)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Skip if the return statement includes computed property access or function calls
|
|
55
|
+
if (hasComputedPropertyOrFunctionCall(returnStatement.argument)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Check if the return statement is accessing a property from a constructor parameter
|
|
59
|
+
if (isConstructorParameterPropertyAccess(returnStatement.argument)) {
|
|
60
|
+
const getterName = (0, getMethodName_1.getMethodName)(node, sourceCode, {
|
|
61
|
+
computedFallbackToText: false,
|
|
62
|
+
}) || 'getter';
|
|
63
|
+
const propertyPath = getMemberPath(returnStatement.argument) ??
|
|
64
|
+
'nested property on a constructor-injected object';
|
|
65
|
+
context.report({
|
|
66
|
+
node,
|
|
67
|
+
messageId: 'noPassthroughGetter',
|
|
68
|
+
data: {
|
|
69
|
+
getterName,
|
|
70
|
+
propertyPath,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Check if the node is a simple property access from a constructor parameter
|
|
79
|
+
* like this.settings.property or this.settings['property'] or this.settings.nested.deep.property
|
|
80
|
+
*/
|
|
81
|
+
function isConstructorParameterPropertyAccess(node) {
|
|
82
|
+
// Check for member expressions like this.settings.property
|
|
83
|
+
if (node.type === 'MemberExpression') {
|
|
84
|
+
return isConstructorParameterAccess(node);
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Check if the node is accessing a property from a constructor parameter
|
|
90
|
+
* Patterns to match: this.constructorParam.property, this.constructorParam['property'], this.constructorParam.nested.deep.property
|
|
91
|
+
* Patterns to NOT match: this.property, SomeClass.property, this.methodCall()
|
|
92
|
+
*/
|
|
93
|
+
function isConstructorParameterAccess(node) {
|
|
94
|
+
let current = node.object;
|
|
95
|
+
let depth = 0;
|
|
96
|
+
// Walk up member expressions until we reach a base
|
|
97
|
+
while (current && current.type === 'MemberExpression') {
|
|
98
|
+
depth += 1;
|
|
99
|
+
current = current.object;
|
|
100
|
+
}
|
|
101
|
+
// Require at least one nesting and a base of `this`
|
|
102
|
+
return depth >= 1 && current?.type === 'ThisExpression';
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Check if the node contains a reference to super
|
|
106
|
+
*/
|
|
107
|
+
function containsSuper(node) {
|
|
108
|
+
let current = node;
|
|
109
|
+
while (current && current.type === 'MemberExpression') {
|
|
110
|
+
if (current.object.type === 'Super') {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
current = current.object;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
function getMemberPath(node) {
|
|
118
|
+
if (node.type === 'ThisExpression') {
|
|
119
|
+
return 'this';
|
|
120
|
+
}
|
|
121
|
+
if (node.type === 'Identifier') {
|
|
122
|
+
return node.name;
|
|
123
|
+
}
|
|
124
|
+
if (node.type !== 'MemberExpression') {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const objectPath = getMemberPath(node.object);
|
|
128
|
+
if (!objectPath) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
if (node.computed && node.property.type === 'Literal') {
|
|
132
|
+
const literalValue = typeof node.property.value === 'string'
|
|
133
|
+
? JSON.stringify(node.property.value)
|
|
134
|
+
: String(node.property.value);
|
|
135
|
+
return `${objectPath}[${literalValue}]`;
|
|
136
|
+
}
|
|
137
|
+
if (node.property.type === 'Identifier') {
|
|
138
|
+
return `${objectPath}.${node.property.name}`;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Check if the node has a type assertion or casting
|
|
144
|
+
*/
|
|
145
|
+
function hasTypeAssertion(node) {
|
|
146
|
+
if (node.type === 'TSAsExpression' || node.type === 'TSTypeAssertion') {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
if (node.type === 'MemberExpression') {
|
|
150
|
+
return hasTypeAssertion(node.object);
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check if the node handles null/undefined values
|
|
156
|
+
*/
|
|
157
|
+
function hasNullUndefinedHandling(node) {
|
|
158
|
+
// Check for logical expressions like this.settings.property || []
|
|
159
|
+
if (node.type === 'LogicalExpression') {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
// Check for conditional expressions like this.settings.property ? this.settings.property : []
|
|
163
|
+
if (node.type === 'ConditionalExpression') {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
// Check for optional chaining like this.settings?.property
|
|
167
|
+
if (node.type === 'MemberExpression' && node.optional) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
// The nullish coalescing check is already covered by the LogicalExpression check above
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Check if the node includes computed property access or function calls
|
|
175
|
+
*/
|
|
176
|
+
function hasComputedPropertyOrFunctionCall(node) {
|
|
177
|
+
if (COMPLEX_EXPRESSION_TYPES.has(node.type)) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
// Check for member expressions with computed properties like this.settings[key]
|
|
181
|
+
if (node.type === 'MemberExpression') {
|
|
182
|
+
// If the property is computed with a dynamic expression (not a literal), it's not a simple property access
|
|
183
|
+
if (node.computed && node.property.type !== 'Literal') {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
// Recursively check the object part
|
|
187
|
+
return hasComputedPropertyOrFunctionCall(node.object);
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
name: 'no-passthrough-getters',
|
|
193
|
+
meta: {
|
|
194
|
+
type: 'suggestion',
|
|
195
|
+
docs: {
|
|
196
|
+
description: 'Avoid getter methods that only re-expose nested properties on constructor-injected objects without adding behavior',
|
|
197
|
+
recommended: 'error',
|
|
198
|
+
},
|
|
199
|
+
schema: [],
|
|
200
|
+
messages: {
|
|
201
|
+
noPassthroughGetter: 'Getter "{{getterName}}" only forwards nested property "{{propertyPath}}" from an object provided via the constructor. This indirection hides the real source of state and expands the class API without adding behavior. Read the constructor-injected object directly or add logic that justifies a getter (validation, memoization, fallback).',
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
defaultOptions: [],
|
|
205
|
+
});
|
|
206
|
+
//# sourceMappingURL=no-passthrough-getters.js.map
|