@blumintinc/eslint-plugin-blumint 1.9.1 → 1.11.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 +99 -83
- package/lib/index.js +16 -1
- package/lib/rules/class-methods-read-top-to-bottom.js +10 -0
- package/lib/rules/consistent-callback-naming.js +20 -16
- package/lib/rules/enforce-assert-throws.js +10 -5
- package/lib/rules/enforce-boolean-naming-prefixes.d.ts +7 -0
- package/lib/rules/enforce-boolean-naming-prefixes.js +462 -0
- package/lib/rules/enforce-callable-types.d.ts +1 -1
- package/lib/rules/enforce-callable-types.js +13 -13
- package/lib/rules/enforce-callback-memo.js +6 -3
- package/lib/rules/enforce-centralized-mock-firestore.js +8 -6
- package/lib/rules/enforce-css-media-queries.js +3 -3
- package/lib/rules/enforce-dynamic-file-naming.d.ts +3 -0
- package/lib/rules/enforce-dynamic-file-naming.js +82 -0
- package/lib/rules/enforce-dynamic-imports.js +1 -1
- package/lib/rules/enforce-firestore-doc-ref-generic.js +16 -12
- package/lib/rules/enforce-firestore-facade.js +7 -3
- package/lib/rules/enforce-firestore-path-utils.js +5 -2
- package/lib/rules/enforce-id-capitalization.js +59 -0
- package/lib/rules/enforce-identifiable-firestore-type.js +17 -10
- package/lib/rules/enforce-memoize-async.js +2 -2
- package/lib/rules/enforce-positive-naming.js +71 -27
- package/lib/rules/enforce-props-argument-name.js +31 -9
- package/lib/rules/enforce-realtimedb-path-utils.js +2 -1
- package/lib/rules/enforce-singular-type-names.js +4 -1
- package/lib/rules/enforce-timestamp-now.js +3 -4
- package/lib/rules/enforce-verb-noun-naming.js +18 -8
- package/lib/rules/ensure-pointer-events-none.js +20 -11
- package/lib/rules/extract-global-constants.js +25 -10
- package/lib/rules/fast-deep-equal-over-microdiff.d.ts +3 -0
- package/lib/rules/fast-deep-equal-over-microdiff.js +182 -0
- package/lib/rules/key-only-outermost-element.js +13 -7
- package/lib/rules/no-always-true-false-conditions.js +189 -5
- package/lib/rules/no-async-array-filter.js +1 -1
- package/lib/rules/no-circular-references.js +121 -59
- package/lib/rules/no-complex-cloud-params.js +4 -2
- package/lib/rules/no-entire-object-hook-deps.js +113 -29
- package/lib/rules/no-explicit-return-type.js +3 -2
- package/lib/rules/no-firestore-jest-mock.js +1 -1
- package/lib/rules/no-firestore-object-arrays.js +2 -1
- package/lib/rules/no-hungarian.js +18 -1
- package/lib/rules/no-jsx-in-hooks.js +4 -3
- package/lib/rules/no-margin-properties.d.ts +1 -0
- package/lib/rules/no-margin-properties.js +314 -0
- package/lib/rules/no-mixed-firestore-transactions.js +8 -4
- package/lib/rules/no-mock-firebase-admin.js +6 -1
- package/lib/rules/no-object-values-on-strings.js +28 -10
- package/lib/rules/no-type-assertion-returns.js +47 -6
- package/lib/rules/no-unnecessary-destructuring.js +1 -1
- package/lib/rules/no-unnecessary-verb-suffix.js +0 -31
- package/lib/rules/no-unused-props.js +10 -7
- package/lib/rules/no-unused-usestate.js +19 -6
- package/lib/rules/omit-index-html.js +1 -1
- package/lib/rules/prefer-batch-operations.js +19 -6
- package/lib/rules/prefer-clone-deep.js +16 -11
- package/lib/rules/prefer-destructuring-no-class.js +19 -0
- package/lib/rules/prefer-fragment-component.js +19 -16
- package/lib/rules/prefer-global-router-state-key.js +6 -4
- package/lib/rules/prefer-settings-object.js +1 -1
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.d.ts +8 -0
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +180 -0
- package/lib/rules/react-usememo-should-be-component.js +139 -17
- package/lib/rules/require-hooks-default-params.js +25 -15
- package/lib/rules/require-usememo-object-literals.js +3 -2
- package/lib/rules/semantic-function-prefixes.js +15 -3
- package/lib/rules/sync-onwrite-name-func.js +5 -3
- package/lib/utils/ASTHelpers.js +6 -2
- package/lib/utils/graph/ClassGraphBuilder.js +4 -1
- package/lib/utils/graph/ClassGraphSorterReadability.js +3 -1
- package/package.json +3 -3
- package/lib/rules/require-image-overlayed.d.ts +0 -7
- package/lib/rules/require-image-overlayed.js +0 -82
|
@@ -5,20 +5,22 @@ const utils_1 = require("@typescript-eslint/utils");
|
|
|
5
5
|
const createRule_1 = require("../utils/createRule");
|
|
6
6
|
// Common negative prefixes for boolean variables
|
|
7
7
|
const BOOLEAN_NEGATIVE_PREFIXES = ['not', 'no', 'non', 'un', 'in', 'dis'];
|
|
8
|
+
// Words that contain negative prefixes but should be treated as valid
|
|
9
|
+
const EXCEPTION_WORDS = ['interaction', 'interactions'];
|
|
8
10
|
// Map of negative boolean terms to suggested positive alternatives
|
|
9
11
|
const BOOLEAN_POSITIVE_ALTERNATIVES = {
|
|
10
12
|
// Boolean prefixes
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
isNot: ['is'],
|
|
14
|
+
isUn: ['is'],
|
|
15
|
+
isDis: ['is'],
|
|
16
|
+
isIn: ['is'],
|
|
17
|
+
isNon: ['is'],
|
|
18
|
+
hasNo: ['has'],
|
|
19
|
+
hasNot: ['has'],
|
|
20
|
+
canNot: ['can'],
|
|
21
|
+
shouldNot: ['should'],
|
|
22
|
+
willNot: ['will'],
|
|
23
|
+
doesNot: ['does'],
|
|
22
24
|
};
|
|
23
25
|
exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
24
26
|
name: 'enforce-positive-naming',
|
|
@@ -56,20 +58,52 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
56
58
|
function hasBooleanNegativeNaming(name) {
|
|
57
59
|
// Check for exact matches in our alternatives map first
|
|
58
60
|
if (BOOLEAN_POSITIVE_ALTERNATIVES[name]) {
|
|
59
|
-
return {
|
|
61
|
+
return {
|
|
62
|
+
isNegative: true,
|
|
63
|
+
alternatives: BOOLEAN_POSITIVE_ALTERNATIVES[name],
|
|
64
|
+
};
|
|
60
65
|
}
|
|
61
66
|
// Check for negative prefixes in boolean-like variables
|
|
62
|
-
if (name.startsWith('is') ||
|
|
63
|
-
name.startsWith('
|
|
67
|
+
if (name.startsWith('is') ||
|
|
68
|
+
name.startsWith('has') ||
|
|
69
|
+
name.startsWith('can') ||
|
|
70
|
+
name.startsWith('should') ||
|
|
71
|
+
name.startsWith('will') ||
|
|
72
|
+
name.startsWith('does')) {
|
|
73
|
+
// Check if the name contains any exception words
|
|
74
|
+
for (const exceptionWord of EXCEPTION_WORDS) {
|
|
75
|
+
const lowerCaseName = name.toLowerCase();
|
|
76
|
+
if (lowerCaseName.includes(exceptionWord.toLowerCase())) {
|
|
77
|
+
return { isNegative: false, alternatives: [] };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
64
80
|
for (const prefix of BOOLEAN_NEGATIVE_PREFIXES) {
|
|
65
81
|
// Check for patterns like isNot, hasNo, canNot, etc.
|
|
66
82
|
const prefixPatterns = [
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{
|
|
72
|
-
|
|
83
|
+
{
|
|
84
|
+
pattern: new RegExp(`^is${prefix}`, 'i'),
|
|
85
|
+
key: `is${prefix.charAt(0).toUpperCase() + prefix.slice(1)}`,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
pattern: new RegExp(`^has${prefix}`, 'i'),
|
|
89
|
+
key: `has${prefix.charAt(0).toUpperCase() + prefix.slice(1)}`,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
pattern: new RegExp(`^can${prefix}`, 'i'),
|
|
93
|
+
key: `can${prefix.charAt(0).toUpperCase() + prefix.slice(1)}`,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
pattern: new RegExp(`^should${prefix}`, 'i'),
|
|
97
|
+
key: `should${prefix.charAt(0).toUpperCase() + prefix.slice(1)}`,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
pattern: new RegExp(`^will${prefix}`, 'i'),
|
|
101
|
+
key: `will${prefix.charAt(0).toUpperCase() + prefix.slice(1)}`,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
pattern: new RegExp(`^does${prefix}`, 'i'),
|
|
105
|
+
key: `does${prefix.charAt(0).toUpperCase() + prefix.slice(1)}`,
|
|
106
|
+
},
|
|
73
107
|
];
|
|
74
108
|
for (const { pattern, key } of prefixPatterns) {
|
|
75
109
|
if (pattern.test(name)) {
|
|
@@ -82,10 +116,16 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
82
116
|
if (alternatives.length > 0) {
|
|
83
117
|
// Suggest the positive version with the rest of the name
|
|
84
118
|
const restOfName = name.replace(pattern, '');
|
|
85
|
-
const suggestedAlternatives = alternatives.map(alt => `${alt}${restOfName.charAt(0).toUpperCase() + restOfName.slice(1)}`);
|
|
86
|
-
return {
|
|
119
|
+
const suggestedAlternatives = alternatives.map((alt) => `${alt}${restOfName.charAt(0).toUpperCase() + restOfName.slice(1)}`);
|
|
120
|
+
return {
|
|
121
|
+
isNegative: true,
|
|
122
|
+
alternatives: suggestedAlternatives,
|
|
123
|
+
};
|
|
87
124
|
}
|
|
88
|
-
return {
|
|
125
|
+
return {
|
|
126
|
+
isNegative: true,
|
|
127
|
+
alternatives: ['a positive alternative'],
|
|
128
|
+
};
|
|
89
129
|
}
|
|
90
130
|
}
|
|
91
131
|
}
|
|
@@ -118,9 +158,12 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
118
158
|
}
|
|
119
159
|
// Check if the node has a name that suggests it's a boolean
|
|
120
160
|
if (node.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
121
|
-
(node.name.startsWith('is') ||
|
|
122
|
-
node.name.startsWith('
|
|
123
|
-
node.name.startsWith('
|
|
161
|
+
(node.name.startsWith('is') ||
|
|
162
|
+
node.name.startsWith('has') ||
|
|
163
|
+
node.name.startsWith('can') ||
|
|
164
|
+
node.name.startsWith('should') ||
|
|
165
|
+
node.name.startsWith('will') ||
|
|
166
|
+
node.name.startsWith('does'))) {
|
|
124
167
|
return true;
|
|
125
168
|
}
|
|
126
169
|
return false;
|
|
@@ -238,7 +281,8 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
238
281
|
return;
|
|
239
282
|
// Only check boolean properties
|
|
240
283
|
if (!isBooleanLike(node.key) &&
|
|
241
|
-
!(node.typeAnnotation?.typeAnnotation.type ===
|
|
284
|
+
!(node.typeAnnotation?.typeAnnotation.type ===
|
|
285
|
+
utils_1.AST_NODE_TYPES.TSBooleanKeyword))
|
|
242
286
|
return;
|
|
243
287
|
const propertyName = node.key.name;
|
|
244
288
|
const { isNegative, alternatives } = hasBooleanNegativeNaming(propertyName);
|
|
@@ -72,7 +72,15 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
72
72
|
if (typeName.endsWith('Props')) {
|
|
73
73
|
return null;
|
|
74
74
|
}
|
|
75
|
-
const suffixes = [
|
|
75
|
+
const suffixes = [
|
|
76
|
+
'Config',
|
|
77
|
+
'Settings',
|
|
78
|
+
'Options',
|
|
79
|
+
'Params',
|
|
80
|
+
'Parameters',
|
|
81
|
+
'Args',
|
|
82
|
+
'Arguments',
|
|
83
|
+
];
|
|
76
84
|
for (const suffix of suffixes) {
|
|
77
85
|
if (typeName.endsWith(suffix)) {
|
|
78
86
|
return suffix;
|
|
@@ -96,13 +104,20 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
96
104
|
}
|
|
97
105
|
const param = node.params[0];
|
|
98
106
|
// Skip primitive parameters
|
|
99
|
-
if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
100
|
-
param.typeAnnotation
|
|
101
|
-
param.typeAnnotation.typeAnnotation.type ===
|
|
107
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
108
|
+
param.typeAnnotation &&
|
|
109
|
+
(param.typeAnnotation.typeAnnotation.type ===
|
|
110
|
+
utils_1.AST_NODE_TYPES.TSStringKeyword ||
|
|
111
|
+
param.typeAnnotation.typeAnnotation.type ===
|
|
112
|
+
utils_1.AST_NODE_TYPES.TSNumberKeyword ||
|
|
113
|
+
param.typeAnnotation.typeAnnotation.type ===
|
|
114
|
+
utils_1.AST_NODE_TYPES.TSBooleanKeyword)) {
|
|
102
115
|
return;
|
|
103
116
|
}
|
|
104
117
|
// Check if the parameter has a type annotation
|
|
105
|
-
if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
118
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
119
|
+
param.typeAnnotation &&
|
|
120
|
+
param.typeAnnotation.typeAnnotation) {
|
|
106
121
|
const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
|
|
107
122
|
if (typeName) {
|
|
108
123
|
const nonPropsSuffix = hasNonPropsSuffix(typeName);
|
|
@@ -128,13 +143,20 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
|
|
|
128
143
|
}
|
|
129
144
|
const param = constructor.params[0];
|
|
130
145
|
// Skip primitive parameters
|
|
131
|
-
if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
132
|
-
param.typeAnnotation
|
|
133
|
-
param.typeAnnotation.typeAnnotation.type ===
|
|
146
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
147
|
+
param.typeAnnotation &&
|
|
148
|
+
(param.typeAnnotation.typeAnnotation.type ===
|
|
149
|
+
utils_1.AST_NODE_TYPES.TSStringKeyword ||
|
|
150
|
+
param.typeAnnotation.typeAnnotation.type ===
|
|
151
|
+
utils_1.AST_NODE_TYPES.TSNumberKeyword ||
|
|
152
|
+
param.typeAnnotation.typeAnnotation.type ===
|
|
153
|
+
utils_1.AST_NODE_TYPES.TSBooleanKeyword)) {
|
|
134
154
|
return;
|
|
135
155
|
}
|
|
136
156
|
// Check if the parameter has a type annotation
|
|
137
|
-
if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
157
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
158
|
+
param.typeAnnotation &&
|
|
159
|
+
param.typeAnnotation.typeAnnotation) {
|
|
138
160
|
const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
|
|
139
161
|
if (typeName) {
|
|
140
162
|
const nonPropsSuffix = hasNonPropsSuffix(typeName);
|
|
@@ -55,7 +55,8 @@ exports.enforceRealtimedbPathUtils = (0, createRule_1.createRule)({
|
|
|
55
55
|
return false;
|
|
56
56
|
}
|
|
57
57
|
function isStringLiteralOrTemplate(node) {
|
|
58
|
-
return ((node.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
58
|
+
return ((node.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
59
|
+
typeof node.value === 'string') ||
|
|
59
60
|
node.type === utils_1.AST_NODE_TYPES.TemplateLiteral);
|
|
60
61
|
}
|
|
61
62
|
function isUtilityFunction(node) {
|
|
@@ -38,7 +38,10 @@ exports.enforceSingularTypeNames = (0, createRule_1.createRule)({
|
|
|
38
38
|
if (name.length < 3)
|
|
39
39
|
return false;
|
|
40
40
|
// Skip checking if name ends with 'Props' or 'Params'
|
|
41
|
-
if (name.endsWith('Props') ||
|
|
41
|
+
if (name.endsWith('Props') ||
|
|
42
|
+
name.endsWith('Params') ||
|
|
43
|
+
name.endsWith('Options') ||
|
|
44
|
+
name.endsWith('Settings'))
|
|
42
45
|
return false;
|
|
43
46
|
// Skip checking if name is already singular according to pluralize
|
|
44
47
|
if (pluralize.isSingular(name))
|
|
@@ -51,8 +51,7 @@ exports.enforceTimestampNow = (0, createRule_1.createRule)({
|
|
|
51
51
|
return true;
|
|
52
52
|
}
|
|
53
53
|
// Check if the argument is a variable reference to a Date object
|
|
54
|
-
if (arg &&
|
|
55
|
-
arg.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
54
|
+
if (arg && arg.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
56
55
|
// If it's a variable, we need to check if it's a Date that's being modified
|
|
57
56
|
// If it's modified, we shouldn't flag it
|
|
58
57
|
return false;
|
|
@@ -95,11 +94,11 @@ exports.enforceTimestampNow = (0, createRule_1.createRule)({
|
|
|
95
94
|
function isDateBeingModified(dateVar) {
|
|
96
95
|
// Look through the scope to find if this variable is modified
|
|
97
96
|
const scope = context.getScope();
|
|
98
|
-
const variable = scope.variables.find(v => v.name === dateVar);
|
|
97
|
+
const variable = scope.variables.find((v) => v.name === dateVar);
|
|
99
98
|
if (!variable)
|
|
100
99
|
return false;
|
|
101
100
|
// Check if any references to this variable are followed by property access and modification
|
|
102
|
-
return variable.references.some(ref => {
|
|
101
|
+
return variable.references.some((ref) => {
|
|
103
102
|
const id = ref.identifier;
|
|
104
103
|
const parent = id.parent;
|
|
105
104
|
// Check for patterns like dateVar.setDate(), dateVar.setHours(), etc.
|
|
@@ -4608,7 +4608,9 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
|
|
|
4608
4608
|
return true;
|
|
4609
4609
|
}
|
|
4610
4610
|
// Check for JSX assigned to variables then returned
|
|
4611
|
-
if (text.includes('const ') &&
|
|
4611
|
+
if (text.includes('const ') &&
|
|
4612
|
+
text.includes('<') &&
|
|
4613
|
+
text.includes('return')) {
|
|
4612
4614
|
return true;
|
|
4613
4615
|
}
|
|
4614
4616
|
return false;
|
|
@@ -4624,9 +4626,11 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
|
|
|
4624
4626
|
if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration && node.id) {
|
|
4625
4627
|
functionName = node.id.name;
|
|
4626
4628
|
}
|
|
4627
|
-
else if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
4629
|
+
else if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
4630
|
+
node.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
4628
4631
|
const parent = node.parent;
|
|
4629
|
-
if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
4632
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
4633
|
+
parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
4630
4634
|
functionName = parent.id.name;
|
|
4631
4635
|
}
|
|
4632
4636
|
}
|
|
@@ -4644,20 +4648,26 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
|
|
|
4644
4648
|
const parent = node.parent;
|
|
4645
4649
|
if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
4646
4650
|
const id = parent.id;
|
|
4647
|
-
if (id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
4648
|
-
|
|
4649
|
-
|
|
4651
|
+
if (id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
4652
|
+
id.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
|
|
4653
|
+
const typeText = context
|
|
4654
|
+
.getSourceCode()
|
|
4655
|
+
.getText(id.typeAnnotation.typeAnnotation);
|
|
4656
|
+
return (typeText.includes('React.') ||
|
|
4657
|
+
typeText.includes('FC') ||
|
|
4658
|
+
typeText.includes('FunctionComponent'));
|
|
4650
4659
|
}
|
|
4651
4660
|
}
|
|
4652
4661
|
}
|
|
4653
4662
|
return false;
|
|
4654
4663
|
})(),
|
|
4655
4664
|
// 5. Component name ends with "Unmemoized" (common React pattern)
|
|
4656
|
-
functionName && functionName.endsWith('Unmemoized')
|
|
4665
|
+
functionName && functionName.endsWith('Unmemoized'),
|
|
4657
4666
|
];
|
|
4658
4667
|
// Consider it a React component if it matches at least 2 indicators
|
|
4659
4668
|
// OR if it has the Unmemoized suffix (strong indicator of a React component)
|
|
4660
|
-
return indicators.filter(Boolean).length >= 2 ||
|
|
4669
|
+
return (indicators.filter(Boolean).length >= 2 ||
|
|
4670
|
+
(!!functionName && functionName.endsWith('Unmemoized')));
|
|
4661
4671
|
}
|
|
4662
4672
|
return {
|
|
4663
4673
|
FunctionDeclaration(node) {
|
|
@@ -59,7 +59,8 @@ exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
|
|
|
59
59
|
if (property.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
60
60
|
propertyName = property.key.name;
|
|
61
61
|
}
|
|
62
|
-
else if (property.key.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
62
|
+
else if (property.key.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
63
|
+
typeof property.key.value === 'string') {
|
|
63
64
|
propertyName = property.key.value;
|
|
64
65
|
}
|
|
65
66
|
// Get property value if it's a string literal
|
|
@@ -97,7 +98,9 @@ exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
|
|
|
97
98
|
const isAbsolutePositioned = absolutePositionedStyles.get(node) || false;
|
|
98
99
|
const pointerEventsValue = stylesWithPointerEvents.get(node);
|
|
99
100
|
// If this is a pseudo-element with absolute positioning but no pointer-events
|
|
100
|
-
if (isPseudoElement &&
|
|
101
|
+
if (isPseudoElement &&
|
|
102
|
+
isAbsolutePositioned &&
|
|
103
|
+
pointerEventsValue === undefined) {
|
|
101
104
|
context.report({
|
|
102
105
|
node,
|
|
103
106
|
messageId: 'missingPointerEventsNone',
|
|
@@ -113,11 +116,13 @@ exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
|
|
|
113
116
|
return fixer.insertTextAfter(lastPropertyToken, `, pointerEvents: 'none'`);
|
|
114
117
|
}
|
|
115
118
|
return null;
|
|
116
|
-
}
|
|
119
|
+
},
|
|
117
120
|
});
|
|
118
121
|
}
|
|
119
122
|
// If this is a pseudo-element with absolute positioning and pointer-events: auto
|
|
120
|
-
if (isPseudoElement &&
|
|
123
|
+
if (isPseudoElement &&
|
|
124
|
+
isAbsolutePositioned &&
|
|
125
|
+
pointerEventsValue === 'auto') {
|
|
121
126
|
// Don't report an error if pointer-events is explicitly set to 'auto'
|
|
122
127
|
// This is an intentional choice by the developer
|
|
123
128
|
}
|
|
@@ -139,15 +144,17 @@ exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
|
|
|
139
144
|
}
|
|
140
145
|
if (isStyledComponent) {
|
|
141
146
|
// For styled-components, we need to check the template content
|
|
142
|
-
const template = node.quasi.quasis.map(q => q.value.raw).join('');
|
|
147
|
+
const template = node.quasi.quasis.map((q) => q.value.raw).join('');
|
|
143
148
|
// Check if it contains a pseudo-element with position: absolute/fixed
|
|
144
149
|
if (hasPseudoElementSelector(template) &&
|
|
145
|
-
(template.includes('position: absolute') ||
|
|
150
|
+
(template.includes('position: absolute') ||
|
|
151
|
+
template.includes('position: fixed'))) {
|
|
146
152
|
// Check if it's missing pointer-events: none
|
|
147
|
-
if (!template.includes('pointer-events: none') &&
|
|
153
|
+
if (!template.includes('pointer-events: none') &&
|
|
154
|
+
!template.includes('pointer-events:none')) {
|
|
148
155
|
context.report({
|
|
149
156
|
node,
|
|
150
|
-
messageId: 'missingPointerEventsNone'
|
|
157
|
+
messageId: 'missingPointerEventsNone',
|
|
151
158
|
});
|
|
152
159
|
}
|
|
153
160
|
}
|
|
@@ -155,7 +162,8 @@ exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
|
|
|
155
162
|
},
|
|
156
163
|
// Process style objects in JSX
|
|
157
164
|
JSXAttribute(node) {
|
|
158
|
-
if (node.name.type !== utils_1.AST_NODE_TYPES.JSXIdentifier ||
|
|
165
|
+
if (node.name.type !== utils_1.AST_NODE_TYPES.JSXIdentifier ||
|
|
166
|
+
node.name.name !== 'style')
|
|
159
167
|
return;
|
|
160
168
|
if (node.value?.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
|
|
161
169
|
node.value.expression.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
@@ -185,7 +193,8 @@ exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
|
|
|
185
193
|
else if (parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
186
194
|
// Check for CSS-in-JS libraries like emotion's css() function
|
|
187
195
|
const callee = parent.callee;
|
|
188
|
-
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
196
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
197
|
+
callee.name === 'css') {
|
|
189
198
|
isStyleObject = true;
|
|
190
199
|
}
|
|
191
200
|
}
|
|
@@ -204,7 +213,7 @@ exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
|
|
|
204
213
|
processStyleObject(node.value);
|
|
205
214
|
checkStyleObject(node.value, node.key.value);
|
|
206
215
|
}
|
|
207
|
-
}
|
|
216
|
+
},
|
|
208
217
|
};
|
|
209
218
|
},
|
|
210
219
|
});
|
|
@@ -54,10 +54,10 @@ function isMutableValue(node) {
|
|
|
54
54
|
if (node.elements.length === 0)
|
|
55
55
|
return true;
|
|
56
56
|
// Arrays with spread elements are mutable
|
|
57
|
-
if (node.elements.some(element => !element || element.type === 'SpreadElement'))
|
|
57
|
+
if (node.elements.some((element) => !element || element.type === 'SpreadElement'))
|
|
58
58
|
return true;
|
|
59
59
|
// Arrays with non-immutable values are mutable
|
|
60
|
-
return node.elements.some(element => !isImmutableValue(element));
|
|
60
|
+
return node.elements.some((element) => !isImmutableValue(element));
|
|
61
61
|
}
|
|
62
62
|
// Check for new expressions (e.g., new Map(), new Set())
|
|
63
63
|
if (node.type === 'NewExpression') {
|
|
@@ -83,7 +83,7 @@ function isMutableValue(node) {
|
|
|
83
83
|
'splice',
|
|
84
84
|
'reverse',
|
|
85
85
|
'sort',
|
|
86
|
-
'fill'
|
|
86
|
+
'fill',
|
|
87
87
|
];
|
|
88
88
|
return mutatingMethods.includes(methodName);
|
|
89
89
|
}
|
|
@@ -93,7 +93,8 @@ function isMutableValue(node) {
|
|
|
93
93
|
function isNumericLiteral(node) {
|
|
94
94
|
if (!node)
|
|
95
95
|
return false;
|
|
96
|
-
return node.type === 'Literal' &&
|
|
96
|
+
return (node.type === 'Literal' &&
|
|
97
|
+
typeof node.value === 'number');
|
|
97
98
|
}
|
|
98
99
|
function isZeroOrOne(node) {
|
|
99
100
|
if (!isNumericLiteral(node))
|
|
@@ -168,7 +169,10 @@ exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
|
168
169
|
// Check initialization
|
|
169
170
|
if (node.init && node.init.type === 'VariableDeclaration') {
|
|
170
171
|
for (const decl of node.init.declarations) {
|
|
171
|
-
if (decl.init &&
|
|
172
|
+
if (decl.init &&
|
|
173
|
+
isNumericLiteral(decl.init) &&
|
|
174
|
+
!isZeroOrOne(decl.init) &&
|
|
175
|
+
!isAsConstExpression(decl.init)) {
|
|
172
176
|
context.report({
|
|
173
177
|
node: decl.init,
|
|
174
178
|
messageId: 'requireAsConst',
|
|
@@ -181,7 +185,9 @@ exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
|
181
185
|
}
|
|
182
186
|
// Check test condition
|
|
183
187
|
if (node.test && node.test.type === 'BinaryExpression') {
|
|
184
|
-
if (isNumericLiteral(node.test.right) &&
|
|
188
|
+
if (isNumericLiteral(node.test.right) &&
|
|
189
|
+
!isZeroOrOne(node.test.right) &&
|
|
190
|
+
!isAsConstExpression(node.test.right)) {
|
|
185
191
|
context.report({
|
|
186
192
|
node: node.test.right,
|
|
187
193
|
messageId: 'requireAsConst',
|
|
@@ -194,7 +200,10 @@ exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
|
194
200
|
// Check update expression
|
|
195
201
|
if (node.update) {
|
|
196
202
|
if (node.update.type === 'AssignmentExpression') {
|
|
197
|
-
if (node.update.right &&
|
|
203
|
+
if (node.update.right &&
|
|
204
|
+
isNumericLiteral(node.update.right) &&
|
|
205
|
+
!isZeroOrOne(node.update.right) &&
|
|
206
|
+
!isAsConstExpression(node.update.right)) {
|
|
198
207
|
context.report({
|
|
199
208
|
node: node.update.right,
|
|
200
209
|
messageId: 'requireAsConst',
|
|
@@ -205,7 +214,9 @@ exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
|
205
214
|
}
|
|
206
215
|
}
|
|
207
216
|
else if (node.update.type === 'BinaryExpression') {
|
|
208
|
-
if (isNumericLiteral(node.update.right) &&
|
|
217
|
+
if (isNumericLiteral(node.update.right) &&
|
|
218
|
+
!isZeroOrOne(node.update.right) &&
|
|
219
|
+
!isAsConstExpression(node.update.right)) {
|
|
209
220
|
context.report({
|
|
210
221
|
node: node.update.right,
|
|
211
222
|
messageId: 'requireAsConst',
|
|
@@ -220,7 +231,9 @@ exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
|
220
231
|
WhileStatement(node) {
|
|
221
232
|
// Check test condition
|
|
222
233
|
if (node.test.type === 'BinaryExpression') {
|
|
223
|
-
if (isNumericLiteral(node.test.right) &&
|
|
234
|
+
if (isNumericLiteral(node.test.right) &&
|
|
235
|
+
!isZeroOrOne(node.test.right) &&
|
|
236
|
+
!isAsConstExpression(node.test.right)) {
|
|
224
237
|
context.report({
|
|
225
238
|
node: node.test.right,
|
|
226
239
|
messageId: 'requireAsConst',
|
|
@@ -234,7 +247,9 @@ exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
|
234
247
|
DoWhileStatement(node) {
|
|
235
248
|
// Check test condition
|
|
236
249
|
if (node.test.type === 'BinaryExpression') {
|
|
237
|
-
if (isNumericLiteral(node.test.right) &&
|
|
250
|
+
if (isNumericLiteral(node.test.right) &&
|
|
251
|
+
!isZeroOrOne(node.test.right) &&
|
|
252
|
+
!isAsConstExpression(node.test.right)) {
|
|
238
253
|
context.report({
|
|
239
254
|
node: node.test.right,
|
|
240
255
|
messageId: 'requireAsConst',
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
type MessageIds = 'useFastDeepEqual' | 'addFastDeepEqualImport';
|
|
2
|
+
export declare const fastDeepEqualOverMicrodiff: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
3
|
+
export {};
|