Skip to content
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

Prerender in subprocess #5306

Closed
benmccann opened this issue Jun 28, 2022 · 20 comments · Fixed by #5678
Closed

Prerender in subprocess #5306

benmccann opened this issue Jun 28, 2022 · 20 comments · Fixed by #5678
Labels
p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc.
Milestone

Comments

@benmccann
Copy link
Member

benmccann commented Jun 28, 2022

Describe the problem

SvelteKit exits:

process.exit(0);

This means you can't have another plugin after the SvelteKit plugin

Describe the proposed solution

Add a new close hook

Have prerender call server.close()

Alternatives considered

No response

Importance

Prevents https://github.com/userquin/pwa-sveltekit-vite from working

Additional Information

No response

@Rich-Harris
Copy link
Member

Adding a new hook just for this purpose would be unfortunate, I think — especially since (as far as the user is concerned) the connection between prerendering and close is unclear. We'd also be relying on people to implement close correctly, otherwise their builds would never complete (and people would assume it was because of a SvelteKit bug).

Instead, if we want to avoid exiting the main process, we should probably just do the prerendering in a child process that we can safely kill once prerendering is complete.

@furudean
Copy link
Contributor

A proper exit can't be guaranteed in all environments, especially serverless ones.

@Conduitry
Copy link
Member

A proper exit can't be guaranteed in all environments, especially serverless ones.

This issue is just referring to things that would happen during prerendering right, and not at runtime? I would certainly hope that we can rely on all the Node stuff we want while the app is being built, regardless of which platform it is being deployed to.

@furudean
Copy link
Contributor

Ack, my bad.

@userquin
Copy link
Contributor

how about just adding a global end build hook, just call it with some context (Vite and/or SvelteKit) before process.exit is called when an adapter is configured (should also be called when the adapter is not configured)? This way you don't need to change anything, just adding the global end hook call before the exit and also when the adapter is not configured.

IIRC I sent a PR for it a few months ago when trying to include pwa plugin in SvelteKit using the rollup interplugin communication api.

@benmccann benmccann changed the title Hook for server shutdown Pretender in subprocess Jul 5, 2022
@benmccann benmccann changed the title Pretender in subprocess Prerender in subprocess Jul 5, 2022
@matiasfha
Copy link

matiasfha commented Jul 9, 2022

Maybe completely wrong my questions here but, that this means that is not possible to run a plugin after the closeBundle hook.
I'm looking for a way to run some transformation on the pre-rendered output files.

Based on what I tried I cannot use writeBundle since the pre-rendered files are not yet there and either closeBundle

Meaning that based on this

async closeBundle() {
			if (svelte_config.kit.adapter) {
				const { adapt } = await import('./build/adapt/index.js');
				await adapt(svelte_config, build_data, prerendered, { log });
			} else {
				console.log(colors.bold().yellow('\nNo adapter specified'));
				// prettier-ignore
				console.log(
					`See ${colors.bold().cyan('https://kit.svelte.dev/docs/adapters')} to learn how to configure your app to run on the platform of your choosing`
				);
			}

			if (is_build && svelte_config.kit.prerender.enabled) {
				// this is necessary to close any open db connections, etc.
				// TODO: prerender in a subprocess so we can exit in isolation
				// https://github.com/sveltejs/kit/issues/5306
				process.exit(0);
			}
		},

The only way to run some transformation on those files is to write a custom adapter right?

@benmccann
Copy link
Member Author

@matiasfha the prerendered output files get generated in writeBundle, so I would expect you to be able to use writeBundle if your plugin is after the SvelteKit plugin. You may have to wait for #5439 to be released first though to ensure that your changes would get copied by the adapters

If exit being called is preventing you from doing what you're trying to do, please feel free to send a PR to fix this issue. I don't know that we're likely to get to it anytime soon on our own since it doesn't affect most users

@benmccann benmccann added the p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. label Jul 10, 2022
@userquin
Copy link
Contributor

@benmccann I like the idea behind Astro and iles, where you have a lot of hooks, this way ppl can Just add their hooks in the plugin.

@matiasfha
Copy link

@benmccann the writeBundle hook can't read the prerendered output files in a reliable way. Looks like at the point the plugin calls this hook the previous plugin (the sveltekit one) is not yet done with it.
But, the adapt function called in closeBundle can do it.
If I write an adapter instead of a plugin the content of the files is available.
So, writing an adapter for this is not the way to go since the adapter is meant to do something different but would it be ok to add a new hook to be call before the adapter? I can do the PR but would like to check if it will be the correct path to allow users to run code before adapter to do some transformations.

@benmccann
Copy link
Member Author

would it be ok to add a new hook to be call before the adapter?

I feel like adding SvelteKit hooks is not the ideal solution for this and we should instead use Vite hooks. That would allow Vite plugins to be used across projects in the Vite ecosystem and not just with SvelteKit

the writeBundle hook can't read the prerendered output files in a reliable way. Looks like at the point the plugin calls this hook the previous plugin (the sveltekit one) is not yet done with it.

It looks like that's because they're called in parallel: https://rollupjs.org/guide/en/#output-generation-hooks

So yeah, that introduces a bit of a conundrum. By default, if you do it in writeBundle you're not sure prerendering has finished and if you do it in closeBundle you're not sure that it will happen before the the adapters have run. I think perhaps the best solution might be if you added enforce: 'post' to your plugin to ensure it occurs after prerendering has finished.

I guess this is why vite-plugin-pwa has had to add a timeout to wait for prerendering to finish, which is not the cleanest solution though it allowed to get and end-to-end demo working. @userquin would you be able to remove prerenderTimeout if you add enforce: 'post' to vite-plugin-pwa?

@matiasfha
Copy link

@benmccann thanks for the answer.
I will check the timeout approach.
Just by using enforce: 'post' is not enough for this case.

@userquin
Copy link
Contributor

@benmccann the plugin (there are 3) that generates the build has enforce: 'post':

@dominikg
Copy link
Member

fyi, it looks like closeBundle is also called when vite dev restarts the server due to a config file change. So anything you do there that should only be done after build+prerender needs to validate that it is actually the build command and not dev.

Details:
server.close calls pluginContainer.close which calls https://github.com/vitejs/vite/blob/e685de39e54123fd86928828fa908ede31a23fa8/packages/vite/src/node/server/pluginContainer.ts#L677

@benmccann
Copy link
Member Author

Hmm. I'm not sure why enforce: 'post' wouldn't work for either of you. The prerendering is awaited and doesn't happen in a worker, so I would expect it to be completely finished.

@userquin
Copy link
Contributor

@dominikg the build plugin has apply: 'build' so it shouldn't be affected ;)

@dominikg
Copy link
Member

@userquin kit plugin does not set apply:

name: 'vite-plugin-svelte-kit',

@userquin
Copy link
Contributor

@dominikg uhmmm, the result is the same, confirmed with patak, so the problem is on the sveltekit plugin...

@dominikg
Copy link
Member

yes, already added a PR to improve the kit plugin. Might be nice to split it into separate dev/build/common plugins to avoid conditionals inside the hooks 🤔 but thats out of scope here. I wanted to mention it as you all were discussing behavior of closeBundle and it caught me by surprise today.

@matiasfha
Copy link

matiasfha commented Jul 11, 2022

The prerendered process is in fact awaited here https://github.com/sveltejs/kit/blob/master/packages/kit/src/vite/index.js#L303

but even with that a new plugin that hooks into using

  • closeBundle nothings happens
  • writeBundle or buildEnd the folder is not present yet

I'm testing with this little snippet

function buildPlugin(ctx) {
    
    return {
        name: 'vite-plugin-image-preprocess:build',
        enforce: 'post',
        apply: 'build',
        async writeBundle() {
            let filesContent = {}
            /**
             * The pre-rendered files will be produced and stored in the output folder
             * .svelte-kit/output/prerender/
             */
            debug(`Wait for pre-render process to finish`)
            const watcher = chokidar.watch('.svelte-kit/output').on('addDir', async (path, stats) => {
                if(path.includes('prerender')) {
                    debug(`Watcher ${path}`, stats)
					const files = fs.readdirSync('.svelte-kit/output')
			        debug(files)
                }
                
            })  
            await new Promise(resolve => setTimeout(resolve, 2000))            
        }
        
    }
}

This can find the folders .svelte-kit/output/client (and children) and .svelte-kit/output/server

This is running against a new sveltekit project from scratch with the simple todo example.

Also based on the rollup documentation

Additionally, closeBundle can be called as the very last hook, but it is the responsibility of the User to manually call bundle.close() to trigger this. The CLI will always make sure this is the case.

If I understood that correctly, after a plugin calls closeBundle there is no other plugin that can call that same hook too right?
I mean, kit plugin is the last one, so plugins that runes before cannot rely on closeBundle (at least that is consistent with tests)

@benmccann
Copy link
Member Author

This Vite folks confirmed that enforce only controls the order the plugins are started, but the parallel ones will all run in parallel still. We may need a new sequential hook in Vite for this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants