Any news about the `<module>` element?

# Axel Rauschmayer (9 years ago)

Is there any news about <module> element, any proposal I could follow to stay up to date?

# caridy (9 years ago)

Axel, we have discussed the minimum set of requirements to get modules implemented by vendors asap (and there is certainly interest). Proposal for phase one:

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

What does this means?

  • no loader (if you need on-demand loading, you can insert script tags with type=module, similar to what we do today for scripts)
  • no hooks or settings (if you need more advanced features, you will have to deal with those manually)

Open questions:

  • how to fallback? ideally, we will need a way to detect modules support, equivalent to <noscript> in semantic.
  • we need to reserve some resolution rules to support mappings and hooks in the future (e.g.: import foo from "foo/mod.js" will not work because foo will require loader configs or hooks to be defined, while import foo from “./foo/mod.js” and import foo from “//cdn.com/foo/mod.js” will work just fine).

Again, these are the basic ideas to get modules implemented soon. There are many other quirks, we should probably post something before the next meeting.

# James Burke (9 years ago)

Also:

  • How does dynamic loading work in a web worker? In general, how does dynamic loading work when there is no DOM.
  • module IDs should not be paths with .js values (see package/main types of layout). Maybe that is what you meant by your second question.

Perhaps the module tag is a DOM implementation detail that backs the standardized API for dynamic loading, but seems odd to focus on that backing detail first. I am sure there is more nuance though, perhaps you were trying to give quick feedback, and if so, apologies for reading too much into it.

# caridy (9 years ago)

inline...

  • How does dynamic loading work in a web worker? In general, how does dynamic loading work when there is no DOM.

think about this as nodejs without NPM and core modules, where we can only do require(‘./path/to/something.js’) and require(‘/full/path/to/something/else.js’), and we can evolve from there. The web worker is definitely in our radar, we just don’t have a solution for it without the loader implementation (same for realms).

  • module IDs should not be paths with .js values (see package/main types of layout). Maybe that is what you meant by your second question.

As for the module IDs, yeah, we need some metadata to resolve module IDs, that metadata is probably just custom loader configurations (phase two of our proposal).

Perhaps the module tag is a DOM implementation detail that backs the standardized API for dynamic loading, but seems odd to focus on that backing detail first. I am sure there is more nuance though, perhaps you were trying to give quick feedback, and if so, apologies for reading too much into it.

Yeah, the idea is to get engines (V8 maybe) to implement what is spec’d in ES6 today, and get a very basic implementation of <script type=module>. Remember, the goal is to get this out there asap to start gathering feedback from the community, and this seems to be the minimum requirements.

The part that we need some help is the fallback mechanism (the <noscript> equivalent feature), we need to find a compelling solution for that.

# John Barton (9 years ago)

On Fri, Dec 19, 2014 at 8:55 PM, caridy <caridy at gmail.com> wrote:

think about this as nodejs without NPM and core modules, where we can only do require(‘./path/to/something.js’) and require(‘/full/path/to/something/else.js’), and we can evolve from there. The web worker is definitely in our radar, we just don’t have a solution for it without the loader implementation (same for realms).

This seems to imply that module loading would be a blocking, synchronous call. Such a change would have huge implications, just as the previous Loader's async dynamic API did in reverse. Either way I hope there will be some public debate on that issue so we come away believing the best choice was made.

# Caridy Patino (9 years ago)

It will not block, modules are async by nature. what make you think this proposal implies blocking?

# Matthew Robb (9 years ago)

​I think he was reading your examples using "require()" and thinking you were suggesting that the semantics would match.​

# John Barton (9 years ago)

Indeed that is what I was thinking.

A non-blocking <module> tag is a poor match to HTML, a declarative language where order of tags means order of parsing and rendering. Giving up this fundamental characteristic of HTML, in the long-shot effort to improve the apparent load time for some amateur Web sites, has become dogmatic so I suppose there is no value in discussing it.

A non-blocking <module> tag would also prevent experienced developers from controlling rendering through JS action. That means they will need to use <script> tags which we'd like to deprecate or we'd have to have a blocking form of <module>. We'll probably end up with the latter choice.

On the node side, require() is curiously synchronous given node's heavy emphasis on asynchronous IO. As with the browser <script> tag, the synchronous require() is the best choice for simplicity. But the synchronous semantics prevents optimizations on both platforms. An asynchronous root-module loading API in a next generation system opens new opportunities. I hope and expect we'll end up with an async option on node.

On balance I think a non-blocking <module> tag with optional blocking is reasonable.

However, the description of the browser loading as "require()" within a asynchronous <module> tag is really a complete departure from all the previous discussions. A system based on require() is not statically analyzable. I could go on, but really a shift to this extreme seems so unlikely that there must be some misunderstanding. Rather I assume that the content of the <module> tag will be ES6 code as we know it now and that we will have an additional dynamic loading API that will be asynchronous much like we had earlier this year. Exactly the same solution would work in node.

In other words, <script> and require() would not be used in future code, an async API would be available for root loading, and most developers most of the time would write synchronous code manipulating modules contents. If we are not heading in this direction I hope there will be more discussions in public.

# Domenic Denicola (9 years ago)

I think the only reason require was used is to draw your attention to the module resolution algorithm used in Node, and not at all to imply anything about the semantics.

# Caridy Patino (9 years ago)

correct, I should have clarified it.

# Caridy Patino (9 years ago)

John, think of <script defer src="mod.js"></script>.

For <script type=module>, async is implicit.

# Allen Wirfs-Brock (9 years ago)

What if you have a series of modules that need to be evaluated in sequential order? (Remember, that a module with no imports is the module worlds equivalent to a simple sequential script.). eg:

<script type="module">
   window.sequence = 10;
   console.log(window.sequence);
</script>

<script type="module">
   console.log(" should be 11: " + ++window.sequence);
</script>
<script type="module">
   console.log(" shoud be 12: " + ++window.sequence);
</script>
# Caridy Patino (9 years ago)

The problem is that those inline modules might import other modules, e.g.:

<script type="module">
   import foo from "./foo.js";
   window.sequence = 10;
   console.log(window.sequence);
</script>
<script type="module">
   console.log(" should be 11: " + ++window.sequence);
</script>

IMO they should be async (implicit and not configurable) and if the order should be preserved then we can explore the defer attribute, saying:

<script type="module" defer>
   import foo from "./foo.js";
   window.sequence = 10;
   console.log(window.sequence);
</script>
<script type="module" defer>
   console.log(" should be 11: " + ++window.sequence);
</script>

Although, I consider this an edge case, and in the majority of cases we will simply use modules that does not rely on any other online module in the page that defines global values, and if they have to share functionality, they can simply rely on a module, saying:

<script type="module">
   import {sequence} from "./mod.js";
   console.log(sequence);
</script>
<script type="module">
   import {sequence, increment} from "./mod.js";
   increment();
   console.log(" should be 11: " + sequence);
</script>

According to the current algorism in the specs, this should work just fine with the right sequence of execution.

# Domenic Denicola (9 years ago)

IMO order should always be preserved.

# James Burke (9 years ago)

On Fri, Dec 19, 2014 at 8:55 PM, caridy <caridy at gmail.com> wrote:

Yeah, the idea is to get engines (V8 maybe) to implement what is spec’d in ES6 today, and get a very basic implementation of <script type=module>. Remember, the goal is to get this out there asap to start gathering feedback from the community, and this seems to be the minimum requirements.

It would be fascinating to know why this is the prioritization. To me, this looks like trying to rush something to be implemented to claim some sort of progress. However, it is so limited and has so many questions around it. It is hard to see how it is worth the swirl in the community to introduce it.

The <module> tag is really a distraction. It is not needed to build a working system. If the following pieces were worked out, it can be skipped completely, and in the Worker case, needs to be skipped (I am sure you are aware of the coming Service Worker bliss, so not just a curious side issue):

  • What has been referred here as “module meta”. This also includes being able to dynamic import into the loader instance that loaded the module.
  • A loader.
  • Module inlining.

Those all exist in some form in existing ES5-based module systems, and all contribute to full module system. They do not need a <module> tag

to work. The browser use case definitely needs some new capabilities to allow a loader to work harmoniously with CORS/Content Security Policy, but that does not mean a <module> tag.

I would much rather see people’s time spent on those other items instead of swirling on a <module> tag.

# John Barton (9 years ago)

Couldn't the <module> tag be restricted to be the last tag inside <html>?

Then we don't need them to be async to avoid blocking rendering and the declarative order would more closely match the semantics. Multiple tags could be loaded in parallel and sequenced on execution.

# Anne van Kesteren (9 years ago)

On Sun, Dec 21, 2014 at 5:42 PM, James Burke <jrburke at gmail.com> wrote:

(I am sure you are aware of the coming Service Worker bliss, so not just a curious side issue):

I and some others have been advocating for service workers to run in strict mode by default, as well as having this be undefined so they could later be upgraded to be module compatible without requiring some new out-of-band switch. It hasn't really gotten much traction unfortunately.

# Allen Wirfs-Brock (9 years ago)

Wait a minute. "Strict mode" is not a runtime mode it is a lexical characteristic of a JS source file (or the source code of a function). You can this take an arbitrary JS file an say its going to be run in "strict mode".

You could say that the source code for a Service Worker must be a Module (which implies that it is strict mode) even in import and export statements aren't yet support. But this would be a bit more work for implementations as it means that top-level module semantics (top level declarations are module local) would have to be implemented.

# Allen Wirfs-Brock (9 years ago)

On Dec 20, 2014, at 6:21 PM, Caridy Patino wrote:

The problem is that those inline modules might import other modules, e.g.:

But because of the improved semantics (implicit strict, file local declaration scope, etc.) we really would like to see a world where people use type=modules for everything including very simple inline scripts that block rendering. Also, it's a fairly easy parse to determine whether a script source contains an import statement or not.

# Isiah Meadows (9 years ago)

I found this a while back, and can't seem to find any discussion or specification more up to date than this document until this thread.

I know it could be a little off topic, but just throwing it out there.

# Caridy Patino (9 years ago)

Isiah, yes, we are still trying to get some traction on that repo. we will be adding some docs related to this discussion soon.

# Caridy Patino (9 years ago)

Allen, that's my hope. It will be ideal, and it solves web workers and service workers use cases, but we need implementers to chime in.

# Isiah Meadows (9 years ago)

Awesome.

# Isiah Meadows (9 years ago)

From: Allen Wirfs-Brock <allen at wirfs-brock.com>

Wait a minute. "Strict mode" is not a runtime mode it is a lexical characteristic of a JS source file (or the source code of a function). You can this take an arbitrary JS file an say its going to be run in "strict mode".

You could say that the source code for a Service Worker must be a Module (which implies that it is strict mode) even in import and export statements aren't yet support. But this would be a bit more work for implementations as it means that top-level module semantics (top level declarations are module local) would have to be implemented.

A far as I know, much of the scope handling, sans strict mode, has been implemented in Node for years, so I don't expect it would be too extraordinarily hard to implement (particularly so for V8/consumers in this case). I may be wrong, though.

# Kevin Smith (9 years ago)

Open questions:

  • how to fallback? ideally, we will need a way to detect modules support, equivalent to <noscript> in semantic.

Is there much interest in a fallback option? I would think that the typical web shop would have little to no interest in dual-delivering both proper modules and transpiled modules.

(It seems to me that the logical next step is to get the front-end community to normalize authoring JS in ES6 modules and transpiling for delivery on the web. We're not there yet, unfortunately.)

  • we need to reserve some resolution rules to support mappings and hooks in the future (e.g.: import foo from "foo/mod.js" will not work because foo will require loader configs or hooks to be defined, while import foo from “./foo/mod.js” and import foo from “//cdn.com/foo/mod.js” will work just fine).

Those rules sound good to me. What's the open question?