@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,76 +3,101 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.RULE_NAME = void 0;
|
|
7
|
-
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.DYNAMIC_RULES_LABEL = exports.REQUIRE_DYNAMIC_FIREBASE_IMPORTS_RULE = exports.RULE_NAME = void 0;
|
|
8
7
|
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const createRule_1 = require("../utils/createRule");
|
|
9
9
|
exports.RULE_NAME = 'enforce-dynamic-file-naming';
|
|
10
|
+
const ENFORCE_DYNAMIC_IMPORTS_RULE = '@blumintinc/blumint/enforce-dynamic-imports';
|
|
11
|
+
exports.REQUIRE_DYNAMIC_FIREBASE_IMPORTS_RULE = '@blumintinc/blumint/require-dynamic-firebase-imports';
|
|
12
|
+
exports.DYNAMIC_RULES_LABEL = `${ENFORCE_DYNAMIC_IMPORTS_RULE} or ${exports.REQUIRE_DYNAMIC_FIREBASE_IMPORTS_RULE}`;
|
|
13
|
+
const SHORTHAND_DISABLE_NEXT_LINE = /\bednl\b/;
|
|
14
|
+
const SHORTHAND_DISABLE_LINE = /\bedl\b/;
|
|
15
|
+
const DISABLE_NEXT_LINE_TOKEN = 'eslint-disable-next-line';
|
|
16
|
+
const DISABLE_LINE_TOKEN = 'eslint-disable-line';
|
|
17
|
+
const DISABLE_BLOCK_PATTERN = /\beslint-disable\b(?!-)/;
|
|
18
|
+
const disabledRuleNameFrom = (commentText) => {
|
|
19
|
+
const mentionsEnforceDynamicImports = commentText.includes(ENFORCE_DYNAMIC_IMPORTS_RULE);
|
|
20
|
+
const mentionsRequireDynamicFirebaseImports = commentText.includes(exports.REQUIRE_DYNAMIC_FIREBASE_IMPORTS_RULE);
|
|
21
|
+
if (mentionsEnforceDynamicImports && mentionsRequireDynamicFirebaseImports) {
|
|
22
|
+
return exports.DYNAMIC_RULES_LABEL;
|
|
23
|
+
}
|
|
24
|
+
if (mentionsEnforceDynamicImports) {
|
|
25
|
+
return ENFORCE_DYNAMIC_IMPORTS_RULE;
|
|
26
|
+
}
|
|
27
|
+
if (mentionsRequireDynamicFirebaseImports) {
|
|
28
|
+
return exports.REQUIRE_DYNAMIC_FIREBASE_IMPORTS_RULE;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
10
32
|
exports.default = (0, createRule_1.createRule)({
|
|
11
33
|
name: exports.RULE_NAME,
|
|
12
34
|
meta: {
|
|
13
35
|
type: 'suggestion',
|
|
14
36
|
docs: {
|
|
15
|
-
description:
|
|
37
|
+
description: `Enforce .dynamic.ts(x) file naming when ${exports.DYNAMIC_RULES_LABEL} rule is disabled`,
|
|
16
38
|
recommended: 'error',
|
|
17
39
|
},
|
|
18
40
|
schema: [],
|
|
19
41
|
messages: {
|
|
20
|
-
requireDynamicExtension: '
|
|
21
|
-
requireDisableDirective: '
|
|
42
|
+
requireDynamicExtension: 'File "{{fileName}}" disables "{{ruleName}}" but keeps the standard {{extension}} extension, hiding that dynamic-import safeguards are bypassed. Rename to "{{suggestedName}}" (or another *.dynamic.ts/tsx name) so the exception is visible and static-import hotspots stay easy to audit.',
|
|
43
|
+
requireDisableDirective: 'File "{{fileName}}" uses the ".dynamic" suffix that signals dynamic-import rules are disabled, but it lacks a disable directive for {{dynamicRulesLabel}}. Add the matching disable comment for the static import you need, or rename the file to "{{standardName}}" so the rules keep protecting other files.',
|
|
22
44
|
},
|
|
23
45
|
},
|
|
24
46
|
defaultOptions: [],
|
|
25
47
|
create(context) {
|
|
26
|
-
// Get the file path and name
|
|
27
48
|
const filePath = context.getFilename();
|
|
28
49
|
const fileName = path_1.default.basename(filePath);
|
|
29
|
-
|
|
30
|
-
// Ignore files with other extensions like .test.ts, .deprecated.ts, etc.
|
|
31
|
-
const isTypeScriptFile = /^[^.]+\.tsx?$/.test(fileName);
|
|
32
|
-
// Check if the file has .dynamic.ts or .dynamic.tsx extension
|
|
50
|
+
const isTypeScriptFile = fileName.endsWith('.ts') || fileName.endsWith('.tsx');
|
|
33
51
|
const hasDynamicExtension = /\.dynamic\.tsx?$/.test(fileName);
|
|
34
|
-
// Skip if not a TypeScript file or has other extensions
|
|
35
52
|
if (!isTypeScriptFile && !hasDynamicExtension) {
|
|
36
53
|
return {};
|
|
37
54
|
}
|
|
38
|
-
// Track if we found a disable directive for enforce-dynamic-imports
|
|
39
55
|
let foundDisableDirective = false;
|
|
56
|
+
let disabledRuleName = null;
|
|
40
57
|
return {
|
|
41
58
|
Program() {
|
|
42
|
-
// Get the source code
|
|
43
59
|
const sourceCode = context.getSourceCode();
|
|
44
60
|
const comments = sourceCode.getAllComments();
|
|
45
|
-
// Check all comments for disable directives
|
|
46
61
|
for (const comment of comments) {
|
|
47
62
|
const commentText = comment.value.trim();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (commentText.includes('eslint-disable ') &&
|
|
58
|
-
(commentText.includes('@blumintinc/blumint/enforce-dynamic-imports') ||
|
|
59
|
-
commentText.includes('@blumintinc/blumint/require-dynamic-firebase-imports'))) {
|
|
63
|
+
const disabledRuleNameForComment = disabledRuleNameFrom(commentText);
|
|
64
|
+
const disablesTargetRule = disabledRuleNameForComment !== null;
|
|
65
|
+
const inlineDisable = (commentText.includes(DISABLE_NEXT_LINE_TOKEN) ||
|
|
66
|
+
commentText.includes(DISABLE_LINE_TOKEN) ||
|
|
67
|
+
SHORTHAND_DISABLE_NEXT_LINE.test(commentText) ||
|
|
68
|
+
SHORTHAND_DISABLE_LINE.test(commentText)) &&
|
|
69
|
+
disablesTargetRule;
|
|
70
|
+
const blockDisable = DISABLE_BLOCK_PATTERN.test(commentText) && disablesTargetRule;
|
|
71
|
+
if (inlineDisable || blockDisable) {
|
|
60
72
|
foundDisableDirective = true;
|
|
73
|
+
disabledRuleName = disabledRuleNameForComment;
|
|
61
74
|
break;
|
|
62
75
|
}
|
|
63
76
|
}
|
|
64
|
-
// If we found a disable directive but the file doesn't have .dynamic extension
|
|
65
77
|
if (foundDisableDirective && !hasDynamicExtension) {
|
|
78
|
+
const suggestedName = fileName.replace(/\.tsx?$/, '.dynamic$&');
|
|
79
|
+
const extension = path_1.default.extname(fileName);
|
|
66
80
|
context.report({
|
|
67
81
|
loc: { line: 1, column: 0 },
|
|
68
82
|
messageId: 'requireDynamicExtension',
|
|
83
|
+
data: {
|
|
84
|
+
fileName,
|
|
85
|
+
ruleName: disabledRuleName ?? exports.DYNAMIC_RULES_LABEL,
|
|
86
|
+
extension,
|
|
87
|
+
suggestedName,
|
|
88
|
+
},
|
|
69
89
|
});
|
|
70
90
|
}
|
|
71
|
-
// If the file has .dynamic extension but no disable directive
|
|
72
91
|
if (hasDynamicExtension && !foundDisableDirective) {
|
|
92
|
+
const standardName = fileName.replace(/\.dynamic(?=\.tsx?$)/, '');
|
|
73
93
|
context.report({
|
|
74
94
|
loc: { line: 1, column: 0 },
|
|
75
95
|
messageId: 'requireDisableDirective',
|
|
96
|
+
data: {
|
|
97
|
+
fileName,
|
|
98
|
+
standardName,
|
|
99
|
+
dynamicRulesLabel: exports.DYNAMIC_RULES_LABEL,
|
|
100
|
+
},
|
|
76
101
|
});
|
|
77
102
|
}
|
|
78
103
|
},
|
|
@@ -7,41 +7,122 @@ exports.enforceFirebaseImports = (0, createRule_1.createRule)({
|
|
|
7
7
|
meta: {
|
|
8
8
|
type: 'problem',
|
|
9
9
|
docs: {
|
|
10
|
-
description: '
|
|
10
|
+
description: 'Require firebaseCloud modules to be loaded via dynamic import so Firebase code stays out of the initial bundle and only loads when needed.',
|
|
11
11
|
recommended: 'error',
|
|
12
12
|
},
|
|
13
|
+
fixable: 'code',
|
|
14
|
+
hasSuggestions: true,
|
|
13
15
|
schema: [],
|
|
14
16
|
messages: {
|
|
15
|
-
noDynamicImport: 'Static
|
|
17
|
+
noDynamicImport: 'Static import from firebaseCloud path "{{importPath}}" eagerly bundles Firebase code into the initial client chunk, which inflates startup time and prevents lazy loading. Replace it with an awaited dynamic import so the code only loads when invoked (e.g., `const module = await import(\'{{importPath}}\')` or destructure the exports you need).',
|
|
16
18
|
},
|
|
17
19
|
},
|
|
18
20
|
defaultOptions: [],
|
|
19
21
|
create(context) {
|
|
20
22
|
return {
|
|
21
23
|
ImportDeclaration(node) {
|
|
22
|
-
// Skip
|
|
24
|
+
// Skip third-party files
|
|
25
|
+
const filename = context.getFilename?.();
|
|
26
|
+
if (filename && /(^|[\\/])node_modules([\\/]|$)/.test(filename)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Skip type-only import declarations
|
|
23
30
|
if (node.importKind === 'type') {
|
|
24
31
|
return;
|
|
25
32
|
}
|
|
26
33
|
const importPath = node.source.value;
|
|
27
34
|
// Check if the import is from firebaseCloud directory
|
|
28
|
-
if (importPath.includes('firebaseCloud/')) {
|
|
29
|
-
|
|
30
|
-
node,
|
|
31
|
-
messageId: 'noDynamicImport',
|
|
32
|
-
fix(fixer) {
|
|
33
|
-
const importSpecifiers = node.specifiers
|
|
34
|
-
.filter((spec) => spec.type === 'ImportSpecifier')
|
|
35
|
-
.map((spec) => spec.imported.name);
|
|
36
|
-
if (importSpecifiers.length === 0) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
const destructuredImports = `{ ${importSpecifiers.join(', ')} }`;
|
|
40
|
-
const dynamicImport = `const ${destructuredImports} = await import('${importPath}');`;
|
|
41
|
-
return fixer.replaceText(node, dynamicImport);
|
|
42
|
-
},
|
|
43
|
-
});
|
|
35
|
+
if (!importPath.includes('firebaseCloud/')) {
|
|
36
|
+
return;
|
|
44
37
|
}
|
|
38
|
+
// Determine specifiers
|
|
39
|
+
const defaultSpecifier = node.specifiers.find((spec) => spec.type === 'ImportDefaultSpecifier');
|
|
40
|
+
const namespaceSpecifier = node.specifiers.find((spec) => spec.type === 'ImportNamespaceSpecifier');
|
|
41
|
+
const namedSpecifiers = node.specifiers.filter((spec) => spec.type === 'ImportSpecifier' && spec.importKind !== 'type');
|
|
42
|
+
const typeOnlySpecifiers = node.specifiers.filter((spec) => spec.type === 'ImportSpecifier' && spec.importKind === 'type');
|
|
43
|
+
// If there are only type-only specifiers, allow
|
|
44
|
+
if (!defaultSpecifier &&
|
|
45
|
+
!namespaceSpecifier &&
|
|
46
|
+
namedSpecifiers.length === 0 &&
|
|
47
|
+
typeOnlySpecifiers.length > 0) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const buildTypeNames = () => typeOnlySpecifiers
|
|
51
|
+
.map((spec) => spec.imported.name === spec.local.name
|
|
52
|
+
? spec.imported.name
|
|
53
|
+
: `${spec.imported.name} as ${spec.local.name}`)
|
|
54
|
+
.join(', ');
|
|
55
|
+
const buildReplacement = (options = {}) => {
|
|
56
|
+
const statements = [];
|
|
57
|
+
if (typeOnlySpecifiers.length > 0) {
|
|
58
|
+
statements.push(`import type { ${buildTypeNames()} } from '${importPath}';`);
|
|
59
|
+
}
|
|
60
|
+
if (namespaceSpecifier) {
|
|
61
|
+
const nsLocal = namespaceSpecifier.local.name;
|
|
62
|
+
statements.push(`const ${nsLocal} = await import('${importPath}');`);
|
|
63
|
+
if (defaultSpecifier) {
|
|
64
|
+
const defLocal = defaultSpecifier.local.name;
|
|
65
|
+
statements.push(`const ${defLocal} = ${nsLocal}.default;`);
|
|
66
|
+
}
|
|
67
|
+
const destructureFromNamespace = [];
|
|
68
|
+
if (namedSpecifiers.length > 0) {
|
|
69
|
+
const destructureParts = namedSpecifiers.map((spec) => {
|
|
70
|
+
const imported = spec.imported.name;
|
|
71
|
+
const local = spec.local.name;
|
|
72
|
+
return imported === local ? imported : `${imported}: ${local}`;
|
|
73
|
+
});
|
|
74
|
+
destructureFromNamespace.push(...destructureParts);
|
|
75
|
+
}
|
|
76
|
+
if (destructureFromNamespace.length > 0) {
|
|
77
|
+
statements.push(`const { ${destructureFromNamespace.join(', ')} } = ${nsLocal};`);
|
|
78
|
+
}
|
|
79
|
+
return statements.join(' ');
|
|
80
|
+
}
|
|
81
|
+
const destructureParts = [];
|
|
82
|
+
if (defaultSpecifier) {
|
|
83
|
+
const defLocal = defaultSpecifier.local.name;
|
|
84
|
+
destructureParts.push(`default: ${defLocal}`);
|
|
85
|
+
}
|
|
86
|
+
if (namedSpecifiers.length > 0) {
|
|
87
|
+
for (const spec of namedSpecifiers) {
|
|
88
|
+
const imported = spec.imported.name;
|
|
89
|
+
const local = spec.local.name;
|
|
90
|
+
destructureParts.push(imported === local ? imported : `${imported}: ${local}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (destructureParts.length > 0) {
|
|
94
|
+
statements.push(`const { ${destructureParts.join(', ')} } = await import('${importPath}');`);
|
|
95
|
+
return statements.join(' ');
|
|
96
|
+
}
|
|
97
|
+
if (node.specifiers.length === 0) {
|
|
98
|
+
return options.allowSideEffectFix !== false
|
|
99
|
+
? `await import('${importPath}');`
|
|
100
|
+
: null;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
};
|
|
104
|
+
context.report({
|
|
105
|
+
node,
|
|
106
|
+
messageId: 'noDynamicImport',
|
|
107
|
+
data: { importPath },
|
|
108
|
+
fix(fixer) {
|
|
109
|
+
const replacement = buildReplacement();
|
|
110
|
+
return replacement ? fixer.replaceText(node, replacement) : null;
|
|
111
|
+
},
|
|
112
|
+
suggest: [
|
|
113
|
+
{
|
|
114
|
+
messageId: 'noDynamicImport',
|
|
115
|
+
fix(fixer) {
|
|
116
|
+
const replacement = buildReplacement({
|
|
117
|
+
allowSideEffectFix: true,
|
|
118
|
+
});
|
|
119
|
+
return replacement
|
|
120
|
+
? fixer.replaceText(node, replacement)
|
|
121
|
+
: null;
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
});
|
|
45
126
|
},
|
|
46
127
|
};
|
|
47
128
|
},
|
|
@@ -29,7 +29,7 @@ exports.default = (0, createRule_1.createRule)({
|
|
|
29
29
|
},
|
|
30
30
|
],
|
|
31
31
|
messages: {
|
|
32
|
-
dynamicImportRequired: 'Static
|
|
32
|
+
dynamicImportRequired: 'Static import from "{{source}}" eagerly pulls the entire package into the entry bundle, inflating download size and delaying the first render. Load it lazily with a dynamic import (for example, useDynamic(() => import("{{source}}"))) so the code is fetched only when needed; if you only need types, use a type-only import with allowImportType enabled.',
|
|
33
33
|
},
|
|
34
34
|
},
|
|
35
35
|
defaultOptions: [
|