diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 71d98a98..c8f83497 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -1,5 +1,4 @@
module.exports = {
- parser: '@babel/eslint-parser',
parserOptions: {
ecmaVersion: 2021,
sourceType: 'module'
diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml
index 580f4be5..226c3067 100644
--- a/.github/workflows/ci-win.yml
+++ b/.github/workflows/ci-win.yml
@@ -28,4 +28,4 @@ jobs:
yarn test --parallel
- name: Build
run: |
- yarn clean && yarn ssg
\ No newline at end of file
+ yarn clean && yarn build
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cbad6811..e28e38f9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,4 +28,4 @@ jobs:
yarn test --parallel
- name: Build
run: |
- yarn clean && yarn ssg
\ No newline at end of file
+ yarn clean && yarn build
\ No newline at end of file
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
new file mode 100644
index 00000000..79afee42
--- /dev/null
+++ b/.github/workflows/master.yml
@@ -0,0 +1,28 @@
+name: Master Integration
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+
+ build:
+ runs-on: ubuntu-18.04
+
+ strategy:
+ matrix:
+ node: [16]
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Use Node.js ${{ matrix.node }}
+ uses: actions/setup-node@v2
+ with:
+ node-version: ${{ matrix.node }}
+ - name: Installing project dependencies
+ run: |
+ yarn install --frozen-lockfile
+ - name: Build
+ run: |
+ yarn clean && yarn build
\ No newline at end of file
diff --git a/README.md b/README.md
index 2385ceb4..ee11c9ea 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,101 @@
# wcc
+[](https://app.netlify.com/sites/merry-caramel-524e61/deploys)
+[](https://github.com/thescientist13/wcc/tags)
+
+[](https://github.com/thescientist13/wcc/issues)
+[](https://raw.githubusercontent.com/thescientist13/wcc/master/LICENSE.md)
+
Experimental native Web Components compiler. (``)
+> _It's Web Components all the way down._ 🐢
+
## Overview
-It's Web Components all the way down. 🐢
+**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/).
+
+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.
+
+> _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/)._
+
+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).
+
+## Key Features
+
+1. Supports the following `HTMLElement` lifecycles and methods on the server side
+ - `connectedCallback`
+ - `attachShadow`
+ - `innerHTML`
+ - `[get|set|has]Attribute`
+1. Recursive rendering of nested custom elements
+1. Optional Declarative Shadow DOM (for producing purely content driven static pages)
+1. Metadata and runtime hints to support progressive hydration and lazy loading strategies
+
+## Installation
+
+TODO
+
+## Usage
+
+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.
+
+1. Given a custom element like so:
+ ```js
+ const template = document.createElement('template');
+
+ template.innerHTML = `
+
+
+
+ `;
+
+ class Footer extends HTMLElement {
+ connectedCallback() {
+ if (!this.shadowRoot) {
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(template.content.cloneNode(true));
+ }
+ }
+ }
+
+ export default Footer;
+
+ customElements.define('wcc-footer', Footer);
+ ```
+
+1. Using NodeJS, create a file that imports `renderToString` and provide it the path to your web component
+ ```js
+ import { renderToString } from 'xxx';
+
+ const { html } = renderToString(new URL('./path/to/footer.js', import.meta.url));
+
+ console.debug({ html })
+ ```
+
+1. You will get the following html output that can be used in conjunction with your preferred site framework or templating solution.
+ ```html
+
+
+
+
+
+
+
+ ```
+
-> _**Make sure to test in Chrome, or other Declarative Shadow DOM compatible browser.**_
\ No newline at end of file
+> _**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).**_
\ No newline at end of file
diff --git a/babel.config.cjs b/babel.config.cjs
deleted file mode 100644
index 9652bf76..00000000
--- a/babel.config.cjs
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
- plugins: ['@babel/plugin-syntax-top-level-await']
-};
\ No newline at end of file
diff --git a/build.js b/build.js
new file mode 100644
index 00000000..1f924d20
--- /dev/null
+++ b/build.js
@@ -0,0 +1,84 @@
+import fs from 'node:fs/promises';
+import rehypeStringify from 'rehype-stringify';
+import rehypeRaw from 'rehype-raw';
+import remarkParse from 'remark-parse';
+import remarkRehype from 'remark-rehype';
+import { unified } from 'unified';
+
+import { renderToString } from './src/wcc.js';
+
+async function init() {
+ const distRoot = './dist';
+ const pagesRoot = './docs/pages';
+ const pages = await fs.readdir(new URL(pagesRoot, import.meta.url));
+
+ // await fs.rm(distRoot, { recursive: true, force: true });
+ // await fs.mkdir('./dist', { recursive: true });
+ // await fse.copy('./www/assets', `${distRoot}/www/assets`);
+ // await fse.copy('./www/components', `${distRoot}/www/components`);
+ // await fse.copy('./docs/pages', `${distRoot}/www/pages`);
+
+ for (const page of pages) {
+ // for now, just repurposing the README for home page content
+ const isHomePage = page === 'index.md';
+ const pageLocation = isHomePage ? './README.md' : `${pagesRoot}/${page}`;
+ const markdown = await fs.readFile(new URL(pageLocation, import.meta.url), 'utf-8');
+ let content = (await unified()
+ .use(remarkParse)
+ .use(remarkRehype, { allowDangerousHtml: true })
+ .use(rehypeRaw)
+ .use(rehypeStringify)
+ .process(markdown)).value;
+
+ if (isHomePage) {
+ const contentFilter = content.substring(content.indexOf('