@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
|
@@ -3,6 +3,36 @@ 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';
|
|
7
|
+
/**
|
|
8
|
+
* Matches a memoize decorator in supported syntaxes:
|
|
9
|
+
* - @Alias()
|
|
10
|
+
* - @Alias
|
|
11
|
+
* - @ns.Alias
|
|
12
|
+
*/
|
|
13
|
+
function isMemoizeDecorator(decorator, alias) {
|
|
14
|
+
const expression = decorator.expression;
|
|
15
|
+
/* @Alias() */
|
|
16
|
+
if (expression.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
17
|
+
const callee = expression.callee;
|
|
18
|
+
return ((callee.type === utils_1.AST_NODE_TYPES.Identifier && callee.name === alias) ||
|
|
19
|
+
(callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
20
|
+
!callee.computed &&
|
|
21
|
+
callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
22
|
+
callee.property.name === alias));
|
|
23
|
+
}
|
|
24
|
+
/* @Alias */
|
|
25
|
+
if (expression.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
26
|
+
return expression.name === alias;
|
|
27
|
+
}
|
|
28
|
+
/* @ns.Alias */
|
|
29
|
+
if (expression.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
30
|
+
!expression.computed &&
|
|
31
|
+
expression.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
32
|
+
return expression.property.name === alias;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
6
36
|
exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
|
|
7
37
|
name: 'enforce-memoize-async',
|
|
8
38
|
meta: {
|
|
@@ -21,17 +51,22 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
|
|
|
21
51
|
create(context) {
|
|
22
52
|
let hasMemoizeImport = false;
|
|
23
53
|
let memoizeAlias = 'Memoize';
|
|
54
|
+
let memoizeNamespace = null;
|
|
55
|
+
let scheduledImportFix = false;
|
|
24
56
|
return {
|
|
25
57
|
ImportDeclaration(node) {
|
|
26
|
-
if (node.source.value ===
|
|
27
|
-
|
|
28
|
-
spec.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
memoizeAlias = memoizeSpecifier.local.name;
|
|
58
|
+
if (node.source.value === MEMOIZE_MODULE) {
|
|
59
|
+
node.specifiers.forEach((spec) => {
|
|
60
|
+
if (spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
61
|
+
spec.imported.name === 'Memoize') {
|
|
62
|
+
hasMemoizeImport = true;
|
|
63
|
+
memoizeAlias = spec.local?.name ?? memoizeAlias;
|
|
33
64
|
}
|
|
34
|
-
|
|
65
|
+
else if (spec.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
66
|
+
hasMemoizeImport = true;
|
|
67
|
+
memoizeNamespace = spec.local.name;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
35
70
|
}
|
|
36
71
|
},
|
|
37
72
|
MethodDefinition(node) {
|
|
@@ -45,26 +80,56 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
|
|
|
45
80
|
if (node.value.params.length > 1) {
|
|
46
81
|
return;
|
|
47
82
|
}
|
|
48
|
-
// Check if method already has @Memoize decorator
|
|
49
|
-
const hasDecorator = node.decorators?.some((decorator) =>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
const callee = decorator.expression.callee;
|
|
54
|
-
return (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
55
|
-
callee.name === memoizeAlias);
|
|
56
|
-
});
|
|
57
|
-
if (!hasDecorator && hasMemoizeImport) {
|
|
58
|
-
context.report({
|
|
59
|
-
node,
|
|
60
|
-
messageId: 'requireMemoize',
|
|
61
|
-
fix(fixer) {
|
|
62
|
-
// Add import if needed
|
|
63
|
-
// Add decorator
|
|
64
|
-
return fixer.insertTextBefore(node, `@${memoizeAlias}()\n${' '.repeat(node.loc.start.column)}`);
|
|
65
|
-
},
|
|
66
|
-
});
|
|
83
|
+
// Check if method already has @Memoize or @Memoize() decorator
|
|
84
|
+
const hasDecorator = node.decorators?.some((decorator) => isMemoizeDecorator(decorator, memoizeAlias));
|
|
85
|
+
if (hasDecorator) {
|
|
86
|
+
return;
|
|
67
87
|
}
|
|
88
|
+
context.report({
|
|
89
|
+
node,
|
|
90
|
+
messageId: 'requireMemoize',
|
|
91
|
+
fix(fixer) {
|
|
92
|
+
const fixes = [];
|
|
93
|
+
const sourceCode = context.sourceCode;
|
|
94
|
+
const decoratorIdent = memoizeNamespace
|
|
95
|
+
? `${memoizeNamespace}.Memoize`
|
|
96
|
+
: memoizeAlias;
|
|
97
|
+
const importStatement = `import { Memoize } from '${MEMOIZE_MODULE}';`;
|
|
98
|
+
// Add import if it's not already present; ensure we only add once per file
|
|
99
|
+
if (!hasMemoizeImport &&
|
|
100
|
+
!memoizeNamespace &&
|
|
101
|
+
!scheduledImportFix &&
|
|
102
|
+
!sourceCode.text.includes(importStatement)) {
|
|
103
|
+
const programBody = sourceCode.ast.body;
|
|
104
|
+
const firstImport = programBody.find((n) => n.type === utils_1.AST_NODE_TYPES.ImportDeclaration);
|
|
105
|
+
const anchorNode = (firstImport ?? programBody[0]);
|
|
106
|
+
if (anchorNode) {
|
|
107
|
+
const text = sourceCode.text;
|
|
108
|
+
const anchorStart = anchorNode.range[0];
|
|
109
|
+
const lineStart = text.lastIndexOf('\n', anchorStart - 1) + 1;
|
|
110
|
+
const leadingWhitespace = text.slice(lineStart, anchorStart).match(/^[ \t]*/)?.[0] ??
|
|
111
|
+
'';
|
|
112
|
+
const importLine = `${leadingWhitespace}${importStatement}\n`;
|
|
113
|
+
fixes.push(fixer.insertTextBeforeRange([lineStart, lineStart], importLine));
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Fallback: empty file
|
|
117
|
+
fixes.push(fixer.insertTextBeforeRange([0, 0], `import { Memoize } from '${MEMOIZE_MODULE}';\n`));
|
|
118
|
+
}
|
|
119
|
+
scheduledImportFix = true;
|
|
120
|
+
}
|
|
121
|
+
// Add decorator for this method
|
|
122
|
+
const insertionTarget = node.decorators && node.decorators.length > 0
|
|
123
|
+
? node.decorators[0]
|
|
124
|
+
: node;
|
|
125
|
+
const insertionStart = insertionTarget.range?.[0] ?? node.range?.[0] ?? 0;
|
|
126
|
+
const text = sourceCode.text;
|
|
127
|
+
const lineStart = text.lastIndexOf('\n', insertionStart - 1) + 1;
|
|
128
|
+
const leadingWhitespace = text.slice(lineStart, insertionStart).match(/^[ \t]*/)?.[0] ?? '';
|
|
129
|
+
fixes.push(fixer.insertTextBeforeRange([lineStart, lineStart], `${leadingWhitespace}@${decoratorIdent}()\n`));
|
|
130
|
+
return fixes;
|
|
131
|
+
},
|
|
132
|
+
});
|
|
68
133
|
},
|
|
69
134
|
};
|
|
70
135
|
},
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforceMemoizeGetters = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const MEMOIZE_PREFERRED_MODULE = '@blumintinc/typescript-memoize';
|
|
7
|
+
const MEMOIZE_MODULES = new Set([
|
|
8
|
+
MEMOIZE_PREFERRED_MODULE,
|
|
9
|
+
'typescript-memoize',
|
|
10
|
+
]);
|
|
11
|
+
function isMemoizeDecorator(decorator, alias) {
|
|
12
|
+
const expression = decorator.expression;
|
|
13
|
+
// @Alias()
|
|
14
|
+
if (expression.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
15
|
+
const callee = expression.callee;
|
|
16
|
+
return ((callee.type === utils_1.AST_NODE_TYPES.Identifier && callee.name === alias) ||
|
|
17
|
+
(callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
18
|
+
!callee.computed &&
|
|
19
|
+
callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
20
|
+
callee.property.name === alias));
|
|
21
|
+
}
|
|
22
|
+
// @Alias
|
|
23
|
+
if (expression.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
24
|
+
return expression.name === alias;
|
|
25
|
+
}
|
|
26
|
+
// @ns.Alias
|
|
27
|
+
if (expression.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
28
|
+
!expression.computed &&
|
|
29
|
+
expression.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
30
|
+
return expression.property.name === alias;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
exports.enforceMemoizeGetters = (0, createRule_1.createRule)({
|
|
35
|
+
name: 'enforce-memoize-getters',
|
|
36
|
+
meta: {
|
|
37
|
+
type: 'suggestion',
|
|
38
|
+
docs: {
|
|
39
|
+
description: 'Enforce @Memoize() decorator on private class getters to avoid re-instantiation and preserve state across accesses.',
|
|
40
|
+
recommended: 'error',
|
|
41
|
+
},
|
|
42
|
+
fixable: 'code',
|
|
43
|
+
schema: [],
|
|
44
|
+
messages: {
|
|
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
|
+
},
|
|
47
|
+
},
|
|
48
|
+
defaultOptions: [],
|
|
49
|
+
create(context) {
|
|
50
|
+
// Only apply in TS/TSX files to avoid JS environments without decorators
|
|
51
|
+
const filename = context.getFilename();
|
|
52
|
+
if (!/\.tsx?$/i.test(filename)) {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
const sourceCode = context.getSourceCode();
|
|
56
|
+
let hasMemoizeImport = false;
|
|
57
|
+
let memoizeAlias = 'Memoize';
|
|
58
|
+
let memoizeNamespace = null;
|
|
59
|
+
let hasNamedImport = false;
|
|
60
|
+
let scheduledImportFix = false;
|
|
61
|
+
return {
|
|
62
|
+
ImportDeclaration(node) {
|
|
63
|
+
if (MEMOIZE_MODULES.has(String(node.source.value))) {
|
|
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
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
MethodDefinition(node) {
|
|
81
|
+
// Target: instance private getters
|
|
82
|
+
if (node.kind !== 'get')
|
|
83
|
+
return;
|
|
84
|
+
// skip static getters
|
|
85
|
+
if (node.static)
|
|
86
|
+
return;
|
|
87
|
+
// enforce only "private" accessibility (undefined => public)
|
|
88
|
+
if (node.accessibility !== 'private')
|
|
89
|
+
return;
|
|
90
|
+
const decoratorAliases = memoizeAlias === 'Memoize' ? ['Memoize'] : ['Memoize', memoizeAlias];
|
|
91
|
+
const hasDecorator = node.decorators?.some((decorator) => decoratorAliases.some((alias) => isMemoizeDecorator(decorator, alias)));
|
|
92
|
+
if (hasDecorator)
|
|
93
|
+
return;
|
|
94
|
+
const propertyName = node.computed
|
|
95
|
+
? '[computed]'
|
|
96
|
+
: sourceCode.getText(node.key);
|
|
97
|
+
context.report({
|
|
98
|
+
node,
|
|
99
|
+
messageId: 'requireMemoizeGetter',
|
|
100
|
+
data: { name: propertyName },
|
|
101
|
+
fix(fixer) {
|
|
102
|
+
const fixes = [];
|
|
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();
|
|
113
|
+
// Insert import if needed, at the top alongside other imports
|
|
114
|
+
if (!hasMemoizeImport && !scheduledImportFix) {
|
|
115
|
+
const programBody = sourceCode.ast.body;
|
|
116
|
+
const firstImport = programBody.find((n) => n.type === utils_1.AST_NODE_TYPES.ImportDeclaration);
|
|
117
|
+
const anchorNode = (firstImport ?? programBody[0]);
|
|
118
|
+
if (anchorNode) {
|
|
119
|
+
const text = sourceCode.text;
|
|
120
|
+
const anchorStart = anchorNode.range[0];
|
|
121
|
+
const lineStart = text.lastIndexOf('\n', anchorStart - 1) + 1;
|
|
122
|
+
const leadingWhitespace = text.slice(lineStart, anchorStart).match(/^[ \t]*/)?.[0] ??
|
|
123
|
+
'';
|
|
124
|
+
const importLine = `${leadingWhitespace}import { Memoize } from '${MEMOIZE_PREFERRED_MODULE}';\n`;
|
|
125
|
+
fixes.push(fixer.insertTextBeforeRange([lineStart, lineStart], importLine));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
fixes.push(fixer.insertTextBeforeRange([0, 0], `import { Memoize } from '${MEMOIZE_PREFERRED_MODULE}';\n`));
|
|
129
|
+
}
|
|
130
|
+
scheduledImportFix = true;
|
|
131
|
+
}
|
|
132
|
+
// Insert decorator above the getter (or before the first decorator), preserving indentation
|
|
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`));
|
|
139
|
+
return fixes;
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
exports.default = exports.enforceMemoizeGetters;
|
|
147
|
+
//# sourceMappingURL=enforce-memoize-getters.js.map
|
|
@@ -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;
|
|
@@ -144,7 +144,8 @@ exports.enforceMicrodiff = (0, createRule_1.createRule)({
|
|
|
144
144
|
// Get the source of the imported function
|
|
145
145
|
const importSource = importedFunctions.get(name);
|
|
146
146
|
// Skip reporting if it's from fast-deep-equal
|
|
147
|
-
if (importSource === 'fast-deep-equal' ||
|
|
147
|
+
if (importSource === 'fast-deep-equal' ||
|
|
148
|
+
importSource === 'fast-deep-equal/es6') {
|
|
148
149
|
return;
|
|
149
150
|
}
|
|
150
151
|
// Report it if it's from any other tracked library
|
|
@@ -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' ||
|
|
@@ -333,6 +333,7 @@ const IN_EXCEPTIONS = [
|
|
|
333
333
|
'insurgents',
|
|
334
334
|
'insurrection',
|
|
335
335
|
'intake',
|
|
336
|
+
'integer',
|
|
336
337
|
'integers',
|
|
337
338
|
'intel',
|
|
338
339
|
'intellect',
|
|
@@ -424,6 +425,8 @@ const IN_EXCEPTIONS = [
|
|
|
424
425
|
'interruptions',
|
|
425
426
|
'interrupts',
|
|
426
427
|
'intersect',
|
|
428
|
+
'intersected',
|
|
429
|
+
'intersecting',
|
|
427
430
|
'intersection',
|
|
428
431
|
'intersections',
|
|
429
432
|
'interspersed',
|
|
@@ -844,14 +847,13 @@ const DIS_EXCEPTIONS = [
|
|
|
844
847
|
'dismissal',
|
|
845
848
|
'disk',
|
|
846
849
|
'disks',
|
|
850
|
+
'dispute',
|
|
851
|
+
'disputed',
|
|
852
|
+
'disputes',
|
|
853
|
+
'disputing',
|
|
847
854
|
];
|
|
848
855
|
// Words that contain negative prefixes but should be treated as valid
|
|
849
|
-
const
|
|
850
|
-
...IN_EXCEPTIONS,
|
|
851
|
-
...NO_EXCEPTIONS,
|
|
852
|
-
...UN_EXCEPTIONS,
|
|
853
|
-
...DIS_EXCEPTIONS,
|
|
854
|
-
];
|
|
856
|
+
const EXCEPTION_WORDS_SET = new Set([...IN_EXCEPTIONS, ...NO_EXCEPTIONS, ...UN_EXCEPTIONS, ...DIS_EXCEPTIONS].map((word) => word.toLowerCase()));
|
|
855
857
|
/**
|
|
856
858
|
* Splits a variable name into individual words based on naming conventions:
|
|
857
859
|
* - camelCase or PascalCase: split on uppercase letters
|
|
@@ -957,7 +959,7 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
957
959
|
// Make sure words[1] exists before trying to access it
|
|
958
960
|
if (words.length > 1) {
|
|
959
961
|
const secondWord = words[1].toLowerCase();
|
|
960
|
-
if (
|
|
962
|
+
if (EXCEPTION_WORDS_SET.has(secondWord)) {
|
|
961
963
|
return { isNegative: false, alternatives: [] };
|
|
962
964
|
}
|
|
963
965
|
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
type
|
|
2
|
-
|
|
3
|
-
enforceDestructuring?: boolean;
|
|
4
|
-
ignoreExternalInterfaces?: boolean;
|
|
5
|
-
}
|
|
6
|
-
];
|
|
7
|
-
export declare const enforcePropsArgumentName: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"usePropsForType", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
1
|
+
type MessageIds = 'usePropsParameterName' | 'usePropsParameterNameWithPrefix';
|
|
2
|
+
export declare const enforcePropsArgumentName: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
8
3
|
export {};
|