@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.
Files changed (2) hide show
  1. package/dist/index.js +149 -3
  2. 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$b = {
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$b = context => {
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',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/eslint-plugin-virtual-dom",
3
- "version": "16.5.0",
3
+ "version": "17.0.1",
4
4
  "main": "dist/index.js",
5
5
  "repository": {
6
6
  "type": "git",