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 stale prerendering bug #1040

Merged
merged 15 commits into from
Apr 15, 2021
5 changes: 5 additions & 0 deletions .changeset/loud-seals-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Fix stale prerendering bug
2 changes: 1 addition & 1 deletion examples/hn.svelte.dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"build": "svelte-kit build --verbose",
"start": "svelte-kit start"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion examples/realworld.svelte.dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"build": "svelte-kit build --verbose",
"start": "svelte-kit start"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion examples/svelte-kit-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"build": "svelte-kit build --verbose",
"start": "svelte-kit start",
"build:vercel": "ADAPTER=@sveltejs/adapter-vercel OPTIONS={} npm run build",
"build:cloudflare-workers": "ADAPTER=@sveltejs/adapter-cloudflare-workers OPTIONS={} npm run build"
Expand Down
4 changes: 2 additions & 2 deletions packages/kit/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ prog

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

console.log(`\nRun ${colors.bold().cyan('npm start')} to try your app locally.`);

if (config.kit.adapter) {
const { adapt } = await import('./core/adapt/index.js');
await adapt(config, { verbose });
await adapt(config, build_data, { verbose });
} else {
console.log(colors.bold().yellow('\nNo adapter specified'));

Expand Down
66 changes: 0 additions & 66 deletions packages/kit/src/core/adapt/AdapterUtils.js

This file was deleted.

9 changes: 5 additions & 4 deletions packages/kit/src/core/adapt/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import colors from 'kleur';
import { logger } from '../utils.js';
import AdapterUtils from './AdapterUtils.js';
import { get_utils } from './utils.js';

/**
* @param {import('../../../types.internal').ValidatedConfig} config
* @param {import('types.internal').ValidatedConfig} config
* @param {import('types.internal').BuildData} build_data
* @param {{ cwd?: string, verbose: boolean }} opts
*/
export async function adapt(config, { cwd = process.cwd(), verbose }) {
export async function adapt(config, build_data, { cwd = process.cwd(), verbose }) {
const { name, adapt } = config.kit.adapter;

console.log(colors.bold().cyan(`\n> Using ${name}`));

const log = logger({ verbose });
const utils = new AdapterUtils({ cwd, config, log });
const utils = get_utils({ cwd, config, build_data, log });
await adapt(utils);

log.success('done');
Expand Down
81 changes: 27 additions & 54 deletions packages/kit/src/core/adapt/prerender.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { dirname, join, resolve as resolve_path, sep as path_separator } from 'path';
import { readFileSync, writeFileSync } from 'fs';
import { dirname, join, resolve as resolve_path } from 'path';
import { parse, pathToFileURL, resolve } from 'url';
import glob from 'tiny-glob/sync.js';
import { mkdirp } from '../filesystem/index.js';

/** @param {string} html */
Expand Down Expand Up @@ -52,13 +51,13 @@ const REDIRECT = 3;
* out: string;
* log: import('../../../types.internal').Logger;
* config: import('../../../types.internal').ValidatedConfig;
* build_data: import('../../../types.internal').BuildData;
* force: boolean; // disregard `export const prerender = true`
* }} opts */
export async function prerender({ cwd, out, log, config, force }) {
export async function prerender({ cwd, out, log, config, build_data, force }) {
const dir = resolve_path(cwd, '.svelte/output');

const seen = new Set();
const seen_files = new Set();

const server_root = resolve_path(dir);

Expand All @@ -79,6 +78,14 @@ export async function prerender({ cwd, out, log, config, force }) {
throw new Error(`${status} ${path}`);
};

const files = new Set([...build_data.static, ...build_data.client]);

build_data.static.forEach((file) => {
if (file.endsWith('/index.html')) {
files.add(file.slice(0, -11));
}
});

/** @param {string} path */
async function visit(path) {
if (seen.has(path)) return;
Expand Down Expand Up @@ -162,8 +169,9 @@ export async function prerender({ cwd, out, log, config, force }) {
let match;
const pattern = /<(a|img|link|source)\s+([\s\S]+?)>/gm;

const hrefs = [];

while ((match = pattern.exec(cleaned))) {
let hrefs = [];
const element = match[1];
const attrs = match[2];

Expand All @@ -175,67 +183,32 @@ export async function prerender({ cwd, out, log, config, force }) {
}
hrefs.push(...get_srcset_urls(attrs));
}
}

hrefs = hrefs.filter(Boolean);

for (const href of hrefs) {
const resolved = resolve(path, href);

if (resolved[0] !== '/') continue;
if (seen_files.has(resolved)) continue;

const parsed = parse(resolved);
for (const href of hrefs) {
if (!href) continue;

const file = parsed.pathname.replace(config.kit.paths.assets, '').slice(1);
const resolved = resolve(path, href);
if (resolved[0] !== '/') continue;

const file_exists =
(file.startsWith(`${config.kit.appDir}/`) && existsSync(`${dir}/client/${file}`)) ||
existsSync(`${out}/${file}`) ||
existsSync(`${config.kit.files.assets}/${file}`) ||
existsSync(`${config.kit.files.assets}/${file}/index.html`);
const parsed = parse(resolved);

if (file_exists) {
seen_files.add(resolved);
continue;
}
const file = parsed.pathname.replace(config.kit.paths.assets, '').slice(1);
if (files.has(file)) continue;

if (parsed.query) {
// TODO warn that query strings have no effect on statically-exported pages
}

await visit(parsed.pathname.replace(config.kit.paths.base, ''));
if (parsed.query) {
// TODO warn that query strings have no effect on statically-exported pages
}

await visit(parsed.pathname.replace(config.kit.paths.base, ''));
}
}
}
}

for (const entry of config.kit.prerender.pages) {
if (entry === '*') {
// remove the prefix '.' from the extensions array
const extensions = config.extensions.map((extension) => extension.slice(1));
const extensions_regex = new RegExp(`\\.(${extensions.join('|')})$`);
const entries = glob(`**/*.{${extensions.join(',')}}`, { cwd: config.kit.files.routes })
.map((file) => {
// support both windows and unix glob results
const parts = file.split(path_separator);

if (parts.some((part) => part[0] === '_' || /\[/.test(part))) {
return null;
}

parts[parts.length - 1] = parts[parts.length - 1].replace(extensions_regex, '');
if (parts[parts.length - 1] === 'index') parts.pop();

if (parts[parts.length - 1] === '$layout' || parts[parts.length - 1] == '$error') {
return null;
}

return `/${parts.join('/')}`;
})
.filter(Boolean);

for (const entry of entries) {
for (const entry of build_data.entries) {
await visit(entry);
}
} else {
Expand Down
52 changes: 26 additions & 26 deletions packages/kit/src/core/adapt/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,33 @@ import * as uvu from 'uvu';
import * as assert from 'uvu/assert';
import rimraf from 'rimraf';
import glob from 'tiny-glob/sync.js';
import AdapterUtils from '../AdapterUtils.js';
import { get_utils } from '../utils.js';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = join(__filename, '..');

/** @param {string} _msg */
const log = (_msg) => {};

/**
* @param {string} cwd
* @param {any} config
*/
const get_utils = (cwd, config) =>
new AdapterUtils({
cwd,
config,
log: Object.assign(log, {
info: log,
minor: log,
warn: log,
error: log,
success: log
})
});

const suite = uvu.suite('AdapterUtils');

suite('utils ', () => {
assert.ok(AdapterUtils);
const logger = (_msg) => {};

/** @type {import('types.internal').Logger} */
const log = Object.assign(logger, {
info: logger,
minor: logger,
warn: logger,
error: logger,
success: logger
});

const suite = uvu.suite('adapter utils');

suite('copy files', () => {
const cwd = join(__dirname, 'fixtures/basic');

/** @type {import('types.internal').ValidatedConfig} */
const config = {
kit: {
// @ts-ignore
files: {
assets: join(__dirname, 'fixtures/basic/static')
},
Expand All @@ -48,7 +38,10 @@ suite('copy files', () => {
}
};

const utils = get_utils(cwd, config);
/** @type {import('types.internal').BuildData} */
const build_data = { client: [], server: [], static: [], entries: [] };

const utils = get_utils({ cwd, config, build_data, log });

const dest = join(__dirname, 'output');

Expand All @@ -71,22 +64,29 @@ suite('copy files', () => {
suite('prerender', async () => {
const cwd = join(__dirname, 'fixtures/prerender');
const prerendered_files = join(__dirname, 'fixtures/prerender/build');

/** @type {import('types.internal').ValidatedConfig} */
const config = {
extensions: ['.svelte'],
kit: {
// @ts-ignore
files: {
assets: join(__dirname, 'fixtures/prerender/static'),
routes: join(__dirname, 'fixtures/prerender/src/routes')
},
appDir: '_app',
// @ts-ignore
prerender: {
pages: ['*'],
enabled: true
}
}
};

const utils = get_utils(cwd, config);
/** @type {import('types.internal').BuildData} */
const build_data = { client: [], server: [], static: [], entries: ['/nested'] };

const utils = get_utils({ cwd, config, build_data, log });

const dest = join(__dirname, 'output');

Expand Down
Loading