wc-compiler 0.2.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +27 -43
  2. package/package.json +5 -2
  3. package/src/wcc.js +28 -23
package/README.md CHANGED
@@ -1,44 +1,17 @@
1
- # wcc
2
-
3
1
  <img src="https://merry-caramel-524e61.netlify.app/assets/wcc-logo.png" width="30%"/>
4
2
 
5
- > _Experimental Web Components compiler. It's Web Components all the way down!_ 🐢
6
-
7
- ## Overview
8
-
9
- **Web Components Compiler (WCC)** is a NodeJS package designed to make server-side rendering (SSR) of native Web Components easier. It can render (within reason 😅) your Web Component into static HTML leveraging [Declarative Shadow DOM](https://web.dev/declarative-shadow-dom/).
10
-
11
- It is not a static site generator or framework. It is focused on producing raw HTML from Web Components with the intent of being easily _integrated_ into a site generator or framework.
12
-
13
- > _The original motivation for this project was to create a [purpose built, lighter weight, alternative to puppeteer for SSR of `HTMLElement`](https://github.com/ProjectEvergreen/greenwood/issues/926) for the project [**Greenwood**](https://www.greenwoodjs.io/)._
14
-
15
- In addition, WCC hopes to provide a surface area to explore patterns around [streaming](https://github.com/thescientist13/wcc/issues/5) and serverless rendering, as well as acting as a test bed for the [Web Components Community Groups](https://github.com/webcomponents-cg) discussions around community protocols, like [hydration](https://github.com/thescientist13/wcc/issues/3).
16
-
17
- ## Key Features
3
+ # Web Components Compiler (WCC)
18
4
 
19
- 1. Supports the following `HTMLElement` lifecycles and methods on the server side
20
- - `constructor`
21
- - `connectedCallback`
22
- - `attachShadow`
23
- - `innerHTML`
24
- - `[get|set|has]Attribute`
25
- 1. Recursive rendering of nested custom elements
26
- 1. Optional Declarative Shadow DOM (for producing purely content driven static pages)
27
- 1. Metadata and runtime hints to support progressive hydration and lazy loading strategies
28
-
29
- ## Installation
30
-
31
- **wcc** can be installed from npm.
32
-
33
- ```shell
34
- $ npm install wc-compiler --save-dev
35
- ```
5
+ [![Netlify Status](https://api.netlify.com/api/v1/badges/e718eac2-b3bc-4986-8569-49706a430beb/deploy-status)](https://app.netlify.com/sites/merry-caramel-524e61/deploys)
6
+ [![GitHub release](https://img.shields.io/github/tag/ProjectEvergreen/wcc.svg)](https://github.com/ProjectEvergreen/wcc/tags)
7
+ ![GitHub Actions status](https://github.com/ProjectEvergreen/wcc/workflows/Master%20Integration/badge.svg)
8
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/ProjectEvergreen/wcc/master/LICENSE.md)
36
9
 
37
- ## Usage
10
+ > _Experimental Web Components compiler. It's Web Components all the way down!_ 🐢
38
11
 
39
- WCC exposes a few utilities to render your Web Components. Below is one example, with [full docs and more examples](https://wcc.greenwoodjs.io) available on the website.
12
+ ## How It Works
40
13
 
41
- 1. Given a custom element like so:
14
+ 1. Write a Web Component
42
15
  ```js
43
16
  const template = document.createElement('template');
44
17
 
@@ -68,17 +41,13 @@ WCC exposes a few utilities to render your Web Components. Below is one example
68
41
 
69
42
  customElements.define('wcc-footer', Footer);
70
43
  ```
71
-
72
- 1. Using NodeJS, create a file that imports `renderToString` and provide it the path to your web component
44
+ 1. Run it through the compiler
73
45
  ```js
74
46
  import { renderToString } from 'wc-compiler';
75
47
 
76
- const { html } = await renderToString(new URL('./path/to/footer.js', import.meta.url));
77
-
78
- console.debug({ html })
48
+ const { html } = await renderToString(new URL('./path/to/component.js', import.meta.url));
79
49
  ```
80
-
81
- 1. You will get the following HTML output that can be used in conjunction with your preferred site framework or templating solution.
50
+ 1. Get HTML!
82
51
  ```html
83
52
  <wcc-footer>
84
53
  <template shadowroot="open">
@@ -96,5 +65,20 @@ WCC exposes a few utilities to render your Web Components. Below is one example
96
65
  </wcc-footer>
97
66
  ```
98
67
 
68
+ ## Installation
69
+
70
+ **WCC** runs on NodeJS and can be installed from npm.
71
+
72
+ ```shell
73
+ $ npm install wc-compiler --save-dev
74
+ ```
75
+
76
+ ## Documentation
77
+
78
+ See our [website](https://merry-caramel-524e61.netlify.app/) for API docs and examples.
79
+
80
+ ## Motivation
81
+
82
+ **WCC** is not a static site generator, framework or bundler. It is focused on producing raw HTML from Web Components with the intent of being easily integrated into a site generator or framework, like [**Greenwood**](https://github.com/ProjectEvergreen/greenwood/), the original motivation for creating [this project](https://github.com/ProjectEvergreen/greenwood/issues/935).
99
83
 
100
- > _**Make sure to test in Chrome, or other Declarative Shadow DOM compatible browser, otherwise you will need to include the [DSD polyfill](https://web.dev/declarative-shadow-dom/#polyfill).**_
84
+ In addition, **WCC** hopes to provide a surface area to explore patterns around [streaming](https://github.com/ProjectEvergreen/wcc/issues/5) and serverless rendering, as well as acting as a test bed for the [Web Components Community Groups](https://github.com/webcomponents-cg) discussions around community protocols, like [hydration](https://github.com/ProjectEvergreen/wcc/issues/3).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wc-compiler",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Experimental native Web Components compiler.",
5
5
  "main": "src/wcc.js",
6
6
  "type": "module",
@@ -42,11 +42,14 @@
42
42
  "mocha": "^9.2.2",
43
43
  "nodemon": "^2.0.15",
44
44
  "prismjs": "^1.28.0",
45
- "simple.css": "^0.1.3",
45
+ "rehype-autolink-headings": "^6.1.1",
46
46
  "rehype-raw": "^6.1.1",
47
+ "rehype-slug": "^5.0.1",
47
48
  "rehype-stringify": "^9.0.3",
48
49
  "remark-parse": "^10.0.1",
49
50
  "remark-rehype": "^10.1.0",
51
+ "remark-toc": "^8.0.1",
52
+ "simple.css": "^0.1.3",
50
53
  "unified": "^10.1.2"
51
54
  }
52
55
  }
package/src/wcc.js CHANGED
@@ -10,7 +10,7 @@ import fs from 'node:fs/promises';
10
10
  let definitions;
11
11
 
12
12
  function getParse(html) {
13
- return html.indexOf('<html>') >= 0 || html.indexOf('<body>') || html.indexOf('<head>')
13
+ return html.indexOf('<html>') >= 0 || html.indexOf('<body>') >= 0 || html.indexOf('<head>') >= 0
14
14
  ? parse
15
15
  : parseFragment;
16
16
  }
@@ -23,26 +23,29 @@ function isCustomElementDefinitionNode(node) {
23
23
  && expression.callee.property.name === 'define';
24
24
  }
25
25
 
26
- async function renderComponentRoots(tree, includeShadowRoots = true) {
26
+ async function renderComponentRoots(tree) {
27
27
  for (const node of tree.childNodes) {
28
28
  if (node.tagName && node.tagName.indexOf('-') > 0) {
29
29
  const { tagName } = node;
30
30
  const { moduleURL } = definitions[tagName];
31
31
  const elementInstance = await initializeCustomElement(moduleURL, tagName, node.attrs);
32
-
33
- const shadowRootHtml = elementInstance.getInnerHTML({ includeShadowRoots });
34
- const shadowRootTree = parseFragment(shadowRootHtml);
35
-
36
- node.childNodes = node.childNodes.length === 0 ? shadowRootTree.childNodes : [...shadowRootTree.childNodes, ...node.childNodes];
32
+ const elementHtml = elementInstance.shadowRoot
33
+ ? elementInstance.getInnerHTML({ includeShadowRoots: true })
34
+ : elementInstance.innerHTML;
35
+ const elementTree = parseFragment(elementHtml);
36
+
37
+ node.childNodes = node.childNodes.length === 0
38
+ ? elementTree.childNodes
39
+ : [...elementTree.childNodes, ...node.childNodes];
37
40
  }
38
41
 
39
42
  if (node.childNodes && node.childNodes.length > 0) {
40
- await renderComponentRoots(node, includeShadowRoots);
43
+ await renderComponentRoots(node);
41
44
  }
42
45
 
43
46
  // does this only apply to `<template>` tags?
44
47
  if (node.content && node.content.childNodes && node.content.childNodes.length > 0) {
45
- await renderComponentRoots(node.content, includeShadowRoots);
48
+ await renderComponentRoots(node.content);
46
49
  }
47
50
  }
48
51
 
@@ -57,9 +60,14 @@ async function registerDependencies(moduleURL) {
57
60
  sourceType: 'module'
58
61
  }), {
59
62
  async ImportDeclaration(node) {
60
- const dependencyModuleURL = new URL(node.source.value, moduleURL);
63
+ const specifier = node.source.value;
64
+ const isBareSpecifier = specifier.indexOf('.') !== 0 && specifier.indexOf('/') !== 0;
65
+
66
+ if (!isBareSpecifier) {
67
+ const dependencyModuleURL = new URL(node.source.value, moduleURL);
61
68
 
62
- await registerDependencies(dependencyModuleURL);
69
+ await registerDependencies(dependencyModuleURL);
70
+ }
63
71
  },
64
72
  async ExpressionStatement(node) {
65
73
  if (isCustomElementDefinitionNode(node)) {
@@ -117,21 +125,21 @@ async function initializeCustomElement(elementURL, tagName, attrs = []) {
117
125
  return elementInstance;
118
126
  }
119
127
 
120
- async function renderToString(elementURL, options = {}) {
128
+ async function renderToString(elementURL) {
121
129
  definitions = [];
122
130
 
123
- const { lightMode = false } = options;
124
- const includeShadowRoots = !lightMode;
125
131
  const elementTagName = await getTagName(elementURL);
126
132
  const elementInstance = await initializeCustomElement(elementURL);
127
133
 
128
- const elementHtml = elementInstance.getInnerHTML({ includeShadowRoots });
134
+ const elementHtml = elementInstance.shadowRoot
135
+ ? elementInstance.getInnerHTML({ includeShadowRoots: true })
136
+ : elementInstance.innerHTML;
129
137
  const elementTree = getParse(elementHtml)(elementHtml);
130
- const finalTree = await renderComponentRoots(elementTree, includeShadowRoots);
131
- const html = !lightMode && elementTagName ? `
138
+ const finalTree = await renderComponentRoots(elementTree);
139
+ const html = elementTagName ? `
132
140
  <${elementTagName}>
133
141
  ${serialize(finalTree)}
134
- </${elementTagName}
142
+ </${elementTagName}>
135
143
  `
136
144
  : serialize(finalTree);
137
145
 
@@ -141,18 +149,15 @@ async function renderToString(elementURL, options = {}) {
141
149
  };
142
150
  }
143
151
 
144
- async function renderFromHTML(html, elements = [], options = {}) {
152
+ async function renderFromHTML(html, elements = []) {
145
153
  definitions = [];
146
154
 
147
- const { lightMode = false } = options;
148
- const includeShadowRoots = !lightMode;
149
-
150
155
  for (const url of elements) {
151
156
  await initializeCustomElement(url);
152
157
  }
153
158
 
154
159
  const elementTree = getParse(html)(html);
155
- const finalTree = await renderComponentRoots(elementTree, includeShadowRoots);
160
+ const finalTree = await renderComponentRoots(elementTree);
156
161
 
157
162
  return {
158
163
  html: serialize(finalTree),