Repository: ProjectEvergreen/wcc (js)

Issue:
handle usage of unregistered custom elements
## Type of Change

<!-- Delete what does not apply -->

- Enhancement

## Summary
Given this custom element definition, where `<wcc-navigiation><wcc-navigiation>` is used but not included via `import`
```js
const template = document.createElement('template');

template.innerHTML = `
  <header>
    <h1>Welcome to my website</h1>
    <wcc-navigation></wcc-navigation>
  </header>
`;

class Header extends HTMLElement {
  connectedCallback() {
    if (!this.shadowRoot) {
      this.attachShadow({ mode: 'open' });
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
  }
}

export { Header };

customElements.define('wcc-header', Header);
```

`wcc` will throw this error
```sh
yarn build
yarn run v1.22.17
$ node ./build.js
file:///Users/owenbuckley/Workspace/github/wcc/src/wcc.js:16
      const { moduleURL } = definitions[tagName];
              ^

TypeError: Cannot destructure property 'moduleURL' of 'definitions[tagName]' as it is undefined.
    at renderComponentRoots (file:///Users/owenbuckley/Workspace/github/wcc/src/wcc.js:16:15)
    at async renderComponentRoots (file:///Users/owenbuckley/Workspace/github/wcc/src/wcc.js:26:7)
    at async renderComponentRoots (file:///Users/owenbuckley/Workspace/github/wcc/src/wcc.js:26:7)
    at async renderComponentRoots (file:///Users/owenbuckley/Workspace/github/wcc/src/wcc.js:26:7)
    at async renderToString (file:///Users/owenbuckley/Workspace/github/wcc/src/wcc.js:102:21)
    at async init (file:///Users/owenbuckley/Workspace/github/wcc/build.js:15:20)
error Command failed with exit code 1.
```

## Details
It would be nice for `wcc` to try and handle this more gracefully, such as:
a)  failing with a more friendly error, and produce the tag name, since we at least know that
b) "skipping" over it and showing a warning message in the console

I think b) (or a combination of a + b) is the way to go, and if so, perhaps this information that could also be updated in `definitions` as part of the returned `metadata`?

----

As a nice to have, where we are setting new custom elements in `definitions`, e.g.
```js
definitions[tagName] = ...
```

We may want to check first ahead of running whatever logic got to that line, as a minimal optimization.

Pull request description:
Gracefully handle undefined custom elements with `console.warn` message
<!--
## Submitting a Pull Request
We love contributions and appreciate any help you can offer!
-->

## Related Issue
resolves #25

## Summary of Changes
1. If a customElement has not been defined, just show a warning message instead of failing outright

> _WARNING: customElement `<xxx-yyyyy>` is not defined.  You may not have imported it yet._

Patch:
diff --git a/src/wcc.js b/src/wcc.js
index 1451138..9a7f896 100644
--- a/src/wcc.js
+++ b/src/wcc.js
@@ -24,16 +24,21 @@ async function renderComponentRoots(tree, definitions) {
   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, definitions);
-      const elementHtml = elementInstance.shadowRoot
-        ? elementInstance.getInnerHTML({ includeShadowRoots: true })
-        : elementInstance.innerHTML;
-      const elementTree = parseFragment(elementHtml);
-
-      node.childNodes = node.childNodes.length === 0
-        ? elementTree.childNodes
-        : [...elementTree.childNodes, ...node.childNodes];
+
+      if (definitions[tagName]) {
+        const { moduleURL } = definitions[tagName];
+        const elementInstance = await initializeCustomElement(moduleURL, tagName, node.attrs, definitions);
+        const elementHtml = elementInstance.shadowRoot
+          ? elementInstance.getInnerHTML({ includeShadowRoots: true })
+          : elementInstance.innerHTML;
+        const elementTree = parseFragment(elementHtml);
+
+        node.childNodes = node.childNodes.length === 0
+          ? elementTree.childNodes
+          : [...elementTree.childNodes, ...node.childNodes];
+      } else {
+        console.warn(`WARNING: customElement <${tagName}> is not defined.  You may not have imported it yet.`);
+      }
     }
 
     if (node.childNodes && node.childNodes.length > 0) {


Test patch:
diff --git a/test/cases/nested-elements/src/pages/index.js b/test/cases/nested-elements/src/pages/index.js
index 2aa2bbc..06f1a7f 100644
--- a/test/cases/nested-elements/src/pages/index.js
+++ b/test/cases/nested-elements/src/pages/index.js
@@ -8,6 +8,9 @@ template.innerHTML = `
 
   <h1>Home Page</h1>
 
+  <!-- https://github.com/ProjectEvergreen/wcc/issues/25 -->
+  <x-undefined-test></x-undefined-test>
+
   <wcc-footer></wcc-footer>
 `;