preload, destinations, and module scripts #486

Closed
domenic opened this Issue Feb 9, 2017 · 48 comments

Comments

9 participants
@domenic
Member

domenic commented Feb 9, 2017

The problem

Given <link rel="preload" href="foo.js" as="script">, we don't know whether foo.js will be a classic script or module script. This means that we can't parse it ahead of time. Additionally, for module scripts I'd expect preloading the module script to also preload its dependencies.

Potential solutions

We could solve this in one of two ways, as far as I can see:

  1. <link rel="preload" href="foo.js" as="module"> or some other similar destination (e.g. modulescript)
  2. <link rel="preload" href="foo.js" as="script" type="module">

(2) was initially rather attractive to me, for matching the syntax used by the <script> element. Note that there's no conflict with the existing semantic of type="" being a MIME type, since in practice that is not used by any part of the HTML or preload specs.

But now I am less sure. It seems hard to spec it in a clean way. We'd either need to thread the type="" metadata through all the fetch locations (including e.g. service worker, not just the preload spec, right?), or we'd need to create some sort of "shadow destination", so breaking the 1:1 mapping of as="" and request destinations. That sounds not great.

I'd love thoughts on the best way to go here. If (1) is the way to go then we can just Bikeshed the destination name and update HTML to use it when fetching module scripts.

/cc @addyosmani @yoavweiss @whatwg/modules

@yoavweiss

This comment has been minimized.

Show comment
Hide comment
@yoavweiss

yoavweiss Feb 9, 2017

Collaborator

I strongly prefer (1) as I think (2) can cause serious confusion, even if we'd find a way to cleanly spec it.

One example where type is currently used (and encouraged) is:
<link href="foo.woff2" rel=preload as=font type="font/woff2">

I think it won't be trivial to explain why type means one thing for some as values, but something else for others.

Collaborator

yoavweiss commented Feb 9, 2017

I strongly prefer (1) as I think (2) can cause serious confusion, even if we'd find a way to cleanly spec it.

One example where type is currently used (and encouraged) is:
<link href="foo.woff2" rel=preload as=font type="font/woff2">

I think it won't be trivial to explain why type means one thing for some as values, but something else for others.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 9, 2017

Member

I disagree on the explanatory issues, but setting that aside for now, why is type used and encouraged there when the preload spec does not use type="" at all? Are implementations doing nonstandard processing of the type="" attribute that is not in the spec?

Member

domenic commented Feb 9, 2017

I disagree on the explanatory issues, but setting that aside for now, why is type used and encouraged there when the preload spec does not use type="" at all? Are implementations doing nonstandard processing of the type="" attribute that is not in the spec?

@yoavweiss

This comment has been minimized.

Show comment
Hide comment
@yoavweiss

yoavweiss Feb 9, 2017

Collaborator

Hmm, you're right that type is missing from the "obtain the preload resource" processing model (It is present in "appropriated times" processing.)
I'll file an issue.

Collaborator

yoavweiss commented Feb 9, 2017

Hmm, you're right that type is missing from the "obtain the preload resource" processing model (It is present in "appropriated times" processing.)
I'll file an issue.

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Feb 10, 2017

Member

How would the type attribute translate into a fetch() call?

(The real problem here is that JavaScript modules don't have their own MIME type. If you want different parsing, you should communicate that there.)

Member

annevk commented Feb 10, 2017

How would the type attribute translate into a fetch() call?

(The real problem here is that JavaScript modules don't have their own MIME type. If you want different parsing, you should communicate that there.)

@yoavweiss

This comment has been minimized.

Show comment
Hide comment
@yoavweiss

yoavweiss Feb 10, 2017

Collaborator

@annevk - I think that varies based on the option we would choose to take.

Current preload implementation (and spec's spirit, despite processing model omissions that @domenic pointed out) treats type (and media) as a "barrier" before fetching the resource. If type is an unsupported media type, the resource should never be fetched. As such, I'm not sure how type should be translated to fetch(), but maybe we're missing a "does this mime type work for this resource type" primitive.

In the (2) option @domenic is proposing, type would have an impact on the resource fetching behavior (it will enable us to change the type of script we're fetching, which can impact ability to ahead-of-time processing, preloading dependencies, etc). That would mean that type should be piped into fetch() in these cases, but not necessarily in others (e.g. fonts and images, which use type to determine "do I preload this at all?")

Collaborator

yoavweiss commented Feb 10, 2017

@annevk - I think that varies based on the option we would choose to take.

Current preload implementation (and spec's spirit, despite processing model omissions that @domenic pointed out) treats type (and media) as a "barrier" before fetching the resource. If type is an unsupported media type, the resource should never be fetched. As such, I'm not sure how type should be translated to fetch(), but maybe we're missing a "does this mime type work for this resource type" primitive.

In the (2) option @domenic is proposing, type would have an impact on the resource fetching behavior (it will enable us to change the type of script we're fetching, which can impact ability to ahead-of-time processing, preloading dependencies, etc). That would mean that type should be piped into fetch() in these cases, but not necessarily in others (e.g. fonts and images, which use type to determine "do I preload this at all?")

@jakearchibald

This comment has been minimized.

Show comment
Hide comment
@jakearchibald

jakearchibald Feb 10, 2017

Collaborator

Somewhat related: We're investigating caching parsed versions of scripts when they're added to the cache API & we've hit a similar problem.

Our current plan is to parse as a script, then try parsing as a module if it bails. We wouldn't have to do this if the info was on the request object. But meh, we're just playing with ideas right now.

Collaborator

jakearchibald commented Feb 10, 2017

Somewhat related: We're investigating caching parsed versions of scripts when they're added to the cache API & we've hit a similar problem.

Our current plan is to parse as a script, then try parsing as a module if it bails. We wouldn't have to do this if the info was on the request object. But meh, we're just playing with ideas right now.

@yoavweiss

This comment has been minimized.

Show comment
Hide comment
@yoavweiss

yoavweiss Feb 10, 2017

Collaborator

@jakearchibald - IIUC, defining a module as the request's destination would solve both cases, right?

Collaborator

yoavweiss commented Feb 10, 2017

@jakearchibald - IIUC, defining a module as the request's destination would solve both cases, right?

@jakearchibald

This comment has been minimized.

Show comment
Hide comment
@jakearchibald

jakearchibald Feb 10, 2017

Collaborator

@yoavweiss only if there was a way of creating a request object with that destination.

Collaborator

jakearchibald commented Feb 10, 2017

@yoavweiss only if there was a way of creating a request object with that destination.

@yoavweiss

This comment has been minimized.

Show comment
Hide comment
@yoavweiss

yoavweiss Feb 10, 2017

Collaborator

Right... That seems like something we'd want to do anyway.
(Potentially by adding destination to RequestInit).

Talking to @slightlyoff last week, he mentioned that currently there's no way for SW to fetch a script as a script. So in terms of network priority, an altered script request will have a significantly lower priority than an equivalent unaltered request, which isn't great.

Collaborator

yoavweiss commented Feb 10, 2017

Right... That seems like something we'd want to do anyway.
(Potentially by adding destination to RequestInit).

Talking to @slightlyoff last week, he mentioned that currently there's no way for SW to fetch a script as a script. So in terms of network priority, an altered script request will have a significantly lower priority than an equivalent unaltered request, which isn't great.

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Feb 10, 2017

Member

From what I remember the problem with that was actually making sure it is indeed used for the stated purpose. It's not entirely clear to me preload guarantees that at the moment, probably in part because the preload cache is not defined...

Because if it's not used for the stated purpose, you can circumvent CSP to some extent, which uses these tags.

Member

annevk commented Feb 10, 2017

From what I remember the problem with that was actually making sure it is indeed used for the stated purpose. It's not entirely clear to me preload guarantees that at the moment, probably in part because the preload cache is not defined...

Because if it's not used for the stated purpose, you can circumvent CSP to some extent, which uses these tags.

@yoavweiss

This comment has been minimized.

Show comment
Hide comment
@yoavweiss

yoavweiss Feb 10, 2017

Collaborator

Doesn't an empty destination let you circumvent CSP in similar ways?

Preload's implementation in Blink/WebKit currently prevents that by using the same request pipelines that prevent such "type-mismatch reuse" on regular resource requests.
(so that <img src=foo><script src=foo></script> send out two different requests, at least as far as the rendering engine is concerned). I'm not aware of those pipelines and checks being specced anywhere.

Collaborator

yoavweiss commented Feb 10, 2017

Doesn't an empty destination let you circumvent CSP in similar ways?

Preload's implementation in Blink/WebKit currently prevents that by using the same request pipelines that prevent such "type-mismatch reuse" on regular resource requests.
(so that <img src=foo><script src=foo></script> send out two different requests, at least as far as the rendering engine is concerned). I'm not aware of those pipelines and checks being specced anywhere.

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Feb 10, 2017

Member

Empty should result in the ultimate fallback of CSP, so no, I don't think so.

And yeah, preload not being defined has been a known problem for a while, I wish it were fixed. (See also #354.)

Member

annevk commented Feb 10, 2017

Empty should result in the ultimate fallback of CSP, so no, I don't think so.

And yeah, preload not being defined has been a known problem for a while, I wish it were fixed. (See also #354.)

@yoavweiss

This comment has been minimized.

Show comment
Hide comment
@yoavweiss

yoavweiss Feb 10, 2017

Collaborator

Empty should result in the ultimate fallback of CSP, so no, I don't think so.

Oh, OK. I thought empty is subject to connect-src for some reason

Collaborator

yoavweiss commented Feb 10, 2017

Empty should result in the ultimate fallback of CSP, so no, I don't think so.

Oh, OK. I thought empty is subject to connect-src for some reason

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 15, 2017

Member

OK, so it sounds like there's more appetite for (1), a new destination. I can work on the appropriate spec PRs after we settle the remaining questions:

  1. Is it OK to have recursive preload behavior, i.e. it fetches the whole module script tree? Basically, I think that would be better, but maybe it doesn't fit with the conception of preload as "declarative fetch", so I wanted to check with people first.
  2. Do we also duplicate all the other script-type destinations? I.e. do we create "moduleserviceworker", "modulesharedworker", "moduleworker"? (Another reason why type=module seems nicer...)
  3. Is "module" a good destination name, or should it maybe be "modulescript"?
Member

domenic commented Feb 15, 2017

OK, so it sounds like there's more appetite for (1), a new destination. I can work on the appropriate spec PRs after we settle the remaining questions:

  1. Is it OK to have recursive preload behavior, i.e. it fetches the whole module script tree? Basically, I think that would be better, but maybe it doesn't fit with the conception of preload as "declarative fetch", so I wanted to check with people first.
  2. Do we also duplicate all the other script-type destinations? I.e. do we create "moduleserviceworker", "modulesharedworker", "moduleworker"? (Another reason why type=module seems nicer...)
  3. Is "module" a good destination name, or should it maybe be "modulescript"?
@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Feb 16, 2017

Member
  1. We don't do that for style, do we? Seems a little "magic".
  2. Module scripts in workers can be processed in the same way, so... no.
  3. I think it should be "modulescript".

It still seems cleaner to give module scripts their own MIME type, especially for other parts of the ecosystem (e.g., what if you put them in the Cache API or generate them dynamically), but I guess we're not going to go over that again?

Member

annevk commented Feb 16, 2017

  1. We don't do that for style, do we? Seems a little "magic".
  2. Module scripts in workers can be processed in the same way, so... no.
  3. I think it should be "modulescript".

It still seems cleaner to give module scripts their own MIME type, especially for other parts of the ecosystem (e.g., what if you put them in the Cache API or generate them dynamically), but I guess we're not going to go over that again?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 16, 2017

Member

We don't do that for style, do we? Seems a little "magic".

Indeed. But it's kind of crucial for usefully preloading modules, which are expected to be deep graphs. If we don't add this to preload, then preload is largely useless for modules, and we'll need to invent a new way of preloading module graphs. Maybe that would be for the best though, because then we can avoid this whole "new destination" business?

Module scripts in workers can be processed in the same way, so... no.

How can this be true? Why do we have separate destinations for script, serviceworker, sharedworker, and worker if they can all be processed the same way?

It still seems cleaner to give module scripts their own MIME type, especially for other parts of the ecosystem (e.g., what if you put them in the Cache API or generate them dynamically), but I guess we're not going to go over that again?

No, I don't think so.

Member

domenic commented Feb 16, 2017

We don't do that for style, do we? Seems a little "magic".

Indeed. But it's kind of crucial for usefully preloading modules, which are expected to be deep graphs. If we don't add this to preload, then preload is largely useless for modules, and we'll need to invent a new way of preloading module graphs. Maybe that would be for the best though, because then we can avoid this whole "new destination" business?

Module scripts in workers can be processed in the same way, so... no.

How can this be true? Why do we have separate destinations for script, serviceworker, sharedworker, and worker if they can all be processed the same way?

It still seems cleaner to give module scripts their own MIME type, especially for other parts of the ecosystem (e.g., what if you put them in the Cache API or generate them dynamically), but I guess we're not going to go over that again?

No, I don't think so.

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Feb 16, 2017

Member

@domenic I forgot that request destination and type are distinct for a reason. Ugh.

However, if you need to preload a graph, perhaps something like rel=modulepreload is better. That still doesn't allow a e.g. a service worker to just fetch a module script and the browser to start lazily compiling it though, so I still think we should consider the MIME type strongly. It's the only thing that's tightly coupled with the thing we care about, the response.

Member

annevk commented Feb 16, 2017

@domenic I forgot that request destination and type are distinct for a reason. Ugh.

However, if you need to preload a graph, perhaps something like rel=modulepreload is better. That still doesn't allow a e.g. a service worker to just fetch a module script and the browser to start lazily compiling it though, so I still think we should consider the MIME type strongly. It's the only thing that's tightly coupled with the thing we care about, the response.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 16, 2017

Member

I don't think it's feasible to change the MIME type; it means people won't be able to use JavaScript modules until they upgrade their server. In any case, let's keep that as a separate thread, if you want to continue pursuing it.

rel=modulepreload sounds pretty good to me. It does lack an imperative API counterpart (in non-DOM contexts), but then, so does rel=preload, right? Since fetch() doesn't have the ability to set the destination? So there's no way with current technology to fetch a module script (or module script graph) and have the browser start lazily compiling. Same for any non-"" destination, really.

I guess speccing rel=modulepreload is fairly simple: it just performs "fetch a module script graph" given a URL. If people are on board with that, I can do that pretty simply. Would love to hear @yoavweiss's thoughts.

Member

domenic commented Feb 16, 2017

I don't think it's feasible to change the MIME type; it means people won't be able to use JavaScript modules until they upgrade their server. In any case, let's keep that as a separate thread, if you want to continue pursuing it.

rel=modulepreload sounds pretty good to me. It does lack an imperative API counterpart (in non-DOM contexts), but then, so does rel=preload, right? Since fetch() doesn't have the ability to set the destination? So there's no way with current technology to fetch a module script (or module script graph) and have the browser start lazily compiling. Same for any non-"" destination, really.

I guess speccing rel=modulepreload is fairly simple: it just performs "fetch a module script graph" given a URL. If people are on board with that, I can do that pretty simply. Would love to hear @yoavweiss's thoughts.

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Feb 16, 2017

Member

Wouldn't you also need to store the result of that fetch someplace? (The part of preload that isn't really defined.)

Member

annevk commented Feb 16, 2017

Wouldn't you also need to store the result of that fetch someplace? (The part of preload that isn't really defined.)

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 16, 2017

Member

I assumed the HTTP cache would suffice, but I guess there is some history here where it does not?

Member

domenic commented Feb 16, 2017

I assumed the HTTP cache would suffice, but I guess there is some history here where it does not?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 16, 2017

Member

Well, OK, I guess even if the HTTP cache doesn't suffice, the module map suffices. So it should be fine.

Member

domenic commented Feb 16, 2017

Well, OK, I guess even if the HTTP cache doesn't suffice, the module map suffices. So it should be fine.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 22, 2017

Member

A couple more thoughts:

  • Maybe we want to solve this subresources problem more generally, e.g. for CSS too.
  • Do we also need something special for prefetch? Remember, preload is a declarative fetch for the current page; prefetch is a hint that the user will probably want this in their HTTP cache sooner rather than later.

This leads me to the following proposal:

  • A new link rel="modulepreload" to do a declarative fetch of an entire module dependency graph for the current page, placing the result in the module map (but not evaluating it). This is a special enough semantic it makes sense to mint a new rel.
  • Optionally, a new attribute, includesubresources, so that you can do <link rel="prefetch" href="module.js" includesubresources> or <link rel="prefetch" href="style.css" includesubresources> or even <link rel="preload" as="stylesheet" href="stylesheet.css" includesubresources>.

In this world, preload by itself doesn't work great for modules. That seems fine, unless we want to create 4 new destinations (modulescript, moduleserviceworker, modulesharedworker, moduleworker) and give them each somewhat-magic semantics where includesubresources implies modifying the module map, instead of the preload cache.

I'll work on modulepreload now, but still would love to hear more...

Member

domenic commented Feb 22, 2017

A couple more thoughts:

  • Maybe we want to solve this subresources problem more generally, e.g. for CSS too.
  • Do we also need something special for prefetch? Remember, preload is a declarative fetch for the current page; prefetch is a hint that the user will probably want this in their HTTP cache sooner rather than later.

This leads me to the following proposal:

  • A new link rel="modulepreload" to do a declarative fetch of an entire module dependency graph for the current page, placing the result in the module map (but not evaluating it). This is a special enough semantic it makes sense to mint a new rel.
  • Optionally, a new attribute, includesubresources, so that you can do <link rel="prefetch" href="module.js" includesubresources> or <link rel="prefetch" href="style.css" includesubresources> or even <link rel="preload" as="stylesheet" href="stylesheet.css" includesubresources>.

In this world, preload by itself doesn't work great for modules. That seems fine, unless we want to create 4 new destinations (modulescript, moduleserviceworker, modulesharedworker, moduleworker) and give them each somewhat-magic semantics where includesubresources implies modifying the module map, instead of the preload cache.

I'll work on modulepreload now, but still would love to hear more...

domenic added a commit to whatwg/html that referenced this issue Feb 22, 2017

Add <link> rel="modulepreload"
This allows preloading module script graphs. The processing model for
this turns out to be different enough that simply extending
rel="preload" is not a good option.

Closes whatwg/fetch#486.

@domenic domenic referenced this issue in whatwg/html Feb 22, 2017

Merged

Add <link> rel="modulepreload" #2383

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Feb 23, 2017

Member

Do we need the destination override given that https://github.com/dherman/esprit supposedly makes it possible to tell whether something is a module script or not?

Member

annevk commented Feb 23, 2017

Do we need the destination override given that https://github.com/dherman/esprit supposedly makes it possible to tell whether something is a module script or not?

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Feb 23, 2017

It seems arbitrary to add a new ref type just for module. Why can't rel=preload as=modulescript not work? Are we going to add rel=prefecthmodule as well?

rniwa commented Feb 23, 2017

It seems arbitrary to add a new ref type just for module. Why can't rel=preload as=modulescript not work? Are we going to add rel=prefecthmodule as well?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 23, 2017

Member

Do we need the destination override given that https://github.com/dherman/esprit supposedly makes it possible to tell whether something is a module script or not?

@annevk if that is the thing dherman was referencing in his email, it is a custom parser that is attempting to implement a proposal that has been rejected by TC39 a couple times (where you add "use module" instead of using type="module"). It doesn't help this situation, which deals with standard JavaScript.

Why can't rel=preload as=modulescript not work?

@rniwa the above discussion goes into some fairly extensive detail on why rel=preload as=modulescript can't work, but I guess it was not summarized nicely for newcomers. Let me try. Here are the main reasons why rel=preload as=modulescript doesn't work.

  • We need to populate the module map, not the preload cache/HTTP cache.
    • In particular, this means that the preload processing model, which e.g. re-fetches whenever various attributes change, does not make sense, since the module map is immutable.
  • We need to fetch all the dependent modules, not just the single resource pointed to by the link tag; the processing model for rel=preload does not allow this.

You might then say, why don't we just patch the preload spec to do an "if as is modulescript, use a completely different processing model" fork? There are three issues with that:

  • We'd actually need to do this for four different as values (modulescript, moduleworker, modulesharedworker, moduleserviceworker), which is fairly messy.
  • This would break the 1:1 correspondence between as="" values and request destinations (i.e., modulescript would mean "module graph processing model with fetch destination script", not "preload processing model with fetch destination modulescript")
  • In general people aren't happy with changing the meaning of preload="" so drastically; for different processing models, different rel=""s are appropriate.

Are we going to add rel=prefecthmodule as well?

We could. However, since prefetch is only about hinting to the browser to populate the HTTP cache, a better solution might be something that expands the hint to ask for all subresources, e.g. HTML documents with images and CSS files with @imports, in addition to JavaScript files with imports. That was what I was saying with my tentative suggestion for an includesubresources="" attribute above. But I think we can separate that from preload, which is about a declarative fetch to make something ready for use by the page right now and thus has much more acute issues with modules.

Member

domenic commented Feb 23, 2017

Do we need the destination override given that https://github.com/dherman/esprit supposedly makes it possible to tell whether something is a module script or not?

@annevk if that is the thing dherman was referencing in his email, it is a custom parser that is attempting to implement a proposal that has been rejected by TC39 a couple times (where you add "use module" instead of using type="module"). It doesn't help this situation, which deals with standard JavaScript.

Why can't rel=preload as=modulescript not work?

@rniwa the above discussion goes into some fairly extensive detail on why rel=preload as=modulescript can't work, but I guess it was not summarized nicely for newcomers. Let me try. Here are the main reasons why rel=preload as=modulescript doesn't work.

  • We need to populate the module map, not the preload cache/HTTP cache.
    • In particular, this means that the preload processing model, which e.g. re-fetches whenever various attributes change, does not make sense, since the module map is immutable.
  • We need to fetch all the dependent modules, not just the single resource pointed to by the link tag; the processing model for rel=preload does not allow this.

You might then say, why don't we just patch the preload spec to do an "if as is modulescript, use a completely different processing model" fork? There are three issues with that:

  • We'd actually need to do this for four different as values (modulescript, moduleworker, modulesharedworker, moduleserviceworker), which is fairly messy.
  • This would break the 1:1 correspondence between as="" values and request destinations (i.e., modulescript would mean "module graph processing model with fetch destination script", not "preload processing model with fetch destination modulescript")
  • In general people aren't happy with changing the meaning of preload="" so drastically; for different processing models, different rel=""s are appropriate.

Are we going to add rel=prefecthmodule as well?

We could. However, since prefetch is only about hinting to the browser to populate the HTTP cache, a better solution might be something that expands the hint to ask for all subresources, e.g. HTML documents with images and CSS files with @imports, in addition to JavaScript files with imports. That was what I was saying with my tentative suggestion for an includesubresources="" attribute above. But I think we can separate that from preload, which is about a declarative fetch to make something ready for use by the page right now and thus has much more acute issues with modules.

@dherman

This comment has been minimized.

Show comment
Hide comment
@dherman

dherman Feb 23, 2017

Not expressing an opinion on this thread (yet anyway) but just a clarification:

@annevk if that is the thing dherman was referencing in his email, it is a custom parser that is attempting to implement a proposal that has been rejected by TC39 a couple times (where you add "use module" instead of using type="module"). It doesn't help this situation, which deals with standard JavaScript.

No, the point is that it's an implementation technique that allows you to cache the work of parsing such that you can reuse the result regardless of whether the source ends up being used as a classic script or module script.

IOW, it's an implementation technique that demonstrates the feasibility of a preload mechanism where the author doesn't specify what kind of JS payload is being parsed, but that gets all the same performance benefits since you can have a single pre-parser that works for any kind of JS payload.

dherman commented Feb 23, 2017

Not expressing an opinion on this thread (yet anyway) but just a clarification:

@annevk if that is the thing dherman was referencing in his email, it is a custom parser that is attempting to implement a proposal that has been rejected by TC39 a couple times (where you add "use module" instead of using type="module"). It doesn't help this situation, which deals with standard JavaScript.

No, the point is that it's an implementation technique that allows you to cache the work of parsing such that you can reuse the result regardless of whether the source ends up being used as a classic script or module script.

IOW, it's an implementation technique that demonstrates the feasibility of a preload mechanism where the author doesn't specify what kind of JS payload is being parsed, but that gets all the same performance benefits since you can have a single pre-parser that works for any kind of JS payload.

@erykpiast

This comment has been minimized.

Show comment
Hide comment
@erykpiast

erykpiast Apr 5, 2017

I guess it's very silly question, but may I ask why this one is a requirement?

We need to fetch all the dependent modules, not just the single resource pointed to by the link tag

It seems to me, that in context of modules, preload is a remedy for waterfall problem, so ideal solution would be creating a flat list of all modules used in the app (as <link> tags). With such approach the browser could prefetch module files (I'm not sure about parsing, is it possible to do it without resolving dependencies?) and pick them from cache later, once it encounters references during recursive dependency resolution, starting from <script type="module"> entrypoint(s). Does it make sense at all? What did I miss? It'd be parallel fetching and sequential/waterfall dependency resolution. Is the point of this feature to provide parallel DR as well?

I guess it's very silly question, but may I ask why this one is a requirement?

We need to fetch all the dependent modules, not just the single resource pointed to by the link tag

It seems to me, that in context of modules, preload is a remedy for waterfall problem, so ideal solution would be creating a flat list of all modules used in the app (as <link> tags). With such approach the browser could prefetch module files (I'm not sure about parsing, is it possible to do it without resolving dependencies?) and pick them from cache later, once it encounters references during recursive dependency resolution, starting from <script type="module"> entrypoint(s). Does it make sense at all? What did I miss? It'd be parallel fetching and sequential/waterfall dependency resolution. Is the point of this feature to provide parallel DR as well?

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Jul 16, 2017

It all seems very complicated to be handling preloading within the pipeline. Agree with @erykpiast that a simple resource-level preloader is all that is needed. Simplest should win. Caches should be simple and low level.

It all seems very complicated to be handling preloading within the pipeline. Agree with @erykpiast that a simple resource-level preloader is all that is needed. Simplest should win. Caches should be simple and low level.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Jul 16, 2017

And especially once custom resolvers are considered - having the resolver called randomly and multiple times by the cache will break a lot of nice expectations one could otherwise have for these representing a direct tree load.

And especially once custom resolvers are considered - having the resolver called randomly and multiple times by the cache will break a lot of nice expectations one could otherwise have for these representing a direct tree load.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Jul 16, 2017

To clarify further, System.load has always been a specified device for preloading through the spec history, and could possibly continue to exist in some form, but there is a different and simpler use case for a straightforward preload cache population for prepopulating the cache (which I'd always assumed was the link preload case for modules). And from my own experience with SystemJS, it was the link preloading cache prepopulation that has been the most relied on (I know of hardly any users using System.load).

To clarify further, System.load has always been a specified device for preloading through the spec history, and could possibly continue to exist in some form, but there is a different and simpler use case for a straightforward preload cache population for prepopulating the cache (which I'd always assumed was the link preload case for modules). And from my own experience with SystemJS, it was the link preloading cache prepopulation that has been the most relied on (I know of hardly any users using System.load).

@shicks

This comment has been minimized.

Show comment
Hide comment
@shicks

shicks Aug 24, 2017

+1. This seems like a prime example of perfect as the enemy of good. Now that browser support for modules is actually rolling out, it's becoming more important to be able to actually load them efficiently. Closure Library is currently working on integrating modules into our debug loader (in which case we already have a flat list of all recursive dependencies), but with zero support for preloading modules, there seems to be no way to interleave non-module execution between two modules without waiting for a bunch of serial fetches. Given that <link rel=preload as=script> works fine for non-recursive loading of scripts, making it also work with the same non-recursive semantics for modules seems like a reasonable option. If we want to later hammer out and add a recursive version, that's fine, but please don't block something that's (seemingly) straightforward and actually very useful on a unicorn that may never materialize.

shicks commented Aug 24, 2017

+1. This seems like a prime example of perfect as the enemy of good. Now that browser support for modules is actually rolling out, it's becoming more important to be able to actually load them efficiently. Closure Library is currently working on integrating modules into our debug loader (in which case we already have a flat list of all recursive dependencies), but with zero support for preloading modules, there seems to be no way to interleave non-module execution between two modules without waiting for a bunch of serial fetches. Given that <link rel=preload as=script> works fine for non-recursive loading of scripts, making it also work with the same non-recursive semantics for modules seems like a reasonable option. If we want to later hammer out and add a recursive version, that's fine, but please don't block something that's (seemingly) straightforward and actually very useful on a unicorn that may never materialize.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 24, 2017

Member

I expect it'd actually be easier to implement recursive semantics since then you could reuse the code already in place for <script type=module>. It'd certainly be easier to spec.

Regardless, recursive semantics vs. not are a tiny, tiny detail in the overall tapestry of problems with naively integrating <link rel=preload>. Please see the summary at #486 (comment) . Those are the real problems here, and it's not perfect vs. good, it's working at all vs. being completely broken.

Member

domenic commented Aug 24, 2017

I expect it'd actually be easier to implement recursive semantics since then you could reuse the code already in place for <script type=module>. It'd certainly be easier to spec.

Regardless, recursive semantics vs. not are a tiny, tiny detail in the overall tapestry of problems with naively integrating <link rel=preload>. Please see the summary at #486 (comment) . Those are the real problems here, and it's not perfect vs. good, it's working at all vs. being completely broken.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Aug 24, 2017

@domenic the argument there seems to jump to some conclusions already.

@rniwa the above discussion goes into some fairly extensive detail on why rel=preload as=modulescript can't work, but I guess it was not summarized nicely for newcomers. Let me try. Here are the main reasons why rel=preload as=modulescript doesn't work.

We need to populate the module map, not the preload cache/HTTP cache. In particular, this means that the preload processing model, which e.g. re-fetches whenever various attributes change, does not make sense, since the module map is immutable.

I don't see it as obvious that this is a necessity. Can a preload cache not be used by the module loading algorithm to populate the module map?

We need to fetch all the dependent modules, not just the single resource pointed to by the link tag; the processing model for rel=preload does not allow this.

As discussed above, a simple solution may be to simply skip this.

guybedford commented Aug 24, 2017

@domenic the argument there seems to jump to some conclusions already.

@rniwa the above discussion goes into some fairly extensive detail on why rel=preload as=modulescript can't work, but I guess it was not summarized nicely for newcomers. Let me try. Here are the main reasons why rel=preload as=modulescript doesn't work.

We need to populate the module map, not the preload cache/HTTP cache. In particular, this means that the preload processing model, which e.g. re-fetches whenever various attributes change, does not make sense, since the module map is immutable.

I don't see it as obvious that this is a necessity. Can a preload cache not be used by the module loading algorithm to populate the module map?

We need to fetch all the dependent modules, not just the single resource pointed to by the link tag; the processing model for rel=preload does not allow this.

As discussed above, a simple solution may be to simply skip this.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 24, 2017

Member

The problems in "There are three issues with that:", especially the first two, are IMO the most fatal.

As for your suggestion to populate the preload cache and then later populate the module map, it's interesting, but I don't think we should keep modules in two separate in-memory caches.

Member

domenic commented Aug 24, 2017

The problems in "There are three issues with that:", especially the first two, are IMO the most fatal.

As for your suggestion to populate the preload cache and then later populate the module map, it's interesting, but I don't think we should keep modules in two separate in-memory caches.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Aug 24, 2017

I see. My knowledge of the internals isn't great, but I assumed that if it was a straightforward preload cache using the same algorithm that would avoid those three issues you refer to? Multi-layering of caches happens naturally anytime caches are used. When hinging arguments on points like this it would help to flesh them out a little more I think.

I see. My knowledge of the internals isn't great, but I assumed that if it was a straightforward preload cache using the same algorithm that would avoid those three issues you refer to? Multi-layering of caches happens naturally anytime caches are used. When hinging arguments on points like this it would help to flesh them out a little more I think.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 24, 2017

Member

No; those issues are independent of the preload cache, and are about fetch's architecture (destinations).

Member

domenic commented Aug 24, 2017

No; those issues are independent of the preload cache, and are about fetch's architecture (destinations).

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Aug 24, 2017

Ok then, moving to the next blocker it sounds like you're specifically referring to the messiness of varying as over modulescript, moduleworker, modulesharedworker, moduleserviceworker. Doesn't as=script not preload worker scripts? Perhaps you can help clarify then why we'd need to specifically split the scenarios for modules?

Thanks for taking the time to explain - and please do point me to where this is discussed elsewhere if it is just rehashing.

Ok then, moving to the next blocker it sounds like you're specifically referring to the messiness of varying as over modulescript, moduleworker, modulesharedworker, moduleserviceworker. Doesn't as=script not preload worker scripts? Perhaps you can help clarify then why we'd need to specifically split the scenarios for modules?

Thanks for taking the time to explain - and please do point me to where this is discussed elsewhere if it is just rehashing.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 24, 2017

Member

as=script does not preload classic worker scripts, indeed. You have to use as=worker. (Or as=sharedworker, as=serviceworker.)

I don't think this is being discussed elsewhere; we're just assuming a decent amount of shared context on what is in the fetch spec (e.g. familiarity with the fetch destination concept), since this is the fetch spec repo. I'm OK taking the time to extract info from the spec into this issue thread as required.

Member

domenic commented Aug 24, 2017

as=script does not preload classic worker scripts, indeed. You have to use as=worker. (Or as=sharedworker, as=serviceworker.)

I don't think this is being discussed elsewhere; we're just assuming a decent amount of shared context on what is in the fetch spec (e.g. familiarity with the fetch destination concept), since this is the fetch spec repo. I'm OK taking the time to extract info from the spec into this issue thread as required.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Aug 24, 2017

As far as I can tell from a quick glance at the spec it sounds like the distinction between worker and script is needed to handle subresource boundaries, whileimportScripts in workers are still loaded as script. Could the same distinction not apply to modules - separating moduleworker, modulesharedworker and moduleserviceworker as simply the top-level goals, while their dependencies would still run through module? If so, moduleworker, modulesharedworker and moduleserviceworker may be less important from a preloading perspective, allowing a module implementation to land as the primary priority first. Personally I'd like to see web assembly and binary ast header-detected under the same module destination name as well, but am weary of taking this too off-topic.

As far as I can tell from a quick glance at the spec it sounds like the distinction between worker and script is needed to handle subresource boundaries, whileimportScripts in workers are still loaded as script. Could the same distinction not apply to modules - separating moduleworker, modulesharedworker and moduleserviceworker as simply the top-level goals, while their dependencies would still run through module? If so, moduleworker, modulesharedworker and moduleserviceworker may be less important from a preloading perspective, allowing a module implementation to land as the primary priority first. Personally I'd like to see web assembly and binary ast header-detected under the same module destination name as well, but am weary of taking this too off-topic.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 24, 2017

Member

Sure. It's still a problem though, even if you think it's a lower-priority problem.

Member

domenic commented Aug 24, 2017

Sure. It's still a problem though, even if you think it's a lower-priority problem.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Aug 24, 2017

Certainly, but from the perspective of being messy I guess while it is from a spec perspective, it doesn't pass on as much cognitive overhead to the average user if module can be reused within the other goals so that most need only learn one thing.

guybedford commented Aug 24, 2017

Certainly, but from the perspective of being messy I guess while it is from a spec perspective, it doesn't pass on as much cognitive overhead to the average user if module can be reused within the other goals so that most need only learn one thing.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 24, 2017

Member

It's just another argument for introducing a different rel, where we can reuse symmetric as= values instead of having a (small) combinatorial explosion.

I'm unclear why you think that reusing link rel=preload is a good idea. Do you think it will be faster if we do? Definitely not; it means we'd have to do a lot more work to reconcile the two processing models.

Member

domenic commented Aug 24, 2017

It's just another argument for introducing a different rel, where we can reuse symmetric as= values instead of having a (small) combinatorial explosion.

I'm unclear why you think that reusing link rel=preload is a good idea. Do you think it will be faster if we do? Definitely not; it means we'd have to do a lot more work to reconcile the two processing models.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Aug 24, 2017

@domenic do you see the combinatorial possibilities extending beyond these four? Or can we agree that a unified module goal may form a possible common denominator? I'm don't see how this is a strong argument for a new rel otherwise.

I'm advocating rel=preload simply because the naive user-facing conceptual models of the web should embrace outward simplicity and reusability as far as possible to remain understandable to all, but I'm more than happy to step back on that if there are untenable spec differences here.

Note also that I'm not trying to throw out the concept of a full graph preload entirely - simply to say that what is needed urgently is a spec that will allow a low-level network-layer caching of module resources. A separate rel for a graph-based preload could potentially be addressed later on - perhaps then more generally too in a way that applies similarly for css, picking up a truly unique semantic meaning then in the process.

@domenic do you see the combinatorial possibilities extending beyond these four? Or can we agree that a unified module goal may form a possible common denominator? I'm don't see how this is a strong argument for a new rel otherwise.

I'm advocating rel=preload simply because the naive user-facing conceptual models of the web should embrace outward simplicity and reusability as far as possible to remain understandable to all, but I'm more than happy to step back on that if there are untenable spec differences here.

Note also that I'm not trying to throw out the concept of a full graph preload entirely - simply to say that what is needed urgently is a spec that will allow a low-level network-layer caching of module resources. A separate rel for a graph-based preload could potentially be addressed later on - perhaps then more generally too in a way that applies similarly for css, picking up a truly unique semantic meaning then in the process.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 24, 2017

Member

do you see the combinatorial possibilities extending beyond these four?

Yes; we just added service worker in the last year or so. The future of the web is long.

I'm don't see how this is a strong argument for a new rel otherwise.

I'll try to be more clear.

Breaking the mapping of destinations to as="" values is pretty bad no matter what. We have a serious conflict here between destinations as they're used in fetch (both classic and module scripts go through as="script"; both classic and module workers go through as="worker"; etc.), and as="" as it's used in preload (we need to distinguish between classic and module scripts/workers/etc.). We need some way to signal "destination=script but treat it as a module script". as=modulescript is a pretty bad way to do that because it breaks the correspondence between as="" and destination.

I'm advocating rel=preload simply because the naive user-facing conceptual models of the web should embrace outward simplicity and reusability as far as possible to remain understandable to all, but I'm more than happy to step back on that if there are untenable spec differences here.

Kind of the point of my post above is that there are untenable spec, implementation, and mental model differences here. It seems you didn't find it convincing, so I think it's a good exercise to go through and help me expand on the points; maybe you can provide your own summary if I manage to convey enough information, and that will be more convincing to others. But it's my strong feeling that based on available evidence, the differences are too large.

Note also that I'm not trying to throw out the concept of a full graph preload entirely - simply to say that what is needed urgently is a spec that will allow a low-level network-layer caching of module resources.

Well, we already have that in HTTP/2. So I'm not sure how urgent it is. But yes, it'd be nice.

Anyway, as I said above, I don't think recursive vs. not is a very interesting aspect of this whole discussion. I think it'll be about as easy to implement either one; maybe a bit easier to implement the recursive one. The hard parts are unrelated to recursive vs. not, but about fetch destinations, credentials modes, preload cache vs. module map, effect of document mutations, etc. Those are the parts that convinced me we need a separate rel="", for the separate processing model.

Member

domenic commented Aug 24, 2017

do you see the combinatorial possibilities extending beyond these four?

Yes; we just added service worker in the last year or so. The future of the web is long.

I'm don't see how this is a strong argument for a new rel otherwise.

I'll try to be more clear.

Breaking the mapping of destinations to as="" values is pretty bad no matter what. We have a serious conflict here between destinations as they're used in fetch (both classic and module scripts go through as="script"; both classic and module workers go through as="worker"; etc.), and as="" as it's used in preload (we need to distinguish between classic and module scripts/workers/etc.). We need some way to signal "destination=script but treat it as a module script". as=modulescript is a pretty bad way to do that because it breaks the correspondence between as="" and destination.

I'm advocating rel=preload simply because the naive user-facing conceptual models of the web should embrace outward simplicity and reusability as far as possible to remain understandable to all, but I'm more than happy to step back on that if there are untenable spec differences here.

Kind of the point of my post above is that there are untenable spec, implementation, and mental model differences here. It seems you didn't find it convincing, so I think it's a good exercise to go through and help me expand on the points; maybe you can provide your own summary if I manage to convey enough information, and that will be more convincing to others. But it's my strong feeling that based on available evidence, the differences are too large.

Note also that I'm not trying to throw out the concept of a full graph preload entirely - simply to say that what is needed urgently is a spec that will allow a low-level network-layer caching of module resources.

Well, we already have that in HTTP/2. So I'm not sure how urgent it is. But yes, it'd be nice.

Anyway, as I said above, I don't think recursive vs. not is a very interesting aspect of this whole discussion. I think it'll be about as easy to implement either one; maybe a bit easier to implement the recursive one. The hard parts are unrelated to recursive vs. not, but about fetch destinations, credentials modes, preload cache vs. module map, effect of document mutations, etc. Those are the parts that convinced me we need a separate rel="", for the separate processing model.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Aug 24, 2017

Yes; we just added service worker in the last year or so. The future of the web is long.

Any new top-level execution goal will likely use the same principles of a module graph at this point. If it is possible to unify on a module parse goal that distinguishes binary formats by header bytes (like wasm and ast binaries, and any future specs), then combinations can be avoided where the binary header space would become the new "version space" of web-based parse goals. If this route isn't taken, then any spec work should be designed to prepare for a much much larger combinatorial explosion here.

So I'd argue this doesn't have to be the case if we can follow the first path above.

We have a serious conflict here between destinations as they're used in fetch (both classic and module scripts go through as="script"; both classic and module workers go through as="worker"; etc.)

Ah, I wasn't aware of this, but of course it seems a result of the process. Would it be too late at this point to alter the destination names of module scripts to go through "modulescript" and "moduleworker" etc? Or would there be other concerns with such a change?

The hard parts are unrelated to recursive vs. not, but about fetch destinations, credentials modes, preload cache vs. module map, effect of document mutations, etc.

I will try to understand the fetch spec concerns a little better here. Perhaps it might help to start considering what issues might arise here to do with credentials modes that are unique to modules and not to importScripts (effectively the existing pattern we have here)?

As you know I'm arguing for preload cache over module map. Document mutation details seem to be fleshed out in the preload spec as well, so naively I would just assume reusing the terminology for things such as these would be beneficial.

Yes; we just added service worker in the last year or so. The future of the web is long.

Any new top-level execution goal will likely use the same principles of a module graph at this point. If it is possible to unify on a module parse goal that distinguishes binary formats by header bytes (like wasm and ast binaries, and any future specs), then combinations can be avoided where the binary header space would become the new "version space" of web-based parse goals. If this route isn't taken, then any spec work should be designed to prepare for a much much larger combinatorial explosion here.

So I'd argue this doesn't have to be the case if we can follow the first path above.

We have a serious conflict here between destinations as they're used in fetch (both classic and module scripts go through as="script"; both classic and module workers go through as="worker"; etc.)

Ah, I wasn't aware of this, but of course it seems a result of the process. Would it be too late at this point to alter the destination names of module scripts to go through "modulescript" and "moduleworker" etc? Or would there be other concerns with such a change?

The hard parts are unrelated to recursive vs. not, but about fetch destinations, credentials modes, preload cache vs. module map, effect of document mutations, etc.

I will try to understand the fetch spec concerns a little better here. Perhaps it might help to start considering what issues might arise here to do with credentials modes that are unique to modules and not to importScripts (effectively the existing pattern we have here)?

As you know I'm arguing for preload cache over module map. Document mutation details seem to be fleshed out in the preload spec as well, so naively I would just assume reusing the terminology for things such as these would be beneficial.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 24, 2017

Member

Any new top-level execution goal will likely use the same principles of a module graph at this point.

No, that's not correct. The preloading concerns for service worker vs. page vs. worker are orthogonal to module graph vs. classic script. They're largely about implementation-level things like prioritization and not really related to moduleness.

Or would there be other concerns with such a change?

My understanding is that would break their integration into other aspects of the system which process scripts in the same way. Maybe it is changeable, but if the motivation is simply avoiding another rel="" value by introducing N new as="" values, it doesn't seem worth it.

As you know I'm arguing for preload cache over module map.

Yes, that also is not a good decision, I believe. Compared to HTTP/2, the benefits are tiny, whereas if we can create something module-specific that takes care of all the module-related pieces for us and avoids loading the bytes into two separate caches, we're in much better shape.

Member

domenic commented Aug 24, 2017

Any new top-level execution goal will likely use the same principles of a module graph at this point.

No, that's not correct. The preloading concerns for service worker vs. page vs. worker are orthogonal to module graph vs. classic script. They're largely about implementation-level things like prioritization and not really related to moduleness.

Or would there be other concerns with such a change?

My understanding is that would break their integration into other aspects of the system which process scripts in the same way. Maybe it is changeable, but if the motivation is simply avoiding another rel="" value by introducing N new as="" values, it doesn't seem worth it.

As you know I'm arguing for preload cache over module map.

Yes, that also is not a good decision, I believe. Compared to HTTP/2, the benefits are tiny, whereas if we can create something module-specific that takes care of all the module-related pieces for us and avoids loading the bytes into two separate caches, we're in much better shape.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Aug 24, 2017

Compared to HTTP/2, the benefits are tiny, whereas if we can create something module-specific that takes care of all the module-related pieces for us and avoids loading the bytes into two separate caches, we're in much better shape.

The cancellation time of HTTP/2 PUSH is a huge problem for avoiding cache redundancy. The best shape for the web is one where a module graph doesn't even have to make a request to the network - which is the goal I'm after here.

We don't need network-based preload for this - we need a flat hinting scheme which preload can offer us.

I don't actually care about the rel to be perfectly honest - the above is all I'm interested in. But please think ahead to the new parsing formats of the web if designing a new rel - that's the combinatorial explosion to worry about.

Compared to HTTP/2, the benefits are tiny, whereas if we can create something module-specific that takes care of all the module-related pieces for us and avoids loading the bytes into two separate caches, we're in much better shape.

The cancellation time of HTTP/2 PUSH is a huge problem for avoiding cache redundancy. The best shape for the web is one where a module graph doesn't even have to make a request to the network - which is the goal I'm after here.

We don't need network-based preload for this - we need a flat hinting scheme which preload can offer us.

I don't actually care about the rel to be perfectly honest - the above is all I'm interested in. But please think ahead to the new parsing formats of the web if designing a new rel - that's the combinatorial explosion to worry about.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Sep 8, 2017

To follow up on the above, I just wanted to illustrate here very briefly my exact argument, to be sure that nothing is being missed. The ideal optimization workflow I'd like to see for modules would be simply inlining the flat preloading and integrity information as a production optimization step:

<link rel="preload" href="/module-dep.js" integrity="..." />
<link rel="preload" href="/module-deep-dep.js" integrity="..." />
<script type="module" src="/module.js" integrity="..."></script>
  1. The above gives a flat preload on the first load, allowing all deep modules to be loaded in a single latency.
  2. When only some of the modules in the tree are cached, the above ensures that only the modules that are needed are fetched, again with a single latency.
  3. Being able to supply subresource integrity at the same time as this deep dependency hinting would allow solving two problems at the same time.

I understand there are a lot of spec concerns here to do with the exact mechanics, but the overall workflow in the above is what I'd really love to see.

To follow up on the above, I just wanted to illustrate here very briefly my exact argument, to be sure that nothing is being missed. The ideal optimization workflow I'd like to see for modules would be simply inlining the flat preloading and integrity information as a production optimization step:

<link rel="preload" href="/module-dep.js" integrity="..." />
<link rel="preload" href="/module-deep-dep.js" integrity="..." />
<script type="module" src="/module.js" integrity="..."></script>
  1. The above gives a flat preload on the first load, allowing all deep modules to be loaded in a single latency.
  2. When only some of the modules in the tree are cached, the above ensures that only the modules that are needed are fetched, again with a single latency.
  3. Being able to supply subresource integrity at the same time as this deep dependency hinting would allow solving two problems at the same time.

I understand there are a lot of spec concerns here to do with the exact mechanics, but the overall workflow in the above is what I'd really love to see.

domenic added a commit to whatwg/html that referenced this issue Nov 7, 2017

Add <link> rel="modulepreload"
This allows preloading module scripts, and optionally their descendants.
The processing model for this turns out to be different enough that
simply extending rel="preload" is not a good option.

Closes whatwg/fetch#486.

domenic added a commit to whatwg/html that referenced this issue Dec 8, 2017

Add <link> rel="modulepreload"
This allows preloading module scripts, and optionally their descendants.
The processing model for this turns out to be different enough that
simply extending rel="preload" is not a good option.

Closes whatwg/fetch#486.

domenic added a commit to whatwg/html that referenced this issue Dec 14, 2017

Add <link> rel="modulepreload"
This allows preloading module scripts, and optionally their descendants.
The processing model for this turns out to be different enough that
simply extending rel="preload" is not a good option.

Closes whatwg/fetch#486.

Tests: https://github.com/w3c/web-platform-tests/blob/master/preload/modulepreload.html

@annevk annevk referenced this issue in w3ctag/design-reviews Feb 3, 2018

Open

<link> rel="modulepreload" #213

3 of 5 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment