ES6 Loader proposed changes

David Herman dherman at mozilla.com
Fri Aug 29 13:22:12 PDT 2014


On Aug 28, 2014, at 10:10 AM, Ian Hickson <ian at hixie.ch> wrote:

> Here are the changes that would be needed to make the ES6 loader 
> infrastructure underpin the Web platform's loader infrastructure 
> (basically, placing the ES6 loader the management layer between the Web 
> platform APIs on top, and Service Workers underneath).
> ...
> If we can reuse this for the rest of the Web platform, it gives authors a 
> really uniform API for all loading operations.

The module loader isn't the right layer for the kinds of things you're trying to do. I understand you're trying to avoid having similar mechanisms at multiple layers of the platform, but repurposing the module loader system as a general purpose web resource loading system means conflating web resources and modules, which leads to a mixing of concerns exposed to the development model. Let me go into some more detail.

## What the `System` module loader is for

First, let's talk about what the module loader *is* for, and what the important requirements the browser's default `System` loader should address. In particular, I'm *not* saying that there should be *no* reflection of web assets to the JS module system.

### JS module name resolution

* *Basic name resolution for JS modules.* Refer to global and relative modules.

* *Module naming conventions that work across JS ecosystems.* Conventions that work on both client and server, particularly with npm.

Straw-examples:
```javascript
import _ from "underscore"; // global module
import spinner from "./spinner.js"; // relative module
```

### HTML interop

* *Making JS modules available to HTML.* Allow HTML files to import from JS.

Straw-examples:
```html
<!-- for now: -->
<script type="module">
import $ from "jquery";
import frobify from "/scripts/frobify.js";
$(".frob").each((i, elt) => {
  frobify(elt);
});
</script>

<!-- eventually, but we can save this discussion for another day: -->
<module>
import $ from "jquery";
// etc.
</module>
```

* *Reflecting web assets as JS modules.* Make web assets available to JS as modules that export a DOM element.

Straw-example:
```javascript
import icon from "./icon.png";
console.log(icon instanceof HTMLImageElement); // true
```


### Installing HTML/CSS components via module import

* *Applying CSS via import.* Mechanism for applying stylesheets by importing them from JS.

Straw-example:
```javascript
import "./main.css"; // guarantees CSS is loaded and applied by the time this module executes
import $ from "jquery";
$("#main").show();
```

* *Installing HTML imports via import.* Mechanism for installing HTML imports by importing them from JS.

Straw-example:
```javascript
import widget from "./widget.html";
console.log(widget instanceof DocumentFragment); // true
```


### Enriched response API

* *Cross-origin fetch.* Cross-origin requests should succeed by returning and opaque response, just as ServiceWorker does, that cannot be inspected but can be returned back into the pipeline for execution.

* *Streaming fetch.* It should be possible for a request to return a stream instead of a string, to allow asynchronous stream processing; this should use the API that results from the stream standardization process.


## What the `System` module loader is NOT for

* *Bundling requests.*

While it's attractive to use the loader for serving multiple assets in a single payload, this wants a more general solution for bundling arbitrary assets -- perhaps for performance (since despite the impending coolness of HTTP2, people will likely still use bundling as a successful optimization technique for a long time), but also for ergonomic deployment and inclusion. Jeni Tennison has done an excellent [proposal based on the link tag](https://github.com/w3ctag/packaging-on-the-web), and I've started exploring another approach based on URLs. Meanwhile people can experiment in userland with ServiceWorker-based techniques for bundling payloads.

* *Repurposing the module registry to reflect caching of arbitrary browser fetches.*

The registry is meant to record installation of things that are conceptually modules, not arbitrary network fetches of web assets. It's not the right place to store a cache of fetches. In particular, notice how above I said that it makes sense for modules to be able to import from, say, PNG files. In that case, the developer has explicitly requested that a web resource be reflected as a module, and it should be installed as a module. But using the registry for all web fetches means that non-JS is polluting the module registry with random modules reflecting arbitrary assets in the application.

* *Forcing resource management to be reflected as modules.*

The ServiceWorker API gives programmatic control over fetching of network payloads and a caching API. The module loader API gives programmatic control over fetching of modules and a caching API. What you're describing is using the latter for programmatic control over fetching and caching web assets, but that means you force them -- in the exposed API -- to be reflected as modules.

* *Exposing loaded assets in a permanent global table.*

The registry is a strongly mapped global table. If the DOM eliminates a resource that was loaded from the network, and that is necessarily installed in the module registry, it's pinned by the registry and uncollectable.


## What I suggest

It's still not completely clear to me what your use cases are, so I'm not sure exactly how much user-visible API you need. But if you are trying to reflect the browser's fetching policies and priorities based on different types of assets, then you are looking for a layer in between ServiceWorker and module loader -- call it an asset manager or resource manager. I'd be happy to discuss this with you and see if we can flesh out the use cases and requirements to get a better handle on the problem space.

Dave


More information about the es-discuss mailing list