sveltejs / sapper

The next small thing in web development, powered by Svelte
https://sapper.svelte.dev
MIT License
6.99k stars 433 forks source link

Consider Sapper build as a Rollup plugin #1346

Open benmccann opened 4 years ago

benmccann commented 4 years ago

Overview

Right now we execute Rollup from the Sapper binary. This is causes a few issues.

Firstly, it's been the source of a few bugs where we haven't reproduced the Rollup CLI faithfully enough. A couple of these have been fixed, but at least one remains: https://github.com/sveltejs/sapper/issues/1266

More importantly though, it makes it difficult to integrate with projects like Nollup (https://github.com/sveltejs/sapper/issues/1207) and Snowpack (https://github.com/sveltejs/sapper/issues/1204). Today there's no way to use an alternative bundler like Nollup with Sapper because Sapper must have an implementation for every bundler. It'd be a small change to enable it in the case of Nollup (rixo@faca5e0), but I'm not sure it's the right separation of concerns that Sapper should have to know about every possible build system

Implementation notes

One thing that'd be a little tricky is that if it were a Rollup plugin we might need multiple plugins. E.g. create_app happens early in the build process right now and extract_css happens later. I'm not sure if we need to maintain that ordering, but I do wonder if extract_css might live in a more generally-available plugin. Also, create_serviceworker_manifest probably deserves its own plugin or we should use Google's existing Workbox plugin

pngwn commented 4 years ago

I don't think when certain parts of the current compiler run would be problematic here, we can just use different hooks in order to do certain work at certain times. What currently happens in create_app, we would want to run during the options hook, so that we could inject options before rollup processes them. The css extraction happens both on every module and at the end for the final build, we can use the transform hook and one of writeBundle or generateBundle, although a better CSS code-splitting solution would be preferable, not sure how possible that is.

If we went down this route, plugins should be split by concern rather than when they happen, assuming rollup can support that. The Router config generation could be a plugin, the Client builder could be a plugin, the Server builder could be a plugin, etc.

I have a few ideas about how this could work it isn't clear in my mind.

pngwn commented 4 years ago

The bigger question in my mind is can we orchestrate things such that we we know which parts of the client bundle are required for a given server route, in order to include the correct assets when server rendering pages, but also in order to give the correct information to the router (if the router config is only finalised at the end of the build then this seems possible). Currently sapper has complete control over how it builds everything, doing things in whatever order it needs to to get the necessary information. We need the client bundle before we generate the server code in order to intelligently fetch assets.

We have access to everything at the end of the bundling process and can still perform whatever steps we need to at that point, if we are splitting per route as a default then it might be reasonable to inspect the output at the end of the bundling process to get the information and make that available to the server-renderer as required. But I'm not sure, I haven't thought it through properly.

benmccann commented 4 years ago

One tricky point @ajbouh pointed out is that you probably only want to run create_app once despite creating two bundles (client and server)

ajbouh commented 4 years ago

@pngwn I'm not sure I completely understand your comment, though based on my recent explorations and refactorings, I believe sapper already has this information (which client resources are needed for each server route). In fact it depends on this to properly load the associated client code and (when possible) issue the proper preload headers.