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 during build #4192

Merged
merged 11 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/gorgeous-beans-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

[breaking] expose entire config to adapters, rather than just appDir and trailingSlash
5 changes: 5 additions & 0 deletions .changeset/khaki-dolls-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

[breaking] replace builder.prerender() with builder.writePrerendered() and builder.prerendered
5 changes: 5 additions & 0 deletions .changeset/large-berries-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

[breaking] prerender pages during build, regardless of adapter
5 changes: 5 additions & 0 deletions .changeset/modern-toys-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Add config.kit.prerender.default option
5 changes: 5 additions & 0 deletions .changeset/nine-walls-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Use prerendered pages in svelte-kit preview
5 changes: 5 additions & 0 deletions .changeset/shaggy-days-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Make prerendered paths available to service workers
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{
"files": [
"packages/kit/src/packaging/test/fixtures/**/expected/**/*",
"packages/kit/src/core/adapt/prerender/fixtures/**/*"
"packages/kit/src/core/build/prerender/fixtures/**/*"
],
"options": {
"requirePragma": true
Expand Down
2 changes: 1 addition & 1 deletion documentation/docs/09-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ The types for `Adapter` and its parameters are available in [types/config.d.ts](
Within the `adapt` method, there are a number of things that an adapter should do:

- Clear out the build directory
- Call `builder.prerender({ dest })` to prerender pages
- Write SvelteKit output with `builder.writeClient`, `builder.writePrerendered`, `builder.writeServer`, and `builder.writeStatic`
- Output code that:
- Imports `App` from `${builder.getServerDirectory()}/app.js`
- Instantiates the app with a manifest generated with `builder.generateManifest({ relativePath })`
Expand Down
14 changes: 11 additions & 3 deletions documentation/docs/10-page-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,24 @@ Ordinarily, SvelteKit [hydrates](/docs/appendix#hydration) your server-rendered

It's likely that at least some pages of your app can be represented as a simple HTML file generated at build time. These pages can be [_prerendered_](/docs/appendix#prerendering) by your [adapter](/docs/adapters).

If your entire app is suitable for prerendering, you could use [`adapter-static`](https://github.com/sveltejs/kit/tree/master/packages/adapter-static), which will generate HTML files for every page, plus additional files that are requested by `load` functions in those pages.

In many cases, you'll only want to prerender specific pages in your app. You'll need to annotate these pages:
Prerendering happens automatically for any page with the `prerender` annotation:

```html
<script context="module">
export const prerender = true;
</script>
```

Alternatively, you can set [`confit.kit.prerender.default`](/docs/configuration#prerender) to `true` and prerender everything except pages that are explicitly marked as _not_ prerenderable:

```html
<script context="module">
export const prerender = false;
</script>
```

> If your entire app is suitable for prerendering, you can use [`adapter-static`](https://github.com/sveltejs/kit/tree/master/packages/adapter-static), which will output files suitable for use with any static webserver.

The prerenderer will start at the root of your app and generate HTML for any prerenderable pages it finds. Each page is scanned for `<a>` elements that point to other pages that are candidates for prerendering — because of this, you generally don't need to specify which pages should be accessed. If you _do_ need to specify which pages should be accessed by the prerenderer, you can do so with the `entries` option in the [prerender configuration](/docs/configuration#prerender).

#### When not to prerender
Expand Down
1 change: 1 addition & 0 deletions documentation/docs/13-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ See [Prerendering](/docs/page-options#prerender). An object containing zero or m

- `concurrency` — how many pages can be prerendered simultaneously. JS is single-threaded, but in cases where prerendering performance is network-bound (for example loading content from a remote CMS) this can speed things up by processing other tasks while waiting on the network response
- `crawl` — determines whether SvelteKit should find pages to prerender by following links from the seed page(s)
- `default` — set to `true` to prerender every page without `export const prerender = false`
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should combine default and enabled into a single option. E.g. with values like true | false | 'never' or 'default-true' | 'default-false' | 'never'

Copy link
Member Author

Choose a reason for hiding this comment

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

I think I prefer the booleans, personally

- `enabled` — set to `false` to disable prerendering altogether
- `entries` — an array of pages to prerender, or start crawling from (if `crawl: true`). The `*` string includes all non-dynamic routes (i.e. pages with no `[parameters]` )
- `onError`
Expand Down
6 changes: 2 additions & 4 deletions packages/adapter-cloudflare-workers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ export default function () {
builder.rimraf(bucket);
builder.rimraf(entrypoint);

builder.log.info('Prerendering static pages...');
const prerendered = await builder.prerender({ dest: bucket });

builder.log.info('Installing worker dependencies...');
builder.copy(`${files}/_package.json`, `${tmp}/package.json`);

Expand All @@ -49,7 +46,7 @@ export default function () {
`${tmp}/manifest.js`,
`export const manifest = ${builder.generateManifest({
relativePath
})};\n\nexport const prerendered = new Set(${JSON.stringify(prerendered.paths)});\n`
})};\n\nexport const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n`
);

await esbuild.build({
Expand All @@ -65,6 +62,7 @@ export default function () {
builder.log.minor('Copying assets...');
builder.writeClient(bucket);
builder.writeStatic(bucket);
builder.writePrerendered(bucket);
}
};
}
Expand Down
5 changes: 2 additions & 3 deletions packages/adapter-cloudflare/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@ export default function (options = {}) {

builder.writeStatic(dest);
builder.writeClient(dest);

const prerendered = await builder.prerender({ dest });
builder.writePrerendered(dest);

const relativePath = posix.relative(tmp, builder.getServerDirectory());

writeFileSync(
`${tmp}/manifest.js`,
`export const manifest = ${builder.generateManifest({
relativePath
})};\n\nexport const prerendered = new Set(${JSON.stringify(prerendered.paths)});\n`
})};\n\nexport const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n`
);

builder.copy(`${files}/worker.js`, `${tmp}/_worker.js`, {
Expand Down
8 changes: 2 additions & 6 deletions packages/adapter-netlify/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ export default function ({ split = false } = {}) {

builder.log.minor(`Publishing to "${publish}"`);

builder.log.minor('Prerendering static pages...');
await builder.prerender({
dest: publish
});

builder.writeServer('.netlify/server');

// for esbuild, use ESM
Expand Down Expand Up @@ -127,6 +122,7 @@ export default function ({ split = false } = {}) {
builder.log.minor('Copying assets...');
builder.writeStatic(publish);
builder.writeClient(publish);
builder.writePrerendered(publish);

builder.log.minor('Writing redirects...');
const redirect_file = join(publish, '_redirects');
Expand All @@ -138,7 +134,7 @@ export default function ({ split = false } = {}) {
builder.copy('_headers', headers_file);
appendFileSync(
headers_file,
`\n\n/${builder.appDir}/*\n cache-control: public\n cache-control: immutable\n cache-control: max-age=31536000\n`
`\n\n/${builder.config.kit.appDir}/*\n cache-control: public\n cache-control: immutable\n cache-control: max-age=31536000\n`
);
}
};
Expand Down
6 changes: 1 addition & 5 deletions packages/adapter-node/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,7 @@ export default function ({
builder.writeClient(`${out}/client`);
builder.writeServer(`${out}/server`);
builder.writeStatic(`${out}/static`);

builder.log.minor('Prerendering static pages');
await builder.prerender({
dest: `${out}/prerendered`
});
builder.writePrerendered(`${out}/prerendered`);

writeFileSync(
`${out}/manifest.js`,
Expand Down
13 changes: 7 additions & 6 deletions packages/adapter-static/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ export default function ({ pages = 'build', assets = pages, fallback, precompres
name: '@sveltejs/adapter-static',

async adapt(builder) {
if (!fallback && !builder.config.kit.prerender.default) {
builder.log.warn(
'You should set `config.kit.prerender.default` to `true` if no fallback is specified'
);
}

builder.rimraf(assets);
builder.rimraf(pages);

builder.writeStatic(assets);
builder.writeClient(assets);

await builder.prerender({
fallback,
all: !fallback,
dest: pages
});
builder.writePrerendered(pages, { fallback });

if (precompress) {
if (pages === assets) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import adapter from '../../../index.js';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter()
adapter: adapter(),

prerender: {
default: true
}
}
};

Expand Down
30 changes: 14 additions & 16 deletions packages/adapter-vercel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,6 @@ export default function ({ external = [] } = {}) {
lambda: `${dir}/functions/node/render`
};

builder.log.minor('Prerendering static pages...');

const prerendered = await builder.prerender({
dest: `${dir}/static`
});

builder.log.minor('Generating serverless function...');

const relativePath = posix.relative(tmp, builder.getServerDirectory());
Expand Down Expand Up @@ -139,32 +133,36 @@ export default function ({ external = [] } = {}) {

builder.writeStatic(dirs.static);
builder.writeClient(dirs.static);
builder.writePrerendered(dirs.static);

builder.log.minor('Writing routes...');

builder.mkdirp(`${dir}/config`);

const prerendered_pages = Array.from(prerendered.pages, ([src, page]) => ({
const prerendered_pages = Array.from(builder.prerendered.pages, ([src, page]) => ({
src,
dest: page.file
}));

const prerendered_redirects = Array.from(prerendered.redirects, ([src, redirect]) => ({
src,
headers: {
Location: redirect.location
},
status: redirect.status
}));
const prerendered_redirects = Array.from(
builder.prerendered.redirects,
([src, redirect]) => ({
src,
headers: {
Location: redirect.location
},
status: redirect.status
})
);

writeFileSync(
`${dir}/config/routes.json`,
JSON.stringify([
...redirects[builder.trailingSlash],
...redirects[builder.config.kit.trailingSlash],
...prerendered_pages,
...prerendered_redirects,
{
src: `/${builder.appDir}/.+`,
src: `/${builder.config.kit.appDir}/.+`,
headers: {
'cache-control': 'public, immutable, max-age=31536000'
}
Expand Down
7 changes: 5 additions & 2 deletions packages/kit/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as ports from 'port-authority';
import { load_config } from './core/config/index.js';
import { networkInterfaces, release } from 'os';
import { coalesce_to_error } from './utils/error.js';
import { logger } from './core/utils.js';

/** @param {unknown} e */
function handle_error(e) {
Expand Down Expand Up @@ -91,16 +92,18 @@ prog
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
const config = await load_config();

const log = logger({ verbose });

const { build } = await import('./core/build/index.js');
const build_data = await build(config);
const { build_data, prerendered } = await build(config, { log });

console.log(
`\nRun ${colors.bold().cyan('npm run preview')} to preview your production build locally.`
);

if (config.kit.adapter) {
const { adapt } = await import('./core/adapt/index.js');
await adapt(config, build_data, { verbose });
await adapt(config, build_data, prerendered, { log });

// this is necessary to close any open db connections, etc
process.exit(0);
Expand Down
Loading