-
Notifications
You must be signed in to change notification settings - Fork 374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exploration: HTML Module Imports and Exports #1059
Comments
I tend to think that importing and doing things with the imports should be separated. Then you have a general mechanism for defining and consuming identifiers, and many features can plug into either side of that. So I'd prefer to have an import just define a identifiers*, and something like a scoped custom element registry just use those identifiers. I don't think an import should also define a custom elements - that seems too coupled. Importing identifiersAn import could allow you to import one or more identifiers from the module, and define a identifiers in some HTML scope: <import src="./element-one.html" imports="element-one element-two"></import> Because we want an equivalence between JS and HTML modules, this should work with JS too: <import src="./element-one.js" type="js" imports="element-one element-two"></import> I think this might be a reason to not use fragments in the imports. Though maybe when you import from JS, the fragments just reference exports? Note that Another option is to just use the existing <script src="./element-one.html" type="html" imports="element-one"></script> If we want to meet the use case of running with JavaScript disabled, could we say that
Declaring local identifiersWe want HTML modules to be able to be be bundled and to just naturally be able to contain multiple component definitions. So we don't want to have to import an identifier, but also be able to declare them locally: <!-- Defines, but does not register, a custom element class -->
<define id="element-one">...</define> Note: I'm using the Using identifiersOnce we have a way to import or declare identifiers, we have unlocked HTML use-cases for non-side-effectful HTML modules. We can import a definition and register it ourselves: <script src="./element-one.html" type="html" imports="element-one"></script>
<define id="element-two">
<registry>
<define tagname="el-one" from="element-one">
</registry>
<template>
<el-one></el-one>
</template>
</define> Note I'm overloading We could also just import templates from a separate file: <script src="./template.html" type="html" imports="template-one"></script>
<define id="element-one">
<template from="template-one"></template>
</define> Or maybe: <script src="./template.html" type="html" imports="template-one"></script>
<define id="element-one" template="template-one">
</define> |
@EisenbergEffect For script tag(s) inside of a module, are you still thinking it could utilize the |
@jaredcwhite Yes, I was thinking we could still use that in script tags within the module. |
import {
pricingCard,
sharedHeaderStyles,
sharedHeader
} from "./my-resources.html" with { type: "html" }; You are importing a CSSStyleSheet from sharedHeaderStyles as type: “html”, which seems weird. |
@o-t-w I agree. It's a bit of a quandry. While it would be more consistent for that to be a style element, what would be practically useful is a CSSStyleSheet. |
I disagree about decoupling of import and registering in the scope. It creates extra unused namespace and problems with name scope negotiation and recognizing by develloper. All languages (try to find otherwise) including JS have import and mapping to name in the same statement. The following convention would fix this irrationality: <define id="element-two">
<script src="./element-one.html" type="html" imports="element-one"></script>
<define tagname="el-one" from="element-one">
<template>
<el-one></el-one>
</template>
</define> Not in favor of tag names though. It overlaps with standard ones (XSLT) wthout inheriting its semantics and syntax. |
I could see enabling decoupled import and define but I also would prefer to be able to do it in one go. That seems like it would be the most common need: <import src="./some-module.html#MyElement" tag="my-element">
<my-element>Hello</my-element>
<my-element>World</my-element> I think I would also want to be able to import and use a template in one go as well. <template src="./some-module.html#myTemplate">
Fallback content while the template is being loaded.
</template> Could we get this working for declarative shadow dom too? <template id="helloTemplate">Hello <slot></slot>!</template>
<say-hello>
<template shadowrootmode="open" src="#helloTemplate"></template>
World
</say-hello>
<say-hello>
<template shadowrootmode="open" src="#helloTemplate"></template>
Web Components
</say-hello> |
what problems, exactly? The reason we should separate importing a thing from registering a custom element is that they will necessarily be different namespaces. From an HTML module we could import a DOM node, say a template or a reference to a custom element, and we need a way to refer to those as well as potentially register custom elements. If the import statement registers elements, it'll need two features - one for importing an identifier, another for registering custom elements. This is too coupled. We'd still need a way to register elements by HTML reference that aren't from an import - like a local definition. |
Fragments already have existing behaviour with JS imports, in particular they trigger multiple evaluations (without triggering multiple fetches compared to using query params): // mod.js
console.log(import.meta.url); await import("./mod.js#one"); // Prints BASEURL/mod.js#one
await import("./mod.js#two"); // Prints BASEURL/mod.js#two I think it would be preferable to have consistent behaviour rather than special casing import behaviour inside HTML. It'd probably be best if One problem that needs to be resolved is how to handle import attributes, in JS import attributes are required to import non-JS things like CSS. There's not really anyway to avoid these in HTML either as they define headers and the request type for the associated fetch. It does seem cumbersome to have to specify with attributes in many cases though, if we import per element kind as suggested by @justinfagnani then we could have defaults: <!-- default behaviour imports the default import with attributes { type: "css" }-->
<style src="./path-to-css-module.css"></style>
<!-- importname and type can be customized with attributes -->
<style src"./path-to-js-module.js" import="cssSheet" importtype="js"></style>
<style src="./path-to-html-module.html" import="cssSheet" importtype="html"></style> |
Why this over |
Would it be better to initially focus on standardising how HTML modules work with import attributes in JavaScript before adding new syntax? Also, I was re-reading the original GitHub issue for HTML modules and this reply seemed worth pondering as an alternative to separate using separate Id and export. #645 (comment) |
Something that still needs to be considered for HTML modules is how to manage base urls for templates in HTML modules. For example if you're using a template from an HTML module to make a custom element like: <!-- template.html -->
<template export="default">
<link rel="stylesheet" href="./styles.css" />
<div id="someThing"></div>
</template> import elTemplate from "./template.html";
class MyElement extends HTMLElement {
#shadowRoot = this.#attachShadow({ mode: "closed" });
constructor() {
super();
// OOPS the links are relative to the CURRENT document not to template.html
this.#shadowRoot.append(elTemplate.content.cloneNode(true));
}
} then any links in your associated templates will refer to the current document not the document containing those templates. |
Could |
Background
Extensive discussion on Declarative Custom Elements has brought the CG to a point of realizing that a good starting point would be HTML Modules. HTML Modules would serve as a container for DCEs, but also other reusable HTML resources, such as templates.
This being the case, we need to focus in on exactly how exports AND imports would work. While the export side of HTML Modules has been explored previously, the import side is largely undefined.
Strawman
In order to get conversation going, I'll propose a few basic ideas here.
HTML Module Exports
I'll begin by submitting that there are five possible export types:
To export any one of these, they must have an
id
, which will become the symbol under which the export is provided, and they must have theexport
attribute. Elements with only anid
can be referenced within the module, but are not able to be imported. Bothid
andexport
are required for that.Here are a few examples.
The above types exist today, but in the future we could also add
<element>
(or some other tag) for declarative custom elements and<registry>
for a declarative custom element registry.Importing HTML Modules in JS
Given the above example, we could import in JavaScript, using import assertions.
You should not be able to import the entire document, as that would break the encapsulation of the module. You should only be able to import elements with an
id
and anexport
attribute. We could also enable an element with anexport
and noid
to be imported as the default export.Importing HTML Modules in HTML
Importing into HTML involves several tasks:
HTML already provides a way to declaratively use a custom element, but not a way to declaratively use a template or fragment. So, we'll need to add something new here.
We also have no way to declaratively define a custom element in a given HTML document or module.
First, let's tackle the issue of importing the resources from an HTML module. We could do that with an
<import>
element as such. Here are a few examples:The
from
attribute provides the path to the HTML module and also includes a fragment identifying the specific export. Theas
attribute provides a way to name the import within the current document or module. In the case of a template or fragment, this creates something with anid
denoted byas
. In the case of a custom element, it will calldefine
with the value ofas
for the tag name.To use an imported custom element, is no different than using any custom element:
Using a template or fragment is different. I propose adding a new element to handle this, named
compose
. It would look something like this:Note: We could enable the
src
attribute to both import and compose as a convenience. But I think importing and using are two different things and minimally need what is shown above.Open Question: Could
compose
be a processing instruction or something else besides an element? Ideally, the composition should not affect the dom structure. It's really only a location where the composition should occur.The above should all work when importing into documents or other HTML modules. One other scenario is worth mentioning: importing DCEs into a potential declarative custom element registry. That could look something like this:
And of course, you could import this entire registry into a document as follows:
This would define all the contained elements in the current document, using their specified tag names. To change the names, we could add a mapping, similar to the way export parts work in shadow dom.
Feedback Request
Ok, that's a rough, initial sketch of how we might put some pieces together. Let's start the conversation around whether this meets all the needs, what are the semantics, etc.
The text was updated successfully, but these errors were encountered: