-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Allow plugins to handle assets #2872
Comments
I built a lot of the functionality you're asking for as part of |
Just some thoughts around this, I think we will need some iterations to get this right: One option could be a This would solve the issue of emitting new chunks. The question is, though, how to reference the emitted chunks from CSS as at the time of emission, the final name is not known. Another issue is how to handle assets that are emitted without a source. Technically, you can emit an asset without source during the "build" phase and then add the source during the "generate" phase. We could run the Referencing chunksPoints to be considered:
Especially the last point means that we need yet ANOTHER hook to handle assets, e.g. To handle the hashing, there are at least two approaches:
Possible solution: We can actually combine both approaches with the first serving as a fallback (with a warning) in case the second cannot be used. I.e.
Just some thoughts, suggestions are very much welcome. As this would be a HUGE refactoring, we could focus on implementing the fallback solution first and tackle the improved hashing later. Alternatively, we could first refactor the existing hashing and then add assets to the mix. |
Real quickly jotting down notes on what I'm hoping to get out of a rollup asset-handling system:
I also want to make sure that whatever we do here is compatible with systems like I need to read @lukastaegert's proposal like 4 more times and jot down what I think the APIs would look like before I can provide more useful feedback, sorry :( |
Thanks a lot for the initial feedback, as always very much welcome.
I still think the "proper" solution would be to define an API to add arbitrary files to the bundle, not unlike what you are doing now but via a utility function that is under Rollup's control and that works from all hooks, not just generateBundle. From my side, I plan on solving this one way or another before the next major release (so that with the next major, we could consider adding a warning to your technique, e.g. via a Proxy). There should also be an API to remove things from the bundle but here, we might also just support |
There are several ways where we might give you the necessary information. One thing that comes to my mind is extending To query inter-asset dependencies, we could add a Then using the information you get in Still, I keep wondering how we want to reference assets in all these APIs. Should we be using auto-generated ids or maybe allow the plugin developer to set an id, e.g. a file name? The latter would also improve readability of the output of getModule/AssetInfo. |
What's the difference between this requirement and becoming webpack or gulp? I think this requirement is real, and not means another general bundle tool, but I can't make it clear while I'm thinking about this. |
@lukastaegert what are your thoughts on continuing to track this discussion? |
Definitely worth keeping, especially with the other issues rolled into this one. |
This would be great. Much of what has been discussed here I've accomplished by mutating the bundle in |
Maybe try this plugin? rollup-plugin-scss-smart-asset |
In terms of handling assets with dependencies, should Then, a CSS plugin could use |
Actually, my plan is to make this go away and fix hashing "for good" because this "augmenting" is a difficult and error-prone thing. Here is my analysis and vision, which I hope to write up properly soon:
This would make sure we aways have correct hashes and also allow adding arbitrary additional dependencies e.g. in |
Some thoughts around this: What if a import url from 'foo'; If An HTML plugin could see: <link rel="stylesheet" href="./styles.css">
<script type="module" src="./script.js"></script> And call: const styleRef = this.emitFile({
id: './styles.css',
importer: htmlId,
});
const scriptRef = this.emitFile({
id: './script.js',
importer: htmlId,
}); And replace The use of I've deliberately left This system would also allow something like HTML to be an entry point, since it wouldn't have to produce a JS module at the end. |
We should not recycle the load hook here IMO, this would be a rather breaking change for many plugins. Also it is completely unclear what it means to "import" a generic asset, i.e. what is actually imported in the JS file. The (relative/absolute) URL? The string content? Some loader code? I think this should remain in the domain of plugins that are responsible to take care of the interface between JavaScript and Assets.
what use is it to provide an importer if you actually know the importer and could just directly apply it? There may be some use in decoupling asset loading/preprocessing and connecting it to JavaScript, though. At the moment the concept of an asset is rather opaque to rollup i.e. it is just a name and some data. Of course the intention here is that most assets are actually based on files, and files usually have a type in form of an extension. So if we want to go there, I would suggest to have something like this.emitFile({type: 'asset-file', id: '/path/to/my-file.css'}); and then have a new chain of (at least) a |
Maybe it shouldn't be recycled, but I don't think it'd be a breaking change. The
I said url in the comment above, but there may be a better idea. But yes, if the default wasn't good enough, you'd write a plugin.
Say the HTML was: <link rel="stylesheet" href="postcss:./styles.css"> The HTML plugin would be able to extract
That could work. Although you'd need another mechanism to allow an entry point to be an asset, which I was trying to avoid. |
My feeling is you are trying to put a lot of things into Rollup that plugins could handle well on their own, and as this is not Webpack I'm trying hard to keep Rollup's API as small and simple as possible. If you want HTML as an entry point, it is just something you want for the user and you can easily have it today:
Since we have emitFile now, there is no need to supply any entry point by the user as long as some plugin emits some in the buildStart hook. Rollup would not know what to do if the load hook returned an asset and as I stated above, I'm unwilling to hardcode one of several solutions here, at least two of which (content or relative URL) seem equally reasonable to me. And for CSS, creating a style tag would be a third reasonable solution. Sure we could make a call here, but this can be easily handled in plugins today so there would be no advantage except some convenience (the usual handling is emitting a virtual module that exports whatever the plugin seems fit). To my knowledge, there is no "canonical" solution yet that is implemented in the majority of actual JS runtimes. |
Maybe it would help more to go on a more abstract level and talk more about the problems and scenarios you want to solve before jumping to solutions. |
Interesting! I hadn't thought of that.
I really appreciate that, and absolutely trust your directing of the project in terms of this.
Imagine an HTML plugin that processes source like: <!DOCTYPE html>
<link rel="stylesheet" href="postcss:./styles.css">
<p>Hello!</p> For each external file reference in the HTML (
If the HTML plugin wanted to inline the contents instead, that seems like something the HTML plugin would handle once it has access to the source of the resulting file. |
We have a the plugin
I personally love the possibility to give it an array of html files (or strings) and it creates the best splitting of js and injects it where needed. It does not (yet) handle advanced processing of css but it might at some point 🤞 You can see more details here: https://www.npmjs.com/package/@open-wc/rollup-plugin-html |
my experiment #2839 (comment) solves the issues with late hasing based on content after all runs as it runs hashing as last step and the modifes the chunks to use the new hash |
I want to pass an html file to rollup and get an output where urls have been hashed. Html file exampleHtml input <!DOCTYPE html>
<html>
<head>
<title>Title</title>
<meta charset="utf-8" />
<link rel="icon" href="./favicon.ico" />
<link rel="stylesheet" type="text/css" href="./main.css" />
</head>
<body>
<script type="module" src="./main.js"></script>
</body>
</html> Html output <!DOCTYPE html>
<html>
<head>
<title>Title</title>
<meta charset="utf-8" />
<link rel="icon" href="assets/favicon-5340s4789a.ico" />
<link rel="stylesheet" type="text/css" href="assets/main-3b329ff0.css" />
</head>
<body>
<script type="module" src="./main-f7379e10.js"></script>
</body>
</html> As mentioned by @surma the css files would be parsed to find stuff like To make this happen I have followed the example from rollup documentation around All this to say that it's possible to write such plugin with the current rollup api with one limitation: You have to wait for It means two things: import { rollup } from 'rollup'
const bundle = await rollup({
input: 'main.html',
})
// here I would expect bundle to contains assets
await bundle.generate({
format: 'es',
dir: 'dist'
}) (2) An asset that is not an entry point cannot reference js. It means you cannot import html containing js from in a js file because rollup would throw with
Rollup throwing source already set code exampleimport { rollup } from "rollup"
let jsReferenceId
let htmlReferenceId
const bundle = await rollup({
input: "main.js",
plugins: [
{
resolveId: (id) => id,
load(id) {
if (id.endsWith(".html")) {
jsReferenceId = this.emitFile({
type: "chunk",
id: "./file.js",
})
htmlReferenceId = this.emitFile({
type: "asset",
name: "file.html",
source: `<!DOCTYPE html>
<html>
<head>
<script type="module" src="./file.js"></script>
</head>
</html>`,
})
return `export default import.meta.ROLLUP_FILE_URL_${htmlReferenceId}`
}
if (id.includes("main.js")) {
return `import htmlUrl from "file.html"
console.log(htmlUrl)`
}
return `export default 42`
},
generateBundle() {
this.setAssetSource(
htmlReferenceId,
`<!DOCTYPE html>
<html>
<head>
<script type="module" src="${this.getFileName(jsReferenceId)}"></script>
</head>
</html>`,
)
},
},
],
})
bundle.write({
format: "es",
dir: "./dist",
}) If I remove the source when emitting
|
If we allow plugins to do additional transformations to assets, surely it would make sense for assets to contain a This is sort of a post process step, but with the standardization of function getAssetSource (ref: string): string | undefined
function deleteAsset (ref: string): void Dealing with deleted assets is still something to be considered (does it just ignore any sort of reference in the source code, or?), but I'm mentioning them here anyway |
(Somewhat related to #2823).
Feature Use Case
With #2742 landed, I tried to set up Rollup to accept
html
files as entry points. HTML can contain references to JavaScript, which clearly should be handled usingemitChunk
.CSS, however, is in a weird spot: It doesn’t map well to JavaScript so is more of an asset, but I want my
css
plugin to parse it to find further assets referenced within (e.g. usingbackground-image: url(...)
). Plugins are currently limited to handling JavaScript. Additionally, if I shoe-horn CSS to be handled as a chunk (which I successfully did in an experiment), Rollup will force the output file to have a.js
file extensions.Feature Proposal
My idea is (and feel free to discard this!) to give plugins a hook to handle newly emitted assets and analyze them for further dependencies (as proposed in #2823).
It’s worth noting that an SVGs are assets, which could contain references to JavaScript (same goes for straight HTML). So there is a chance that an asset has a dependency on a JavaScript chunk.
The text was updated successfully, but these errors were encountered: