Skip to content
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

HTML Modules as a leaf node in the module graph or not? #805

Open
travisleithead opened this issue Apr 3, 2019 · 9 comments
Open

HTML Modules as a leaf node in the module graph or not? #805

travisleithead opened this issue Apr 3, 2019 · 9 comments
Assignees
Labels

Comments

@travisleithead
Copy link
Member

As noted by Ashley in discourse, one interesting point of view on HTML Modules is whether they should be considered leaf nodes in the module graph or not. As a leaf-node, only the HTML would be parsed and returned as a document without having any other side-effects (e.g., no running of script, loading of any resources, importing of any other content whatsoever).

This has certain simplifying advantages, but also drawbacks in terms of flexibility of the design.

@travisleithead
Copy link
Member Author

Note: in our conversations with customers, primarily Vue.js, they've noted that Single File Components really benefit from having HTML modules not be a leaf node, but be able to process the related JavaScript (and CSS) for a given component all in one HTML Module package.

@matthewp
Copy link

matthewp commented Apr 3, 2019

Can you expand upon the idea? Are you saying that an HTML module of:

<template> content </template>

<script type="module">
  // stuff here
</script>

That the script would not run? What would be the reason for having this restriction?

I would find this to be far less useful and pretty disappointing. The reason for wanting HTML modules is to have the script that defines the custom element in the same file as the HTML template (and without having to put the HTML template in JS some how).

@annevk
Copy link
Collaborator

annevk commented Apr 4, 2019

Depending on the CSP integration and whether or not we do sandboxing, you might be able to impose this on yourself as well.

@bahrus
Copy link

bahrus commented Apr 4, 2019

I work in a place that uses iFrames out the gazoo. Mostly to host client-centric applications (like extjs / angular / preact), sometimes to host server-side solutions. iFrames have been very powerful, but quite frustrating at the same time, so I'm really hoping HTML Modules can help us move to a much better solution.

I like the way sandboxed iFrames allow you to specify what features are allowed, including whether scripting is allowed. Maybe both models could be supported? I could see scenarios where a site doesn't want any dependency script to load on its own (if I squint hard enough).

I think it's critical that the HTML Module be able to specify dependencies, and that the consumer can resolve them properly (maybe with the help of import maps). Without that, the usefulness of HTML Modules goes way down in my opinion.

At the other extreme, it would be great if HTML Modules could be imported from a CDN just like js modules can.

Basically I want everything (but of course I want the browser to be secure, so I recognize there's a balancing act with CSP's and all that).

@bahrus
Copy link

bahrus commented Apr 6, 2019

I've proposed being able to add a src attribute to the template element. Maybe this could be used to import HTML only as a kind of leaf node, but let HTML Modules serve on an equal footing with ES Modules (as far as recursive imports).

@matthewp
Copy link

matthewp commented Apr 6, 2019

@bahrus Would you mind elaborating on the use-case any more? What is it about iframes that don't work for you? If you are willing to run the template in the top frame, then presumably it is a trusted source, no?

@bahrus
Copy link

bahrus commented Apr 6, 2019

Hi @matthewp

Over a decade ago, we wanted to solve lots of things with iframes. We were inspired by the iGoogle portal, and in fact used some of their code (like a pub/sub library for messaging between widgets). We are a fair size organization -- different teams working with different technology stacks, but with a need to integrate content together. So we embarked on using iFrames to do that. Each piece of functionality would be a small standalone widget which we could iframe in, just like the link above shows. Users could build their own workspaces of content they needed from a gallery.

Issues we ran into was terrible performance, largely because everyone was using a heavy framework (extjs, Silverlight, GWT, WebLogic server-side-centric applications, etc) which we were loading over and over again into the same window, and content was confined to a rectangle. Messaging between widgets using pub/sub was also quite limited. So we had to scale the concept way back, and just load the main view as an iframe. It was quite the debacle, which put me on a lookout for something better, and I've been pinning my hopes on web components ever since. Basically, the technology limitations forced us to adopt an entirely different integration model from what we really wanted.

Even now, within that scaled back portal, we are often finding ourselves sharing content from one application into another via iframes, just because it's a lot of work to recreate the content in each application (and we don't want to give each application access to the databases, etc).

Problems it solved really well was the ability of different content providers to have their own release schedule and manage their own dependencies. And styles from one app not affecting others. And allowing functions with the same name not causing interference. I.e. scope isolation. Also great is that each team can build and test their own content in standalone windows, without having to pull down everyone else's content. And sometimes we want users to be able to open that sub content without loading the entire portal.

Just the other day, I was talking to a group who is building a new complex application (converting over a native windows WPF application) and they want to break it down into separately hosted sub application "modules" so they could have separate release cycles. I recommended iFrames, but said to watch the web component space (hoping that HTML Modules will help).

The key is the federated model -- although it makes sense to share common libraries via the top frame, the application code needs to be managed by the content provider, not by the top frame. The people managing the top frame have to be extremely careful about making any changes.

Hope that makes sense. Would leaf node HTML Modules solve our conundrum? I don't see how, but I could be missing something.

@bahrus
Copy link

bahrus commented Apr 8, 2019

I forgot to mention another benefit of the iframe model, which I'm hoping web components can provide in a better way (while freeing us from the badly performing / overly complex client-side-centric, code-centric JavaScript monoculture which lack of HTML modules is promoting):

Although most individual workspaces got locked into a time warp due to framework lock-in, the portal container was able to switch from one popular framework to another in order to reduce the memory footprint and eliminate memory leaks, without content providers making any adjustments. And a few workspaces got the funding to do total rewrites of themselves from one framework to another (e.g. Silverlight replacements) It's kind of interesting that few if any workspaces ever were able to make the move from one version of the framework to the next, even, due to lack of funding and the amount of adjustments needed with each upgrade).

And new workspaces could be built with newer technologies (including a few with web components, which I'm hoping will break the lock-in issue just as IFrames did for the entire site)

@clshortfuse
Copy link

clshortfuse commented Mar 24, 2023

I have an expectation of this being the case: HTML importing inactive document fragments. Now that dust seems to be settling with the basic JSON/CSS import, it seems HTML would likely be next.

Currently, I have two instances where processing of the HTML as an active document would produce incorrect reaction. They actually sit in the same code:

<link _if={_showSlot} id=link rel=stylesheet href={fontLibrary} />
<svg _if="{showSVG}" id="svg" viewBox="{_computedViewBox}">
  <use id="use" _if="{svg}" href="{svg}" fill="currentColor"/>
  <path id="path" _if="{_computedSVGPath}" d="{_computedSVGPath}"/>
</svg>
<img _if={src} id=img
  disabled={disabled}
  alt={alt} src={src} srcset={srcset} sizes={sizes}
  crossorigin={crossOrigin} usemap={useMap} ismap={isMap}
  referrerpolicy={referrerPolicy} decoding={decoding} loading={loading}
  width={width} height={height}
/>
<slot id=icon class={fontClass} hidden={!_showSlot} aria-hidden=true></slot>

Context

I would like to move the above out of the JS and into an HTML file. That's how the CSS import works and it's easier to manage linting and minification when we don't try to put layout, styles, and scripts all into one file (JS). If we want everything in one file, we'd just use JS. Also inline styles are both less performance than CSS Stylesheets and we have CSP concerns about JS in HTML.

To the point:

  1. We would expect the <link> to not get process until the templating system has run its course since the [href] is invalid at the time of parsing, to be populated based on input later.

  2. We would expect the <img> to also no get process since the [src] is not valid yet.

Both cases are solved in HTML-in-JS by using an inactive document to generate a fragment later. If the fragment has <script> or has <styles> nodes, those don't "activate" until there are adopted into the active document. That happens when we take "template" fragment, and interpolate it into an "cloneable" fragment. That fragment then gets cloned into the shadow root.

root.append(this.cloneable.cloneNode(true));

Considering how Web Component only really trigger this process if they are present in the page (ie: just defining in registry doesn't immediately call these steps), it would be wasteful to have to import HTML and "activate" them during the definition phase of Web Components. If authors want to immediately trigger document fragment adoption, document.adoptNode(fragment) seems to work just fine:

let inactiveFragment = document.implementation.createHTMLDocument()
  .createRange().createContextualFragment('<img src="http://invalid" />');
/* Nothing happens */
document.adoptNode(inactiveFragment);
/* Browser throws error trying to fetch `http://invalid` */

I'd imagine the newer syntax would look like this:

import template from "./template.html" with { type: "html" };
document.adoptNode(template);

Again, you only need to call adoptNode unless you want it start processing immediately. Cloning the template into Element already on the active document will automatically adopt it: (eg: shadowRoot.append(template.cloneNodes(true))).


Minor edit: I had discussed the point of templating, but also fragments may want to include <link> resources with valid URLs. It may include links to large resources like font icons. It may not be performant to have the browser immediately start fetching those resources considering it may not even be known if the Custom Element in question is even going to be loaded. It makes sense for it to be opt-in via manual adoption.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants