diff --git a/README.md b/README.md
index ee11c9ea..991e2b18 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ In addition, WCC hopes to provide a surface area to explore patterns around [str
## Key Features
1. Supports the following `HTMLElement` lifecycles and methods on the server side
+ - `constructor`
- `connectedCallback`
- `attachShadow`
- `innerHTML`
diff --git a/build.js b/build.js
index e4f99f94..ea57d40f 100644
--- a/build.js
+++ b/build.js
@@ -11,7 +11,9 @@ async function init() {
const distRoot = './dist';
const pagesRoot = './docs/pages';
const pages = await fs.readdir(new URL(pagesRoot, import.meta.url));
- const { html } = await renderToString(new URL('./docs/index.js', import.meta.url), false);
+ const { html } = await renderToString(new URL('./docs/index.js', import.meta.url), {
+ lightMode: true
+ });
// await fs.rm(distRoot, { recursive: true, force: true });
// await fs.mkdir('./dist', { recursive: true });
@@ -65,7 +67,7 @@ async function init() {
- Web Components Compiler (WCC)
+ WCC - Web Components Compiler
diff --git a/docs/components/footer.js b/docs/components/footer.js
index c77dddcb..8ad789d3 100644
--- a/docs/components/footer.js
+++ b/docs/components/footer.js
@@ -3,24 +3,20 @@ const template = document.createElement('template');
template.innerHTML = `
diff --git a/docs/components/navigation.js b/docs/components/navigation.js
index 2e68709b..7b8cb3b3 100644
--- a/docs/components/navigation.js
+++ b/docs/components/navigation.js
@@ -3,22 +3,21 @@ const template = document.createElement('template');
template.innerHTML = `
diff --git a/docs/pages/docs.md b/docs/pages/docs.md
index c520904d..6b7919f5 100644
--- a/docs/pages/docs.md
+++ b/docs/pages/docs.md
@@ -11,6 +11,11 @@ This function takes a `URL` to a JavaScript file that defines a custom element,
const { html } = await renderToString(new URL('./src/index.js', import.meta.url));
```
+#### Options
+
+`renderToString` also supports a second parameter that is an object, called `options`, which supports the following configurations:
+
+- `lightMode`: For more static outcomes (e.g. no declarative shadow DOM), this option will omit all wrapping `` tags when rendering out custom elements. Useful for static sites or working with global CSS libraries.
## Metadata
@@ -72,6 +77,7 @@ export async function getData() {
> _See our [examples page](/examples/) for more info._
## Conventions
+
- Make sure to define your custom elements with `customElements.define`
- Make sure to `export default` your custom element base class
- Avoid [touching the DOM in `constructor` methods](https://twitter.com/techytacos/status/1514029967981494280)
\ No newline at end of file
diff --git a/src/wcc.js b/src/wcc.js
index c08c4197..157aacdb 100644
--- a/src/wcc.js
+++ b/src/wcc.js
@@ -9,14 +9,14 @@ import fs from 'node:fs/promises';
let definitions;
-async function renderComponentRoots(tree) {
+async function renderComponentRoots(tree, includeShadowRoots = true) {
for (const node of tree.childNodes) {
if (node.tagName && node.tagName.indexOf('-') > 0) {
const { tagName } = node;
const { moduleURL } = definitions[tagName];
const elementInstance = await initializeCustomElement(moduleURL, tagName, node.attrs);
- const shadowRootHtml = elementInstance.getInnerHTML({ includeShadowRoots: true });
+ const shadowRootHtml = elementInstance.getInnerHTML({ includeShadowRoots });
const shadowRootTree = parseFragment(shadowRootHtml);
// TODO safeguard against non-declared custom elements, e.g. using
@@ -25,12 +25,12 @@ async function renderComponentRoots(tree) {
}
if (node.childNodes && node.childNodes.length > 0) {
- await renderComponentRoots(node);
+ await renderComponentRoots(node, includeShadowRoots);
}
// does this only apply to `` tags?
if (node.content && node.content.childNodes && node.content.childNodes.length > 0) {
- await renderComponentRoots(node.content);
+ await renderComponentRoots(node.content, includeShadowRoots);
}
}
@@ -92,18 +92,18 @@ async function initializeCustomElement(elementURL, tagName, attrs = []) {
return elementInstance;
}
-async function renderToString(elementURL, fragment = true) {
+async function renderToString(elementURL, options = {}) {
definitions = [];
+ const { lightMode = false } = options;
+ const includeShadowRoots = !lightMode;
const elementInstance = await initializeCustomElement(elementURL);
- const elementHtml = elementInstance.getInnerHTML({ includeShadowRoots: false });
+ const elementHtml = elementInstance.getInnerHTML({ includeShadowRoots });
const elementTree = parseFragment(elementHtml);
- const finalTree = await renderComponentRoots(elementTree);
-
- elementInstance.shadowRoot.innerHTML = serialize(finalTree);
+ const finalTree = await renderComponentRoots(elementTree, includeShadowRoots);
return {
- html: elementInstance.getInnerHTML({ includeShadowRoots: fragment }),
+ html: serialize(finalTree),
metadata: definitions
};
}
diff --git a/test/cases/config-light-mode/config-light-mode.spec.js b/test/cases/config-light-mode/config-light-mode.spec.js
new file mode 100644
index 00000000..8999bbf9
--- /dev/null
+++ b/test/cases/config-light-mode/config-light-mode.spec.js
@@ -0,0 +1,88 @@
+/*
+ * Use Case
+ * Run wcc against nested custom elements with nested declarative shadow dom and ensure no shadow is included
+ *
+ * User Result
+ * Should return the expected HTML output for all levels of element nesting.
+ *
+ * User Workspace
+ * src/
+ * components/
+ * navigation.js
+ * header.js
+ * pages/
+ * index.js
+ *
+ * Config
+ * {
+ * lightMode: true
+ * }
+ */
+
+import chai from 'chai';
+import { JSDOM } from 'jsdom';
+import { renderToString } from '../../../src/wcc.js';
+
+const expect = chai.expect;
+
+describe('Run WCC For ', function() {
+ const LABEL = 'Nested Custom Element w/ no using Light Mode configuration';
+ let dom;
+
+ before(async function() {
+ const { html } = await renderToString(new URL('./src/pages/index.js', import.meta.url), {
+ lightMode: true
+ });
+
+ dom = new JSDOM(html);
+ });
+
+ describe(LABEL, function() {
+ it('should not have one top level with an open shadowroot', function() {
+ expect(dom.window.document.querySelectorAll('template[shadowroot="open"]').length).to.equal(0);
+ expect(dom.window.document.querySelectorAll('template').length).to.equal(0);
+ });
+
+ describe('static page content', function() {
+ it('should have the expected static content for the page', function() {
+ expect(dom.window.document.querySelector('h1').textContent).to.equal('Home Page');
+ });
+ });
+
+ describe('custom header element with nested navigation element', function() {
+ let headerContentsDom;
+
+ before(function() {
+ headerContentsDom = new JSDOM(dom.window.document.querySelectorAll('header')[0].innerHTML);
+ });
+
+ it('should have a tag within the shadowroot', function() {
+ expect(dom.window.document.querySelectorAll('header').length).to.equal(1);
+ });
+
+ it('should have expected content within the tag', function() {
+ const content = headerContentsDom.window.document.querySelector('a h4').textContent;
+
+ expect(content).to.contain('My Personal Blog');
+ });
+
+ describe('nested navigation element', function() {
+ let navigationContentsDom;
+
+ before(function() {
+ navigationContentsDom = new JSDOM(headerContentsDom.window.document.querySelectorAll('wcc-navigation')[0].innerHTML);
+ });
+
+ it('should have a