@blumintinc/eslint-plugin-blumint 1.8.0 → 1.8.1

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 (39) hide show
  1. package/README.md +84 -39
  2. package/lib/index.js +50 -2
  3. package/lib/rules/enforce-css-media-queries.d.ts +5 -0
  4. package/lib/rules/enforce-css-media-queries.js +87 -0
  5. package/lib/rules/enforce-dynamic-imports.d.ts +9 -0
  6. package/lib/rules/enforce-dynamic-imports.js +84 -0
  7. package/lib/rules/enforce-id-capitalization.d.ts +6 -0
  8. package/lib/rules/enforce-id-capitalization.js +78 -0
  9. package/lib/rules/enforce-mui-rounded-icons.d.ts +1 -0
  10. package/lib/rules/enforce-mui-rounded-icons.js +54 -0
  11. package/lib/rules/enforce-positive-naming.js +30 -6
  12. package/lib/rules/enforce-react-type-naming.d.ts +3 -0
  13. package/lib/rules/enforce-react-type-naming.js +191 -0
  14. package/lib/rules/enforce-singular-type-names.d.ts +2 -0
  15. package/lib/rules/enforce-singular-type-names.js +112 -0
  16. package/lib/rules/enforce-verb-noun-naming.js +5 -2
  17. package/lib/rules/ensure-pointer-events-none.d.ts +1 -0
  18. package/lib/rules/ensure-pointer-events-none.js +211 -0
  19. package/lib/rules/key-only-outermost-element.d.ts +3 -0
  20. package/lib/rules/key-only-outermost-element.js +152 -0
  21. package/lib/rules/no-always-true-false-conditions.js +19 -0
  22. package/lib/rules/no-circular-references.d.ts +1 -0
  23. package/lib/rules/no-circular-references.js +523 -0
  24. package/lib/rules/no-hungarian.d.ts +5 -0
  25. package/lib/rules/no-hungarian.js +223 -0
  26. package/lib/rules/no-object-values-on-strings.d.ts +2 -0
  27. package/lib/rules/no-object-values-on-strings.js +294 -0
  28. package/lib/rules/no-type-assertion-returns.js +10 -0
  29. package/lib/rules/no-unnecessary-destructuring.d.ts +5 -0
  30. package/lib/rules/no-unnecessary-destructuring.js +69 -0
  31. package/lib/rules/no-unused-props.js +10 -5
  32. package/lib/rules/no-unused-usestate.d.ts +8 -0
  33. package/lib/rules/no-unused-usestate.js +84 -0
  34. package/lib/rules/omit-index-html.d.ts +7 -0
  35. package/lib/rules/omit-index-html.js +91 -0
  36. package/lib/rules/prefer-usememo-over-useeffect-usestate.d.ts +5 -0
  37. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +129 -0
  38. package/lib/rules/react-usememo-should-be-component.js +369 -2
  39. package/package.json +7 -4
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensurePointerEventsNone = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ /**
7
+ * Checks if a string contains a pseudo-element selector (::before or ::after)
8
+ */
9
+ function hasPseudoElementSelector(selector) {
10
+ return /::?(before|after)\b/i.test(selector);
11
+ }
12
+ /**
13
+ * Checks if a property name is position with absolute or fixed value
14
+ */
15
+ function isAbsoluteOrFixedPosition(propertyName, propertyValue) {
16
+ if (propertyName !== 'position')
17
+ return false;
18
+ return propertyValue === 'absolute' || propertyValue === 'fixed';
19
+ }
20
+ /**
21
+ * Checks if a property is pointer-events with a value
22
+ */
23
+ function isPointerEventsProperty(propertyName) {
24
+ return propertyName === 'pointerEvents' || propertyName === 'pointer-events';
25
+ }
26
+ exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
27
+ name: 'ensure-pointer-events-none',
28
+ meta: {
29
+ type: 'suggestion',
30
+ docs: {
31
+ description: 'Ensure pointer-events: none is added to non-interactive pseudo-elements',
32
+ recommended: 'error',
33
+ },
34
+ fixable: 'code',
35
+ schema: [],
36
+ messages: {
37
+ missingPointerEventsNone: 'Pseudo-elements (::before, ::after) with position: absolute or fixed should have pointer-events: none to prevent blocking interactions with underlying elements',
38
+ },
39
+ },
40
+ defaultOptions: [],
41
+ create(context) {
42
+ // Track style objects that have position: absolute or fixed
43
+ const absolutePositionedStyles = new Map();
44
+ // Track style objects that already have pointer-events defined
45
+ const stylesWithPointerEvents = new Map();
46
+ /**
47
+ * Process a CSS-in-JS style object to check for position: absolute/fixed and pointer-events
48
+ */
49
+ function processStyleObject(node) {
50
+ let hasAbsolutePosition = false;
51
+ let pointerEventsValue;
52
+ // Check each property in the style object
53
+ for (const property of node.properties) {
54
+ if (property.type !== utils_1.AST_NODE_TYPES.Property)
55
+ continue;
56
+ let propertyName = '';
57
+ let propertyValue;
58
+ // Get property name
59
+ if (property.key.type === utils_1.AST_NODE_TYPES.Identifier) {
60
+ propertyName = property.key.name;
61
+ }
62
+ else if (property.key.type === utils_1.AST_NODE_TYPES.Literal && typeof property.key.value === 'string') {
63
+ propertyName = property.key.value;
64
+ }
65
+ // Get property value if it's a string literal
66
+ if (property.value.type === utils_1.AST_NODE_TYPES.Literal) {
67
+ propertyValue = String(property.value.value);
68
+ }
69
+ else if (property.value.type === utils_1.AST_NODE_TYPES.Identifier) {
70
+ propertyValue = property.value.name;
71
+ }
72
+ // Check if this is position: absolute/fixed
73
+ if (isAbsoluteOrFixedPosition(propertyName, propertyValue)) {
74
+ hasAbsolutePosition = true;
75
+ }
76
+ // Check if this is pointer-events property
77
+ if (isPointerEventsProperty(propertyName)) {
78
+ if (property.value.type === utils_1.AST_NODE_TYPES.Literal) {
79
+ pointerEventsValue = String(property.value.value);
80
+ }
81
+ else if (property.value.type === utils_1.AST_NODE_TYPES.Identifier) {
82
+ pointerEventsValue = property.value.name;
83
+ }
84
+ }
85
+ }
86
+ // Store the results for this style object
87
+ absolutePositionedStyles.set(node, hasAbsolutePosition);
88
+ if (pointerEventsValue !== undefined) {
89
+ stylesWithPointerEvents.set(node, pointerEventsValue);
90
+ }
91
+ }
92
+ /**
93
+ * Check if a style object needs pointer-events: none
94
+ */
95
+ function checkStyleObject(node, selector) {
96
+ const isPseudoElement = selector && hasPseudoElementSelector(selector);
97
+ const isAbsolutePositioned = absolutePositionedStyles.get(node) || false;
98
+ const pointerEventsValue = stylesWithPointerEvents.get(node);
99
+ // If this is a pseudo-element with absolute positioning but no pointer-events
100
+ if (isPseudoElement && isAbsolutePositioned && pointerEventsValue === undefined) {
101
+ context.report({
102
+ node,
103
+ messageId: 'missingPointerEventsNone',
104
+ fix(fixer) {
105
+ // Find the last property in the object
106
+ const sourceCode = context.getSourceCode();
107
+ const properties = node.properties;
108
+ if (properties.length === 0)
109
+ return null;
110
+ const lastProperty = properties[properties.length - 1];
111
+ const lastPropertyToken = sourceCode.getLastToken(lastProperty);
112
+ if (lastPropertyToken) {
113
+ return fixer.insertTextAfter(lastPropertyToken, `, pointerEvents: 'none'`);
114
+ }
115
+ return null;
116
+ }
117
+ });
118
+ }
119
+ // If this is a pseudo-element with absolute positioning and pointer-events: auto
120
+ if (isPseudoElement && isAbsolutePositioned && pointerEventsValue === 'auto') {
121
+ // Don't report an error if pointer-events is explicitly set to 'auto'
122
+ // This is an intentional choice by the developer
123
+ }
124
+ }
125
+ return {
126
+ // Check for pseudo-element selectors in styled-components and similar libraries
127
+ TaggedTemplateExpression(node) {
128
+ // Check if this is a styled-components template
129
+ const tag = node.tag;
130
+ let isStyledComponent = false;
131
+ if (tag.type === utils_1.AST_NODE_TYPES.MemberExpression &&
132
+ tag.object.type === utils_1.AST_NODE_TYPES.Identifier &&
133
+ tag.object.name === 'styled') {
134
+ isStyledComponent = true;
135
+ }
136
+ else if (tag.type === utils_1.AST_NODE_TYPES.Identifier &&
137
+ (tag.name === 'styled' || tag.name === 'css')) {
138
+ isStyledComponent = true;
139
+ }
140
+ if (isStyledComponent) {
141
+ // For styled-components, we need to check the template content
142
+ const template = node.quasi.quasis.map(q => q.value.raw).join('');
143
+ // Check if it contains a pseudo-element with position: absolute/fixed
144
+ if (hasPseudoElementSelector(template) &&
145
+ (template.includes('position: absolute') || template.includes('position: fixed'))) {
146
+ // Check if it's missing pointer-events: none
147
+ if (!template.includes('pointer-events: none') && !template.includes('pointer-events:none')) {
148
+ context.report({
149
+ node,
150
+ messageId: 'missingPointerEventsNone'
151
+ });
152
+ }
153
+ }
154
+ }
155
+ },
156
+ // Process style objects in JSX
157
+ JSXAttribute(node) {
158
+ if (node.name.type !== utils_1.AST_NODE_TYPES.JSXIdentifier || node.name.name !== 'style')
159
+ return;
160
+ if (node.value?.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
161
+ node.value.expression.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
162
+ processStyleObject(node.value.expression);
163
+ checkStyleObject(node.value.expression);
164
+ }
165
+ },
166
+ // Process style objects in regular JavaScript/TypeScript
167
+ ObjectExpression(node) {
168
+ // Skip if parent is not a variable declaration or assignment
169
+ const parent = node.parent;
170
+ if (!parent)
171
+ return;
172
+ // Check if this might be a style object
173
+ let isStyleObject = false;
174
+ let selector;
175
+ if (parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
176
+ parent.id.type === utils_1.AST_NODE_TYPES.Identifier &&
177
+ /style/i.test(parent.id.name)) {
178
+ isStyleObject = true;
179
+ }
180
+ else if (parent.type === utils_1.AST_NODE_TYPES.Property &&
181
+ parent.key.type === utils_1.AST_NODE_TYPES.Identifier &&
182
+ /style/i.test(parent.key.name)) {
183
+ isStyleObject = true;
184
+ }
185
+ else if (parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
186
+ // Check for CSS-in-JS libraries like emotion's css() function
187
+ const callee = parent.callee;
188
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier && callee.name === 'css') {
189
+ isStyleObject = true;
190
+ }
191
+ }
192
+ if (isStyleObject) {
193
+ processStyleObject(node);
194
+ checkStyleObject(node, selector);
195
+ }
196
+ },
197
+ // Process CSS-in-JS libraries that use objects with selectors
198
+ Property(node) {
199
+ // Check for patterns like { '&::before': { ... } }
200
+ if (node.key.type === utils_1.AST_NODE_TYPES.Literal &&
201
+ typeof node.key.value === 'string' &&
202
+ hasPseudoElementSelector(node.key.value) &&
203
+ node.value.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
204
+ processStyleObject(node.value);
205
+ checkStyleObject(node.value, node.key.value);
206
+ }
207
+ }
208
+ };
209
+ },
210
+ });
211
+ //# sourceMappingURL=ensure-pointer-events-none.js.map
@@ -0,0 +1,3 @@
1
+ type MessageIds = 'keyOnlyOutermostElement' | 'fragmentShouldHaveKey';
2
+ export declare const keyOnlyOutermostElement: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
3
+ export {};
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.keyOnlyOutermostElement = void 0;
4
+ const createRule_1 = require("../utils/createRule");
5
+ exports.keyOnlyOutermostElement = (0, createRule_1.createRule)({
6
+ name: 'key-only-outermost-element',
7
+ meta: {
8
+ type: 'problem',
9
+ docs: {
10
+ description: 'Enforce that only the outermost element in list rendering has a key prop',
11
+ recommended: 'error',
12
+ },
13
+ messages: {
14
+ keyOnlyOutermostElement: 'Only the outermost element in a list rendering should have a key prop. Remove the key from this nested element.',
15
+ fragmentShouldHaveKey: 'Fragment used as the outermost element in a list should use <React.Fragment key={...}> instead of shorthand syntax.',
16
+ },
17
+ schema: [],
18
+ fixable: 'code',
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ // Track JSXElements that are direct children of a map callback
23
+ const mapCallbackElements = new Set();
24
+ // Track JSXFragments that are direct children of a map callback
25
+ const mapCallbackFragments = new Set();
26
+ // Track JSX attributes that have already been reported to avoid duplicate reports
27
+ const reportedAttributes = new Set();
28
+ // Helper function to process map calls
29
+ const processMapCall = (node) => {
30
+ // Get the callback function
31
+ const callback = node.arguments[0];
32
+ if (callback && (callback.type === 'ArrowFunctionExpression' ||
33
+ callback.type === 'FunctionExpression')) {
34
+ // Find the return statement or expression
35
+ let returnExpr = null;
36
+ if (callback.type === 'ArrowFunctionExpression' &&
37
+ callback.expression &&
38
+ callback.body.type !== 'BlockStatement') {
39
+ // Arrow function with implicit return
40
+ returnExpr = callback.body;
41
+ }
42
+ else {
43
+ // Look for return statements in the function body
44
+ const body = callback.body;
45
+ if (body.type === 'BlockStatement') {
46
+ for (const stmt of body.body) {
47
+ if (stmt.type === 'ReturnStatement' && stmt.argument) {
48
+ returnExpr = stmt.argument;
49
+ break;
50
+ }
51
+ }
52
+ }
53
+ }
54
+ // If we found a JSX element or fragment as the return value, mark it
55
+ if (returnExpr) {
56
+ if (returnExpr.type === 'JSXElement') {
57
+ mapCallbackElements.add(returnExpr);
58
+ }
59
+ else if (returnExpr.type === 'JSXFragment') {
60
+ mapCallbackFragments.add(returnExpr);
61
+ // Check if it's a shorthand fragment (<>)
62
+ // Shorthand fragments can't have keys, so suggest using React.Fragment
63
+ context.report({
64
+ node: returnExpr.openingFragment,
65
+ messageId: 'fragmentShouldHaveKey',
66
+ });
67
+ }
68
+ }
69
+ }
70
+ };
71
+ return {
72
+ // Find array.map() calls
73
+ 'CallExpression[callee.property.name="map"]'(node) {
74
+ processMapCall(node);
75
+ },
76
+ // Check all JSX elements for key props
77
+ JSXElement(node) {
78
+ // Skip if this is the outermost element in a map callback
79
+ if (mapCallbackElements.has(node)) {
80
+ return;
81
+ }
82
+ // Check if this element has a key prop
83
+ const openingElement = node.openingElement;
84
+ const attributes = openingElement.attributes;
85
+ for (let i = 0; i < attributes.length; i++) {
86
+ const attr = attributes[i];
87
+ if (attr.type === 'JSXAttribute' && attr.name.name === 'key' && !reportedAttributes.has(attr)) {
88
+ // Check if this element is nested inside a map callback element or fragment
89
+ let parent = node.parent;
90
+ let isNestedInMapCallback = false;
91
+ while (parent) {
92
+ if ((parent.type === 'JSXElement' && mapCallbackElements.has(parent)) ||
93
+ (parent.type === 'JSXFragment' && mapCallbackFragments.has(parent))) {
94
+ isNestedInMapCallback = true;
95
+ break;
96
+ }
97
+ parent = parent.parent;
98
+ }
99
+ if (isNestedInMapCallback) {
100
+ // Mark this attribute as reported to avoid duplicate reports
101
+ reportedAttributes.add(attr);
102
+ const sourceCode = context.getSourceCode();
103
+ context.report({
104
+ node: attr,
105
+ messageId: 'keyOnlyOutermostElement',
106
+ fix(fixer) {
107
+ // Find the exact range of the attribute in the source code
108
+ const startPos = attr.range[0];
109
+ const endPos = attr.range[1];
110
+ // Get the text before and after the attribute to check for whitespace
111
+ const fullText = sourceCode.getText();
112
+ // Check if there's a space after the attribute
113
+ let rangeEnd = endPos;
114
+ if (rangeEnd < fullText.length && fullText[rangeEnd] === ' ') {
115
+ rangeEnd++;
116
+ }
117
+ return fixer.replaceTextRange([startPos, rangeEnd], '');
118
+ },
119
+ });
120
+ }
121
+ }
122
+ }
123
+ },
124
+ // Handle conditional expressions that might contain map callbacks
125
+ ConditionalExpression(node) {
126
+ // Check both the consequent and alternate branches
127
+ if (node.consequent.type === 'CallExpression' &&
128
+ node.consequent.callee.type === 'MemberExpression' &&
129
+ node.consequent.callee.property.type === 'Identifier' &&
130
+ node.consequent.callee.property.name === 'map') {
131
+ processMapCall(node.consequent);
132
+ }
133
+ if (node.alternate.type === 'CallExpression' &&
134
+ node.alternate.callee.type === 'MemberExpression' &&
135
+ node.alternate.callee.property.type === 'Identifier' &&
136
+ node.alternate.callee.property.name === 'map') {
137
+ processMapCall(node.alternate);
138
+ }
139
+ },
140
+ // Handle logical expressions (&&, ||) that might contain map callbacks
141
+ LogicalExpression(node) {
142
+ if (node.right.type === 'CallExpression' &&
143
+ node.right.callee.type === 'MemberExpression' &&
144
+ node.right.callee.property.type === 'Identifier' &&
145
+ node.right.callee.property.name === 'map') {
146
+ processMapCall(node.right);
147
+ }
148
+ }
149
+ };
150
+ },
151
+ });
152
+ //# sourceMappingURL=key-only-outermost-element.js.map
@@ -301,6 +301,25 @@ exports.noAlwaysTrueFalseConditions = (0, createRule_1.createRule)({
301
301
  }
302
302
  // For ||: if either side is always truthy, the whole expression is truthy
303
303
  if (node.operator === '||') {
304
+ // Skip evaluation if this is a destructuring assignment with fallback pattern
305
+ if (node.parent) {
306
+ // Check for variable declarator with destructuring pattern
307
+ if (node.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
308
+ (node.parent.id.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
309
+ node.parent.id.type === utils_1.AST_NODE_TYPES.ArrayPattern)) {
310
+ // This is a destructuring with fallback pattern like: const { x } = obj || {}
311
+ // Don't flag this as an always true/false condition
312
+ return {};
313
+ }
314
+ // Check for assignment expression with destructuring pattern
315
+ if (node.parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
316
+ (node.parent.left.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
317
+ node.parent.left.type === utils_1.AST_NODE_TYPES.ArrayPattern)) {
318
+ // This is a destructuring with fallback pattern like: ({ x } = obj || {})
319
+ // Don't flag this as an always true/false condition
320
+ return {};
321
+ }
322
+ }
304
323
  if (leftResult.isTruthy) {
305
324
  // Short circuit for || - if left is truthy, right is never evaluated
306
325
  return { isTruthy: true };
@@ -0,0 +1 @@
1
+ export declare const noCircularReferences: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"circularReference", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;