investigate requirement of needing a `default export` for rendering entry points
Type of Change
- Question / Documentation
Summary
Currently, wcc requires a default export from the first entry point custom element definition it parses.
For example, is you do this
// src/index.js
import './components/footer.js';
import './components/header.js';
const template = document.createElement('template');
template.innerHTML = `
<wcc-header></wcc-header>
<h1>Hello!</h1>
<wcc-footer></wcc-footer>
</main>
`;
class Home extends HTMLElement {
connectedCallback() {
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
}
}
export { Home };
const { html } = await renderToString(new URL('./src/index.js', import.meta.url));
wcc will throw this error
file:///Users/owenbuckley/Workspace/github/repos/wcc/src/wcc.js:80
const elementInstance = new element(data); // eslint-disable-line new-cap
^
TypeError: element is not a constructor
at initializeCustomElement (file:///Users/owenbuckley/Workspace/github/repos/wcc/src/wcc.js:80:27)
at async renderToString (file:///Users/owenbuckley/Workspace/github/repos/wcc/src/wcc.js:99:27)
at async init (file:///Users/owenbuckley/Workspace/github/repos/wcc/build.js:38:22)
Details
Maybe it's because this doesn't call customElements.define, so we have no way to know the name? Perhaps that's the tradeoff
// THIS
customElement.define('wcc-header', Header);
// OR THIS?
export default Home
Not sure if this is the same thing, or supplemental to https://github.com/ProjectEvergreen/wcc/issues/117 so should review them both as part of doing either these tasks.
Somewhat related, we should better handle the case where you use a custom element, but not import it
// NO FOOTER IMPORT, but `wcc-footer` is still used in the HTML
import './components/header.js';
const template = document.createElement('template');
template.innerHTML = `
<wcc-header></wcc-header>
<h1>Hello!</h1>
<wcc-footer></wcc-footer>
`;
class Home extends HTMLElement {
connectedCallback() {
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
}
}
export { Home };
which will casue wcc to throw this error
[0] file:///Users/owenbuckley/Workspace/github/repos/wcc/src/wcc.js:23
[0] const { moduleURL } = deps[tagName];
[0] ^
[0]
[0] TypeError: Cannot destructure property 'moduleURL' of 'deps[tagName]' as it is undefined.
[0] at renderComponentRoots (file:///Users/owenbuckley/Workspace/github/repos/wcc/src/wcc.js:23:17)
[0] at renderComponentRoots (file:///Users/owenbuckley/Workspace/github/repos/wcc/src/wcc.js:36:13)
[0] at renderComponentRoots (file:///Users/owenbuckley/Workspace/github/repos/wcc/src/wcc.js:36:13)
[0] at async renderToString (file:///Users/owenbuckley/Workspace/github/repos/wcc/src/wcc.js:107:21)
[0] at async file:///Users/owenbuckley/Workspace/github/repos/wcc/build.js:16:28
I suppose I was just missing the obvious here at first, but unless we pick an explicit name (named imports) then default is the most standards compliant, unassuming API we could pick. Unless we specifically dictated it as something like
class EntryComponent extends HTMLElement { ... }
export { EntryComponent }
So, unless we try to guess / enforce names, default just removes the discussion entirely it seems.
But again, maybe there is another more appropriate hint I am missing here? Maybe I am overthinking it? 🤔