@lvce-editor/eslint-plugin-virtual-dom 16.5.0 → 17.0.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.
- package/dist/index.js +149 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -77,7 +77,7 @@ const isStringLiteral = node => {
|
|
|
77
77
|
return isLiteralNode(node) && typeof node.value === 'string';
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
-
const meta$
|
|
80
|
+
const meta$c = {
|
|
81
81
|
docs: {
|
|
82
82
|
description: 'Require an explicit role on clickable virtual-dom div nodes'
|
|
83
83
|
},
|
|
@@ -86,7 +86,7 @@ const meta$b = {
|
|
|
86
86
|
},
|
|
87
87
|
type: 'problem'
|
|
88
88
|
};
|
|
89
|
-
const create$
|
|
89
|
+
const create$c = context => {
|
|
90
90
|
return {
|
|
91
91
|
ObjectExpression(node) {
|
|
92
92
|
if (!isVirtualDomNode(node)) {
|
|
@@ -107,6 +107,114 @@ const create$b = context => {
|
|
|
107
107
|
};
|
|
108
108
|
|
|
109
109
|
const clickableDivNeedsRole = {
|
|
110
|
+
__proto__: null,
|
|
111
|
+
create: create$c,
|
|
112
|
+
meta: meta$c
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const meta$b = {
|
|
116
|
+
docs: {
|
|
117
|
+
description: 'Require static virtual-dom nodes created inside functions to be hoisted to module scope'
|
|
118
|
+
},
|
|
119
|
+
messages: {
|
|
120
|
+
hoistStaticNode: 'Hoist this static virtual-dom node to module scope.'
|
|
121
|
+
},
|
|
122
|
+
type: 'suggestion'
|
|
123
|
+
};
|
|
124
|
+
const isFunctionNode$1 = node => {
|
|
125
|
+
return ['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression'].includes(node.type);
|
|
126
|
+
};
|
|
127
|
+
const findVariable = (sourceCode, identifier) => {
|
|
128
|
+
let scope = sourceCode.getScope(identifier);
|
|
129
|
+
while (scope) {
|
|
130
|
+
const variable = scope.set.get(identifier.name);
|
|
131
|
+
if (variable) {
|
|
132
|
+
return variable;
|
|
133
|
+
}
|
|
134
|
+
scope = scope.upper;
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
};
|
|
138
|
+
const isStaticModuleVariable = variable => {
|
|
139
|
+
if (!['global', 'module'].includes(variable.scope.type)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return variable.defs.every(definition => {
|
|
143
|
+
if (['ClassName', 'FunctionName', 'ImportBinding'].includes(definition.type)) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
return definition.type === 'Variable' && definition.parent.kind === 'const';
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const isStaticIdentifier = (sourceCode, node) => {
|
|
150
|
+
const variable = findVariable(sourceCode, node);
|
|
151
|
+
return Boolean(variable && isStaticModuleVariable(variable));
|
|
152
|
+
};
|
|
153
|
+
const isVirtualDomElement = node => {
|
|
154
|
+
if (!isVirtualDomNode(node) || !hasProperty(node, 'childCount')) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const type = getProperty(node, 'type');
|
|
158
|
+
return Boolean(type && !isStringLiteral(type.value));
|
|
159
|
+
};
|
|
160
|
+
const isStaticProperty = (sourceCode, node) => {
|
|
161
|
+
if (node.type === 'SpreadElement' || node.kind !== 'init' || node.method) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (node.computed && !isStaticExpression(sourceCode, node.key)) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return isStaticExpression(sourceCode, node.value);
|
|
168
|
+
};
|
|
169
|
+
const isStaticExpression = (sourceCode, node) => {
|
|
170
|
+
if (!node) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
switch (node.type) {
|
|
174
|
+
case 'ArrayExpression':
|
|
175
|
+
return node.elements.every(element => !element || element.type !== 'SpreadElement' && isStaticExpression(sourceCode, element));
|
|
176
|
+
case 'BinaryExpression':
|
|
177
|
+
case 'LogicalExpression':
|
|
178
|
+
return isStaticExpression(sourceCode, node.left) && isStaticExpression(sourceCode, node.right);
|
|
179
|
+
case 'ConditionalExpression':
|
|
180
|
+
return isStaticExpression(sourceCode, node.test) && isStaticExpression(sourceCode, node.consequent) && isStaticExpression(sourceCode, node.alternate);
|
|
181
|
+
case 'Identifier':
|
|
182
|
+
return isStaticIdentifier(sourceCode, node);
|
|
183
|
+
case 'Literal':
|
|
184
|
+
return true;
|
|
185
|
+
case 'MemberExpression':
|
|
186
|
+
return isStaticExpression(sourceCode, node.object) && (!node.computed || node.property.type !== 'PrivateIdentifier' && isStaticExpression(sourceCode, node.property));
|
|
187
|
+
case 'ObjectExpression':
|
|
188
|
+
return node.properties.every(property => isStaticProperty(sourceCode, property));
|
|
189
|
+
case 'TemplateLiteral':
|
|
190
|
+
return node.expressions.every(expression => isStaticExpression(sourceCode, expression));
|
|
191
|
+
case 'UnaryExpression':
|
|
192
|
+
return node.operator !== 'delete' && isStaticExpression(sourceCode, node.argument);
|
|
193
|
+
default:
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const create$b = context => {
|
|
198
|
+
return {
|
|
199
|
+
ObjectExpression(node) {
|
|
200
|
+
if (!isVirtualDomElement(node)) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (!context.sourceCode.getAncestors(node).some(isFunctionNode$1)) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (!isStaticExpression(context.sourceCode, node)) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
context.report({
|
|
210
|
+
messageId: 'hoistStaticNode',
|
|
211
|
+
node
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const hoistStaticNodes = {
|
|
110
218
|
__proto__: null,
|
|
111
219
|
create: create$b,
|
|
112
220
|
meta: meta$b
|
|
@@ -452,6 +560,9 @@ const preferLazyDateTimeFormat = {
|
|
|
452
560
|
const isClassNameKey = node => {
|
|
453
561
|
return getStaticPropertyName(node) === 'className';
|
|
454
562
|
};
|
|
563
|
+
const isClassNameProperty = node => {
|
|
564
|
+
return isPropertyNode(node) && isClassNameKey(node);
|
|
565
|
+
};
|
|
455
566
|
const isStringLiteralWithSpace = node => {
|
|
456
567
|
return isStringLiteral(node) && /\s/.test(node.value);
|
|
457
568
|
};
|
|
@@ -473,6 +584,24 @@ const isManualClassNameConcatenation = node => {
|
|
|
473
584
|
}
|
|
474
585
|
return isStringLiteralWithSpace(node.left) || isStringLiteralWithSpace(node.right) || isManualClassNameConcatenation(node.left) || isManualClassNameConcatenation(node.right);
|
|
475
586
|
};
|
|
587
|
+
const hasClassNamesReference = node => {
|
|
588
|
+
if (isMemberExpressionNode(node)) {
|
|
589
|
+
return isIdentifierNode(node.object) && node.object.name === 'ClassNames' || hasClassNamesReference(node.object) || node.computed && hasClassNamesReference(node.property);
|
|
590
|
+
}
|
|
591
|
+
if (isBinaryExpressionNode(node)) {
|
|
592
|
+
return hasClassNamesReference(node.left) || hasClassNamesReference(node.right);
|
|
593
|
+
}
|
|
594
|
+
if (isTemplateLiteralNode(node)) {
|
|
595
|
+
return node.expressions.some(hasClassNamesReference);
|
|
596
|
+
}
|
|
597
|
+
return false;
|
|
598
|
+
};
|
|
599
|
+
const getParent = node => {
|
|
600
|
+
if (typeof node === 'object' && node !== null && 'parent' in node) {
|
|
601
|
+
return node.parent;
|
|
602
|
+
}
|
|
603
|
+
return undefined;
|
|
604
|
+
};
|
|
476
605
|
const meta$2 = {
|
|
477
606
|
docs: {
|
|
478
607
|
description: 'Prefer mergeClassNames for composing virtual-dom className values'
|
|
@@ -483,7 +612,21 @@ const meta$2 = {
|
|
|
483
612
|
type: 'suggestion'
|
|
484
613
|
};
|
|
485
614
|
const create$2 = context => {
|
|
615
|
+
const reportNamespacedClassNameComposition = node => {
|
|
616
|
+
if (!isManualClassNameConcatenation(node) || !hasClassNamesReference(node)) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const parent = getParent(node);
|
|
620
|
+
if (isManualClassNameConcatenation(parent) || isClassNameProperty(parent)) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
context.report({
|
|
624
|
+
messageId: 'preferMergeClassNames',
|
|
625
|
+
node: node
|
|
626
|
+
});
|
|
627
|
+
};
|
|
486
628
|
return {
|
|
629
|
+
BinaryExpression: reportNamespacedClassNameComposition,
|
|
487
630
|
Property(node) {
|
|
488
631
|
if (!isPropertyNode(node) || !isClassNameKey(node) || !isManualClassNameConcatenation(node.value)) {
|
|
489
632
|
return;
|
|
@@ -492,7 +635,8 @@ const create$2 = context => {
|
|
|
492
635
|
messageId: 'preferMergeClassNames',
|
|
493
636
|
node: node.value
|
|
494
637
|
});
|
|
495
|
-
}
|
|
638
|
+
},
|
|
639
|
+
TemplateLiteral: reportNamespacedClassNameComposition
|
|
496
640
|
};
|
|
497
641
|
};
|
|
498
642
|
|
|
@@ -678,6 +822,7 @@ const plugin = {
|
|
|
678
822
|
},
|
|
679
823
|
rules: {
|
|
680
824
|
'clickable-div-needs-role': clickableDivNeedsRole,
|
|
825
|
+
'hoist-static-nodes': hoistStaticNodes,
|
|
681
826
|
'no-empty-aria': noEmptyAria,
|
|
682
827
|
'no-empty-class-name': noEmptyClassName,
|
|
683
828
|
'no-inline-event-handlers': noInlineEventHandlers,
|
|
@@ -698,6 +843,7 @@ const recommended = [{
|
|
|
698
843
|
},
|
|
699
844
|
rules: {
|
|
700
845
|
'virtual-dom/clickable-div-needs-role': 'error',
|
|
846
|
+
'virtual-dom/hoist-static-nodes': 'error',
|
|
701
847
|
'virtual-dom/no-empty-aria': 'error',
|
|
702
848
|
'virtual-dom/no-empty-class-name': 'error',
|
|
703
849
|
'virtual-dom/no-inline-event-handlers': 'error',
|