wc-compiler 0.10.0 → 0.12.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/dist/wcc.dist.cjs +2505 -11748
- package/package.json +13 -13
- package/src/dom-shim.js +25 -19
- package/src/jsx-loader.js +27 -16
- package/src/wcc.js +3 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wc-compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Experimental native Web Components compiler.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,11 +28,12 @@
|
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"clean": "rimraf ./dist",
|
|
31
|
-
"lint": "ls-lint && eslint \"*.*js\" \"./src/**/**/*.js*\" \"./docs/**/*.md\" \"./test/**/**/*.js*\"",
|
|
32
|
-
"
|
|
33
|
-
"build": "node ./build.js",
|
|
34
|
-
"serve": "
|
|
35
|
-
"
|
|
31
|
+
"lint": "ls-lint && eslint \"*.*js\" \"./src/**/**/*.js*\" \"./sandbox/**/**/*.js*\" \"./docs/**/*.md\" \"./test/**/**/*.js*\"",
|
|
32
|
+
"docs:dev": "concurrently \"nodemon --watch src --watch docs -e js,md,css,html,jsx ./build.js\" \"http-server ./dist --open\"",
|
|
33
|
+
"docs:build": "node ./build.js",
|
|
34
|
+
"docs:serve": "npm run clean && npm run docs:build && http-server ./dist --open",
|
|
35
|
+
"sandbox": "npm run clean && concurrently \"nodemon --experimental-loader ./test-exp-loader.js --watch src --watch sandbox -e js,md,css,html,jsx ./sandbox.js\" \"http-server ./dist --open\" \"livereload ./dist\"",
|
|
36
|
+
"start": "npm run docs:serve",
|
|
36
37
|
"test": "mocha --exclude \"./test/cases/jsx*/**\" --exclude \"./test/cases/custom-extension/**\" \"./test/**/**/*.spec.js\"",
|
|
37
38
|
"test:exp": "c8 node --experimental-loader ./test-exp-loader.js ./node_modules/mocha/bin/mocha \"./test/**/**/*.spec.js\"",
|
|
38
39
|
"test:tdd": "npm run test -- --watch",
|
|
@@ -41,18 +42,17 @@
|
|
|
41
42
|
"prepublishOnly": "npm run clean && npm run dist"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|
|
45
|
+
"@projectevergreen/acorn-jsx-esm": "~0.1.0",
|
|
46
|
+
"@projectevergreen/escodegen-esm": "~0.1.0",
|
|
44
47
|
"acorn": "^8.7.0",
|
|
45
|
-
"acorn-jsx": "^5.3.2",
|
|
46
48
|
"acorn-walk": "^8.2.0",
|
|
47
|
-
"escodegen": "^2.0.0",
|
|
48
49
|
"parse5": "^6.0.1"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@ls-lint/ls-lint": "^1.10.0",
|
|
52
53
|
"@mapbox/rehype-prism": "^0.8.0",
|
|
53
|
-
"@rollup/plugin-commonjs": "^
|
|
54
|
-
"@rollup/plugin-
|
|
55
|
-
"@rollup/plugin-node-resolve": "^13.3.0",
|
|
54
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
55
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
56
56
|
"c8": "^7.11.2",
|
|
57
57
|
"chai": "^4.3.6",
|
|
58
58
|
"concurrently": "^7.1.0",
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
"eslint-plugin-no-only-tests": "^2.6.0",
|
|
62
62
|
"http-server": "^14.1.0",
|
|
63
63
|
"jsdom": "^19.0.0",
|
|
64
|
+
"livereload": "^0.9.3",
|
|
64
65
|
"mocha": "^9.2.2",
|
|
65
|
-
"node-fetch": "^3.2.6",
|
|
66
66
|
"nodemon": "^2.0.15",
|
|
67
67
|
"prismjs": "^1.28.0",
|
|
68
68
|
"rehype-autolink-headings": "^6.1.1",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"remark-rehype": "^10.1.0",
|
|
74
74
|
"remark-toc": "^8.0.1",
|
|
75
75
|
"rimraf": "^3.0.2",
|
|
76
|
-
"rollup": "^
|
|
76
|
+
"rollup": "^3.29.3",
|
|
77
77
|
"simple.css": "^0.1.3",
|
|
78
78
|
"unified": "^10.1.2"
|
|
79
79
|
}
|
package/src/dom-shim.js
CHANGED
|
@@ -31,6 +31,18 @@ class Element extends Node {
|
|
|
31
31
|
this.attributes = {};
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
attachShadow(options) {
|
|
35
|
+
this.shadowRoot = new ShadowRoot(options);
|
|
36
|
+
|
|
37
|
+
return this.shadowRoot;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// https://github.com/mfreed7/declarative-shadow-dom/blob/master/README.md#serialization
|
|
41
|
+
// eslint-disable-next-line
|
|
42
|
+
getInnerHTML() {
|
|
43
|
+
return this.shadowRoot ? this.shadowRoot.innerHTML : this.innerHTML;
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
setAttribute(name, value) {
|
|
35
47
|
this.attributes[name] = value;
|
|
36
48
|
}
|
|
@@ -68,19 +80,7 @@ class Document extends Node {
|
|
|
68
80
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
|
|
69
81
|
// EventTarget <- Node <- Element <- HTMLElement
|
|
70
82
|
class HTMLElement extends Element {
|
|
71
|
-
attachShadow(options) {
|
|
72
|
-
this.shadowRoot = new ShadowRoot(options);
|
|
73
|
-
|
|
74
|
-
return this.shadowRoot;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
83
|
connectedCallback() { }
|
|
78
|
-
|
|
79
|
-
// https://github.com/mfreed7/declarative-shadow-dom/blob/master/README.md#serialization
|
|
80
|
-
// eslint-disable-next-line
|
|
81
|
-
getInnerHTML(options = {}) {
|
|
82
|
-
return this.shadowRoot.innerHTML;
|
|
83
|
-
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment
|
|
@@ -123,21 +123,27 @@ class HTMLTemplateElement extends HTMLElement {
|
|
|
123
123
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry
|
|
124
124
|
class CustomElementsRegistry {
|
|
125
125
|
constructor() {
|
|
126
|
-
this
|
|
126
|
+
// TODO this should probably be a set or otherwise follow the spec?
|
|
127
|
+
// https://github.com/ProjectEvergreen/wcc/discussions/145
|
|
128
|
+
this.customElementsRegistry = new Map();
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
define(tagName, BaseClass) {
|
|
130
|
-
this
|
|
132
|
+
// TODO this should probably fail as per the spec...
|
|
133
|
+
// e.g. if(this.customElementsRegistry.get(tagName))
|
|
134
|
+
// https://github.com/ProjectEvergreen/wcc/discussions/145
|
|
135
|
+
this.customElementsRegistry.set(tagName, BaseClass);
|
|
131
136
|
}
|
|
132
137
|
|
|
133
138
|
get(tagName) {
|
|
134
|
-
return this.customElementsRegistry
|
|
139
|
+
return this.customElementsRegistry.get(tagName);
|
|
135
140
|
}
|
|
136
141
|
}
|
|
137
142
|
|
|
138
143
|
// mock top level aliases (globalThis === window)
|
|
139
144
|
// https://developer.mozilla.org/en-US/docs/Web/API/Window
|
|
140
|
-
|
|
141
|
-
globalThis.
|
|
142
|
-
globalThis.
|
|
143
|
-
globalThis.
|
|
145
|
+
// make this "idempotent" for now until a better idea comes along - https://github.com/ProjectEvergreen/wcc/discussions/145
|
|
146
|
+
globalThis.addEventListener = globalThis.addEventListener ?? noop;
|
|
147
|
+
globalThis.document = globalThis.document ?? new Document();
|
|
148
|
+
globalThis.customElements = globalThis.customElements ?? new CustomElementsRegistry();
|
|
149
|
+
globalThis.HTMLElement = globalThis.HTMLElement ?? HTMLElement;
|
package/src/jsx-loader.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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
|
|
5
|
+
import { generate } from '@projectevergreen/escodegen-esm';
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import jsx from 'acorn-jsx';
|
|
8
8
|
import { parse, parseFragment, serialize } from 'parse5';
|
|
@@ -38,6 +38,7 @@ export function getParser(moduleURL) {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// replace all instances of __this__ marker with relative reference to the custom element parent node
|
|
41
42
|
function applyDomDepthSubstitutions(tree, currentDepth = 1, hasShadowRoot = false) {
|
|
42
43
|
try {
|
|
43
44
|
for (const node of tree.childNodes) {
|
|
@@ -50,9 +51,9 @@ function applyDomDepthSubstitutions(tree, currentDepth = 1, hasShadowRoot = fals
|
|
|
50
51
|
const { value } = attrs[attr];
|
|
51
52
|
|
|
52
53
|
if (value.indexOf('__this__.') >= 0) {
|
|
53
|
-
const root = hasShadowRoot ? '
|
|
54
|
+
const root = hasShadowRoot ? '.getRootNode().host' : `${'.parentElement'.repeat(currentDepth)}`;
|
|
54
55
|
|
|
55
|
-
node.attrs[attr].value = value.replace(/__this__/g, `this${
|
|
56
|
+
node.attrs[attr].value = value.replace(/__this__/g, `this${root}`);
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
}
|
|
@@ -265,8 +266,26 @@ export function parseJsx(moduleURL) {
|
|
|
265
266
|
|
|
266
267
|
applyDomDepthSubstitutions(elementTree, undefined, hasShadowRoot);
|
|
267
268
|
|
|
268
|
-
const
|
|
269
|
-
|
|
269
|
+
const serializedHtml = serialize(elementTree);
|
|
270
|
+
// we have to Shadow DOM use cases here
|
|
271
|
+
// 1. No shadowRoot, so we attachShadow and append the template
|
|
272
|
+
// 2. If there is root from the attachShadow signal, so we just need to inject innerHTML, say in an htmx
|
|
273
|
+
// could / should we do something else instead of .innerHTML
|
|
274
|
+
// https://github.com/ProjectEvergreen/wcc/issues/138
|
|
275
|
+
const renderHandler = hasShadowRoot
|
|
276
|
+
? `
|
|
277
|
+
const template = document.createElement('template');
|
|
278
|
+
template.innerHTML = \`${serializedHtml}\`;
|
|
279
|
+
|
|
280
|
+
if(!${elementRoot}) {
|
|
281
|
+
this.attachShadow({ mode: 'open' });
|
|
282
|
+
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
|
283
|
+
} else {
|
|
284
|
+
this.shadowRoot.innerHTML = template.innerHTML;
|
|
285
|
+
}
|
|
286
|
+
`
|
|
287
|
+
: `${elementRoot}.innerHTML = \`${serializedHtml}\`;`;
|
|
288
|
+
const transformed = acorn.parse(renderHandler, {
|
|
270
289
|
ecmaVersion: 'latest',
|
|
271
290
|
sourceType: 'module'
|
|
272
291
|
});
|
|
@@ -300,19 +319,11 @@ export function parseJsx(moduleURL) {
|
|
|
300
319
|
for (const line of tree.body) {
|
|
301
320
|
// test for class MyComponent vs export default class MyComponent
|
|
302
321
|
if (line.type === 'ClassDeclaration' || (line.declaration && line.declaration.type) === 'ClassDeclaration') {
|
|
303
|
-
|
|
304
|
-
? line.body.body
|
|
305
|
-
: line.declaration.body.body;
|
|
306
|
-
for (const method of children) {
|
|
307
|
-
if (method.key.name === 'constructor') {
|
|
308
|
-
insertPoint = method.start - 1;
|
|
309
|
-
break;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
322
|
+
insertPoint = line.declaration.body.start + 1;
|
|
312
323
|
}
|
|
313
324
|
}
|
|
314
325
|
|
|
315
|
-
let newModuleContents =
|
|
326
|
+
let newModuleContents = generate(tree);
|
|
316
327
|
|
|
317
328
|
// TODO better way to determine value type?
|
|
318
329
|
/* eslint-disable indent */
|
|
@@ -380,7 +391,7 @@ export async function load(url, context, defaultLoad) {
|
|
|
380
391
|
|
|
381
392
|
return {
|
|
382
393
|
format: 'module',
|
|
383
|
-
source:
|
|
394
|
+
source: generate(jsFromJsx),
|
|
384
395
|
shortCircuit: true
|
|
385
396
|
};
|
|
386
397
|
}
|
package/src/wcc.js
CHANGED
|
@@ -4,7 +4,7 @@ import './dom-shim.js';
|
|
|
4
4
|
|
|
5
5
|
import * as acorn from 'acorn';
|
|
6
6
|
import * as walk from 'acorn-walk';
|
|
7
|
-
import
|
|
7
|
+
import { generate } from '@projectevergreen/escodegen-esm';
|
|
8
8
|
import { getParser, parseJsx } from './jsx-loader.js';
|
|
9
9
|
import { parse, parseFragment, serialize } from 'parse5';
|
|
10
10
|
import fs from 'fs';
|
|
@@ -94,7 +94,7 @@ function registerDependencies(moduleURL, definitions, depth = 0) {
|
|
|
94
94
|
definitions[tagName] = {
|
|
95
95
|
instanceName: args[1].name,
|
|
96
96
|
moduleURL,
|
|
97
|
-
source:
|
|
97
|
+
source: generate(tree),
|
|
98
98
|
url: moduleURL,
|
|
99
99
|
isEntry
|
|
100
100
|
};
|
|
@@ -134,9 +134,7 @@ async function initializeCustomElement(elementURL, tagName, attrs = [], definiti
|
|
|
134
134
|
|
|
135
135
|
// https://github.com/ProjectEvergreen/wcc/pull/67/files#r902061804
|
|
136
136
|
const { pathname } = elementURL;
|
|
137
|
-
const element = tagName
|
|
138
|
-
? customElements.get(tagName)
|
|
139
|
-
: (await import(pathname)).default;
|
|
137
|
+
const element = customElements.get(tagName) ?? (await import(pathname)).default;
|
|
140
138
|
const dataLoader = (await import(pathname)).getData;
|
|
141
139
|
const data = props
|
|
142
140
|
? props
|