@blumintinc/eslint-plugin-blumint 1.13.0 → 1.15.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 +169 -139
- package/lib/index.js +200 -9
- package/lib/rules/array-methods-this-context.js +1 -1
- package/lib/rules/avoid-utils-directory.js +0 -4
- package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
- package/lib/rules/consistent-callback-naming.js +43 -4
- package/lib/rules/dynamic-https-errors.d.ts +1 -1
- package/lib/rules/dynamic-https-errors.js +134 -44
- package/lib/rules/enforce-assert-safe-object-key.js +11 -11
- package/lib/rules/enforce-boolean-naming-prefixes.js +11 -235
- 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-date-ttime.d.ts +1 -0
- package/lib/rules/enforce-date-ttime.js +156 -0
- 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.d.ts +2 -1
- package/lib/rules/enforce-dynamic-firebase-imports.js +3 -2
- 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-f-extension-for-entry-points.d.ts +8 -0
- package/lib/rules/enforce-f-extension-for-entry-points.js +283 -0
- 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 +4 -4
- package/lib/rules/enforce-id-capitalization.js +1 -1
- package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
- package/lib/rules/enforce-memoize-async.js +71 -21
- 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 +44 -18
- 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 +3818 -4644
- 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 +66 -23
- 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 +801 -0
- package/lib/rules/memo-nested-react-components.d.ts +8 -0
- package/lib/rules/memo-nested-react-components.js +415 -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 +215 -12
- package/lib/rules/no-circular-references.d.ts +2 -1
- package/lib/rules/no-circular-references.js +31 -32
- 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 +527 -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 +52 -7
- package/lib/rules/no-excessive-parent-chain.js +4 -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 +465 -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.d.ts +2 -2
- package/lib/rules/no-passthrough-getters.js +120 -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 +508 -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 +344 -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 +143 -59
- 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 +118 -8
- 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 +221 -172
- package/lib/rules/prefer-settings-object.js +27 -10
- package/lib/rules/prefer-type-alias-over-typeof-constant.js +9 -0
- 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-usememo-over-useeffect-usestate.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 +540 -0
- package/lib/rules/react-memoize-literals.d.ts +4 -0
- package/lib/rules/react-memoize-literals.js +614 -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 +155 -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 +18 -10
- 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 +47 -2
- package/lib/utils/ASTHelpers.js +370 -112
- 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
|
@@ -5,8 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.enforceIdentifiableFirestoreType = void 0;
|
|
7
7
|
const utils_1 = require("@typescript-eslint/utils");
|
|
8
|
-
const createRule_1 = require("../utils/createRule");
|
|
9
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const createRule_1 = require("../utils/createRule");
|
|
10
|
+
const TRANSPARENT_TYPE_NAMES = new Set(['Readonly', 'Resolve']);
|
|
10
11
|
exports.enforceIdentifiableFirestoreType = (0, createRule_1.createRule)({
|
|
11
12
|
name: 'enforce-identifiable-firestore-type',
|
|
12
13
|
meta: {
|
|
@@ -63,94 +64,143 @@ exports.enforceIdentifiableFirestoreType = (0, createRule_1.createRule)({
|
|
|
63
64
|
TSTypeAliasDeclaration(node) {
|
|
64
65
|
if (node.id.name === folderName) {
|
|
65
66
|
hasExpectedType = true;
|
|
66
|
-
|
|
67
|
+
const findTypeAliasAnnotation = (typeName) => {
|
|
68
|
+
let scope = context.getScope();
|
|
69
|
+
while (scope) {
|
|
70
|
+
const variable = scope.variables.find((variableNode) => variableNode.name === typeName);
|
|
71
|
+
if (variable) {
|
|
72
|
+
const definition = variable.defs.find((definition) => definition.node.type ===
|
|
73
|
+
utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration);
|
|
74
|
+
if (definition &&
|
|
75
|
+
definition.node.type ===
|
|
76
|
+
utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration &&
|
|
77
|
+
definition.node.typeAnnotation) {
|
|
78
|
+
return definition.node.typeAnnotation;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
scope = scope.upper;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
};
|
|
85
|
+
const isParenthesizedType = (node) => node?.type === 'TSParenthesizedType';
|
|
86
|
+
const isReadonlyTypeOperator = (node) => node?.type === utils_1.AST_NODE_TYPES.TSTypeOperator &&
|
|
87
|
+
node.operator === 'readonly';
|
|
88
|
+
const unwrapTransparentType = (typeNode) => {
|
|
89
|
+
let current = typeNode;
|
|
90
|
+
while (current) {
|
|
91
|
+
if (isParenthesizedType(current)) {
|
|
92
|
+
const parenthesized = current;
|
|
93
|
+
current = parenthesized.typeAnnotation ?? null;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (isReadonlyTypeOperator(current)) {
|
|
97
|
+
current = current.typeAnnotation ?? null;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
return current ?? null;
|
|
103
|
+
};
|
|
67
104
|
const findIdentifiable = (type, checkedTypes = new Set()) => {
|
|
68
|
-
|
|
105
|
+
const resolvedType = unwrapTransparentType(type);
|
|
106
|
+
if (!resolvedType) {
|
|
69
107
|
return false;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
108
|
+
}
|
|
109
|
+
if (resolvedType.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
110
|
+
resolvedType.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
111
|
+
const typeName = resolvedType.typeName.name;
|
|
73
112
|
if (typeName === 'Identifiable') {
|
|
74
113
|
return true;
|
|
75
114
|
}
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const def = variable.defs.find((d) => d.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration);
|
|
83
|
-
if (def &&
|
|
84
|
-
'typeAnnotation' in def.node &&
|
|
85
|
-
def.node.typeAnnotation) {
|
|
86
|
-
return findIdentifiable(def.node.typeAnnotation, checkedTypes);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
// Try looking in the parent scope
|
|
90
|
-
if (scope.upper) {
|
|
91
|
-
const parentVariable = scope.upper.variables.find((v) => v.name === typeName);
|
|
92
|
-
if (parentVariable) {
|
|
93
|
-
const def = parentVariable.defs.find((d) => d.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration);
|
|
94
|
-
if (def &&
|
|
95
|
-
'typeAnnotation' in def.node &&
|
|
96
|
-
def.node.typeAnnotation) {
|
|
97
|
-
return findIdentifiable(def.node.typeAnnotation, checkedTypes);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
115
|
+
if (TRANSPARENT_TYPE_NAMES.has(typeName) &&
|
|
116
|
+
resolvedType.typeParameters?.params?.some((param) => findIdentifiable(param, checkedTypes))) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (checkedTypes.has(typeName)) {
|
|
120
|
+
return false;
|
|
101
121
|
}
|
|
122
|
+
checkedTypes.add(typeName);
|
|
123
|
+
const aliasAnnotation = findTypeAliasAnnotation(typeName);
|
|
124
|
+
return findIdentifiable(aliasAnnotation, checkedTypes);
|
|
102
125
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return type.types.some((part) => findIdentifiable(part, checkedTypes));
|
|
126
|
+
if (resolvedType.type === utils_1.AST_NODE_TYPES.TSIntersectionType) {
|
|
127
|
+
return resolvedType.types.some((part) => findIdentifiable(part, new Set(checkedTypes)));
|
|
106
128
|
}
|
|
107
129
|
return false;
|
|
108
130
|
};
|
|
109
|
-
// Check if type extends Identifiable
|
|
110
|
-
const checkIdentifiableExtension = (type) => {
|
|
111
|
-
if (!type)
|
|
112
|
-
return false;
|
|
113
|
-
return findIdentifiable(type);
|
|
114
|
-
};
|
|
115
131
|
// Check if type has id: string field
|
|
116
|
-
const checkIdField = (type) => {
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
return
|
|
132
|
+
const checkIdField = (type, visitedTypes = new Set()) => {
|
|
133
|
+
const resolvedType = unwrapTransparentType(type);
|
|
134
|
+
if (!resolvedType) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
if (resolvedType.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
|
|
138
|
+
return resolvedType.members.some((member) => member.type === utils_1.AST_NODE_TYPES.TSPropertySignature &&
|
|
120
139
|
member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
121
140
|
member.key.name === 'id' &&
|
|
122
141
|
member.typeAnnotation?.typeAnnotation.type ===
|
|
123
142
|
utils_1.AST_NODE_TYPES.TSStringKeyword);
|
|
124
143
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
144
|
+
if (resolvedType.type === utils_1.AST_NODE_TYPES.TSIntersectionType) {
|
|
145
|
+
return resolvedType.types.some((part) => checkIdField(part, new Set(visitedTypes)));
|
|
146
|
+
}
|
|
147
|
+
if (resolvedType.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
148
|
+
if (resolvedType.typeName.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const typeName = resolvedType.typeName.name;
|
|
152
|
+
if (typeName === 'Identifiable') {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
if (TRANSPARENT_TYPE_NAMES.has(typeName) &&
|
|
156
|
+
resolvedType.typeParameters?.params?.some((param) => checkIdField(param, visitedTypes))) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
if (visitedTypes.has(typeName)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
visitedTypes.add(typeName);
|
|
163
|
+
const referencedType = findTypeAliasAnnotation(typeName);
|
|
164
|
+
return checkIdField(referencedType, visitedTypes);
|
|
128
165
|
}
|
|
129
166
|
return false;
|
|
130
167
|
};
|
|
131
168
|
// Check if type is wrapped in a utility type
|
|
132
169
|
const isUtilityType = (type) => {
|
|
170
|
+
if (!type) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
133
173
|
return (type.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
134
174
|
type.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
135
175
|
type.typeName.name === 'Resolve');
|
|
136
176
|
};
|
|
137
177
|
// Recursively check the type and its parameters
|
|
138
|
-
const checkType = (type) => {
|
|
139
|
-
|
|
140
|
-
if (
|
|
178
|
+
const checkType = (type, visitedTypes = new Set()) => {
|
|
179
|
+
const resolvedType = unwrapTransparentType(type);
|
|
180
|
+
if (!resolvedType) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
if (findIdentifiable(resolvedType)) {
|
|
141
184
|
return true;
|
|
142
185
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
checkIdField(
|
|
186
|
+
if (isUtilityType(resolvedType) &&
|
|
187
|
+
resolvedType.typeParameters?.params?.[0] &&
|
|
188
|
+
checkIdField(resolvedType.typeParameters.params[0])) {
|
|
146
189
|
return true;
|
|
147
190
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
191
|
+
if (resolvedType.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
192
|
+
resolvedType.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
193
|
+
const typeName = resolvedType.typeName.name;
|
|
194
|
+
if (visitedTypes.has(typeName)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
visitedTypes.add(typeName);
|
|
198
|
+
const aliasAnnotation = findTypeAliasAnnotation(typeName);
|
|
199
|
+
return checkType(aliasAnnotation, visitedTypes);
|
|
200
|
+
}
|
|
201
|
+
if (resolvedType.type === utils_1.AST_NODE_TYPES.TSIntersectionType) {
|
|
202
|
+
return resolvedType.types.some((part) => checkType(part, new Set(visitedTypes)));
|
|
152
203
|
}
|
|
153
|
-
// For direct type definitions, require extending Identifiable
|
|
154
204
|
return false;
|
|
155
205
|
};
|
|
156
206
|
typeHasIdentifiable = checkType(node.typeAnnotation);
|
|
@@ -3,7 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.enforceMemoizeAsync = void 0;
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
5
|
const createRule_1 = require("../utils/createRule");
|
|
6
|
-
const MEMOIZE_MODULE = 'typescript-memoize';
|
|
6
|
+
const MEMOIZE_MODULE = '@blumintinc/typescript-memoize';
|
|
7
|
+
const ALLOWED_MEMOIZE_MODULES = new Set([
|
|
8
|
+
MEMOIZE_MODULE,
|
|
9
|
+
'typescript-memoize',
|
|
10
|
+
]);
|
|
7
11
|
/**
|
|
8
12
|
* Matches a memoize decorator in supported syntaxes:
|
|
9
13
|
* - @Alias()
|
|
@@ -38,33 +42,34 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
|
|
|
38
42
|
meta: {
|
|
39
43
|
type: 'suggestion',
|
|
40
44
|
docs: {
|
|
41
|
-
description: 'Enforce @Memoize() decorator on async methods with 0-1 parameters to cache results and prevent redundant API calls or expensive computations.
|
|
45
|
+
description: 'Enforce @Memoize() decorator on async methods with 0-1 parameters to cache results and prevent redundant API calls or expensive computations. Without memoization, repeated calls trigger redundant requests or expensive computations, increasing latency. @Memoize() caches results by parameter, ensuring subsequent calls with identical inputs return immediately.',
|
|
42
46
|
recommended: 'error',
|
|
43
47
|
},
|
|
44
48
|
fixable: 'code',
|
|
45
49
|
schema: [],
|
|
46
50
|
messages: {
|
|
47
|
-
requireMemoize: 'Async methods with 0-1 parameters should be decorated with @Memoize()
|
|
51
|
+
requireMemoize: 'Async methods with 0-1 parameters should be decorated with @Memoize(). Without memoization, repeated calls trigger redundant network requests or expensive computations, increasing latency and risk of rate-limiting. @Memoize() caches results by parameter, ensuring subsequent calls with identical inputs return immediately. Fix: add @Memoize() above the method and import Memoize from "@blumintinc/typescript-memoize".',
|
|
48
52
|
},
|
|
49
53
|
},
|
|
50
54
|
defaultOptions: [],
|
|
51
55
|
create(context) {
|
|
52
56
|
let hasMemoizeImport = false;
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
const memoizeAliases = new Map(); // alias -> source module
|
|
58
|
+
const memoizeNamespaces = new Map(); // namespace -> source module
|
|
55
59
|
let scheduledImportFix = false;
|
|
56
60
|
return {
|
|
57
61
|
ImportDeclaration(node) {
|
|
58
|
-
if (node.source.value
|
|
62
|
+
if (ALLOWED_MEMOIZE_MODULES.has(String(node.source.value))) {
|
|
59
63
|
node.specifiers.forEach((spec) => {
|
|
60
64
|
if (spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
65
|
+
spec.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
61
66
|
spec.imported.name === 'Memoize') {
|
|
62
67
|
hasMemoizeImport = true;
|
|
63
|
-
|
|
68
|
+
memoizeAliases.set(spec.local.name, String(node.source.value));
|
|
64
69
|
}
|
|
65
70
|
else if (spec.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
66
71
|
hasMemoizeImport = true;
|
|
67
|
-
|
|
72
|
+
memoizeNamespaces.set(spec.local.name, String(node.source.value));
|
|
68
73
|
}
|
|
69
74
|
});
|
|
70
75
|
}
|
|
@@ -81,7 +86,39 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
|
|
|
81
86
|
return;
|
|
82
87
|
}
|
|
83
88
|
// Check if method already has @Memoize or @Memoize() decorator
|
|
84
|
-
const hasDecorator = node.decorators?.some((decorator) =>
|
|
89
|
+
const hasDecorator = node.decorators?.some((decorator) => {
|
|
90
|
+
// If no named imports were found, we assume 'Memoize' is the intended name (for legacy/global support)
|
|
91
|
+
const aliasesToCheck = memoizeAliases.size === 0
|
|
92
|
+
? ['Memoize']
|
|
93
|
+
: Array.from(memoizeAliases.keys());
|
|
94
|
+
// Check against all known aliases
|
|
95
|
+
for (const alias of aliasesToCheck) {
|
|
96
|
+
if (isMemoizeDecorator(decorator, alias)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Also check against namespaces
|
|
101
|
+
const expression = decorator.expression;
|
|
102
|
+
if (expression.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
103
|
+
!expression.computed &&
|
|
104
|
+
expression.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
105
|
+
expression.property.name === 'Memoize' &&
|
|
106
|
+
expression.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
107
|
+
memoizeNamespaces.has(expression.object.name)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
// Handle namespace call: @ns.Memoize()
|
|
111
|
+
if (expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
112
|
+
expression.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
113
|
+
!expression.callee.computed &&
|
|
114
|
+
expression.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
115
|
+
expression.callee.property.name === 'Memoize' &&
|
|
116
|
+
expression.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
117
|
+
memoizeNamespaces.has(expression.callee.object.name)) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
});
|
|
85
122
|
if (hasDecorator) {
|
|
86
123
|
return;
|
|
87
124
|
}
|
|
@@ -90,16 +127,33 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
|
|
|
90
127
|
messageId: 'requireMemoize',
|
|
91
128
|
fix(fixer) {
|
|
92
129
|
const fixes = [];
|
|
93
|
-
const sourceCode = context.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
130
|
+
const sourceCode = context.sourceCode;
|
|
131
|
+
// Determine which identifier to use for the decorator
|
|
132
|
+
let decoratorIdent = 'Memoize';
|
|
133
|
+
if (hasMemoizeImport) {
|
|
134
|
+
// Prefer 'Memoize' from the new package if available
|
|
135
|
+
if (memoizeAliases.has('Memoize') &&
|
|
136
|
+
memoizeAliases.get('Memoize') === MEMOIZE_MODULE) {
|
|
137
|
+
decoratorIdent = 'Memoize';
|
|
138
|
+
}
|
|
139
|
+
else if (memoizeAliases.size > 0) {
|
|
140
|
+
// Find first alias from new package, fallback to any alias
|
|
141
|
+
const newPackageAlias = Array.from(memoizeAliases.entries()).find(([_, pkg]) => pkg === MEMOIZE_MODULE)?.[0];
|
|
142
|
+
decoratorIdent =
|
|
143
|
+
newPackageAlias || Array.from(memoizeAliases.keys())[0];
|
|
144
|
+
}
|
|
145
|
+
else if (memoizeNamespaces.size > 0) {
|
|
146
|
+
// Prefer namespace from the new package if available
|
|
147
|
+
const newPackageNs = Array.from(memoizeNamespaces.entries()).find(([_, pkg]) => pkg === MEMOIZE_MODULE)?.[0];
|
|
148
|
+
const selectedNs = newPackageNs || Array.from(memoizeNamespaces.keys())[0];
|
|
149
|
+
decoratorIdent = `${selectedNs}.Memoize`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
97
152
|
const importStatement = `import { Memoize } from '${MEMOIZE_MODULE}';`;
|
|
98
153
|
// Add import if it's not already present; ensure we only add once per file
|
|
99
154
|
if (!hasMemoizeImport &&
|
|
100
|
-
|
|
101
|
-
!scheduledImportFix
|
|
102
|
-
!sourceCode.text.includes(importStatement)) {
|
|
155
|
+
memoizeNamespaces.size === 0 &&
|
|
156
|
+
!scheduledImportFix) {
|
|
103
157
|
const programBody = sourceCode.ast.body;
|
|
104
158
|
const firstImport = programBody.find((n) => n.type === utils_1.AST_NODE_TYPES.ImportDeclaration);
|
|
105
159
|
const anchorNode = (firstImport ?? programBody[0]);
|
|
@@ -122,11 +176,7 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
|
|
|
122
176
|
const insertionTarget = node.decorators && node.decorators.length > 0
|
|
123
177
|
? node.decorators[0]
|
|
124
178
|
: node;
|
|
125
|
-
const insertionStart = insertionTarget.range
|
|
126
|
-
? insertionTarget.range[0]
|
|
127
|
-
: node.range
|
|
128
|
-
? node.range[0]
|
|
129
|
-
: 0;
|
|
179
|
+
const insertionStart = insertionTarget.range?.[0] ?? node.range?.[0] ?? 0;
|
|
130
180
|
const text = sourceCode.text;
|
|
131
181
|
const lineStart = text.lastIndexOf('\n', insertionStart - 1) + 1;
|
|
132
182
|
const leadingWhitespace = text.slice(lineStart, insertionStart).match(/^[ \t]*/)?.[0] ?? '';
|
|
@@ -42,7 +42,7 @@ exports.enforceMemoizeGetters = (0, createRule_1.createRule)({
|
|
|
42
42
|
fixable: 'code',
|
|
43
43
|
schema: [],
|
|
44
44
|
messages: {
|
|
45
|
-
requireMemoizeGetter: 'Private
|
|
45
|
+
requireMemoizeGetter: 'Private getter "{{name}}" should use @Memoize() so repeated accesses reuse the same instance instead of re-instantiating and losing internal state. Add @Memoize() and import Memoize from "@blumintinc/typescript-memoize" to avoid redundant setup work.',
|
|
46
46
|
},
|
|
47
47
|
},
|
|
48
48
|
defaultOptions: [],
|
|
@@ -52,17 +52,28 @@ exports.enforceMemoizeGetters = (0, createRule_1.createRule)({
|
|
|
52
52
|
if (!/\.tsx?$/i.test(filename)) {
|
|
53
53
|
return {};
|
|
54
54
|
}
|
|
55
|
+
const sourceCode = context.getSourceCode();
|
|
55
56
|
let hasMemoizeImport = false;
|
|
56
57
|
let memoizeAlias = 'Memoize';
|
|
58
|
+
let memoizeNamespace = null;
|
|
59
|
+
let hasNamedImport = false;
|
|
57
60
|
let scheduledImportFix = false;
|
|
58
61
|
return {
|
|
59
62
|
ImportDeclaration(node) {
|
|
60
63
|
if (MEMOIZE_MODULES.has(String(node.source.value))) {
|
|
61
|
-
const spec
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
for (const spec of node.specifiers) {
|
|
65
|
+
if (spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
66
|
+
spec.imported.name === 'Memoize') {
|
|
67
|
+
hasMemoizeImport = true;
|
|
68
|
+
hasNamedImport = true;
|
|
69
|
+
memoizeAlias = spec.local.name;
|
|
70
|
+
}
|
|
71
|
+
else if (spec.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
72
|
+
hasMemoizeImport = true;
|
|
73
|
+
if (!hasNamedImport) {
|
|
74
|
+
memoizeNamespace = spec.local.name;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
66
77
|
}
|
|
67
78
|
}
|
|
68
79
|
},
|
|
@@ -76,16 +87,29 @@ exports.enforceMemoizeGetters = (0, createRule_1.createRule)({
|
|
|
76
87
|
// enforce only "private" accessibility (undefined => public)
|
|
77
88
|
if (node.accessibility !== 'private')
|
|
78
89
|
return;
|
|
79
|
-
|
|
80
|
-
const hasDecorator = node.decorators?.some((
|
|
90
|
+
const decoratorAliases = memoizeAlias === 'Memoize' ? ['Memoize'] : ['Memoize', memoizeAlias];
|
|
91
|
+
const hasDecorator = node.decorators?.some((decorator) => decoratorAliases.some((alias) => isMemoizeDecorator(decorator, alias)));
|
|
81
92
|
if (hasDecorator)
|
|
82
93
|
return;
|
|
94
|
+
const propertyName = node.computed
|
|
95
|
+
? '[computed]'
|
|
96
|
+
: sourceCode.getText(node.key);
|
|
83
97
|
context.report({
|
|
84
98
|
node,
|
|
85
99
|
messageId: 'requireMemoizeGetter',
|
|
100
|
+
data: { name: propertyName },
|
|
86
101
|
fix(fixer) {
|
|
87
102
|
const fixes = [];
|
|
88
|
-
const
|
|
103
|
+
const getDecoratorIdent = () => {
|
|
104
|
+
if (hasNamedImport) {
|
|
105
|
+
return memoizeAlias;
|
|
106
|
+
}
|
|
107
|
+
if (memoizeNamespace) {
|
|
108
|
+
return `${memoizeNamespace}.Memoize`;
|
|
109
|
+
}
|
|
110
|
+
return memoizeAlias;
|
|
111
|
+
};
|
|
112
|
+
const decoratorIdent = getDecoratorIdent();
|
|
89
113
|
// Insert import if needed, at the top alongside other imports
|
|
90
114
|
if (!hasMemoizeImport && !scheduledImportFix) {
|
|
91
115
|
const programBody = sourceCode.ast.body;
|
|
@@ -106,12 +130,12 @@ exports.enforceMemoizeGetters = (0, createRule_1.createRule)({
|
|
|
106
130
|
scheduledImportFix = true;
|
|
107
131
|
}
|
|
108
132
|
// Insert decorator above the getter (or before the first decorator), preserving indentation
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
fixes.push(fixer.insertTextBeforeRange([
|
|
133
|
+
const insertionTarget = node.decorators?.[0] ?? node;
|
|
134
|
+
const insertionStart = insertionTarget.range[0];
|
|
135
|
+
const text = sourceCode.text;
|
|
136
|
+
const lineStart = text.lastIndexOf('\n', insertionStart - 1) + 1;
|
|
137
|
+
const leadingWhitespace = text.slice(lineStart, insertionStart).match(/^[ \t]*/)?.[0] ?? '';
|
|
138
|
+
fixes.push(fixer.insertTextBeforeRange([lineStart, lineStart], `${leadingWhitespace}@${decoratorIdent}()\n`));
|
|
115
139
|
return fixes;
|
|
116
140
|
},
|
|
117
141
|
});
|
|
@@ -20,7 +20,7 @@ exports.enforceMicrodiff = (0, createRule_1.createRule)({
|
|
|
20
20
|
},
|
|
21
21
|
defaultOptions: [],
|
|
22
22
|
create(context) {
|
|
23
|
-
const sourceCode = context.
|
|
23
|
+
const sourceCode = context.sourceCode;
|
|
24
24
|
const importedDiffLibraries = new Map();
|
|
25
25
|
const importedFunctions = new Map(); // Map of imported function names to their sources
|
|
26
26
|
let hasMicrodiffImport = false;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.enforceObjectLiteralAsConst = void 0;
|
|
4
4
|
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
const ASTHelpers_1 = require("../utils/ASTHelpers");
|
|
5
6
|
exports.enforceObjectLiteralAsConst = (0, createRule_1.createRule)({
|
|
6
7
|
name: 'enforce-object-literal-as-const',
|
|
7
8
|
meta: {
|
|
@@ -55,10 +56,9 @@ exports.enforceObjectLiteralAsConst = (0, createRule_1.createRule)({
|
|
|
55
56
|
return;
|
|
56
57
|
}
|
|
57
58
|
// Check if the return statement is inside a function
|
|
58
|
-
const sourceCode = context.
|
|
59
|
-
// Use
|
|
60
|
-
|
|
61
|
-
const ancestors = context.getAncestors();
|
|
59
|
+
const sourceCode = context.sourceCode;
|
|
60
|
+
// Use ASTHelpers.getAncestors for ESLint v8/v9 compatibility
|
|
61
|
+
const ancestors = ASTHelpers_1.ASTHelpers.getAncestors(context, node);
|
|
62
62
|
const isInFunction = ancestors.some((ancestor) => ancestor.type === 'FunctionDeclaration' ||
|
|
63
63
|
ancestor.type === 'ArrowFunctionExpression' ||
|
|
64
64
|
ancestor.type === 'FunctionExpression' ||
|
|
@@ -847,6 +847,10 @@ const DIS_EXCEPTIONS = [
|
|
|
847
847
|
'dismissal',
|
|
848
848
|
'disk',
|
|
849
849
|
'disks',
|
|
850
|
+
'dispute',
|
|
851
|
+
'disputed',
|
|
852
|
+
'disputes',
|
|
853
|
+
'disputing',
|
|
850
854
|
];
|
|
851
855
|
// Words that contain negative prefixes but should be treated as valid
|
|
852
856
|
const EXCEPTION_WORDS_SET = new Set([...IN_EXCEPTIONS, ...NO_EXCEPTIONS, ...UN_EXCEPTIONS, ...DIS_EXCEPTIONS].map((word) => word.toLowerCase()));
|
|
@@ -34,14 +34,48 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
34
34
|
function endsWithProps(typeName) {
|
|
35
35
|
return typeName.endsWith('Props');
|
|
36
36
|
}
|
|
37
|
+
// Get all parameters that have a "Props" type
|
|
38
|
+
function getPropsParams(params) {
|
|
39
|
+
return params
|
|
40
|
+
.map((param) => {
|
|
41
|
+
const id = getIdFromParam(param);
|
|
42
|
+
if (id && id.typeAnnotation && id.typeAnnotation.typeAnnotation) {
|
|
43
|
+
const paramTypeName = getTypeName(id.typeAnnotation.typeAnnotation);
|
|
44
|
+
if (paramTypeName && endsWithProps(paramTypeName)) {
|
|
45
|
+
return { id, typeName: paramTypeName };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
})
|
|
50
|
+
.filter((p) => !!p);
|
|
51
|
+
}
|
|
37
52
|
// Generate suggested parameter name for Props types
|
|
38
|
-
function getSuggestedParameterName(typeName, allParams, currentParam) {
|
|
53
|
+
function getSuggestedParameterName(typeName, allParams, currentParam, propsParams) {
|
|
39
54
|
const currentId = getIdFromParam(currentParam);
|
|
40
55
|
const currentName = currentId?.name;
|
|
41
56
|
const existingNames = new Set(allParams
|
|
42
57
|
.map((p) => getIdFromParam(p))
|
|
43
58
|
.filter((id) => !!id && id.name !== currentName)
|
|
44
59
|
.map((id) => id.name));
|
|
60
|
+
// If multiple parameters have the exact same Props type, allow the current names.
|
|
61
|
+
// This handles cases like (prevProps: TProps, nextProps: TProps) where
|
|
62
|
+
// forcing a single name based on the type would cause a collision or
|
|
63
|
+
// break descriptive naming.
|
|
64
|
+
const sameTypeParams = propsParams.filter((p) => p.typeName === typeName);
|
|
65
|
+
if (sameTypeParams.length > 1) {
|
|
66
|
+
// Keep the current name when multiple params share the same Props type.
|
|
67
|
+
// This preserves descriptive names like prevProps/nextProps.
|
|
68
|
+
if (!currentName) {
|
|
69
|
+
// Fallback should not occur since destructured params are filtered upstream.
|
|
70
|
+
let candidate = 'props';
|
|
71
|
+
let suffix = 2;
|
|
72
|
+
while (existingNames.has(candidate)) {
|
|
73
|
+
candidate = `props${suffix++}`;
|
|
74
|
+
}
|
|
75
|
+
return candidate;
|
|
76
|
+
}
|
|
77
|
+
return currentName;
|
|
78
|
+
}
|
|
45
79
|
if (typeName === 'Props') {
|
|
46
80
|
let candidate = 'props';
|
|
47
81
|
let suffix = 2;
|
|
@@ -50,17 +84,7 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
50
84
|
}
|
|
51
85
|
return candidate;
|
|
52
86
|
}
|
|
53
|
-
//
|
|
54
|
-
const propsParams = allParams.filter((param) => {
|
|
55
|
-
if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
56
|
-
param.typeAnnotation &&
|
|
57
|
-
param.typeAnnotation.typeAnnotation) {
|
|
58
|
-
const paramTypeName = getTypeName(param.typeAnnotation.typeAnnotation);
|
|
59
|
-
return paramTypeName && endsWithProps(paramTypeName);
|
|
60
|
-
}
|
|
61
|
-
return false;
|
|
62
|
-
});
|
|
63
|
-
// If there's only one Props parameter, suggest "props"
|
|
87
|
+
// If there's only one Props parameter (regardless of type name), suggest "props"
|
|
64
88
|
if (propsParams.length <= 1) {
|
|
65
89
|
let candidate = 'props';
|
|
66
90
|
let suffix = 2;
|
|
@@ -69,8 +93,8 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
69
93
|
}
|
|
70
94
|
return candidate;
|
|
71
95
|
}
|
|
72
|
-
// If there are multiple Props parameters, use prefixed names
|
|
73
|
-
// For types like "UserProps", suggest "userProps"
|
|
96
|
+
// If there are multiple Props parameters with different types, use prefixed names.
|
|
97
|
+
// For types like "UserProps", suggest "userProps".
|
|
74
98
|
const baseName = typeName.slice(0, -5); // Remove "Props"
|
|
75
99
|
const camelCaseBase = baseName.charAt(0).toLowerCase() + baseName.slice(1);
|
|
76
100
|
let candidate = `${camelCaseBase}Props`;
|
|
@@ -137,7 +161,8 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
137
161
|
node.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
|
|
138
162
|
return;
|
|
139
163
|
}
|
|
140
|
-
const sourceCode = context.
|
|
164
|
+
const sourceCode = context.sourceCode;
|
|
165
|
+
const propsParams = getPropsParams(node.params);
|
|
141
166
|
node.params.forEach((param) => {
|
|
142
167
|
if (isDestructuredParameter(param)) {
|
|
143
168
|
return;
|
|
@@ -146,7 +171,7 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
146
171
|
if (id && id.typeAnnotation && id.typeAnnotation.typeAnnotation) {
|
|
147
172
|
const typeName = getTypeName(id.typeAnnotation.typeAnnotation);
|
|
148
173
|
if (typeName && endsWithProps(typeName)) {
|
|
149
|
-
const suggestedName = getSuggestedParameterName(typeName, node.params, param);
|
|
174
|
+
const suggestedName = getSuggestedParameterName(typeName, node.params, param, propsParams);
|
|
150
175
|
if (id.name !== suggestedName) {
|
|
151
176
|
const messageId = suggestedName === 'props'
|
|
152
177
|
? 'usePropsParameterName'
|
|
@@ -173,7 +198,8 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
173
198
|
// Check class method parameters (including constructors)
|
|
174
199
|
function checkClassMethod(node) {
|
|
175
200
|
const method = node.value;
|
|
176
|
-
const sourceCode = context.
|
|
201
|
+
const sourceCode = context.sourceCode;
|
|
202
|
+
const propsParams = getPropsParams(method.params);
|
|
177
203
|
method.params.forEach((param) => {
|
|
178
204
|
if (isDestructuredParameter(param)) {
|
|
179
205
|
return;
|
|
@@ -182,7 +208,7 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
182
208
|
if (id && id.typeAnnotation && id.typeAnnotation.typeAnnotation) {
|
|
183
209
|
const typeName = getTypeName(id.typeAnnotation.typeAnnotation);
|
|
184
210
|
if (typeName && endsWithProps(typeName)) {
|
|
185
|
-
const suggestedName = getSuggestedParameterName(typeName, method.params, param);
|
|
211
|
+
const suggestedName = getSuggestedParameterName(typeName, method.params, param, propsParams);
|
|
186
212
|
if (id.name !== suggestedName) {
|
|
187
213
|
const messageId = suggestedName === 'props'
|
|
188
214
|
? 'usePropsParameterName'
|
|
@@ -40,7 +40,7 @@ exports.enforceStableStringify = (0, createRule_1.createRule)({
|
|
|
40
40
|
const fixes = [];
|
|
41
41
|
// Add import if not present
|
|
42
42
|
if (!hasStringifyImport) {
|
|
43
|
-
const program = context.
|
|
43
|
+
const program = context.sourceCode.ast;
|
|
44
44
|
const firstImport = program.body.find((node) => node.type === utils_1.AST_NODE_TYPES.ImportDeclaration);
|
|
45
45
|
const importStatement = "import stringify from 'safe-stable-stringify';\n";
|
|
46
46
|
if (firstImport) {
|