wc-compiler 0.15.1 → 0.17.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/src/wcc.js CHANGED
@@ -1,39 +1,14 @@
1
- /* eslint-disable max-depth */
2
1
  // this must come first
3
- import './dom-shim.js';
2
+ import { getParse } from './dom-shim.js';
4
3
 
5
4
  import * as acorn from 'acorn';
6
5
  import * as walk from 'acorn-walk';
7
6
  import { generate } from 'astring';
8
7
  import { getParser, parseJsx } from './jsx-loader.js';
9
- import { parse, parseFragment, serialize } from 'parse5';
8
+ import { serialize } from 'parse5';
10
9
  import { transform } from 'sucrase';
11
10
  import fs from 'fs';
12
11
 
13
- // https://developer.mozilla.org/en-US/docs/Glossary/Void_element
14
- const VOID_ELEMENTS = [
15
- 'area',
16
- 'base',
17
- 'br',
18
- 'col',
19
- 'embed',
20
- 'hr',
21
- 'img',
22
- 'input',
23
- 'link',
24
- 'meta',
25
- 'param', // deprecated
26
- 'source',
27
- 'track',
28
- 'wbr'
29
- ];
30
-
31
- function getParse(html) {
32
- return html.indexOf('<html>') >= 0 || html.indexOf('<body>') >= 0 || html.indexOf('<head>') >= 0
33
- ? parse
34
- : parseFragment;
35
- }
36
-
37
12
  function isCustomElementDefinitionNode(node) {
38
13
  const { expression } = node;
39
14
 
@@ -45,7 +20,7 @@ function isCustomElementDefinitionNode(node) {
45
20
  async function renderComponentRoots(tree, definitions) {
46
21
  for (const node of tree.childNodes) {
47
22
  if (node.tagName && node.tagName.indexOf('-') > 0) {
48
- const { tagName } = node;
23
+ const { attrs, tagName } = node;
49
24
 
50
25
  if (definitions[tagName]) {
51
26
  const { moduleURL } = definitions[tagName];
@@ -53,31 +28,35 @@ async function renderComponentRoots(tree, definitions) {
53
28
 
54
29
  if (elementInstance) {
55
30
  const hasShadow = elementInstance.shadowRoot;
56
- const elementHtml = hasShadow
57
- ? elementInstance.getInnerHTML({ includeShadowRoots: true })
58
- : elementInstance.innerHTML;
59
- const elementTree = parseFragment(elementHtml);
60
- const hasLight = elementTree.childNodes > 0;
61
-
62
- node.childNodes = node.childNodes.length === 0 && hasLight && !hasShadow
63
- ? elementTree.childNodes
64
- : hasShadow
65
- ? [...elementTree.childNodes, ...node.childNodes]
66
- : elementTree.childNodes;
31
+
32
+ node.childNodes = hasShadow
33
+ ? [...elementInstance.shadowRoot.childNodes, ...node.childNodes]
34
+ : elementInstance.childNodes;
67
35
  } else {
68
36
  console.warn(`WARNING: customElement <${tagName}> detected but not serialized. You may not have exported it.`);
69
37
  }
70
38
  } else {
71
39
  console.warn(`WARNING: customElement <${tagName}> is not defined. You may not have imported it.`);
72
40
  }
41
+
42
+ attrs.forEach((attr) => {
43
+ if (attr.name === 'hydrate') {
44
+ definitions[tagName].hydrate = attr.value;
45
+ }
46
+ });
47
+
73
48
  }
74
49
 
75
50
  if (node.childNodes && node.childNodes.length > 0) {
76
51
  await renderComponentRoots(node, definitions);
77
52
  }
78
53
 
54
+ if (node.shadowRoot && node.shadowRoot.childNodes?.length > 0) {
55
+ await renderComponentRoots(node.shadowRoot, definitions);
56
+ }
57
+
79
58
  // does this only apply to `<template>` tags?
80
- if (node.content && node.content.childNodes && node.content.childNodes.length > 0) {
59
+ if (node.content && node.content.childNodes?.length > 0) {
81
60
  await renderComponentRoots(node.content, definitions);
82
61
  }
83
62
  }
@@ -104,19 +83,23 @@ function registerDependencies(moduleURL, definitions, depth = 0) {
104
83
  }), {
105
84
  ImportDeclaration(node) {
106
85
  const specifier = node.source.value;
107
- const isBareSpecifier = specifier.indexOf('.') !== 0 && specifier.indexOf('/') !== 0;
108
- const extension = specifier.split('.').pop();
109
86
 
110
- // would like to decouple .jsx from the core, ideally
111
- // https://github.com/ProjectEvergreen/wcc/issues/122
112
- if (!isBareSpecifier && ['js', 'jsx', 'ts'].includes(extension)) {
113
- const dependencyModuleURL = new URL(node.source.value, moduleURL);
87
+ if (typeof specifier === 'string') {
88
+ const isBareSpecifier = specifier.indexOf('.') !== 0 && specifier.indexOf('/') !== 0;
89
+ const extension = typeof specifier === "string" ? specifier.split('.').pop() : "";
90
+
91
+ // would like to decouple .jsx from the core, ideally
92
+ // https://github.com/ProjectEvergreen/wcc/issues/122
93
+ if (!isBareSpecifier && ['js', 'jsx', 'ts'].includes(extension)) {
94
+ const dependencyModuleURL = new URL(specifier, moduleURL);
114
95
 
115
- registerDependencies(dependencyModuleURL, definitions, nextDepth);
96
+ registerDependencies(dependencyModuleURL, definitions, nextDepth);
97
+ }
116
98
  }
117
99
  },
118
100
  ExpressionStatement(node) {
119
101
  if (isCustomElementDefinitionNode(node)) {
102
+ // @ts-ignore
120
103
  const { arguments: args } = node.expression;
121
104
  const tagName = args[0].type === 'Literal'
122
105
  ? args[0].value // single and double quotes
@@ -155,6 +138,7 @@ async function getTagName(moduleURL) {
155
138
  }), {
156
139
  ExpressionStatement(node) {
157
140
  if (isCustomElementDefinitionNode(node)) {
141
+ // @ts-ignore
158
142
  tagName = node.expression.arguments[0].value;
159
143
  }
160
144
  }
@@ -163,98 +147,60 @@ async function getTagName(moduleURL) {
163
147
  return tagName;
164
148
  }
165
149
 
166
- function renderLightDomChildren(childNodes, iHTML = '') {
167
- let innerHTML = iHTML;
168
-
169
- childNodes.forEach((child) => {
170
- const { nodeName, attrs = [], value } = child;
171
-
172
- if (nodeName !== '#text') {
173
- innerHTML += `<${nodeName}`;
174
-
175
- if (attrs.length > 0) {
176
- attrs.forEach(attr => {
177
- innerHTML += ` ${attr.name}="${attr.value}"`;
178
- });
179
- }
180
-
181
- innerHTML += '>';
182
-
183
- if (child.childNodes.length > 0) {
184
- innerHTML = renderLightDomChildren(child.childNodes, innerHTML);
185
- }
186
-
187
- innerHTML += VOID_ELEMENTS.includes(nodeName)
188
- ? ''
189
- : `</${nodeName}>`;
190
- } else if (nodeName === '#text') {
191
- innerHTML += value;
192
- }
193
- });
194
-
195
- return innerHTML;
196
- }
197
-
198
- async function initializeCustomElement(elementURL, tagName, node = {}, definitions = [], isEntry, props = {}) {
199
- const { attrs = [], childNodes = [] } = node;
150
+ async function initializeCustomElement(elementURL, tagName, node = {}, definitions = {}, isEntry, props = {}) {
200
151
 
201
152
  if (!tagName) {
202
153
  const depth = isEntry ? 1 : 0;
203
154
  registerDependencies(elementURL, definitions, depth);
204
155
  }
205
156
 
206
- // https://github.com/ProjectEvergreen/wcc/pull/67/files#r902061804
207
- // https://github.com/ProjectEvergreen/wcc/pull/159
208
- const { href } = elementURL;
209
- const element = customElements.get(tagName) ?? (await import(href)).default;
210
- const dataLoader = (await import(href)).getData;
211
- const data = props
212
- ? props
213
- : dataLoader
214
- ? await dataLoader(props)
215
- : {};
157
+ const element = customElements.get(tagName) ?? (await import(elementURL)).default;
158
+ const dataLoader = (await import(elementURL)).getData;
159
+ const data = props ? props : dataLoader ? await dataLoader(props) : {};
216
160
 
217
161
  if (element) {
218
- const elementInstance = new element(data); // eslint-disable-line new-cap
162
+ const elementInstance = new element(data);
219
163
 
220
- // support for HTML (Light DOM) Web Components
221
- elementInstance.innerHTML = renderLightDomChildren(childNodes);
164
+ Object.assign(elementInstance, node);
222
165
 
223
- attrs.forEach((attr) => {
224
- elementInstance.setAttribute(attr.name, attr.value);
225
-
226
- if (attr.name === 'hydrate') {
227
- definitions[tagName].hydrate = attr.value;
228
- }
229
- });
230
-
231
166
  await elementInstance.connectedCallback();
232
-
167
+
233
168
  return elementInstance;
234
169
  }
235
170
  }
236
171
 
172
+ /** @type {import('./index.d.ts').renderToString} */
237
173
  async function renderToString(elementURL, wrappingEntryTag = true, props = {}) {
238
- const definitions = [];
174
+ /** @type {import('./index.d.ts').Metadata} */
175
+ const definitions = {};
239
176
  const elementTagName = wrappingEntryTag && await getTagName(elementURL);
240
177
  const isEntry = !!elementTagName;
241
178
  const elementInstance = await initializeCustomElement(elementURL, undefined, undefined, definitions, isEntry, props);
179
+
242
180
  let html;
243
181
 
244
182
  // in case the entry point isn't valid
245
183
  if (elementInstance) {
246
- const elementHtml = elementInstance.shadowRoot
247
- ? elementInstance.getInnerHTML({ includeShadowRoots: true })
248
- : elementInstance.innerHTML;
249
- const elementTree = getParse(elementHtml)(elementHtml);
250
- const finalTree = await renderComponentRoots(elementTree, definitions);
184
+ elementInstance.nodeName = elementTagName ?? '';
185
+ elementInstance.tagName = elementTagName ?? '';
186
+
187
+ await renderComponentRoots(
188
+ elementInstance.shadowRoot
189
+ ?
190
+ {
191
+ nodeName: '#document-fragment',
192
+ childNodes: [elementInstance]
193
+ }
194
+ : elementInstance,
195
+ definitions
196
+ );
251
197
 
252
198
  html = wrappingEntryTag && elementTagName ? `
253
199
  <${elementTagName}>
254
- ${serialize(finalTree)}
200
+ ${serialize(elementInstance)}
255
201
  </${elementTagName}>
256
202
  `
257
- : serialize(finalTree);
203
+ : serialize(elementInstance);
258
204
  } else {
259
205
  console.warn('WARNING: No custom element class found for this entry point.');
260
206
  }
@@ -265,8 +211,10 @@ async function renderToString(elementURL, wrappingEntryTag = true, props = {}) {
265
211
  };
266
212
  }
267
213
 
214
+ /** @type {import('./index.d.ts').renderFromHTML} */
268
215
  async function renderFromHTML(html, elements = []) {
269
- const definitions = [];
216
+ /** @type {import('./index.d.ts').Metadata} */
217
+ const definitions = {};
270
218
 
271
219
  for (const url of elements) {
272
220
  registerDependencies(url, definitions, 1);