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

[fix] prerender in subprocess #5678

Merged
merged 14 commits into from
Jul 25, 2022
5 changes: 5 additions & 0 deletions .changeset/witty-avocados-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

[breaking] remove Prerendered.assets
3 changes: 2 additions & 1 deletion packages/kit/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ export default [
{
input: {
cli: 'src/cli.js',
hooks: 'src/hooks.js',
node: 'src/node/index.js',
'node/polyfills': 'src/node/polyfills.js',
hooks: 'src/hooks.js',
prerender: 'src/core/prerender/prerender.js',
vite: 'src/vite/index.js'
},
output: {
Expand Down
37 changes: 32 additions & 5 deletions packages/kit/src/core/prerender/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ import { is_root_relative, resolve } from '../../utils/url.js';
import { queue } from './queue.js';
import { crawl } from './crawl.js';
import { escape_html_attr } from '../../utils/escape.js';
import { logger } from '../utils.js';
import { load_config } from '../config/index.js';

/**
* @typedef {import('types').PrerenderErrorHandler} PrerenderErrorHandler
* @typedef {import('types').Logger} Logger
*/

const [, , client_out_dir, results_path, manifest_path, verbose] = process.argv;

prerender({ client_out_dir, results_path, manifest_path, verbose: verbose === 'true' });

/**
* @param {Parameters<PrerenderErrorHandler>[0]} details
* @param {import('types').ValidatedKitConfig} config
Expand Down Expand Up @@ -49,15 +55,29 @@ function normalise_error_handler(log, config) {
const OK = 2;
const REDIRECT = 3;

/**
* @param {string} path
* @param {import('types').Prerendered} prerendered
*/
const output_and_exit = (path, prerendered) => {
writeFileSync(
path,
JSON.stringify(prerendered, (_key, value) =>
value instanceof Map ? Array.from(value.entries()) : value
)
);
process.exit(0);
};

/**
* @param {{
* config: import('types').ValidatedKitConfig;
* client_out_dir: string;
* results_path: string;
* manifest_path: string;
* log: Logger;
* verbose: boolean;
* }} opts
*/
export async function prerender({ config, client_out_dir, manifest_path, log }) {
export async function prerender({ client_out_dir, results_path, manifest_path, verbose }) {
/** @type {import('types').Prerendered} */
const prerendered = {
pages: new Map(),
Expand All @@ -66,10 +86,17 @@ export async function prerender({ config, client_out_dir, manifest_path, log })
paths: []
};

/** @type {import('types').ValidatedKitConfig} */
const config = (await load_config()).kit;

if (!config.prerender.enabled) {
return prerendered;
output_and_exit(results_path, prerendered);
return;
}

/** @type {import('types').Logger} */
const log = logger({ verbose });

installPolyfills();
const { fetch } = globalThis;
globalThis.fetch = async (info, init) => {
Expand Down Expand Up @@ -349,7 +376,7 @@ export async function prerender({ config, client_out_dir, manifest_path, log })
mkdirp(dirname(file));
writeFileSync(file, await rendered.text());

return prerendered;
output_and_exit(results_path, prerendered);
}

/** @return {string} */
Expand Down
48 changes: 30 additions & 18 deletions packages/kit/src/vite/index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import fs from 'fs';
import path from 'path';
import { spawnSync } from 'node:child_process';
import fs, { existsSync } from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import colors from 'kleur';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import * as vite from 'vite';
import { mkdirp, posixify, rimraf } from '../utils/filesystem.js';
import * as sync from '../core/sync/sync.js';
import { build_server } from './build/build_server.js';
import { build_service_worker } from './build/build_service_worker.js';
import { prerender } from '../core/prerender/prerender.js';
import { load_config } from '../core/config/index.js';
import { dev } from './dev/index.js';
import { generate_manifest } from '../core/generate_manifest/index.js';
import { get_runtime_directory, logger } from '../core/utils.js';
import { find_deps, get_default_config as get_default_build_config } from './build/utils.js';
import { preview } from './preview/index.js';
import { get_aliases, resolve_entry } from './utils.js';
import { fileURLToPath } from 'node:url';

const cwd = process.cwd();

Expand Down Expand Up @@ -273,9 +275,8 @@ function kit() {
* then use this hook to kick off builds for the server and service worker.
*/
async writeBundle(_options, bundle) {
log = logger({
verbose: vite_config.logLevel === 'info'
});
const verbose = vite_config.logLevel === 'info';
log = logger({ verbose });

fs.writeFileSync(
`${paths.client_out_dir}/${svelte_config.kit.appDir}/version.json`,
Expand Down Expand Up @@ -320,11 +321,29 @@ function kit() {
process.env.SVELTEKIT_SERVER_BUILD_COMPLETED = 'true';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated to this PR, but pretty sure we can get rid of this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No objections assuming it's not used anywhere. It was an internal-only thing, so we shouldn't worry about anyone else relying on it

log.info('Prerendering');

prerendered = await prerender({
config: svelte_config.kit,
client_out_dir: vite_config.build.outDir,
manifest_path,
log
const results_path = `${svelte_config.kit.outDir}/generated/prerendered.json`;

// do prerendering in a subprocess so any dangling stuff gets killed upon completion
const file = fileURLToPath(import.meta.url);
const dir = file.substring(0, file.lastIndexOf(path.sep));
const dist_script = path.join(dir, 'prerender.js');
const script = existsSync(dist_script)
? dist_script
: path.join(dir, '../core/prerender/prerender.js');
const { status } = spawnSync(
'node',
[script, vite_config.build.outDir, results_path, manifest_path, '' + verbose],
{ stdio: 'inherit' }
);
if (status !== 0) {
throw new Error('prerendering failed');
}

prerendered = JSON.parse(fs.readFileSync(results_path, 'utf8'), (key, value) => {
if (key === 'pages' || key === 'assets' || key === 'redirects') {
return new Map(value);
}
return value;
});

if (options.service_worker_entry_file) {
Expand Down Expand Up @@ -364,13 +383,6 @@ function kit() {
`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 (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 and then remove this
// https://github.com/sveltejs/kit/issues/5306
process.exit(0);
}
},

/**
Expand Down