WebReflection / uce-template

A Vue 3 inspired Custom Elements toolless alternative.
https://medium.com/@WebReflection/a-new-web-components-wonder-f9e042785a91
ISC License
106 stars 7 forks source link

Relative `.uce` imports #13

Open brettz9 opened 2 years ago

brettz9 commented 2 years ago

From https://github.com/WebReflection/uce-template/blob/master/extra-details.md :

Modules can also be loaded at runtime, but only if relative or if the CDN supports Cross Origin Requests

And there is this suggesting that one exception is the builtin (virtual) imports:

// provided by uce-template import reactive from '@uce/reactive';

...but what about the style of loading I see referenced at https://webcomponents.dev/edit/BtM0JrRI7aZjvKbgmUWt/stories/index.stories.js?p=stories , namely:

import "../src/my-counter.uce";

...where a .uce file is loaded by relative path.

When I try this from within a component script, I get:

Uncaught SyntaxError: unexpected token: identifier

Is the demo loader able to load the files with such an import because of some special complex set-up particular to that environment?

In any case, can I myself get such relative loading of ".uce" files out of the box (or do you have any suggestions or code samples if one can implement it oneself)?

I'd like to keep things modular without need of specifying .uce dependencies (beyond the top level ones) in a global file while still being able to use the .uce format in dependent modules.

WebReflection commented 2 years ago

the webcomponents.dev example is specific for webcomponents.dev toolchain/loader ... uce-template just assume HTML files, so it's your ide/BE that should return those files as HTML ... the second point in the README explains how to do that with VSCode https://github.com/WebReflection/uce-template#how-to--examples and the third one explain how to have a loader for .uce files ... basically it's all in the README and webcomponents.dev just applied those entries in such README.

brettz9 commented 2 years ago

the webcomponents.dev example is specific for webcomponents.dev toolchain/loader ... uce-template just assume HTML files, so it's your ide/BE that should return those files as HTML ...

BE == Browser environment?

the second point in the README explains how to do that with VSCode https://github.com/WebReflection/uce-template#how-to--examples

Thank you, yes. I added it in Atom thus:

"*":
  core:
    customFileTypes:
      "text.html.basic": [
        "html"
        "htm"
        "uce"
      ]

and the third one explain how to have a loader for .uce files ... basically it's all in the README and webcomponents.dev just applied those entries in such README.

Yes, thank you. The loading docs were helpful.

FWIW, here is what I was after. It allows for relative .uce file loading, thus allowing the main project to not care about what its trusted components' dependencies are (including potentially 3rd party ones), while still allowing those dependencies to have the option of exclusively using the .uce file format internally.

// Use rolled up versions to stay ESM-only while build-free after a one-time
//   build
import {parse} from './vendor/uce-template/esm/index.js';
import loader from './vendor/uce-loader/esm/index.js';

const localComponents = [
  'choose-work-section-paragraph'
];

// const remoteComponents = [
//   // Remote component names
//   'some-imported-component-at-a-reliable-location'
// ];

loader({
  // eslint-disable-next-line unicorn/no-useless-spread -- Placeholding
  known: new Map([
    ...localComponents.map((path) => [path, `./views/`])
    // , ...remoteComponents.map((path) => [
    //   path, './node_modules/exported-components/'
    // ])
  ]),
  async on (componentName) {
    if (this.known.has(componentName)) {
      const basePath = this.known.get(componentName);
      const componentPath = `${basePath}${componentName}.uce`;
      const topLevelComponent = await fetch(componentPath);

      const parseComponent = async (component) => {
        const definition = await component.text();
        const template = parse(definition);

        const script = template.content.querySelector('script');
        if (script) {
          const imports = [];
          script.textContent = script.textContent.replace(
            /^\s*import\s+(?<quote>["'])(?<path>\..*?\.uce)\k<quote>/gum,
            (_, __, ___, $, $$, groups) => {
              imports.push(
                fetch(new URL(groups.path, new URL(componentPath, location)))
              );
              return '';
            }
          );
          const subComponents = await Promise.all(imports);
          subComponents.forEach((subComponent) => {
            parseComponent(subComponent);
          });
        }
        document.body.append(template);
      };
      await parseComponent(topLevelComponent);
    }
  }
});

In order to take this a step further, so as to allow 3rd party modules to themselves support 3rd party modules without using error prone relative paths (i.e., to be able to use bare import specifiers like import "@someModule/somePackage/components/component.uce"), I figure this script could consult an import map, using the likes of https://www.npmjs.com/package/importly or https://www.npmjs.com/package/@jsenv/importmap-node-module to get a full map built, only needing this build step when updating 3rd party dependencies (it could be run in a prepare script). To find the component, it would, I expect, just need to look for the part of the matching import map path after the last node_modules.

Anyways, I know you gave instructions so people could build their own like this according to their own needs, but in order that "leaving it as an exercise to the reader" does not cause users to forego your excellent work in favor of much bloated tools which might, however, have the advantage of supporting full modularity, and so as to give users of uce-template of the benefit of potentially finding more compatible published components for their ready use, I didn't know if you might be open to advertising such an approach or providing one out of the box. Thanks!

WebReflection commented 2 years ago

BE as backend but maybe that’s irrelevant