Skip to content
This repository was archived by the owner on Jul 13, 2020. It is now read-only.

support <script type=module> within HTML imports #95

Closed
guybedford opened this issue Feb 9, 2014 · 26 comments
Closed

support <script type=module> within HTML imports #95

guybedford opened this issue Feb 9, 2014 · 26 comments

Comments

@guybedford
Copy link
Member

To summarize <script type=module>, there are two forms - an anonymous form with module code inside:

<script type="module">
  import 'x';
  x.y();
</script>

and a named form for loading from external module files:

<script type="module" src="some/module.js" />

In both cases, the parsing of the tag above can immediately trigger the fetch cycle for modules, which will in turn fetch their dependencies.

Execution itself is deferred only until the execution thread of the HTML page reaches that point in the page, just like a normal script tag.

This behaviour then needs to thread into HTML imports in the same way as for a script tag - that is with the delayed execution following the import readiness, and the parser triggering the tree loading.

@addyosmani
Copy link
Member

Are we limiting the scope of this to native imports or including Polymer polyfilled imports?

@guybedford
Copy link
Member Author

If it is possible to make it work for the polyfilled imports, without too much extra code, then I think we should enable that support.

@guybedford
Copy link
Member Author

Note that since this behaviour is not specified anywhere, it is important to ensure all decisions are carefully documented / communicated if we do implement this.

@nevir
Copy link
Contributor

nevir commented Mar 9, 2015

Digging up this old issue, 'cause I was just linked to it, and everyone's thoughts may have changed since then.


Really not a fan of relying on the parser-blocking behavior of imports to make that go. E.g. assuming that the <link rel="import" ...> has completed its load prior to executing the <script type="module">. Couple reasons:

  • Have to double declare the dependency is no fun
  • The polyfill won't work in the master document, but should everywhere else. (Going to be a frequent, and very confusing, gotcha)
  • Also, @arv's approach there is more limited in scope (the imported document also needs to declare a <script type="module">), so may not be usable wholesale.

We really need to be able to declare dependencies across content types (and make sure that the loader isn't imposing too many assumptions about JS-only source)

@guybedford
Copy link
Member Author

@nevir thanks for picking up on this discussion! Can you clarify what you mean about having to double declare the dependency?

The HTML imports polyfill cannot perfectly simulate the execution order either, so I guess it is exactly the same issue here? Would be interesting to hear how this has come up for users there. As far as I'm aware though, the inability for a polyfill to work has never been accepted as a valid argument for altering specified behaviour?

@guybedford
Copy link
Member Author

@nevir I've updated the top description here to reflect the latest information as I know it. Perhaps that clarifies some things?

@MajorBreakfast
Copy link

Why is type="module" needed? It only makes sense in conjunction with name="my-module" to me. Or is that just for the polyfill?

My hope is that

<script>
  System.import('main.js').catch(function(e) { console.error(e && e.stack || e)})
</script>

eventually becomes

<script src="main.js"></script>

and works everywhere, not just in HTML imports.

@guybedford
Copy link
Member Author

@MajorBreakfast yes that becomes <script type="module" src="main.js"></script>.

@MajorBreakfast
Copy link

@guybedford type="module" can be implicit by having import statements in the script, can't it?

@guybedford
Copy link
Member Author

No, script is completely different and stays backwards-compatible.

@probins
Copy link
Contributor

probins commented Apr 15, 2015

can I check what is being proposed here? At the moment, the loader is looking for script tags on DOMContentLoaded. As I understand the HTML Imports spec, this would not be fired until the imports and all nested/dependent imports were also loaded. So what is proposed here is that the function would also loop through all the import documents looking for module tags. Is that right? Would it know (or need to know) in which order to execute them?

One thing that occurs to me whilst thinking about processing web components is that the polyfill would not be able to execute any modules within a <template>, as these should only be executed when added to the main document.

@guybedford
Copy link
Member Author

@probins yes exactly, it would have to go through the logic as best as possible and execution order as well. Currently this feature is on hold, until there is real implementation pressure from web components users / authors.

@probins
Copy link
Contributor

probins commented Apr 15, 2015

To be honest, I would doubt whether most developers are even aware of the module tag. Web components is in much the same status as the module loader: people are still working out how best to use them. So to me this comes under 'nice to have' rather than 'must have', particularly as you can probably use System.import in a component just as well as <import>. And System.import should work in a <template> too (not tested; I'm just theorising :-)

@probins
Copy link
Contributor

probins commented Apr 28, 2015

and another thing this proposal wouldn't handle is HTML imports loaded dynamically. As the current code does System.module(), I suppose I could wrap the code in that.

@probins
Copy link
Contributor

probins commented May 4, 2015

ok, so here's my solution for components, which works with both static and dynamic HTML Imports, and allows for bundled/concatenated templates/scripts. I've added the following function (a variant of the existing one):

    System.componentCallback = function(e) {
      var scripts = e.target.import.getElementsByTagName('script');
      for (var i = 0; i < scripts.length; i++) {
        var script = scripts[i];
        if (script.type == 'module') {
          var source = script.innerHTML.substr(1);
          System.module(source)
          .catch(function(err) { setTimeout(function() { throw err; }); });
        }
      }
    };

This would be used as the onload callback on static links:
<link rel="import" href="...html" id="mylink" onload="System.componentCallback(event)">

For dynamic links (link injection), I'm using:

    function importComponent(element, href) {
      var frag = document.createDocumentFragment();
      var link = document.createElement('link');
      link.rel = 'import';
      link.href = href;
      link.id = element + 'link';
      link.addEventListener('load', System.componentCallback);
      frag.appendChild(link);
      document.head.appendChild(frag);
    }

A self-including component might look like:

<template id="mytemplate">
  <div>Hello world</div>
</template>

<script type="module">
  document.body.appendChild(document.importNode(document.getElementById('mylink').import
      .getElementById('mytemplate').content, true));
</script>

With scripts, you can do document.currentScript.parentDocument instead of having an id on the links, but this probably won't work for modules and definitely doesn't work in the polyfill. I couldn't think of a way of binding the component to the module, so you could use this to get the template; this seems to always be window.

I'm using CJS in my module, and this works fine.

One small issue I noticed when you have several components with an anonymous module is that the browser's devtools refers to them all as 'undefined', so when you click on the link from the console you always get the first one - makes debugging a tad problematic. Best to make sure your anonymous modules don't have any errors :-)

And of course you have to use Polymer's HTML Imports polyfill if you're not using Chrome/Opera or Firefox with flag set (and template tag isn't currently supported in IE).

@probins
Copy link
Contributor

probins commented Jun 6, 2015

my example above doesn't work if a component is requested twice, as the callback is executed twice (at least, that's the way Chrome has implemented it - not sure if that's a bug or not). The easiest way around this is to move the module code into a separate file, so it's imported with the loader. You could do <module src="..."> to execute this but this has no advantages that I can see over <script>System.import('...')</script>. One of the main advantages claimed for HTML Imports is that you combine html and script in one file, but if your code is in external modules, this is no longer the case.

In addition, there is no link between the module code and the imported document, as this isn't the ownerDocument. This is a pain if you have nested <link>s, as you have to loop through the whole tree to find the document that contains the template or other HTML that belongs with the module.

All in all, I've come to the conclusion that HTML Imports with modules instead of script tags aren't worth the bother, and have gone back to importing HTML from the modules with the text plugin and good ol innerHTML.

So, @guybedford no 'pressure' from me to implement this - continue to concentrate on the loader :-)

@Hypercubed
Copy link

@probins The componentCallback function is very useful. May I use it here: https://github.com/Hypercubed/systemjs-plugin-html

@probins
Copy link
Contributor

probins commented Jul 13, 2015

feel free 😄

@Hypercubed
Copy link

Hey folks, I'm having some success over here: https://github.com/Hypercubed/systemjs-plugin-html/ . One issue I am having is that SystemJS imports within a HTML import are relative to the root document url. Not sure how to fix that.

@guybedford
Copy link
Member Author

I guess you could consider supporting <script type=module src="module-name.js"> within the HTML imports shimming so that the module name would be resolved as System.import('module-name.js', htmlImportParentURL).

I've hesitated implementing the above in SystemJS as the semantics of the above, while likely, are still to be specified, but it may be an option.

@Hypercubed
Copy link

So right now I am calling System.module(sourceText) for each link[rel="import"]. If I understand what you mean I should run something that registers the src attribute as a dependency for links that have a src attribute? Ideally the plugin should somehow also register the dependencies of the <script type=module> inline scripts.

P.S. I have a branch that is building imports using polymer vulcanize: https://github.com/Hypercubed/systemjs-plugin-html/tree/vulcanize

@probins
Copy link
Contributor

probins commented Jul 17, 2015

I guess you could consider supporting <script type=module src="module-name.js"> within the HTML imports shimming so that the module name would be resolved as System.import('module-name.js', htmlImportParentURL)

but the discussion on the loader spec seems to conclude that modules loaded with a module tag would always be anonymous, so would be System.module not System.import. This is related to the issue I encountered when a component is included in more than one HTML Import: the polyfill doesn't know it's already loaded that one (as it's not in the registry) so loads (and executes) it again. This problem might well go away in native implementations though, as the browser would know it's already got it in cache.

IMHO the HTML Import spec needs revisiting/revising to define how they should be handled in a modular environment.

@guybedford
Copy link
Member Author

@probins
Copy link
Contributor

probins commented Jan 5, 2016

thanx for the link - that looks promising

@probins
Copy link
Contributor

probins commented Apr 6, 2016

and https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/uba6pMr-jec/tXdg6YYPBAAJ gives some info on status in Chrome/blink

@probins
Copy link
Contributor

probins commented Apr 9, 2016

ISTM you can close this with a "won't fix". <script type="module"> is being worked on in browsers and we're likely to see the first results later this year some time. Chrome/Blink/V8 seem to be working with the Web Components people to decide how to get HTML Imports to play nicely with that. So I don't see any need for the polyfill to do anything.

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

No branches or pull requests

6 participants