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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wc-compiler",
3
- "version": "0.10.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
- "develop": "concurrently \"nodemon --watch src --watch docs -e js,md,css,html,jsx ./build.js\" \"http-server ./dist --open\"",
33
- "build": "node ./build.js",
34
- "serve": "node ./build.js && http-server ./dist --open",
35
- "start": "npm run develop",
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": "^22.0.0",
54
- "@rollup/plugin-json": "^4.1.0",
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": "^2.75.7",
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.customElementsRegistry = {};
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.customElementsRegistry[tagName] = BaseClass;
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[tagName];
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
- globalThis.addEventListener = noop;
141
- globalThis.document = new Document();
142
- globalThis.customElements = new CustomElementsRegistry();
143
- globalThis.HTMLElement = HTMLElement;
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 escodegen from 'escodegen';
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 ? 'parentNode.host' : 'parentElement';
54
+ const root = hasShadowRoot ? '.getRootNode().host' : `${'.parentElement'.repeat(currentDepth)}`;
54
55
 
55
- node.attrs[attr].value = value.replace(/__this__/g, `this${'.parentElement'.repeat(currentDepth - 1)}.${root}`);
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 finalHtml = serialize(elementTree);
269
- const transformed = acorn.parse(`${elementRoot}.innerHTML = \`${finalHtml}\`;`, {
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
- const children = !line.declaration
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 = escodegen.generate(tree);
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: escodegen.generate(jsFromJsx),
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 escodegen from 'escodegen';
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: escodegen.generate(tree),
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