@blumintinc/eslint-plugin-blumint 1.14.0 → 1.16.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.
Files changed (68) hide show
  1. package/README.md +36 -14
  2. package/lib/index.js +20 -12
  3. package/lib/rules/avoid-utils-directory.js +0 -4
  4. package/lib/rules/consistent-callback-naming.js +68 -3
  5. package/lib/rules/dynamic-https-errors.d.ts +1 -1
  6. package/lib/rules/dynamic-https-errors.js +119 -49
  7. package/lib/rules/enforce-boolean-naming-prefixes.d.ts +1 -0
  8. package/lib/rules/enforce-boolean-naming-prefixes.js +86 -331
  9. package/lib/rules/enforce-date-ttime.d.ts +1 -0
  10. package/lib/rules/enforce-date-ttime.js +156 -0
  11. package/lib/rules/enforce-dynamic-firebase-imports.d.ts +2 -1
  12. package/lib/rules/enforce-dynamic-firebase-imports.js +3 -2
  13. package/lib/rules/enforce-dynamic-imports.d.ts +2 -1
  14. package/lib/rules/enforce-dynamic-imports.js +42 -21
  15. package/lib/rules/enforce-f-extension-for-entry-points.d.ts +8 -0
  16. package/lib/rules/enforce-f-extension-for-entry-points.js +283 -0
  17. package/lib/rules/enforce-global-constants.js +3 -3
  18. package/lib/rules/enforce-id-capitalization.js +1 -1
  19. package/lib/rules/enforce-memoize-async.js +66 -15
  20. package/lib/rules/enforce-mui-rounded-icons.js +42 -1
  21. package/lib/rules/enforce-props-argument-name.js +42 -16
  22. package/lib/rules/enforce-stable-hash-spread-props.js +3 -3
  23. package/lib/rules/enforce-transform-memoization.js +1 -1
  24. package/lib/rules/enforce-verb-noun-naming.js +3817 -4641
  25. package/lib/rules/global-const-style.js +25 -4
  26. package/lib/rules/logical-top-to-bottom-grouping.js +80 -2
  27. package/lib/rules/memo-compare-deeply-complex-props.js +183 -6
  28. package/lib/rules/memo-nested-react-components.js +243 -103
  29. package/lib/rules/no-array-length-in-deps.js +74 -3
  30. package/lib/rules/no-async-foreach.js +7 -2
  31. package/lib/rules/no-circular-references.d.ts +2 -1
  32. package/lib/rules/no-circular-references.js +150 -489
  33. package/lib/rules/no-compositing-layer-props.js +31 -0
  34. package/lib/rules/no-console-error.js +12 -10
  35. package/lib/rules/no-empty-dependency-use-callbacks.js +1 -1
  36. package/lib/rules/no-entire-object-hook-deps.js +147 -65
  37. package/lib/rules/no-excessive-parent-chain.js +3 -0
  38. package/lib/rules/no-explicit-return-type.js +6 -0
  39. package/lib/rules/no-hungarian.js +119 -24
  40. package/lib/rules/no-inline-component-prop.js +16 -7
  41. package/lib/rules/no-margin-properties.js +7 -38
  42. package/lib/rules/no-passthrough-getters.d.ts +2 -2
  43. package/lib/rules/no-passthrough-getters.js +83 -1
  44. package/lib/rules/no-redundant-this-params.js +50 -1
  45. package/lib/rules/no-unmemoized-memo-without-props.js +1 -1
  46. package/lib/rules/no-unnecessary-destructuring-rename.js +2 -5
  47. package/lib/rules/no-unnecessary-verb-suffix.js +79 -0
  48. package/lib/rules/no-unused-props.js +215 -37
  49. package/lib/rules/no-useless-fragment.js +10 -2
  50. package/lib/rules/parallelize-async-operations.js +117 -54
  51. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +109 -4
  52. package/lib/rules/prefer-params-over-parent-id.js +1 -1
  53. package/lib/rules/prefer-settings-object.js +27 -10
  54. package/lib/rules/prefer-type-alias-over-typeof-constant.js +75 -4
  55. package/lib/rules/prefer-use-deep-compare-memo.js +8 -14
  56. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +1 -1
  57. package/lib/rules/prevent-children-clobber.js +9 -5
  58. package/lib/rules/react-memoize-literals.js +218 -13
  59. package/lib/rules/require-https-error-cause.js +30 -11
  60. package/lib/rules/require-memo.js +17 -9
  61. package/lib/rules/require-migration-script-metadata.d.ts +9 -0
  62. package/lib/rules/require-migration-script-metadata.js +206 -0
  63. package/lib/rules/warn-https-error-message-user-friendly.d.ts +1 -0
  64. package/lib/rules/warn-https-error-message-user-friendly.js +239 -0
  65. package/lib/utils/ASTHelpers.d.ts +49 -1
  66. package/lib/utils/ASTHelpers.js +394 -112
  67. package/package.json +7 -6
  68. package/release-manifest.json +166 -0
@@ -3,18 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.dynamicHttpsErrors = void 0;
4
4
  const createRule_1 = require("../utils/createRule");
5
5
  const utils_1 = require("@typescript-eslint/utils");
6
- const isHttpsErrorCall = (callee) => {
7
- if (callee.type === 'MemberExpression') {
8
- return (callee.object.type === 'Identifier' &&
9
- callee.object.name === 'https' &&
10
- callee.property.type === 'Identifier' &&
11
- callee.property.name === 'HttpsError');
12
- }
13
- else if (callee.type === 'Identifier') {
14
- return callee.name === 'HttpsError';
15
- }
16
- return false;
17
- };
6
+ const ASTHelpers_1 = require("../utils/ASTHelpers");
7
+ const findPropertyByName = (properties, name) => properties.find((p) => p.type === utils_1.AST_NODE_TYPES.Property &&
8
+ !p.computed &&
9
+ ((p.key.type === utils_1.AST_NODE_TYPES.Identifier && p.key.name === name) ||
10
+ (p.key.type === utils_1.AST_NODE_TYPES.Literal && p.key.value === name)));
18
11
  exports.dynamicHttpsErrors = (0, createRule_1.createRule)({
19
12
  name: 'dynamic-https-errors',
20
13
  meta: {
@@ -27,57 +20,134 @@ exports.dynamicHttpsErrors = (0, createRule_1.createRule)({
27
20
  messages: {
28
21
  dynamicHttpsErrors: 'The HttpsError message (second argument) must stay static. Template expressions here change the hashed message and explode the number of error ids for the same failure. Keep this argument constant and move interpolated values into the third "details" argument so monitoring groups the error while still capturing request context.',
29
22
  missingThirdArgument: 'HttpsError calls must include a third "details" argument. The message (second argument) is hashed into a stable identifier, so omitting details leaves errors hard to debug and encourages packing variables into the hashed message. Provide a third argument with the request-specific context (object or string) to keep identifiers stable and diagnostics useful.',
23
+ missingDetailsProperty: 'HttpsError calls must include a "details" property. The message is hashed into a stable identifier, so omitting details leaves errors hard to debug and encourages packing variables into the hashed message. Provide a details property with the request-specific context (object or string) to keep identifiers stable and diagnostics useful.',
24
+ missingDetailsDueToSpread: 'HttpsError calls must include a "details" property. This call uses an object spread, which prevents static verification that "details" is present. Ensure the spread object contains "details" or provide it explicitly to keep identifiers stable and diagnostics useful.',
25
+ unexpectedExtraArgumentForObjectCall: 'Object-based HttpsError calls must have exactly one argument containing code, message, and details properties. Remove extra arguments or use the positional signature (code, message, details).',
30
26
  },
31
27
  },
32
28
  defaultOptions: [],
33
29
  create(context) {
34
- const checkForHttpsError = (node) => {
35
- const callee = node.callee;
36
- if (isHttpsErrorCall(callee)) {
37
- // Check for missing third argument
38
- if (node.arguments.length < 3) {
30
+ // Only string concatenation with "+" can be static; all other operators
31
+ // are treated as dynamic to avoid hashing non-literal message content.
32
+ const isDynamicBinaryExpression = (expression) => {
33
+ if (expression.operator !== '+')
34
+ return true;
35
+ const isStaticLiteral = (expr) => {
36
+ const inner = ASTHelpers_1.ASTHelpers.unwrapTSAssertions(expr);
37
+ return (inner.type === utils_1.AST_NODE_TYPES.Literal &&
38
+ typeof inner.value === 'string');
39
+ };
40
+ const isSafe = (expr) => {
41
+ if (expr.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
42
+ return false;
43
+ }
44
+ const inner = ASTHelpers_1.ASTHelpers.unwrapTSAssertions(expr);
45
+ if (inner.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
46
+ return !isDynamicBinaryExpression(inner);
47
+ }
48
+ return isStaticLiteral(inner);
49
+ };
50
+ return !(isSafe(expression.left) && isSafe(expression.right));
51
+ };
52
+ /**
53
+ * Determines if a node should be validated for staticness.
54
+ *
55
+ * Pragmatic Exception: Identifier, MemberExpression, and ChainExpression nodes
56
+ * (e.g., `props.message`, `props?.message`, `ERROR_MSG`) are excluded from
57
+ * staticness validation. While they can be dynamic and may affect message
58
+ * stability, they are permitted to support common React/props patterns and
59
+ * constants, preserving developer ergonomics as an intentional trade-off.
60
+ */
61
+ const shouldValidateForStaticness = (node) => {
62
+ return (node.type !== utils_1.AST_NODE_TYPES.Identifier &&
63
+ node.type !== utils_1.AST_NODE_TYPES.MemberExpression &&
64
+ node.type !== utils_1.AST_NODE_TYPES.ChainExpression &&
65
+ node.type !== utils_1.AST_NODE_TYPES.SpreadElement &&
66
+ // Explicitly exclude patterns and other non-Expression nodes that can appear in Property.value
67
+ node.type !== utils_1.AST_NODE_TYPES.ArrayPattern &&
68
+ node.type !== utils_1.AST_NODE_TYPES.AssignmentPattern &&
69
+ node.type !== utils_1.AST_NODE_TYPES.ObjectPattern &&
70
+ node.type !== utils_1.AST_NODE_TYPES.RestElement &&
71
+ node.type !== utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression);
72
+ };
73
+ /**
74
+ * Checks if the message node is static.
75
+ *
76
+ * A message is considered static if it's a Literal (string), a TemplateLiteral with no expressions,
77
+ * or a BinaryExpression (string concatenation with '+') where all parts are static.
78
+ *
79
+ * This function reports `dynamicHttpsErrors` for all other expression types (CallExpression,
80
+ * ConditionalExpression, etc.) that reach it, except for those explicitly excluded by
81
+ * `shouldValidateForStaticness`.
82
+ */
83
+ const checkMessageIsStatic = (messageNode) => {
84
+ const currentNode = ASTHelpers_1.ASTHelpers.unwrapTSAssertions(messageNode);
85
+ if (currentNode.type === utils_1.AST_NODE_TYPES.Literal) {
86
+ return;
87
+ }
88
+ if (currentNode.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
89
+ currentNode.expressions.length === 0) {
90
+ return;
91
+ }
92
+ if (currentNode.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
93
+ if (isDynamicBinaryExpression(currentNode)) {
39
94
  context.report({
40
- node,
41
- messageId: 'missingThirdArgument',
95
+ node: messageNode,
96
+ messageId: 'dynamicHttpsErrors',
42
97
  });
43
98
  }
44
- // Check for dynamic content in second argument (existing functionality)
45
- const secondArg = node.arguments[1];
46
- if (!secondArg)
47
- return;
48
- if (secondArg.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
49
- secondArg.expressions.length > 0) {
99
+ return;
100
+ }
101
+ // Catch-all for other dynamic forms (CallExpression, ConditionalExpression, etc.)
102
+ context.report({
103
+ node: messageNode,
104
+ messageId: 'dynamicHttpsErrors',
105
+ });
106
+ };
107
+ const checkForHttpsError = (node) => {
108
+ const callee = node.callee;
109
+ if (!ASTHelpers_1.ASTHelpers.isHttpsErrorCall(callee))
110
+ return;
111
+ // Signature 1: Object-based constructor (HttpsErrorProps)
112
+ if (node.arguments.length >= 1 &&
113
+ node.arguments[0].type === utils_1.AST_NODE_TYPES.ObjectExpression) {
114
+ if (node.arguments.length > 1) {
50
115
  context.report({
51
- node: secondArg,
52
- messageId: 'dynamicHttpsErrors',
116
+ node,
117
+ messageId: 'unexpectedExtraArgumentForObjectCall',
53
118
  });
54
119
  return;
55
120
  }
56
- // Only string concatenation with "+" can be static; all other operators
57
- // are treated as dynamic to avoid hashing non-literal message content.
58
- const isDynamicBinaryExpression = (expression) => {
59
- if (expression.operator !== '+')
60
- return true;
61
- const isStaticLiteral = (expr) => expr.type === utils_1.AST_NODE_TYPES.Literal &&
62
- typeof expr.value === 'string';
63
- const isSafe = (expr) => {
64
- if (expr.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
65
- return false;
66
- }
67
- if (expr.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
68
- return !isDynamicBinaryExpression(expr);
69
- }
70
- return isStaticLiteral(expr);
71
- };
72
- return !(isSafe(expression.left) && isSafe(expression.right));
73
- };
74
- if (secondArg.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
75
- isDynamicBinaryExpression(secondArg)) {
121
+ const props = node.arguments[0];
122
+ const messageProperty = findPropertyByName(props.properties, 'message');
123
+ const detailsProperty = findPropertyByName(props.properties, 'details');
124
+ if (!detailsProperty) {
125
+ const hasSpread = props.properties.some((p) => p.type === utils_1.AST_NODE_TYPES.SpreadElement);
76
126
  context.report({
77
- node: secondArg,
78
- messageId: 'dynamicHttpsErrors',
127
+ node,
128
+ messageId: hasSpread
129
+ ? 'missingDetailsDueToSpread'
130
+ : 'missingDetailsProperty',
79
131
  });
80
132
  }
133
+ if (messageProperty &&
134
+ shouldValidateForStaticness(messageProperty.value)) {
135
+ checkMessageIsStatic(messageProperty.value);
136
+ }
137
+ return;
138
+ }
139
+ // Signature 2: Positional arguments (code, message, details)
140
+ // Check for missing third argument
141
+ if (node.arguments.length < 3) {
142
+ context.report({
143
+ node,
144
+ messageId: 'missingThirdArgument',
145
+ });
146
+ }
147
+ // Check for dynamic content in second argument
148
+ const secondArg = node.arguments[1];
149
+ if (secondArg && shouldValidateForStaticness(secondArg)) {
150
+ checkMessageIsStatic(secondArg);
81
151
  }
82
152
  };
83
153
  return {
@@ -3,6 +3,7 @@ type Options = [
3
3
  {
4
4
  prefixes?: string[];
5
5
  ignoreOverriddenGetters?: boolean;
6
+ enforceForPropertySignatures?: boolean;
6
7
  }
7
8
  ];
8
9
  export declare const enforceBooleanNamingPrefixes: TSESLint.RuleModule<"missingBooleanPrefix", Options, TSESLint.RuleListener>;