wc-compiler 0.14.0 → 0.15.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wc-compiler",
3
- "version": "0.14.0",
3
+ "version": "0.15.1",
4
4
  "description": "Experimental native Web Components compiler.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,10 +43,9 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@projectevergreen/acorn-jsx-esm": "~0.1.0",
46
- "@projectevergreen/escodegen-esm": "~0.1.0",
47
- "acorn": "^8.7.0",
48
- "acorn-import-attributes": "^1.9.5",
49
- "acorn-walk": "^8.2.0",
46
+ "acorn": "^8.14.0",
47
+ "acorn-walk": "^8.3.4",
48
+ "astring": "^1.9.0",
50
49
  "parse5": "^6.0.1",
51
50
  "sucrase": "^3.35.0"
52
51
  },
@@ -57,7 +56,7 @@
57
56
  "@babel/preset-react": "^7.24.1",
58
57
  "@ls-lint/ls-lint": "^1.10.0",
59
58
  "@mapbox/rehype-prism": "^0.8.0",
60
- "@rollup/plugin-commonjs": "^25.0.7",
59
+ "@rollup/plugin-commonjs": "^28.0.0",
61
60
  "@rollup/plugin-node-resolve": "^15.2.3",
62
61
  "c8": "^7.11.2",
63
62
  "chai": "^4.3.6",
@@ -79,7 +78,7 @@
79
78
  "remark-rehype": "^10.1.0",
80
79
  "remark-toc": "^8.0.1",
81
80
  "rimraf": "^3.0.2",
82
- "rollup": "^3.29.3",
81
+ "rollup": "^4.26.0",
83
82
  "simple.css": "^0.1.3",
84
83
  "unified": "^10.1.2"
85
84
  }
package/src/jsx-loader.js CHANGED
@@ -2,12 +2,12 @@
2
2
  // https://nodejs.org/api/esm.html#esm_loaders
3
3
  import * as acorn from 'acorn';
4
4
  import * as walk from 'acorn-walk';
5
- import { generate } from '@projectevergreen/escodegen-esm';
5
+ import { generate } from 'astring';
6
6
  import fs from 'fs';
7
+ // ideally we can eventually adopt an ESM compatible version of this plugin
8
+ // https://github.com/acornjs/acorn-jsx/issues/112
7
9
  import jsx from '@projectevergreen/acorn-jsx-esm';
8
10
  import { parse, parseFragment, serialize } from 'parse5';
9
- // Need an acorn plugin for now - https://github.com/ProjectEvergreen/greenwood/issues/1218
10
- import { importAttributes } from 'acorn-import-attributes';
11
11
  import { transform } from 'sucrase';
12
12
 
13
13
  const jsxRegex = /\.(jsx)$/;
@@ -32,7 +32,7 @@ export function getParser(moduleURL) {
32
32
  }
33
33
 
34
34
  return {
35
- parser: acorn.Parser.extend(jsx(), importAttributes),
35
+ parser: acorn.Parser.extend(jsx()),
36
36
  config: {
37
37
  // https://github.com/acornjs/acorn/issues/829#issuecomment-1172586171
38
38
  ...walk.base,
@@ -243,7 +243,7 @@ export function parseJsx(moduleURL) {
243
243
  const hasOwnObservedAttributes = undefined;
244
244
  let inferredObservability = false;
245
245
  let observedAttributes = [];
246
- let tree = acorn.Parser.extend(jsx(), importAttributes).parse(result.code, {
246
+ let tree = acorn.Parser.extend(jsx()).parse(result.code, {
247
247
  ecmaVersion: 'latest',
248
248
  sourceType: 'module'
249
249
  });
package/src/wcc.js CHANGED
@@ -4,14 +4,30 @@ import './dom-shim.js';
4
4
 
5
5
  import * as acorn from 'acorn';
6
6
  import * as walk from 'acorn-walk';
7
- import { generate } from '@projectevergreen/escodegen-esm';
7
+ import { generate } from 'astring';
8
8
  import { getParser, parseJsx } from './jsx-loader.js';
9
9
  import { parse, parseFragment, serialize } from 'parse5';
10
- // Need an acorn plugin for now - https://github.com/ProjectEvergreen/greenwood/issues/1218
11
- import { importAttributes } from 'acorn-import-attributes';
12
10
  import { transform } from 'sucrase';
13
11
  import fs from 'fs';
14
12
 
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
+
15
31
  function getParse(html) {
16
32
  return html.indexOf('<html>') >= 0 || html.indexOf('<body>') >= 0 || html.indexOf('<head>') >= 0
17
33
  ? parse
@@ -33,17 +49,26 @@ async function renderComponentRoots(tree, definitions) {
33
49
 
34
50
  if (definitions[tagName]) {
35
51
  const { moduleURL } = definitions[tagName];
36
- const elementInstance = await initializeCustomElement(moduleURL, tagName, node.attrs, definitions);
37
- const elementHtml = elementInstance.shadowRoot
38
- ? elementInstance.getInnerHTML({ includeShadowRoots: true })
39
- : elementInstance.innerHTML;
40
- const elementTree = parseFragment(elementHtml);
41
-
42
- node.childNodes = node.childNodes.length === 0
43
- ? elementTree.childNodes
44
- : [...elementTree.childNodes, ...node.childNodes];
52
+ const elementInstance = await initializeCustomElement(moduleURL, tagName, node, definitions);
53
+
54
+ if (elementInstance) {
55
+ 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;
67
+ } else {
68
+ console.warn(`WARNING: customElement <${tagName}> detected but not serialized. You may not have exported it.`);
69
+ }
45
70
  } else {
46
- console.warn(`WARNING: customElement <${tagName}> is not defined. You may not have imported it yet.`);
71
+ console.warn(`WARNING: customElement <${tagName}> is not defined. You may not have imported it.`);
47
72
  }
48
73
  }
49
74
 
@@ -73,7 +98,7 @@ function registerDependencies(moduleURL, definitions, depth = 0) {
73
98
  ...walk.base
74
99
  };
75
100
 
76
- walk.simple(parser.extend(importAttributes).parse(result.code, {
101
+ walk.simple(parser.parse(result.code, {
77
102
  ecmaVersion: 'latest',
78
103
  sourceType: 'module'
79
104
  }), {
@@ -82,7 +107,7 @@ function registerDependencies(moduleURL, definitions, depth = 0) {
82
107
  const isBareSpecifier = specifier.indexOf('.') !== 0 && specifier.indexOf('/') !== 0;
83
108
  const extension = specifier.split('.').pop();
84
109
 
85
- // TODO would like to decouple .jsx from the core, ideally
110
+ // would like to decouple .jsx from the core, ideally
86
111
  // https://github.com/ProjectEvergreen/wcc/issues/122
87
112
  if (!isBareSpecifier && ['js', 'jsx', 'ts'].includes(extension)) {
88
113
  const dependencyModuleURL = new URL(node.source.value, moduleURL);
@@ -124,7 +149,7 @@ async function getTagName(moduleURL) {
124
149
  };
125
150
  let tagName;
126
151
 
127
- walk.simple(parser.extend(importAttributes).parse(result.code, {
152
+ walk.simple(parser.parse(result.code, {
128
153
  ecmaVersion: 'latest',
129
154
  sourceType: 'module'
130
155
  }), {
@@ -138,7 +163,41 @@ async function getTagName(moduleURL) {
138
163
  return tagName;
139
164
  }
140
165
 
141
- async function initializeCustomElement(elementURL, tagName, attrs = [], definitions = [], isEntry, props = {}) {
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;
200
+
142
201
  if (!tagName) {
143
202
  const depth = isEntry ? 1 : 0;
144
203
  registerDependencies(elementURL, definitions, depth);
@@ -158,6 +217,9 @@ async function initializeCustomElement(elementURL, tagName, attrs = [], definiti
158
217
  if (element) {
159
218
  const elementInstance = new element(data); // eslint-disable-line new-cap
160
219
 
220
+ // support for HTML (Light DOM) Web Components
221
+ elementInstance.innerHTML = renderLightDomChildren(childNodes);
222
+
161
223
  attrs.forEach((attr) => {
162
224
  elementInstance.setAttribute(attr.name, attr.value);
163
225
 
@@ -207,7 +269,7 @@ async function renderFromHTML(html, elements = []) {
207
269
  const definitions = [];
208
270
 
209
271
  for (const url of elements) {
210
- await initializeCustomElement(url, undefined, undefined, definitions, true);
272
+ registerDependencies(url, definitions, 1);
211
273
  }
212
274
 
213
275
  const elementTree = getParse(html)(html);