@blumintinc/eslint-plugin-blumint 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +170 -139
- package/lib/index.js +190 -1
- package/lib/rules/array-methods-this-context.js +1 -1
- package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
- package/lib/rules/consistent-callback-naming.js +1 -1
- package/lib/rules/dynamic-https-errors.js +3 -4
- package/lib/rules/enforce-assert-safe-object-key.js +11 -11
- package/lib/rules/enforce-boolean-naming-prefixes.js +11 -23
- package/lib/rules/enforce-callback-memo.js +1 -1
- package/lib/rules/enforce-centralized-mock-firestore.js +1 -1
- package/lib/rules/enforce-console-error.js +35 -7
- package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
- package/lib/rules/enforce-dynamic-file-naming.js +53 -28
- package/lib/rules/enforce-early-destructuring.d.ts +2 -0
- package/lib/rules/enforce-early-destructuring.js +980 -0
- package/lib/rules/enforce-empty-object-check.d.ts +11 -0
- package/lib/rules/enforce-empty-object-check.js +502 -0
- package/lib/rules/enforce-exported-function-types.js +1 -1
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +100 -50
- package/lib/rules/enforce-firestore-doc-ref-generic.js +15 -3
- package/lib/rules/enforce-firestore-facade.js +4 -2
- package/lib/rules/enforce-firestore-set-merge.js +3 -4
- package/lib/rules/enforce-global-constants.js +1 -1
- package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
- package/lib/rules/enforce-memoize-async.js +2 -6
- package/lib/rules/enforce-memoize-getters.js +39 -15
- package/lib/rules/enforce-microdiff.js +1 -1
- package/lib/rules/enforce-object-literal-as-const.js +4 -4
- package/lib/rules/enforce-positive-naming.js +4 -0
- package/lib/rules/enforce-props-argument-name.js +2 -2
- package/lib/rules/enforce-safe-stringify.js +1 -1
- package/lib/rules/enforce-serializable-params.js +3 -3
- package/lib/rules/enforce-singular-type-names.js +1 -1
- package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
- package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
- package/lib/rules/enforce-storage-context.d.ts +9 -0
- package/lib/rules/enforce-storage-context.js +646 -0
- package/lib/rules/enforce-timestamp-now.js +23 -7
- package/lib/rules/enforce-transform-memoization.d.ts +4 -0
- package/lib/rules/enforce-transform-memoization.js +416 -0
- package/lib/rules/enforce-typescript-markdown-code-blocks.js +7 -3
- package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
- package/lib/rules/enforce-unique-cursor-headers.js +365 -0
- package/lib/rules/enforce-verb-noun-naming.js +5 -4
- package/lib/rules/ensure-pointer-events-none.js +28 -2
- package/lib/rules/export-if-in-doubt.js +27 -4
- package/lib/rules/extract-global-constants.js +40 -13
- package/lib/rules/fast-deep-equal-over-microdiff.js +29 -39
- package/lib/rules/firestore-transaction-reads-before-writes.js +50 -4
- package/lib/rules/flatten-push-calls.d.ts +2 -0
- package/lib/rules/flatten-push-calls.js +428 -0
- package/lib/rules/global-const-style.js +50 -19
- package/lib/rules/jsdoc-above-field.d.ts +11 -0
- package/lib/rules/jsdoc-above-field.js +208 -0
- package/lib/rules/key-only-outermost-element.js +21 -3
- package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
- package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
- package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
- package/lib/rules/memo-compare-deeply-complex-props.js +788 -0
- package/lib/rules/memo-nested-react-components.d.ts +8 -0
- package/lib/rules/memo-nested-react-components.js +410 -0
- package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
- package/lib/rules/no-always-true-false-conditions.js +68 -8
- package/lib/rules/no-array-length-in-deps.js +9 -3
- package/lib/rules/no-async-foreach.js +210 -12
- package/lib/rules/no-circular-references.js +18 -17
- package/lib/rules/no-class-instance-destructuring.js +55 -12
- package/lib/rules/no-complex-cloud-params.js +7 -3
- package/lib/rules/no-compositing-layer-props.js +2 -2
- package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
- package/lib/rules/no-console-error.d.ts +9 -0
- package/lib/rules/no-console-error.js +525 -0
- package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
- package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
- package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
- package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
- package/lib/rules/no-entire-object-hook-deps.js +4 -6
- package/lib/rules/no-excessive-parent-chain.js +1 -1
- package/lib/rules/no-explicit-return-type.d.ts +2 -1
- package/lib/rules/no-explicit-return-type.js +215 -26
- package/lib/rules/no-filter-without-return.js +5 -1
- package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
- package/lib/rules/no-firestore-jest-mock.js +126 -8
- package/lib/rules/no-firestore-object-arrays.js +67 -12
- package/lib/rules/no-handler-suffix.d.ts +12 -0
- package/lib/rules/no-handler-suffix.js +305 -0
- package/lib/rules/no-hungarian.js +1 -1
- package/lib/rules/no-inline-component-prop.d.ts +10 -0
- package/lib/rules/no-inline-component-prop.js +456 -0
- package/lib/rules/no-jsx-in-hooks.js +6 -1
- package/lib/rules/no-jsx-whitespace-literal.js +8 -2
- package/lib/rules/no-margin-properties.js +6 -6
- package/lib/rules/no-memoize-on-static.js +9 -1
- package/lib/rules/no-misleading-boolean-prefixes.js +7 -3
- package/lib/rules/no-misused-switch-case.js +22 -1
- package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
- package/lib/rules/no-mixed-firestore-transactions.js +296 -34
- package/lib/rules/no-mock-firebase-admin.js +5 -2
- package/lib/rules/no-object-values-on-strings.js +10 -2
- package/lib/rules/no-overridable-method-calls-in-constructor.js +29 -17
- package/lib/rules/no-passthrough-getters.js +38 -2
- package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
- package/lib/rules/no-redundant-annotation-assertion.js +402 -0
- package/lib/rules/no-redundant-param-types.js +16 -6
- package/lib/rules/no-redundant-this-params.d.ts +3 -0
- package/lib/rules/no-redundant-this-params.js +459 -0
- package/lib/rules/no-redundant-usecallback-wrapper.js +16 -3
- package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
- package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
- package/lib/rules/no-restricted-properties-fix.js +11 -10
- package/lib/rules/no-separate-loading-state.js +7 -16
- package/lib/rules/no-stale-state-across-await.js +1 -1
- package/lib/rules/no-type-assertion-returns.js +43 -49
- package/lib/rules/no-undefined-null-passthrough.js +61 -31
- package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
- package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
- package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
- package/lib/rules/no-unnecessary-destructuring-rename.js +347 -0
- package/lib/rules/no-unnecessary-destructuring.js +14 -4
- package/lib/rules/no-unnecessary-verb-suffix.js +2 -1
- package/lib/rules/no-unpinned-dependencies.js +36 -5
- package/lib/rules/no-unused-props.d.ts +2 -2
- package/lib/rules/no-unused-props.js +295 -91
- package/lib/rules/no-unused-usestate.js +6 -2
- package/lib/rules/no-useless-fragment.js +28 -2
- package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
- package/lib/rules/no-useless-usememo-primitives.js +393 -0
- package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
- package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
- package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
- package/lib/rules/omit-index-html.d.ts +2 -1
- package/lib/rules/omit-index-html.js +62 -7
- package/lib/rules/optimize-object-boolean-conditions.js +6 -4
- package/lib/rules/parallelize-async-operations.js +24 -5
- package/lib/rules/prefer-batch-operations.d.ts +1 -3
- package/lib/rules/prefer-batch-operations.js +32 -5
- package/lib/rules/prefer-block-comments-for-declarations.js +9 -4
- package/lib/rules/prefer-clone-deep.js +3 -3
- package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
- package/lib/rules/prefer-destructuring-no-class.js +244 -59
- package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
- package/lib/rules/prefer-docsetter-setall.js +243 -0
- package/lib/rules/prefer-field-paths-in-transforms.js +4 -3
- package/lib/rules/prefer-fragment-component.js +1 -1
- package/lib/rules/prefer-fragment-shorthand.js +2 -1
- package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
- package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
- package/lib/rules/prefer-global-router-state-key.js +88 -13
- package/lib/rules/prefer-memoized-props.d.ts +3 -0
- package/lib/rules/prefer-memoized-props.js +445 -0
- package/lib/rules/prefer-next-dynamic.js +60 -69
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +9 -4
- package/lib/rules/prefer-nullish-coalescing-override.d.ts +2 -1
- package/lib/rules/prefer-nullish-coalescing-override.js +11 -5
- package/lib/rules/prefer-params-over-parent-id.js +220 -171
- package/lib/rules/prefer-settings-object.js +2 -2
- package/lib/rules/prefer-type-over-interface.js +7 -2
- package/lib/rules/prefer-url-tostring-over-tojson.js +6 -3
- package/lib/rules/prefer-use-deep-compare-memo.js +8 -5
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +1 -1
- package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
- package/lib/rules/prevent-children-clobber.d.ts +2 -0
- package/lib/rules/prevent-children-clobber.js +536 -0
- package/lib/rules/react-memoize-literals.d.ts +4 -0
- package/lib/rules/react-memoize-literals.js +495 -0
- package/lib/rules/react-usememo-should-be-component.js +2 -2
- package/lib/rules/require-hooks-default-params.js +17 -16
- package/lib/rules/require-https-error-cause.d.ts +4 -0
- package/lib/rules/require-https-error-cause.js +136 -0
- package/lib/rules/require-https-error.js +43 -21
- package/lib/rules/require-image-optimized.js +1 -1
- package/lib/rules/require-memo.js +1 -1
- package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
- package/lib/rules/require-memoize-jsx-returners.js +485 -0
- package/lib/rules/require-usememo-object-literals.js +2 -3
- package/lib/rules/test-file-location-enforcement.js +1 -1
- package/lib/rules/use-latest-callback.js +6 -5
- package/lib/rules/vertically-group-related-functions.d.ts +16 -0
- package/lib/rules/vertically-group-related-functions.js +480 -0
- package/lib/utils/ASTHelpers.d.ts +13 -1
- package/lib/utils/ASTHelpers.js +24 -0
- package/lib/utils/getMethodName.d.ts +27 -0
- package/lib/utils/getMethodName.js +35 -0
- package/lib/utils/tsTypeClassifier.d.ts +30 -0
- package/lib/utils/tsTypeClassifier.js +149 -0
- package/package.json +3 -1
|
@@ -34,7 +34,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
34
34
|
* Helper function to add assertSafe import if needed
|
|
35
35
|
*/
|
|
36
36
|
const addAssertSafeImport = (fixer) => {
|
|
37
|
-
const program = context.
|
|
37
|
+
const program = context.sourceCode.ast;
|
|
38
38
|
const firstImport = program.body.find((node) => node.type === utils_1.AST_NODE_TYPES.ImportDeclaration);
|
|
39
39
|
const importStatement = `import { assertSafe } from '${importPath}';\n`;
|
|
40
40
|
if (firstImport) {
|
|
@@ -83,7 +83,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
83
83
|
key.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
84
84
|
key.callee.name === 'String') {
|
|
85
85
|
const arg = key.arguments[0];
|
|
86
|
-
const argText = context.
|
|
86
|
+
const argText = context.sourceCode.getText(arg);
|
|
87
87
|
reportUseAssertSafe(key, argText);
|
|
88
88
|
}
|
|
89
89
|
// Check for template literals like `${id}`
|
|
@@ -93,7 +93,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
93
93
|
key.quasis[0].value.raw === '' &&
|
|
94
94
|
key.quasis[1].value.raw === '') {
|
|
95
95
|
const expr = key.expressions[0];
|
|
96
|
-
const exprText = context.
|
|
96
|
+
const exprText = context.sourceCode.getText(expr);
|
|
97
97
|
reportUseAssertSafe(key, exprText);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -107,7 +107,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
107
107
|
left.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
108
108
|
left.callee.name === 'String') {
|
|
109
109
|
const arg = left.arguments[0];
|
|
110
|
-
const argText = context.
|
|
110
|
+
const argText = context.sourceCode.getText(arg);
|
|
111
111
|
reportUseAssertSafe(left, argText);
|
|
112
112
|
}
|
|
113
113
|
// Check for template literals like `${id}`
|
|
@@ -117,7 +117,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
117
117
|
left.quasis[0].value.raw === '' &&
|
|
118
118
|
left.quasis[1].value.raw === '') {
|
|
119
119
|
const expr = left.expressions[0];
|
|
120
|
-
const exprText = context.
|
|
120
|
+
const exprText = context.sourceCode.getText(expr);
|
|
121
121
|
reportUseAssertSafe(left, exprText);
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -158,7 +158,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
158
158
|
property.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
159
159
|
property.callee.name === 'String') {
|
|
160
160
|
const arg = property.arguments[0];
|
|
161
|
-
const argText = context.
|
|
161
|
+
const argText = context.sourceCode.getText(arg);
|
|
162
162
|
reportUseAssertSafe(property, argText);
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
@@ -179,7 +179,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
179
179
|
return;
|
|
180
180
|
}
|
|
181
181
|
const expr = property.expressions[0];
|
|
182
|
-
const exprText = context.
|
|
182
|
+
const exprText = context.sourceCode.getText(expr);
|
|
183
183
|
reportUseAssertSafe(property, exprText);
|
|
184
184
|
return;
|
|
185
185
|
}
|
|
@@ -193,7 +193,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
193
193
|
if (isLikelyArray) {
|
|
194
194
|
return;
|
|
195
195
|
}
|
|
196
|
-
const propText = context.
|
|
196
|
+
const propText = context.sourceCode.getText(property);
|
|
197
197
|
reportUseAssertSafe(property, propText);
|
|
198
198
|
return;
|
|
199
199
|
}
|
|
@@ -203,7 +203,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
203
203
|
if (isLikelyArray) {
|
|
204
204
|
return;
|
|
205
205
|
}
|
|
206
|
-
const propText = context.
|
|
206
|
+
const propText = context.sourceCode.getText(property);
|
|
207
207
|
reportUseAssertSafe(property, propText);
|
|
208
208
|
return;
|
|
209
209
|
}
|
|
@@ -215,7 +215,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
215
215
|
if (isLikelyArray) {
|
|
216
216
|
return;
|
|
217
217
|
}
|
|
218
|
-
const propText = context.
|
|
218
|
+
const propText = context.sourceCode.getText(property);
|
|
219
219
|
reportUseAssertSafe(property, propText);
|
|
220
220
|
return;
|
|
221
221
|
}
|
|
@@ -228,7 +228,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
228
228
|
if (isLikelyArray) {
|
|
229
229
|
return;
|
|
230
230
|
}
|
|
231
|
-
const propText = context.
|
|
231
|
+
const propText = context.sourceCode.getText(property);
|
|
232
232
|
reportUseAssertSafe(property, propText);
|
|
233
233
|
return;
|
|
234
234
|
}
|
|
@@ -34,6 +34,7 @@ const DEFAULT_OPTIONS = {
|
|
|
34
34
|
prefixes: DEFAULT_BOOLEAN_PREFIXES,
|
|
35
35
|
ignoreOverriddenGetters: false,
|
|
36
36
|
};
|
|
37
|
+
const BOOLEANISH_BINARY_OPERATORS = new Set(['===', '!==', '==', '!=', '>', '<', '>=', '<=', 'in', 'instanceof']);
|
|
37
38
|
exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
38
39
|
name: 'enforce-boolean-naming-prefixes',
|
|
39
40
|
meta: {
|
|
@@ -62,15 +63,16 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
62
63
|
},
|
|
63
64
|
],
|
|
64
65
|
messages: {
|
|
65
|
-
missingBooleanPrefix: 'Boolean {{type}} "{{name}}"
|
|
66
|
-
'
|
|
67
|
-
'
|
|
66
|
+
missingBooleanPrefix: 'Boolean {{type}} "{{name}}" is missing a common approved boolean prefix ({{prefixes}}). ' +
|
|
67
|
+
'Prefixes immediately communicate that the value is a true/false predicate; without one, checks like `if ({{name}})` read as generic truthiness guards and hide the boolean intent. ' +
|
|
68
|
+
'Rename by prepending any approved prefix so the name becomes `<prefix>{{capitalizedName}}`, making the boolean contract obvious at call sites and API boundaries.',
|
|
68
69
|
},
|
|
69
70
|
},
|
|
70
71
|
defaultOptions: [DEFAULT_OPTIONS],
|
|
71
72
|
create(context, [options]) {
|
|
72
73
|
const approvedPrefixes = options.prefixes || DEFAULT_OPTIONS.prefixes;
|
|
73
|
-
const ignoreOverriddenGetters = options.ignoreOverriddenGetters ??
|
|
74
|
+
const ignoreOverriddenGetters = options.ignoreOverriddenGetters ??
|
|
75
|
+
DEFAULT_OPTIONS.ignoreOverriddenGetters;
|
|
74
76
|
const importStatusCache = new Map();
|
|
75
77
|
const externalApiUsageCache = new Map();
|
|
76
78
|
function findVariableInScopes(name) {
|
|
@@ -127,8 +129,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
127
129
|
];
|
|
128
130
|
return (hasApprovedPrefix(normalizedName, {
|
|
129
131
|
treatLeadingUnderscoreAsApproved: false,
|
|
130
|
-
}) ||
|
|
131
|
-
suffixKeywords.some((keyword) => lowerName.endsWith(keyword)));
|
|
132
|
+
}) || suffixKeywords.some((keyword) => lowerName.endsWith(keyword)));
|
|
132
133
|
}
|
|
133
134
|
/**
|
|
134
135
|
* Capitalize the first letter of a name for use in suggested alternatives
|
|
@@ -200,7 +201,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
200
201
|
}
|
|
201
202
|
// Check for logical expressions that typically return boolean
|
|
202
203
|
if (node.init.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
|
203
|
-
|
|
204
|
+
BOOLEANISH_BINARY_OPERATORS.has(node.init.operator)) {
|
|
204
205
|
return true;
|
|
205
206
|
}
|
|
206
207
|
// Check for logical expressions (&&)
|
|
@@ -377,7 +378,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
377
378
|
return true;
|
|
378
379
|
}
|
|
379
380
|
if (node.body.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
|
380
|
-
|
|
381
|
+
BOOLEANISH_BINARY_OPERATORS.has(node.body.operator)) {
|
|
381
382
|
return true;
|
|
382
383
|
}
|
|
383
384
|
if (node.body.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
|
|
@@ -434,9 +435,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
434
435
|
const calleeName = callExpression.callee.name;
|
|
435
436
|
const lowerCallee = calleeName.toLowerCase();
|
|
436
437
|
if (lowerCallee.startsWith('assert')) {
|
|
437
|
-
return identifierReturnsBoolean(calleeName)
|
|
438
|
-
? 'boolean'
|
|
439
|
-
: 'unknown';
|
|
438
|
+
return identifierReturnsBoolean(calleeName) ? 'boolean' : 'unknown';
|
|
440
439
|
}
|
|
441
440
|
const matchesPrefix = approvedPrefixes.some((prefix) => prefix !== 'asserts' &&
|
|
442
441
|
lowerCallee.startsWith(prefix.toLowerCase()));
|
|
@@ -516,18 +515,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
516
515
|
return callExpressionLooksBoolean(currentExpression);
|
|
517
516
|
}
|
|
518
517
|
if (currentExpression.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
|
|
519
|
-
if (
|
|
520
|
-
'===',
|
|
521
|
-
'!==',
|
|
522
|
-
'==',
|
|
523
|
-
'!=',
|
|
524
|
-
'>',
|
|
525
|
-
'<',
|
|
526
|
-
'>=',
|
|
527
|
-
'<=',
|
|
528
|
-
'in',
|
|
529
|
-
'instanceof',
|
|
530
|
-
].includes(currentExpression.operator)) {
|
|
518
|
+
if (BOOLEANISH_BINARY_OPERATORS.has(currentExpression.operator)) {
|
|
531
519
|
return 'boolean';
|
|
532
520
|
}
|
|
533
521
|
}
|
|
@@ -90,7 +90,7 @@ exports.default = (0, createRule_1.createRule)({
|
|
|
90
90
|
return [];
|
|
91
91
|
}
|
|
92
92
|
function referencesParentScopeVariables(functionNode, parentParams) {
|
|
93
|
-
const scopeManager = context.
|
|
93
|
+
const scopeManager = context.sourceCode.scopeManager;
|
|
94
94
|
if (!scopeManager)
|
|
95
95
|
return false;
|
|
96
96
|
const scope = scopeManager.acquire(functionNode);
|
|
@@ -140,7 +140,7 @@ exports.enforceCentralizedMockFirestore = (0, createRule_1.createRule)({
|
|
|
140
140
|
},
|
|
141
141
|
'Program:exit'() {
|
|
142
142
|
if (mockFirestoreNodes.size > 0) {
|
|
143
|
-
const sourceCode = context.
|
|
143
|
+
const sourceCode = context.sourceCode;
|
|
144
144
|
// Report only once for the entire file
|
|
145
145
|
context.report({
|
|
146
146
|
node: Array.from(mockFirestoreNodes)[0],
|
|
@@ -8,13 +8,13 @@ exports.enforceConsoleError = (0, createRule_1.createRule)({
|
|
|
8
8
|
meta: {
|
|
9
9
|
type: 'problem',
|
|
10
10
|
docs: {
|
|
11
|
-
description: 'Enforce proper logging for useAlertDialog based on severity. When severity is "error", console.error must be included. When severity is "warning", console.warn must be included. This ensures all user-facing errors and warnings are properly logged to
|
|
11
|
+
description: 'Enforce proper logging for useAlertDialog based on severity. When severity is "error", console.error must be included. When severity is "warning", console.warn must be included. This ensures all user-facing errors and warnings are properly logged to observability systems.',
|
|
12
12
|
recommended: 'error',
|
|
13
13
|
},
|
|
14
14
|
messages: {
|
|
15
|
-
missingConsoleError: 'useAlertDialog with severity "
|
|
16
|
-
missingConsoleWarn: 'useAlertDialog with severity "
|
|
17
|
-
missingConsoleBoth: 'useAlertDialog
|
|
15
|
+
missingConsoleError: 'useAlertDialog call with severity "{{severity}}" shows an error dialog but this function never logs with {{consoleMethod}}. Without the log the alert is invisible to observability and post-incident breadcrumbs. Add a {{consoleMethod}} call in this function scope before or after the open() call so the dialog is paired with an observable error trail.',
|
|
16
|
+
missingConsoleWarn: 'useAlertDialog call with severity "{{severity}}" shows a warning dialog but this function never logs with {{consoleMethod}}. Without the log the warning is invisible to observability, making degraded states hard to diagnose. Add a {{consoleMethod}} call in this function scope before or after the open() call so the dialog is paired with an observable warning trail.',
|
|
17
|
+
missingConsoleBoth: 'useAlertDialog call uses dynamic severity, so runtime may render an error or a warning dialog. It is missing {{missingMethods}} in this function scope, leaving one or more runtime severity branches (error or warning) without observability breadcrumbs: {{missingPaths}}. Branch on the severity value and add the matching console method before or after the open() call (console.error for error branches, console.warn for warning branches), as in the conditional pattern shown in the rule docs, so each branch leaves a single appropriate log trail instead of double-logging both methods.',
|
|
18
18
|
},
|
|
19
19
|
schema: [],
|
|
20
20
|
},
|
|
@@ -145,13 +145,33 @@ exports.enforceConsoleError = (0, createRule_1.createRule)({
|
|
|
145
145
|
const needsConsoleError = hasError || hasDynamic;
|
|
146
146
|
const needsConsoleWarn = hasWarning || hasDynamic;
|
|
147
147
|
if (hasDynamic && (!hasConsoleError || !hasConsoleWarn)) {
|
|
148
|
-
const
|
|
149
|
-
|
|
148
|
+
const missingMethods = [];
|
|
149
|
+
const missingSeverities = [];
|
|
150
|
+
if (!hasConsoleError) {
|
|
151
|
+
missingMethods.push('console.error');
|
|
152
|
+
missingSeverities.push('error');
|
|
153
|
+
}
|
|
154
|
+
if (!hasConsoleWarn) {
|
|
155
|
+
missingMethods.push('console.warn');
|
|
156
|
+
missingSeverities.push('warning');
|
|
157
|
+
}
|
|
158
|
+
const missingMethodsLabel = missingMethods.length === 2
|
|
159
|
+
? 'console.error and console.warn'
|
|
160
|
+
: missingMethods[0];
|
|
161
|
+
const missingPathsLabel = missingSeverities.length === 2
|
|
162
|
+
? 'the error and warning severity paths'
|
|
163
|
+
: `the ${missingSeverities[0]} severity path`;
|
|
164
|
+
const dynamicCalls = group.openCalls.filter((call) => call.severity === 'dynamic');
|
|
165
|
+
dynamicCalls.forEach((dynamicCall) => {
|
|
150
166
|
context.report({
|
|
151
167
|
node: dynamicCall.node,
|
|
152
168
|
messageId: 'missingConsoleBoth',
|
|
169
|
+
data: {
|
|
170
|
+
missingMethods: missingMethodsLabel,
|
|
171
|
+
missingPaths: missingPathsLabel,
|
|
172
|
+
},
|
|
153
173
|
});
|
|
154
|
-
}
|
|
174
|
+
});
|
|
155
175
|
}
|
|
156
176
|
else {
|
|
157
177
|
if (needsConsoleError && !hasConsoleError) {
|
|
@@ -160,6 +180,10 @@ exports.enforceConsoleError = (0, createRule_1.createRule)({
|
|
|
160
180
|
context.report({
|
|
161
181
|
node: errorCall.node,
|
|
162
182
|
messageId: 'missingConsoleError',
|
|
183
|
+
data: {
|
|
184
|
+
severity: 'error',
|
|
185
|
+
consoleMethod: 'console.error',
|
|
186
|
+
},
|
|
163
187
|
});
|
|
164
188
|
}
|
|
165
189
|
}
|
|
@@ -169,6 +193,10 @@ exports.enforceConsoleError = (0, createRule_1.createRule)({
|
|
|
169
193
|
context.report({
|
|
170
194
|
node: warningCall.node,
|
|
171
195
|
messageId: 'missingConsoleWarn',
|
|
196
|
+
data: {
|
|
197
|
+
severity: 'warning',
|
|
198
|
+
consoleMethod: 'console.warn',
|
|
199
|
+
},
|
|
172
200
|
});
|
|
173
201
|
}
|
|
174
202
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export declare const RULE_NAME = "enforce-dynamic-file-naming";
|
|
2
|
+
export declare const REQUIRE_DYNAMIC_FIREBASE_IMPORTS_RULE = "@blumintinc/blumint/require-dynamic-firebase-imports";
|
|
3
|
+
export declare const DYNAMIC_RULES_LABEL: string;
|
|
2
4
|
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"requireDynamicExtension" | "requireDisableDirective", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
3
5
|
export default _default;
|
|
@@ -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
|
},
|