wc-compiler 0.1.0 → 0.2.2

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 +8 -14
  2. package/package.json +1 -1
  3. package/src/wcc.js +55 -14
package/README.md CHANGED
@@ -1,24 +1,18 @@
1
1
  # wcc
2
2
 
3
- [![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)
4
- [![GitHub release](https://img.shields.io/github/tag/thescientist13/wcc.svg)](https://github.com/thescientist13/wcc/tags)
5
- ![GitHub Actions status](https://github.com/thescientist13/wcc/workflows/Master%20Integration/badge.svg)
6
- [![GitHub issues](https://img.shields.io/github/issues-pr-raw/thescientist13/wcc.svg)](https://github.com/thescientist13/wcc/issues)
7
- [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/thescientist13/wcc/master/LICENSE.md)
3
+ <img src="https://merry-caramel-524e61.netlify.app/assets/wcc-logo.png" width="30%"/>
8
4
 
9
- Experimental native Web Components compiler. (`<w⚙️⚙️/>`)
10
-
11
- > _It's Web Components all the way down._ 🐢
5
+ > _Experimental Web Components compiler. It's Web Components all the way down!_ 🐢
12
6
 
13
7
  ## Overview
14
8
 
15
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/).
16
10
 
17
- 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.
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.
18
12
 
19
- > _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/)._
13
+ > _The original motivation for this project was to create a [purpose built, lighter weight alternative to puppeteer for SSR of native `HTMLElement` based Web Components](https://github.com/ProjectEvergreen/greenwood/issues/935) for the project [**Greenwood**](https://www.greenwoodjs.io/)._
20
14
 
21
- 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).
15
+ 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).
22
16
 
23
17
  ## Key Features
24
18
 
@@ -42,7 +36,7 @@ $ npm install wc-compiler --save-dev
42
36
 
43
37
  ## Usage
44
38
 
45
- 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.
39
+ WCC exposes a few utilities to render your Web Components. Below is one example, with [full docs and more examples](https://merry-caramel-524e61.netlify.app/) available on the website.
46
40
 
47
41
  1. Given a custom element like so:
48
42
  ```js
@@ -79,12 +73,12 @@ WCC exposes a few utilities to render your Web Components. Below is one example
79
73
  ```js
80
74
  import { renderToString } from 'wc-compiler';
81
75
 
82
- const { html } = renderToString(new URL('./path/to/footer.js', import.meta.url));
76
+ const { html } = await renderToString(new URL('./path/to/footer.js', import.meta.url));
83
77
 
84
78
  console.debug({ html })
85
79
  ```
86
80
 
87
- 1. You will get the following html output that can be used in conjunction with your preferred site framework or templating solution.
81
+ 1. You will get the following HTML output that can be used in conjunction with your preferred site framework or templating solution.
88
82
  ```html
89
83
  <wcc-footer>
90
84
  <template shadowroot="open">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wc-compiler",
3
- "version": "0.1.0",
3
+ "version": "0.2.2",
4
4
  "description": "Experimental native Web Components compiler.",
5
5
  "main": "src/wcc.js",
6
6
  "type": "module",
package/src/wcc.js CHANGED
@@ -9,6 +9,20 @@ import fs from 'node:fs/promises';
9
9
 
10
10
  let definitions;
11
11
 
12
+ function getParse(html) {
13
+ return html.indexOf('<html>') >= 0 || html.indexOf('<body>') >= 0 || html.indexOf('<head>') >= 0
14
+ ? parse
15
+ : parseFragment;
16
+ }
17
+
18
+ function isCustomElementDefinitionNode(node) {
19
+ const { expression } = node;
20
+
21
+ return expression.type === 'CallExpression' && expression.callee && expression.callee.object
22
+ && expression.callee.property && expression.callee.object.name === 'customElements'
23
+ && expression.callee.property.name === 'define';
24
+ }
25
+
12
26
  async function renderComponentRoots(tree, includeShadowRoots = true) {
13
27
  for (const node of tree.childNodes) {
14
28
  if (node.tagName && node.tagName.indexOf('-') > 0) {
@@ -43,21 +57,22 @@ async function registerDependencies(moduleURL) {
43
57
  sourceType: 'module'
44
58
  }), {
45
59
  async ImportDeclaration(node) {
46
- const dependencyModuleURL = new URL(node.source.value, moduleURL);
60
+ const specifier = node.source.value;
61
+ const isBareSpecifier = specifier.indexOf('.') !== 0 && specifier.indexOf('/') !== 0;
62
+
63
+ if (!isBareSpecifier) {
64
+ const dependencyModuleURL = new URL(node.source.value, moduleURL);
47
65
 
48
- await registerDependencies(dependencyModuleURL);
66
+ await registerDependencies(dependencyModuleURL);
67
+ }
49
68
  },
50
69
  async ExpressionStatement(node) {
51
- const { expression } = node;
52
-
53
- if (expression.type === 'CallExpression' && expression.callee && expression.callee.object
54
- && expression.callee.property && expression.callee.object.name === 'customElements'
55
- && expression.callee.property.name === 'define') {
56
-
57
- const tagName = node.expression.arguments[0].value;
70
+ if (isCustomElementDefinitionNode(node)) {
71
+ const { arguments: args } = node.expression;
72
+ const tagName = args[0].value;
58
73
 
59
74
  definitions[tagName] = {
60
- instanceName: node.expression.arguments[1].name,
75
+ instanceName: args[1].name,
61
76
  moduleURL
62
77
  };
63
78
  }
@@ -65,6 +80,25 @@ async function registerDependencies(moduleURL) {
65
80
  });
66
81
  }
67
82
 
83
+ async function getTagName(moduleURL) {
84
+ const moduleContents = await fs.readFile(moduleURL, 'utf-8');
85
+ let tagName;
86
+
87
+ walk.simple(acorn.parse(moduleContents, {
88
+ ecmaVersion: 'latest',
89
+ sourceType: 'module'
90
+ }), {
91
+ async ExpressionStatement(node) {
92
+ if (isCustomElementDefinitionNode(node)) {
93
+
94
+ tagName = node.expression.arguments[0].value;
95
+ }
96
+ }
97
+ });
98
+
99
+ return tagName;
100
+ }
101
+
68
102
  async function initializeCustomElement(elementURL, tagName, attrs = []) {
69
103
  await registerDependencies(elementURL);
70
104
 
@@ -93,14 +127,21 @@ async function renderToString(elementURL, options = {}) {
93
127
 
94
128
  const { lightMode = false } = options;
95
129
  const includeShadowRoots = !lightMode;
96
-
130
+ const elementTagName = await getTagName(elementURL);
97
131
  const elementInstance = await initializeCustomElement(elementURL);
132
+
98
133
  const elementHtml = elementInstance.getInnerHTML({ includeShadowRoots });
99
- const elementTree = parseFragment(elementHtml);
134
+ const elementTree = getParse(elementHtml)(elementHtml);
100
135
  const finalTree = await renderComponentRoots(elementTree, includeShadowRoots);
136
+ const html = !lightMode && elementTagName ? `
137
+ <${elementTagName}>
138
+ ${serialize(finalTree)}
139
+ </${elementTagName}>
140
+ `
141
+ : serialize(finalTree);
101
142
 
102
143
  return {
103
- html: serialize(finalTree),
144
+ html,
104
145
  metadata: definitions
105
146
  };
106
147
  }
@@ -115,7 +156,7 @@ async function renderFromHTML(html, elements = [], options = {}) {
115
156
  await initializeCustomElement(url);
116
157
  }
117
158
 
118
- const elementTree = parse(html);
159
+ const elementTree = getParse(html)(html);
119
160
  const finalTree = await renderComponentRoots(elementTree, includeShadowRoots);
120
161
 
121
162
  return {