@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
|
@@ -97,13 +97,40 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
97
97
|
},
|
|
98
98
|
],
|
|
99
99
|
messages: {
|
|
100
|
-
noTypeAssertionReturns: '
|
|
101
|
-
|
|
100
|
+
noTypeAssertionReturns: `What's wrong: Return value uses a type assertion to "{{assertedType}}", which bypasses TypeScript checks. ` +
|
|
101
|
+
`→ Why it matters: Casts can hide missing or invalid data and let unsafe values reach callers without compile-time errors. ` +
|
|
102
|
+
`→ How to fix: Create a typed variable or add a narrowing step before returning so the value is validated by the type system instead of forced with a cast.`,
|
|
103
|
+
useExplicitVariable: `What's wrong: Return type "{{returnType}}" is declared while returning an unchecked expression, so TypeScript trusts the annotation instead of validating the actual value. ` +
|
|
104
|
+
`→ Why it matters: This hides mismatches between implementation and contract, letting incorrect return values propagate without type errors. ` +
|
|
105
|
+
`→ How to fix: Assign the expression to a typed variable or narrow it first, then return that variable so TypeScript can verify it against the declared return type.`,
|
|
102
106
|
},
|
|
103
107
|
},
|
|
104
108
|
defaultOptions: [defaultOptions],
|
|
105
109
|
create(context, [options]) {
|
|
106
110
|
const mergedOptions = { ...defaultOptions, ...options };
|
|
111
|
+
const sourceCode = context.getSourceCode();
|
|
112
|
+
function getTypeText(typeNode, fallback) {
|
|
113
|
+
const text = typeNode ? sourceCode.getText(typeNode).trim() : '';
|
|
114
|
+
return text || fallback;
|
|
115
|
+
}
|
|
116
|
+
function reportTypeAssertion(node) {
|
|
117
|
+
context.report({
|
|
118
|
+
node,
|
|
119
|
+
messageId: 'noTypeAssertionReturns',
|
|
120
|
+
data: {
|
|
121
|
+
assertedType: getTypeText(node.typeAnnotation, 'the asserted type'),
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function reportReturnTypeAnnotation(typeAnnotation) {
|
|
126
|
+
context.report({
|
|
127
|
+
node: typeAnnotation,
|
|
128
|
+
messageId: 'useExplicitVariable',
|
|
129
|
+
data: {
|
|
130
|
+
returnType: getTypeText(typeAnnotation.typeAnnotation, 'the declared return type'),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
107
134
|
/**
|
|
108
135
|
* Checks if a node is inside a JSX attribute or object property (which could be JSX props)
|
|
109
136
|
*/
|
|
@@ -202,7 +229,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
202
229
|
/**
|
|
203
230
|
* Check if the current file is a .f.ts file
|
|
204
231
|
*/
|
|
205
|
-
function isFunctionFile(
|
|
232
|
+
function isFunctionFile() {
|
|
206
233
|
const filename = context.getFilename();
|
|
207
234
|
return filename.endsWith('.f.ts');
|
|
208
235
|
}
|
|
@@ -213,7 +240,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
213
240
|
if (!node.returnType)
|
|
214
241
|
return;
|
|
215
242
|
// Skip checking return types in .f.ts files
|
|
216
|
-
if (isFunctionFile(
|
|
243
|
+
if (isFunctionFile()) {
|
|
217
244
|
return;
|
|
218
245
|
}
|
|
219
246
|
// Allow type predicates if configured
|
|
@@ -224,10 +251,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
224
251
|
// If type predicates are not allowed, report them
|
|
225
252
|
if (!mergedOptions.allowTypePredicates &&
|
|
226
253
|
isTypePredicate(node.returnType)) {
|
|
227
|
-
|
|
228
|
-
node: node.returnType,
|
|
229
|
-
messageId: 'useExplicitVariable',
|
|
230
|
-
});
|
|
254
|
+
reportReturnTypeAnnotation(node.returnType);
|
|
231
255
|
return;
|
|
232
256
|
}
|
|
233
257
|
// Check if the function has a return statement with a direct value (not a variable)
|
|
@@ -244,10 +268,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
244
268
|
if (statement.argument.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
|
|
245
269
|
statement.argument.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
|
|
246
270
|
statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
247
|
-
|
|
248
|
-
node: node.returnType,
|
|
249
|
-
messageId: 'useExplicitVariable',
|
|
250
|
-
});
|
|
271
|
+
reportReturnTypeAnnotation(node.returnType);
|
|
251
272
|
break;
|
|
252
273
|
}
|
|
253
274
|
}
|
|
@@ -267,23 +288,14 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
267
288
|
}
|
|
268
289
|
// For nested type assertions, only report the outermost one
|
|
269
290
|
if (node.argument.expression.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
|
|
270
|
-
|
|
271
|
-
node: node.argument,
|
|
272
|
-
messageId: 'noTypeAssertionReturns',
|
|
273
|
-
});
|
|
291
|
+
reportTypeAssertion(node.argument);
|
|
274
292
|
return;
|
|
275
293
|
}
|
|
276
|
-
|
|
277
|
-
node: node.argument,
|
|
278
|
-
messageId: 'noTypeAssertionReturns',
|
|
279
|
-
});
|
|
294
|
+
reportTypeAssertion(node.argument);
|
|
280
295
|
}
|
|
281
296
|
// Check for type assertions using angle bracket syntax
|
|
282
297
|
if (node.argument.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
|
|
283
|
-
|
|
284
|
-
node: node.argument,
|
|
285
|
-
messageId: 'noTypeAssertionReturns',
|
|
286
|
-
});
|
|
298
|
+
reportTypeAssertion(node.argument);
|
|
287
299
|
}
|
|
288
300
|
},
|
|
289
301
|
// Check functions with explicit return types
|
|
@@ -301,7 +313,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
301
313
|
// Check for explicit return type
|
|
302
314
|
if (node.returnType) {
|
|
303
315
|
// Skip checking return types in .f.ts files
|
|
304
|
-
if (isFunctionFile(
|
|
316
|
+
if (isFunctionFile()) {
|
|
305
317
|
return;
|
|
306
318
|
}
|
|
307
319
|
// Allow type predicates if configured
|
|
@@ -312,19 +324,13 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
312
324
|
// If type predicates are not allowed, report them
|
|
313
325
|
if (!mergedOptions.allowTypePredicates &&
|
|
314
326
|
isTypePredicate(node.returnType)) {
|
|
315
|
-
|
|
316
|
-
node: node.returnType,
|
|
317
|
-
messageId: 'useExplicitVariable',
|
|
318
|
-
});
|
|
327
|
+
reportReturnTypeAnnotation(node.returnType);
|
|
319
328
|
return;
|
|
320
329
|
}
|
|
321
330
|
// If returning an object literal with explicit return type, report it
|
|
322
331
|
if (node.body.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
|
|
323
332
|
node.body.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
|
|
324
|
-
|
|
325
|
-
node: node.returnType,
|
|
326
|
-
messageId: 'useExplicitVariable',
|
|
327
|
-
});
|
|
333
|
+
reportReturnTypeAnnotation(node.returnType);
|
|
328
334
|
}
|
|
329
335
|
}
|
|
330
336
|
// Check for type assertions in the body
|
|
@@ -333,16 +339,10 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
333
339
|
if (mergedOptions.allowAsConst && isAsConstAssertion(node.body)) {
|
|
334
340
|
return;
|
|
335
341
|
}
|
|
336
|
-
|
|
337
|
-
node: node.body,
|
|
338
|
-
messageId: 'noTypeAssertionReturns',
|
|
339
|
-
});
|
|
342
|
+
reportTypeAssertion(node.body);
|
|
340
343
|
}
|
|
341
344
|
else if (node.body.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
|
|
342
|
-
|
|
343
|
-
node: node.body,
|
|
344
|
-
messageId: 'noTypeAssertionReturns',
|
|
345
|
-
});
|
|
345
|
+
reportTypeAssertion(node.body);
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
else if (node.returnType) {
|
|
@@ -365,10 +365,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
365
365
|
return;
|
|
366
366
|
}
|
|
367
367
|
// For standalone type assertions in expressions
|
|
368
|
-
|
|
369
|
-
node,
|
|
370
|
-
messageId: 'noTypeAssertionReturns',
|
|
371
|
-
});
|
|
368
|
+
reportTypeAssertion(node);
|
|
372
369
|
},
|
|
373
370
|
// Check for type assertions using angle bracket syntax
|
|
374
371
|
TSTypeAssertion(node) {
|
|
@@ -377,10 +374,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
377
374
|
return;
|
|
378
375
|
}
|
|
379
376
|
// For standalone type assertions in expressions
|
|
380
|
-
|
|
381
|
-
node,
|
|
382
|
-
messageId: 'noTypeAssertionReturns',
|
|
383
|
-
});
|
|
377
|
+
reportTypeAssertion(node);
|
|
384
378
|
},
|
|
385
379
|
};
|
|
386
380
|
},
|
|
@@ -2,18 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.noUndefinedNullPassthrough = void 0;
|
|
4
4
|
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
6
|
exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
|
|
6
7
|
create(context) {
|
|
7
8
|
return {
|
|
8
9
|
FunctionDeclaration(node) {
|
|
9
|
-
|
|
10
|
-
if (node.params.length === 0) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
// Skip React hooks (functions starting with 'use')
|
|
14
|
-
if (node.id &&
|
|
15
|
-
node.id.type === 'Identifier' &&
|
|
16
|
-
node.id.name.startsWith('use')) {
|
|
10
|
+
if (shouldSkipFunction(node)) {
|
|
17
11
|
return;
|
|
18
12
|
}
|
|
19
13
|
// For functions with exactly one parameter, check if it's passing through null/undefined
|
|
@@ -26,16 +20,7 @@ exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
|
|
|
26
20
|
}
|
|
27
21
|
},
|
|
28
22
|
ArrowFunctionExpression(node) {
|
|
29
|
-
|
|
30
|
-
if (node.params.length === 0) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
// Skip if the function is part of a variable declaration that starts with 'use' (React hook)
|
|
34
|
-
const parent = node.parent;
|
|
35
|
-
if (parent &&
|
|
36
|
-
parent.type === 'VariableDeclarator' &&
|
|
37
|
-
parent.id.type === 'Identifier' &&
|
|
38
|
-
parent.id.name.startsWith('use')) {
|
|
23
|
+
if (shouldSkipFunction(node)) {
|
|
39
24
|
return;
|
|
40
25
|
}
|
|
41
26
|
// For arrow functions with block body
|
|
@@ -54,16 +39,7 @@ exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
|
|
|
54
39
|
}
|
|
55
40
|
},
|
|
56
41
|
FunctionExpression(node) {
|
|
57
|
-
|
|
58
|
-
if (node.params.length === 0) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
// Skip if the function is part of a variable declaration that starts with 'use' (React hook)
|
|
62
|
-
const parent = node.parent;
|
|
63
|
-
if (parent &&
|
|
64
|
-
parent.type === 'VariableDeclarator' &&
|
|
65
|
-
parent.id.type === 'Identifier' &&
|
|
66
|
-
parent.id.name.startsWith('use')) {
|
|
42
|
+
if (shouldSkipFunction(node)) {
|
|
67
43
|
return;
|
|
68
44
|
}
|
|
69
45
|
// For functions with exactly one parameter, check if it's passing through null/undefined
|
|
@@ -86,7 +62,7 @@ exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
|
|
|
86
62
|
},
|
|
87
63
|
schema: [],
|
|
88
64
|
messages: {
|
|
89
|
-
unexpected: '
|
|
65
|
+
unexpected: 'Function returns null or undefined when "{{paramName}}" is nullish, which just passes the missing value to callers. This hides where validation belongs, delays failures until runtime as null values propagate unpredictably, and forces every caller to add defensive branching—making bugs and debugging more likely. Validate the argument before calling (throw an error if it is required) or return a concrete fallback (for example, an empty array or default object) so this function always provides a meaningful result.',
|
|
90
66
|
},
|
|
91
67
|
},
|
|
92
68
|
defaultOptions: [],
|
|
@@ -126,6 +102,7 @@ function checkFunctionBody(body, param, context) {
|
|
|
126
102
|
context.report({
|
|
127
103
|
node: statement,
|
|
128
104
|
messageId: 'unexpected',
|
|
105
|
+
data: { paramName },
|
|
129
106
|
});
|
|
130
107
|
}
|
|
131
108
|
return;
|
|
@@ -142,6 +119,7 @@ function checkFunctionBody(body, param, context) {
|
|
|
142
119
|
context.report({
|
|
143
120
|
node: statement,
|
|
144
121
|
messageId: 'unexpected',
|
|
122
|
+
data: { paramName },
|
|
145
123
|
});
|
|
146
124
|
}
|
|
147
125
|
return;
|
|
@@ -254,6 +232,7 @@ function checkImplicitReturn(node, context) {
|
|
|
254
232
|
context.report({
|
|
255
233
|
node,
|
|
256
234
|
messageId: 'unexpected',
|
|
235
|
+
data: { paramName },
|
|
257
236
|
});
|
|
258
237
|
}
|
|
259
238
|
}
|
|
@@ -264,6 +243,7 @@ function checkImplicitReturn(node, context) {
|
|
|
264
243
|
context.report({
|
|
265
244
|
node,
|
|
266
245
|
messageId: 'unexpected',
|
|
246
|
+
data: { paramName },
|
|
267
247
|
});
|
|
268
248
|
}
|
|
269
249
|
}
|
|
@@ -272,6 +252,7 @@ function checkImplicitReturn(node, context) {
|
|
|
272
252
|
context.report({
|
|
273
253
|
node,
|
|
274
254
|
messageId: 'unexpected',
|
|
255
|
+
data: { paramName },
|
|
275
256
|
});
|
|
276
257
|
}
|
|
277
258
|
}
|
|
@@ -360,8 +341,8 @@ function checkFunctionBodyForEarlyReturns(body, params, context) {
|
|
|
360
341
|
if (statement.type === 'IfStatement') {
|
|
361
342
|
const test = statement.test;
|
|
362
343
|
// Check if the test is checking any parameter for null/undefined
|
|
363
|
-
const
|
|
364
|
-
if (
|
|
344
|
+
const matchedParam = paramNames.find((paramName) => isNullUndefinedCheck(test, paramName));
|
|
345
|
+
if (matchedParam) {
|
|
365
346
|
// Check if the consequent is a block statement with a return
|
|
366
347
|
if (statement.consequent.type === 'BlockStatement') {
|
|
367
348
|
for (const consequentStmt of statement.consequent.body) {
|
|
@@ -374,6 +355,7 @@ function checkFunctionBodyForEarlyReturns(body, params, context) {
|
|
|
374
355
|
context.report({
|
|
375
356
|
node: statement,
|
|
376
357
|
messageId: 'unexpected',
|
|
358
|
+
data: { paramName: matchedParam },
|
|
377
359
|
});
|
|
378
360
|
}
|
|
379
361
|
return;
|
|
@@ -390,6 +372,7 @@ function checkFunctionBodyForEarlyReturns(body, params, context) {
|
|
|
390
372
|
context.report({
|
|
391
373
|
node: statement,
|
|
392
374
|
messageId: 'unexpected',
|
|
375
|
+
data: { paramName: matchedParam },
|
|
393
376
|
});
|
|
394
377
|
}
|
|
395
378
|
return;
|
|
@@ -398,4 +381,51 @@ function checkFunctionBodyForEarlyReturns(body, params, context) {
|
|
|
398
381
|
}
|
|
399
382
|
}
|
|
400
383
|
}
|
|
384
|
+
function shouldSkipFunction(node) {
|
|
385
|
+
// Skip functions with no parameters
|
|
386
|
+
if (node.params.length === 0) {
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
// Skip functions with any parameter typed as 'unknown'
|
|
390
|
+
if (node.params.some(isUnknownParameter)) {
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
// Skip React hooks (functions starting with 'use')
|
|
394
|
+
let name;
|
|
395
|
+
if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
|
|
396
|
+
name = node.id?.name;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
const parent = node.parent;
|
|
400
|
+
if (parent &&
|
|
401
|
+
parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
402
|
+
parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
403
|
+
name = parent.id.name;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (name?.startsWith('use')) {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Exempts type-narrowing functions from the rule by checking for 'unknown' parameters.
|
|
413
|
+
* These functions often narrow from 'unknown' and return null/undefined for invalid types,
|
|
414
|
+
* which is a valid pattern.
|
|
415
|
+
*
|
|
416
|
+
* TODO: Handle edge cases like rest parameters (...args: unknown[]),
|
|
417
|
+
* destructured parameters ({ x }: unknown), and array patterns ([a]: unknown).
|
|
418
|
+
*/
|
|
419
|
+
function isUnknownParameter(param) {
|
|
420
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
421
|
+
return (param.typeAnnotation?.typeAnnotation.type ===
|
|
422
|
+
utils_1.AST_NODE_TYPES.TSUnknownKeyword);
|
|
423
|
+
}
|
|
424
|
+
if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
|
|
425
|
+
param.left.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
426
|
+
return (param.left.typeAnnotation?.typeAnnotation.type ===
|
|
427
|
+
utils_1.AST_NODE_TYPES.TSUnknownKeyword);
|
|
428
|
+
}
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
401
431
|
//# sourceMappingURL=no-undefined-null-passthrough.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type Options = [
|
|
2
|
+
{
|
|
3
|
+
ignoreHooks?: string[];
|
|
4
|
+
ignoreHocs?: string[];
|
|
5
|
+
}
|
|
6
|
+
];
|
|
7
|
+
export declare const noUnmemoizedMemoWithoutProps: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"unnecessaryMemoWithoutProps", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
8
|
+
export {};
|