-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A request data -> request hook (e.g. module specifiers -> module request) #2640
Comments
One use-case I have came from @domenic: // main.1.0.0.js
import {something} from './tiny-library.1.0.0.js';
// …LOADS OF CODE… But uh oh, there's a bug in tiny-library! Thankfully it's fixed in You can workaround this with the service worker, but you'll miss it on the first load. It'd be nice to be able to do something like: <script type="module-map">
{
"main": "./main.1.0.0.js",
"tiny-library": "./tiny-library.1.0.1.js"
}
</script>
<script type="module">
import "main";
</script> Where import {something} from 'tiny-library';
// …LOADS OF CODE… Now you can bump the version of particular modules in the mapping, without invalidating the whole branch. |
Any solution here should not be module specific. |
It would have to be declarative though, right? Pre-parsers would need to execute it. |
Yeah, which makes this design process very tricky (easy to end up with app cache). That's why I think we need a lot more prototyping and experimentation and use case gathering, even if prototypes defeat preload scanners. |
Is there an up to date list of design requirements for this? I am working on hooks for Node, and trying to get a hold of compatibility path (I assume it will require a build step most likely). |
Maybe someone from @whatwg/loader has up-to-date insights, but I suspect there simply hasn't been much bandwidth to get this to move yet. And modules still not being widely deployed doesn't help with feeding requirements and use cases. |
@annevk some still seem to exist though, like the preparser requirement. edit: this is of note since it was not in @whatwg/loader |
OK, this discussion is fairly little, happy to chime in after seeing this issue closed as duplicate. Use caseI think @jakearchibald already mentioned the most common use case of all, which is the reason we all use Moreover, having a way to map Today the dynamic Web can hardly exist as a platform without bundlers. Current ES2015 browsers module implementation makes usage of these bundlers mandatory indeed. Nobody can load libraries in any reasonable way and make the same code portable between environments (transpiled, compiled, etc). SimplicityI think both me and Jake having same idea indicates it's obvious/simple to think about it, explain it, and also implement it. Accordingly, since
Modules are the only thing that are frequently needed, differently from images or CSS, and used multiple times per each file, differently from anything else on the Web. How come the most needed bit, which is the ability to load modules the way we've been doing for the last 10 years, is suddenly something every Web resource needs to do? I don't see the use case for that. Not only Service WorkersCurrently Service Workers do their job only the second time the page is loaded. Mapping there is probably a no brainer, but everything would be broken without SW. Does it really have to be mandatory? So I loop back to Simplicity. AlternativeMy proposal (on a second though) was based on a static file on the server with a specific script type too. <script type="module-namespaces" src="/mjs.json"></script> call it It'd be just one extra request, if present, and it can instrument bundlers/loaders/polyfills to resolve dependencies in a better way upfront. This is a module specific solution because AFAIK there are no other use cases that really need this solution so I'd say why not moving forward the easy way? |
Summarizing here the pros on using a static json file since there's nothing else really interesting in the other thread at this point: Pros
Specially last point ensures JS code can be shared and ported across different environments that uses different bundlers or different sources based on mjs.json example{
"hyperhtml": "https://unpkg.com/hyperhtml@latest/min.mjs",
"lodash": "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js",
"site-lib": "/js/lib/site-lib/index.mjs"
} mjs.json via web<!doctype html>
<script type="module-map" src="/mjs.json"></script>
<script type="module">
import hyper from "hyperhtml";
addEventListener('load', () => {
hyper(document.body)
`<h1>Welcome in ${location.hostname}</h1>`;
}, {once: true});
</script> |
after @guybedford comment on the other thread:
I don't think there's a need for a universal solution that covers both node and web. NodeJS has no
I have a concrete example right here of what this proposal would solve:
Shipping twice the first library would be redundant. Using relative path would be not portable. Using bundlers would mean being incapable of serving library 2 from a CDN. The solution is to let the environment solve the issue, simply importing library 1 on top. NodeJS would know how to load and resolve that while on my webpage, my Any tool could eventually pre-parse dependencies upfront per each needed module, and automate the creation of such Accordingly, I see this thread as a generic NodeJS solution, but not necessarily a good fit for what the Web needs. |
About being module specificlast thought on this:
I don't understand how importing {
"bootstrap": {
"module": "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js",
"style": "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
}
} The Of course if there are better names for that, I wouldn't mind a change. |
@WebReflection how it's supposed to resolve dependencies of dependencies? {
"bootstrap": {
"module": "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
}
} Now let's say Should your Technically it needs to be lean and fast (as it's with bundling) for cases where we deal with 500+ of modules with deep nested dependencies, and sometimes containing same packages at different versions (that's reflection of real world setup of many today's applications). |
@medikoo I've answered already at that: TL;DR YAGNI, libraries are deployed to CDNs already as bundle. |
Ok, so you see ESM purely as a modules format for bundles (or large modules)? Or do you suggest that transpilation should be a mandatory step for web (when we deal with more complex applications e.g. built of 100+ of smaller modules), as we cannot bundle few ESM modules into one ESM without transpiling them into something else. |
No. relative and absolute paths are still fundamental for ESM. Here we are trying to solve external libraries dependencies. You don't want to dig into the "need to resolve also their inner dependencies" rabbit hole because that is not a real-world use case. Libraries are published to CDNs already as bundle, this is a simple fact.
No. I am actually promoting what's already there and what worked already. External dependencies are already bundled, nothing new, nothing different to learn, nothing to change, except exporting as However, your own code in your own site can use as many relative/absolute dependencies. I am solving the only issue I have with the current ES2015: I cannot require an external library. |
Moreover The format I've proposed is compatible with any external dependency as flat tree. If you can statically analyze the file you can also statically analyze each If this approach doesn't scale enough, we can eventually use the latest proposed format and simulate what {
"bootstrap": {
"module": "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js",
"style": "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css",
"dependencies": {
"jquery": {
"module": "https://maxcdn.bootstrapcdn.com/jquery/3.3.7/jquery.mjs"
}
}
}
} Every remote library can use relative paths that will be resolved through their remote location and every remote library can import its dependencies as defined in that file. This would cover all scenarios edit nested dependencies are actually a road to hell here .... so I am not in favor of my own alternative solution to dependencies |
Last thought for @medikoo : you never want in production to load relative files because these will never be minified/optimized for the web, which is why I am insisting external dependencies bundled for production should not be a concern here. I would never import in production something that hasn't been optimized and AFAIK nobody imports relative files using I also would love to keep it simple as much as possible instead of being stuck with this for years. |
I have a feeling that you're trying to send us back to times pre node/npm where we e.g. landed The way modules are connected and resolved now in node.js/npm (with semver on board) env, goes far beyond and changed whole lot in a great way. Applications I work with now are built of 700+ modules, where majority of that is external dependencies with it's dependencies etc.. Additionally 40% of that codebase is also run in Node.js env. It's pure CJS modules, bundled for browsers, and which work with no transpilation in both Node.js and browser environments efficiently. I totally don't see how what you propose can replace setup (I described above) in a good way, at least it definitely doesn't empower ESM with CJS capabilities it lacks, but maybe my problem is that I assumed you try to solve exactly this issue, when it's not the case. |
I am just bringing you back to reality. Every dependency we have on the web is bundled because:
I can link the million libraries out there served through CDNs but I am fairly sure you understand what I am talking about, which is not jQuery.
Exactly. You are confirming what I am saying. Libraries/applications ships already bundled, because of the points I have already mentioned. You don't want to trigger 700+ network requests * 700 multiple dependencies on the Web, do you understand what I am saying? You want to load a library published in the CDN, optimized, and free of dependencies resolutions, like it is already for every library on the Web, unless you bundle it. This means it's not me bringing you back to bundled libraries, it's you stuck behind bundlers no matter what, because that's optimal for the web. The Web is not NodeJS, and it should never bury itself to have full CJS capabilities: that is not the Web use case, that is not what we need to improve modules sharing, IMO. |
I think you're focused on optimisations that target production environments, and forgot about other cases. By no means practices as minification, transpilation etc. were in a past (or should be now) mandatory for normal web development (no matter whether app is really big and complex or we'll learning to build something). Thing that transpilation step is required now with ESM is main reason behind JS fatigue we frequently read about. If that's not solved then ESM (in my personal opinion) is not worth consideration. |
transpilation and ESM have nothing to do with each other ... indeed, you never want to transpile ESM on the Web, different story for NodeJS, where everyone trapped itself behind transpilers. If transpilers were not so popular, people using Today we have everyone using ESM, transpiled to CJS, so that shenanigans like The Web doesn't need that, and there are best practices on the Web since ever. For non production use cases, you can do whatever you want. You have tools, and you're using tools, and you'll always do that because that's the way to go, that's the way to create production bundles. My proposal addresses this case, which is the only one that's relevant: production external dependencies. Everything else can be solved by tooling and relative paths if you want, that's not what the Web need, just what lazy developers might use. Is that mandatory? Not for local usage, where you use rollup, webpack, browserify, you name it ... nobody cares. On the web? You want to be sure you can use external dependencies and these are always served already bundled. This is how the efficient, production web, works. |
Also ... we can agree to disagree, 'cause I've nothing else to say about it. |
@annevk do you still want to wait on userland for this? |
Yeah, I think service workers being deployed everywhere will help drive more of a need for this. The other thing I'm interested in seeing is if https://wicg.github.io/origin-policy/ will work out. If that's successful maybe there's an opening for loading a small service worker before the main content as well, solving the scanner issue. |
@annevk with all the caveats of current limitations, I've thrown up https://bmeck.github.io/node-sw-compat-loader-test/dist/ as an example of what it takes to do this today. Extra network requests are plentiful and so are CPU draining source code transforms inside of service workers (doesn't seem to be a way to throw things into a worker). The proof of concept is minimal and could be expanded further to do things like code instrumentation instead of being focused on rewriting specifiers to other URLs but I wanted to leave that out for now. |
Going to close this in favor of #4938. |
(Note: OP edited by @domenic to add some more context)
A request that often comes up in the context of modules, but is also applicable to other resources, is the ability to map resource specifiers (e.g.
import "x"
or<img src="x">
) to different URLs than the one that would be naively computed.@guybedford closed #2547 where we discussed possible alternatives to the Loader API and what role service workers could play in them. The TL;DR is that we first want more experimentation to see what folks come up with.
However, given that there's continued interest in this area and I think such experimentation will show the need for a hook of sorts to make solutions less of a hack, I'm opening this issue.
Please read #2547 for more background about the problem (for modules) and discussions toward a solution (for requests in general). This thread proposes a specific direction, although we can also use it to discuss other solutions to the problem.
The rough idea is that in the same task we dispatch the service worker fetch event, we first dispatch another event to transform a set of requests inputs into a request. That way you can create your own identifiers for resources and let the service worker take care of mapping those identifiers to URL, integrity, referrer, etc. information (a request) that is then used to fetch the resource during the fetch event.
How this works in detail is a little tricky. If you have something like
<img src=identifier>
onhttps://example.com/
,<img>.src
will returnhttps://example.com/identifier
whereas that might not be the mapping the service worker has in mind. But that is probably fine as the service worker can already do whatever it wants so<img>.src
is not trustworthy information. All we need to make sure of is that it getsidentifier
as "raw data" as well.cc @jakearchibald @dherman
The text was updated successfully, but these errors were encountered: