wc-compiler 0.15.0 → 0.16.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 +1 -1
- package/dist/wcc.dist.cjs +16889 -18357
- package/package.json +7 -8
- package/src/dom-shim.js +170 -34
- package/src/jsx-loader.js +5 -5
- package/src/wcc.js +41 -100
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wc-compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Experimental native Web Components compiler.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -43,11 +43,10 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@projectevergreen/acorn-jsx-esm": "~0.1.0",
|
|
46
|
-
"
|
|
47
|
-
"acorn": "^8.
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"parse5": "^6.0.1",
|
|
46
|
+
"acorn": "^8.14.0",
|
|
47
|
+
"acorn-walk": "^8.3.4",
|
|
48
|
+
"astring": "^1.9.0",
|
|
49
|
+
"parse5": "^7.2.1",
|
|
51
50
|
"sucrase": "^3.35.0"
|
|
52
51
|
},
|
|
53
52
|
"devDependencies": {
|
|
@@ -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": "^
|
|
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": "^
|
|
81
|
+
"rollup": "^4.26.0",
|
|
83
82
|
"simple.css": "^0.1.3",
|
|
84
83
|
"unified": "^10.1.2"
|
|
85
84
|
}
|
package/src/dom-shim.js
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
+
/* eslint-disable no-warning-comments */
|
|
2
|
+
|
|
3
|
+
import { parse, parseFragment, serialize } from 'parse5';
|
|
4
|
+
|
|
5
|
+
export function getParse(html) {
|
|
6
|
+
return html.indexOf('<html>') >= 0 || html.indexOf('<body>') >= 0 || html.indexOf('<head>') >= 0
|
|
7
|
+
? parse
|
|
8
|
+
: parseFragment;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isShadowRoot(element) {
|
|
12
|
+
return Object.getPrototypeOf(element).constructor.name === 'ShadowRoot';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function deepClone(obj, map = new WeakMap()) {
|
|
16
|
+
if (obj === null || typeof obj !== 'object') {
|
|
17
|
+
return obj;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (typeof obj === 'function') {
|
|
21
|
+
const clonedFn = obj.bind({});
|
|
22
|
+
Object.assign(clonedFn, obj);
|
|
23
|
+
return clonedFn;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (map.has(obj)) {
|
|
27
|
+
return map.get(obj);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const result = Array.isArray(obj) ? [] : {};
|
|
31
|
+
map.set(obj, result);
|
|
32
|
+
|
|
33
|
+
for (const key of Object.keys(obj)) {
|
|
34
|
+
result[key] = deepClone(obj[key], map);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Creates an empty parse5 element without the parse5 overhead resulting in better performance
|
|
41
|
+
function getParse5ElementDefaults(element, tagName) {
|
|
42
|
+
return {
|
|
43
|
+
addEventListener: noop,
|
|
44
|
+
attrs: [],
|
|
45
|
+
parentNode: element.parentNode,
|
|
46
|
+
childNodes: [],
|
|
47
|
+
nodeName: tagName,
|
|
48
|
+
tagName: tagName,
|
|
49
|
+
namespaceURI: 'http://www.w3.org/1999/xhtml',
|
|
50
|
+
// eslint-disable-next-line no-extra-parens
|
|
51
|
+
...(tagName === 'template' ? { content: { nodeName: '#document-fragment', childNodes: [] } } : {})
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
1
55
|
function noop() { }
|
|
2
56
|
|
|
3
57
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet
|
|
@@ -19,13 +73,81 @@ class EventTarget {
|
|
|
19
73
|
// EventTarget <- Node
|
|
20
74
|
// TODO should be an interface?
|
|
21
75
|
class Node extends EventTarget {
|
|
22
|
-
|
|
76
|
+
constructor() {
|
|
77
|
+
super();
|
|
78
|
+
// Parse5 properties
|
|
79
|
+
this.attrs = [];
|
|
80
|
+
this.parentNode = null;
|
|
81
|
+
this.childNodes = [];
|
|
82
|
+
this.nodeName = '';
|
|
83
|
+
}
|
|
84
|
+
|
|
23
85
|
cloneNode(deep) {
|
|
24
|
-
return this;
|
|
86
|
+
return deep ? deepClone(this) : Object.assign({}, this);
|
|
25
87
|
}
|
|
26
88
|
|
|
27
89
|
appendChild(node) {
|
|
28
|
-
|
|
90
|
+
const childNodes = (this.nodeName === 'template' ? this.content : this).childNodes;
|
|
91
|
+
|
|
92
|
+
if (node.parentNode) {
|
|
93
|
+
node.parentNode?.removeChild?.(node);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (node.nodeName === 'template') {
|
|
97
|
+
if (isShadowRoot(this) && this.mode) {
|
|
98
|
+
node.attrs = [{ name: 'shadowrootmode', value: this.mode }];
|
|
99
|
+
childNodes.push(node);
|
|
100
|
+
node.parentNode = this;
|
|
101
|
+
} else {
|
|
102
|
+
this.childNodes = [...this.childNodes, ...node.content.childNodes];
|
|
103
|
+
}
|
|
104
|
+
} else if (node instanceof DocumentFragment) {
|
|
105
|
+
this.childNodes = [...this.childNodes, ...node.childNodes];
|
|
106
|
+
} else {
|
|
107
|
+
childNodes.push(node);
|
|
108
|
+
node.parentNode = this;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return node;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
removeChild(node) {
|
|
115
|
+
const childNodes = (this.nodeName === 'template' ? this.content : this).childNodes;
|
|
116
|
+
if (!childNodes || !childNodes.length) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const index = childNodes.indexOf(node);
|
|
121
|
+
if (index === -1) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
childNodes.splice(index, 1);
|
|
126
|
+
node.parentNode = null;
|
|
127
|
+
|
|
128
|
+
return node;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
get textContent() {
|
|
132
|
+
if (this.nodeName === '#text') {
|
|
133
|
+
return this.value || '';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return this.childNodes
|
|
137
|
+
.map((child) => child.nodeName === '#text' ? child.value : child.textContent)
|
|
138
|
+
.join('');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
set textContent(value) {
|
|
142
|
+
this.childNodes = [];
|
|
143
|
+
|
|
144
|
+
if (value) {
|
|
145
|
+
const textNode = new Node();
|
|
146
|
+
textNode.nodeName = '#text';
|
|
147
|
+
textNode.value = value;
|
|
148
|
+
textNode.parentNode = this;
|
|
149
|
+
this.childNodes.push(textNode);
|
|
150
|
+
}
|
|
29
151
|
}
|
|
30
152
|
}
|
|
31
153
|
|
|
@@ -34,33 +156,44 @@ class Node extends EventTarget {
|
|
|
34
156
|
class Element extends Node {
|
|
35
157
|
constructor() {
|
|
36
158
|
super();
|
|
37
|
-
this.shadowRoot = null;
|
|
38
|
-
this.innerHTML = '';
|
|
39
|
-
this.attributes = {};
|
|
40
159
|
}
|
|
41
160
|
|
|
42
161
|
attachShadow(options) {
|
|
43
162
|
this.shadowRoot = new ShadowRoot(options);
|
|
44
|
-
|
|
163
|
+
this.shadowRoot.parentNode = this;
|
|
45
164
|
return this.shadowRoot;
|
|
46
165
|
}
|
|
47
166
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
getInnerHTML() {
|
|
51
|
-
return this.shadowRoot ? this.shadowRoot.innerHTML : this.innerHTML;
|
|
167
|
+
getHTML({ serializableShadowRoots = false }) {
|
|
168
|
+
return this.shadowRoot && serializableShadowRoots && this.shadowRoot.serializable ? this.shadowRoot.innerHTML : '';
|
|
52
169
|
}
|
|
53
170
|
|
|
54
|
-
|
|
55
|
-
this.
|
|
171
|
+
get innerHTML() {
|
|
172
|
+
const childNodes = (this.nodeName === 'template' ? this.content : this).childNodes;
|
|
173
|
+
return childNodes ? serialize({ childNodes }) : '';
|
|
56
174
|
}
|
|
57
175
|
|
|
58
|
-
|
|
59
|
-
|
|
176
|
+
set innerHTML(html) {
|
|
177
|
+
(this.nodeName === 'template' ? this.content : this).childNodes = getParse(html)(html).childNodes;
|
|
60
178
|
}
|
|
61
179
|
|
|
62
180
|
hasAttribute(name) {
|
|
63
|
-
return
|
|
181
|
+
return this.attrs.some((attr) => attr.name === name);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
getAttribute(name) {
|
|
185
|
+
const attr = this.attrs.find((attr) => attr.name === name);
|
|
186
|
+
return attr ? attr.value : null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
setAttribute(name, value) {
|
|
190
|
+
const attr = this.attrs?.find((attr) => attr.name === name);
|
|
191
|
+
|
|
192
|
+
if (attr) {
|
|
193
|
+
attr.value = value;
|
|
194
|
+
} else {
|
|
195
|
+
this.attrs?.push({ name, value });
|
|
196
|
+
}
|
|
64
197
|
}
|
|
65
198
|
}
|
|
66
199
|
|
|
@@ -75,7 +208,7 @@ class Document extends Node {
|
|
|
75
208
|
return new HTMLTemplateElement();
|
|
76
209
|
|
|
77
210
|
default:
|
|
78
|
-
return new HTMLElement();
|
|
211
|
+
return new HTMLElement(tagName);
|
|
79
212
|
|
|
80
213
|
}
|
|
81
214
|
}
|
|
@@ -88,6 +221,10 @@ class Document extends Node {
|
|
|
88
221
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
|
|
89
222
|
// EventTarget <- Node <- Element <- HTMLElement
|
|
90
223
|
class HTMLElement extends Element {
|
|
224
|
+
constructor(tagName) {
|
|
225
|
+
super();
|
|
226
|
+
Object.assign(this, getParse5ElementDefaults(this, tagName));
|
|
227
|
+
}
|
|
91
228
|
connectedCallback() { }
|
|
92
229
|
}
|
|
93
230
|
|
|
@@ -100,9 +237,18 @@ class DocumentFragment extends Node { }
|
|
|
100
237
|
class ShadowRoot extends DocumentFragment {
|
|
101
238
|
constructor(options) {
|
|
102
239
|
super();
|
|
103
|
-
this.mode = options.mode
|
|
240
|
+
this.mode = options.mode ?? 'closed';
|
|
241
|
+
this.serializable = options.serializable ?? false;
|
|
104
242
|
this.adoptedStyleSheets = [];
|
|
105
243
|
}
|
|
244
|
+
|
|
245
|
+
get innerHTML() {
|
|
246
|
+
return this.childNodes?.[0]?.content?.childNodes ? serialize({ childNodes: this.childNodes[0].content.childNodes }) : '';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
set innerHTML(html) {
|
|
250
|
+
this.childNodes = getParse(html)(`<template shadowrootmode="${this.mode}">${html}</template>`).childNodes;
|
|
251
|
+
}
|
|
106
252
|
}
|
|
107
253
|
|
|
108
254
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement
|
|
@@ -110,22 +256,11 @@ class ShadowRoot extends DocumentFragment {
|
|
|
110
256
|
class HTMLTemplateElement extends HTMLElement {
|
|
111
257
|
constructor() {
|
|
112
258
|
super();
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (this.content) {
|
|
119
|
-
this.content.innerHTML = `
|
|
120
|
-
<template shadowrootmode="open">
|
|
121
|
-
${html}
|
|
122
|
-
</template>
|
|
123
|
-
`;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
get innerHTML() {
|
|
128
|
-
return this.content && this.content.innerHTML ? this.content.innerHTML : undefined;
|
|
259
|
+
// Gets element defaults for template element instead of parsing a
|
|
260
|
+
// <template></template> with parse5. Results in better performance
|
|
261
|
+
// when creating templates
|
|
262
|
+
Object.assign(this, getParse5ElementDefaults(this, 'template'));
|
|
263
|
+
this.content.cloneNode = this.cloneNode.bind(this);
|
|
129
264
|
}
|
|
130
265
|
}
|
|
131
266
|
|
|
@@ -156,4 +291,5 @@ globalThis.addEventListener = globalThis.addEventListener ?? noop;
|
|
|
156
291
|
globalThis.document = globalThis.document ?? new Document();
|
|
157
292
|
globalThis.customElements = globalThis.customElements ?? new CustomElementsRegistry();
|
|
158
293
|
globalThis.HTMLElement = globalThis.HTMLElement ?? HTMLElement;
|
|
294
|
+
globalThis.DocumentFragment = globalThis.DocumentFragment ?? DocumentFragment;
|
|
159
295
|
globalThis.CSSStyleSheet = globalThis.CSSStyleSheet ?? CSSStyleSheet;
|
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 '
|
|
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()
|
|
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()
|
|
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
|
@@ -1,41 +1,15 @@
|
|
|
1
1
|
/* eslint-disable max-depth */
|
|
2
2
|
// this must come first
|
|
3
|
-
import './dom-shim.js';
|
|
3
|
+
import { getParse } from './dom-shim.js';
|
|
4
4
|
|
|
5
5
|
import * as acorn from 'acorn';
|
|
6
6
|
import * as walk from 'acorn-walk';
|
|
7
|
-
import { generate } from '
|
|
7
|
+
import { generate } from 'astring';
|
|
8
8
|
import { getParser, parseJsx } from './jsx-loader.js';
|
|
9
|
-
import {
|
|
10
|
-
// Need an acorn plugin for now - https://github.com/ProjectEvergreen/greenwood/issues/1218
|
|
11
|
-
import { importAttributes } from 'acorn-import-attributes';
|
|
9
|
+
import { serialize } from 'parse5';
|
|
12
10
|
import { transform } from 'sucrase';
|
|
13
11
|
import fs from 'fs';
|
|
14
12
|
|
|
15
|
-
// https://developer.mozilla.org/en-US/docs/Glossary/Void_element
|
|
16
|
-
const VOID_ELEMENTS = [
|
|
17
|
-
'area',
|
|
18
|
-
'base',
|
|
19
|
-
'br',
|
|
20
|
-
'col',
|
|
21
|
-
'embed',
|
|
22
|
-
'hr',
|
|
23
|
-
'img',
|
|
24
|
-
'input',
|
|
25
|
-
'link',
|
|
26
|
-
'meta',
|
|
27
|
-
'param', // deprecated
|
|
28
|
-
'source',
|
|
29
|
-
'track',
|
|
30
|
-
'wbr'
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
function getParse(html) {
|
|
34
|
-
return html.indexOf('<html>') >= 0 || html.indexOf('<body>') >= 0 || html.indexOf('<head>') >= 0
|
|
35
|
-
? parse
|
|
36
|
-
: parseFragment;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
13
|
function isCustomElementDefinitionNode(node) {
|
|
40
14
|
const { expression } = node;
|
|
41
15
|
|
|
@@ -47,7 +21,7 @@ function isCustomElementDefinitionNode(node) {
|
|
|
47
21
|
async function renderComponentRoots(tree, definitions) {
|
|
48
22
|
for (const node of tree.childNodes) {
|
|
49
23
|
if (node.tagName && node.tagName.indexOf('-') > 0) {
|
|
50
|
-
const { tagName } = node;
|
|
24
|
+
const { attrs, tagName } = node;
|
|
51
25
|
|
|
52
26
|
if (definitions[tagName]) {
|
|
53
27
|
const { moduleURL } = definitions[tagName];
|
|
@@ -55,31 +29,35 @@ async function renderComponentRoots(tree, definitions) {
|
|
|
55
29
|
|
|
56
30
|
if (elementInstance) {
|
|
57
31
|
const hasShadow = elementInstance.shadowRoot;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const hasLight = elementTree.childNodes > 0;
|
|
63
|
-
|
|
64
|
-
node.childNodes = node.childNodes.length === 0 && hasLight && !hasShadow
|
|
65
|
-
? elementTree.childNodes
|
|
66
|
-
: hasShadow
|
|
67
|
-
? [...elementTree.childNodes, ...node.childNodes]
|
|
68
|
-
: elementTree.childNodes;
|
|
32
|
+
|
|
33
|
+
node.childNodes = hasShadow
|
|
34
|
+
? [...elementInstance.shadowRoot.childNodes, ...node.childNodes]
|
|
35
|
+
: elementInstance.childNodes;
|
|
69
36
|
} else {
|
|
70
37
|
console.warn(`WARNING: customElement <${tagName}> detected but not serialized. You may not have exported it.`);
|
|
71
38
|
}
|
|
72
39
|
} else {
|
|
73
40
|
console.warn(`WARNING: customElement <${tagName}> is not defined. You may not have imported it.`);
|
|
74
41
|
}
|
|
42
|
+
|
|
43
|
+
attrs.forEach((attr) => {
|
|
44
|
+
if (attr.name === 'hydrate') {
|
|
45
|
+
definitions[tagName].hydrate = attr.value;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
75
49
|
}
|
|
76
50
|
|
|
77
51
|
if (node.childNodes && node.childNodes.length > 0) {
|
|
78
52
|
await renderComponentRoots(node, definitions);
|
|
79
53
|
}
|
|
80
54
|
|
|
55
|
+
if (node.shadowRoot && node.shadowRoot.childNodes?.length > 0) {
|
|
56
|
+
await renderComponentRoots(node.shadowRoot, definitions);
|
|
57
|
+
}
|
|
58
|
+
|
|
81
59
|
// does this only apply to `<template>` tags?
|
|
82
|
-
if (node.content && node.content.childNodes
|
|
60
|
+
if (node.content && node.content.childNodes?.length > 0) {
|
|
83
61
|
await renderComponentRoots(node.content, definitions);
|
|
84
62
|
}
|
|
85
63
|
}
|
|
@@ -100,7 +78,7 @@ function registerDependencies(moduleURL, definitions, depth = 0) {
|
|
|
100
78
|
...walk.base
|
|
101
79
|
};
|
|
102
80
|
|
|
103
|
-
walk.simple(parser.
|
|
81
|
+
walk.simple(parser.parse(result.code, {
|
|
104
82
|
ecmaVersion: 'latest',
|
|
105
83
|
sourceType: 'module'
|
|
106
84
|
}), {
|
|
@@ -151,7 +129,7 @@ async function getTagName(moduleURL) {
|
|
|
151
129
|
};
|
|
152
130
|
let tagName;
|
|
153
131
|
|
|
154
|
-
walk.simple(parser.
|
|
132
|
+
walk.simple(parser.parse(result.code, {
|
|
155
133
|
ecmaVersion: 'latest',
|
|
156
134
|
sourceType: 'module'
|
|
157
135
|
}), {
|
|
@@ -165,40 +143,7 @@ async function getTagName(moduleURL) {
|
|
|
165
143
|
return tagName;
|
|
166
144
|
}
|
|
167
145
|
|
|
168
|
-
function renderLightDomChildren(childNodes, iHTML = '') {
|
|
169
|
-
let innerHTML = iHTML;
|
|
170
|
-
|
|
171
|
-
childNodes.forEach((child) => {
|
|
172
|
-
const { nodeName, attrs = [], value } = child;
|
|
173
|
-
|
|
174
|
-
if (nodeName !== '#text') {
|
|
175
|
-
innerHTML += `<${nodeName}`;
|
|
176
|
-
|
|
177
|
-
if (attrs.length > 0) {
|
|
178
|
-
attrs.forEach(attr => {
|
|
179
|
-
innerHTML += ` ${attr.name}="${attr.value}"`;
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
innerHTML += '>';
|
|
184
|
-
|
|
185
|
-
if (child.childNodes.length > 0) {
|
|
186
|
-
innerHTML = renderLightDomChildren(child.childNodes, innerHTML);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
innerHTML += VOID_ELEMENTS.includes(nodeName)
|
|
190
|
-
? ''
|
|
191
|
-
: `</${nodeName}>`;
|
|
192
|
-
} else if (nodeName === '#text') {
|
|
193
|
-
innerHTML += value;
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
return innerHTML;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
146
|
async function initializeCustomElement(elementURL, tagName, node = {}, definitions = [], isEntry, props = {}) {
|
|
201
|
-
const { attrs = [], childNodes = [] } = node;
|
|
202
147
|
|
|
203
148
|
if (!tagName) {
|
|
204
149
|
const depth = isEntry ? 1 : 0;
|
|
@@ -210,28 +155,15 @@ async function initializeCustomElement(elementURL, tagName, node = {}, definitio
|
|
|
210
155
|
const { href } = elementURL;
|
|
211
156
|
const element = customElements.get(tagName) ?? (await import(href)).default;
|
|
212
157
|
const dataLoader = (await import(href)).getData;
|
|
213
|
-
const data = props
|
|
214
|
-
? props
|
|
215
|
-
: dataLoader
|
|
216
|
-
? await dataLoader(props)
|
|
217
|
-
: {};
|
|
158
|
+
const data = props ? props : dataLoader ? await dataLoader(props) : {};
|
|
218
159
|
|
|
219
160
|
if (element) {
|
|
220
161
|
const elementInstance = new element(data); // eslint-disable-line new-cap
|
|
221
162
|
|
|
222
|
-
|
|
223
|
-
elementInstance.innerHTML = renderLightDomChildren(childNodes);
|
|
163
|
+
Object.assign(elementInstance, node);
|
|
224
164
|
|
|
225
|
-
attrs.forEach((attr) => {
|
|
226
|
-
elementInstance.setAttribute(attr.name, attr.value);
|
|
227
|
-
|
|
228
|
-
if (attr.name === 'hydrate') {
|
|
229
|
-
definitions[tagName].hydrate = attr.value;
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
|
|
233
165
|
await elementInstance.connectedCallback();
|
|
234
|
-
|
|
166
|
+
|
|
235
167
|
return elementInstance;
|
|
236
168
|
}
|
|
237
169
|
}
|
|
@@ -241,22 +173,31 @@ async function renderToString(elementURL, wrappingEntryTag = true, props = {}) {
|
|
|
241
173
|
const elementTagName = wrappingEntryTag && await getTagName(elementURL);
|
|
242
174
|
const isEntry = !!elementTagName;
|
|
243
175
|
const elementInstance = await initializeCustomElement(elementURL, undefined, undefined, definitions, isEntry, props);
|
|
176
|
+
|
|
244
177
|
let html;
|
|
245
178
|
|
|
246
179
|
// in case the entry point isn't valid
|
|
247
180
|
if (elementInstance) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
181
|
+
elementInstance.nodeName = elementTagName ?? '';
|
|
182
|
+
elementInstance.tagName = elementTagName ?? '';
|
|
183
|
+
|
|
184
|
+
await renderComponentRoots(
|
|
185
|
+
elementInstance.shadowRoot
|
|
186
|
+
?
|
|
187
|
+
{
|
|
188
|
+
nodeName: '#document-fragment',
|
|
189
|
+
childNodes: [elementInstance]
|
|
190
|
+
}
|
|
191
|
+
: elementInstance,
|
|
192
|
+
definitions
|
|
193
|
+
);
|
|
253
194
|
|
|
254
195
|
html = wrappingEntryTag && elementTagName ? `
|
|
255
196
|
<${elementTagName}>
|
|
256
|
-
${serialize(
|
|
197
|
+
${serialize(elementInstance)}
|
|
257
198
|
</${elementTagName}>
|
|
258
199
|
`
|
|
259
|
-
: serialize(
|
|
200
|
+
: serialize(elementInstance);
|
|
260
201
|
} else {
|
|
261
202
|
console.warn('WARNING: No custom element class found for this entry point.');
|
|
262
203
|
}
|