-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
[RFC] Parcel 2: Loader Design #2507
Comments
Explored this a bunch more and came to the following conclusions. There are basically 3 ways we could go about this in order to keep Parcel core language agnostic.
I think number 3 is probably the best of the above options, but there are still some unanswered questions. I've prototyped it here. Let me know what you think. |
Thinking about this more, we might actually be trying to offer too much in terms of configuration. What if instead we shipped a single "runtime" (plugin) that is responsible for loading every asset type? {
"runtime": "@parcel/runtime-web"
} It would have to implement logic to load every possible asset type. Luckily, in terms of the final transforms assets, there aren't that many unique asset types you load on the web:
A singular runtime would have a lot more control than what we can offer through configuration. It'd be easier to ship a |
Yeah that could possibly work. Perhaps it is too rare a case to need to customize the way bundles are loaded on demand. Parcel 1 has:
We could allow multiple runtime plugins to run as well. That way people could add their own in addition to the default if they wanted. I wonder if we should support multiple runtime plugins that depend on the target environment. That way you could have a multi-target build (e.g. web + electron), and have it go through different runtime plugins. {
"runtime": {
"browser": ["@parcel/runtime-browser"],
"node": ["@parcel/runtime-node"],
"electron": ["@parcel/runtime-electron"]
}
} Thoughts? |
I'm not sure, because there you are talking about targets where you have "main/module/browser/electron" which have no meaning besides their associated environment... Technically you could redefine the "electron" target to have an environment that has nothing to do with electron. Really this configuration is more aligned with the "environment" than it is the "target", so you'd have one of two options that I can see:
{
"targets": {
"electron": {
"engines": { "electron": ">=2.x" },
"runtime": "@parcel/runtime-electron"
}
}
}
{
"runtime": {
"...if electron in engines...": "@parcel/runtime-electron",
"...if has browserlist....": "@parcel/runtime-browser",
"...if node in engines...": "@parcel/runtime-node"
}
}
Do you mean this separately from the |
Each bundle has an associated environment, which gets propagated from the target, and which can possibly change at an async import (e.g. "browser" -> "web-worker"). This is the environment's context, which represent the globals and such that are available in that environment. This is basically what a runtime plugin would need to target. It's not based on the name given by the user in package.json which is meaningless as you say, but is resolved based on engines specified there or static analysis. The ones we have so far are here.
Accepting an array of runtimes. I don't think there is a reason not to? |
Oh I understand.
Depends on what you want to allow runtimes to do. If you just want a fallback system of loaders like the "resolver" plugins, I think that's fine. |
I could see someone wanting to inject some custom code not even related to loaders or hmr into every bundle for some purpose. Basically runtime plugins are an opportunity to add things to a bundle in some way. |
runtime mean loader can control all the bundle? |
I prototyped the above idea here. Overall, I like it but it ended up with some duplication between the browser and node runtimes. The actual loaders are all different, but the runtime that calls the loaders is the same, along with the code that inserts it into the bundle. HMR, when it comes, will probably be similar but slightly different. I guess we could do a single runtime package with all the loaders in it, e.g. |
I think the good idea was the idea of a runtime plugin that is resolved by environment. Seems okay to me to have the same plugin resolve for different environments if those environments are mostly the same. Doesn't seem like we would gain much from splitting it into 3 different packages, at least at this point. |
Created a PR here: #2534 |
This issue is to discuss the design of loaders in Parcel 2. It will be closed once resolved and a new one will be opened for the implementation.
Loader plugins in Parcel 2 are responsible for generating runtime modules that know how to load assets of various types over the network, e.g. JS, CSS, WASM, etc. Loaders are added to the bundle graph just before packaging, once code split points are known. There are loader plugins defined in the
.parcelrc
config for various types of assets, and theirgenerate
method is called to generate an asset to add to the graph for each bundle reference.The problem with loaders is that they are pretty JavaScript specific, at least how they are currently defined. They load bundles of some type into a JS context. This is somewhat limiting potentially if we want Parcel to support more target languages, like native or something. They are also the only the only plugin type that is specific to JS in Parcel core.
I looked into implementing loaders in the BundlerRunner, just after naming and before packaging. There were a few issues:
bundle
node in an individual bundle's asset graph, which is kinda weird but works. Would love to find a better design for that. This does mean that these dependencies are not watched (they are not in the main asset graph), but that would probably be ok I think.bundle-loader.js
in the above graph). An individual loader only knows how to load one bundle, but multiple bundles may need to be loaded in parallel for a bundle group, and we'd want to cache in order to not load the same bundle twice. The runtime handles that. However, the runtime is pretty specific to the packager's implementation of the module system - needs to hook into module registration etc. Seems like the runtime should actually be injected by the packager...One idea is to generalize the idea of loaders into "runtime" plugins, which run after bundlers, and can inject any sort of runtime into a bundle. For example, there would be a JS runtime plugin which would inject loaders for JS, the runtime for loaders, the HMR runtime, etc. It still seems like packagers and runtimes are tightly coupled though. The way the packager writes the module system would have an effect on how the runtime could work.
Any other ideas would be greatly appreciated. I think this is the last big piece we need to figure out for Parcel 2!
cc. @padmaia @jamiebuilds
The text was updated successfully, but these errors were encountered: