@sveltejs/kit 1.15.10 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/core/config/options.js +14 -0
- package/src/core/generate_manifest/index.js +6 -21
- package/src/core/postbuild/analyse.js +14 -5
- package/src/core/postbuild/prerender.js +42 -3
- package/src/core/sync/write_types/index.js +6 -0
- package/src/exports/hooks/sequence.js +15 -3
- package/src/exports/vite/build/build_server.js +13 -14
- package/src/exports/vite/build/utils.js +2 -1
- package/src/exports/vite/dev/index.js +1 -1
- package/src/exports/vite/index.js +0 -21
- package/src/runtime/app/forms.js +3 -13
- package/src/runtime/app/navigation.js +17 -22
- package/src/runtime/client/client.js +7 -10
- package/src/runtime/client/singletons.js +30 -0
- package/src/runtime/client/types.d.ts +1 -1
- package/src/runtime/server/data/index.js +0 -2
- package/src/runtime/server/fetch.js +0 -11
- package/src/runtime/server/respond.js +15 -6
- package/src/runtime/shared.js +2 -0
- package/src/utils/exports.js +31 -17
- package/src/utils/options.js +3 -3
- package/src/utils/routing.js +55 -0
- package/types/ambient.d.ts +17 -0
- package/types/index.d.ts +21 -9
- package/types/internal.d.ts +6 -0
- package/types/private.d.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "The fastest way to build Svelte apps",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"marked": "^4.2.3",
|
|
37
37
|
"rollup": "^3.7.0",
|
|
38
38
|
"svelte": "^3.56.0",
|
|
39
|
-
"svelte-preprocess": "^5.0.
|
|
39
|
+
"svelte-preprocess": "^5.0.3",
|
|
40
40
|
"typescript": "^4.9.4",
|
|
41
41
|
"uvu": "^0.5.6",
|
|
42
42
|
"vite": "^4.3.0"
|
|
@@ -229,6 +229,20 @@ const options = object(
|
|
|
229
229
|
}
|
|
230
230
|
),
|
|
231
231
|
|
|
232
|
+
handleEntryGeneratorMismatch: validate(
|
|
233
|
+
(/** @type {any} */ { message }) => {
|
|
234
|
+
throw new Error(
|
|
235
|
+
message +
|
|
236
|
+
`\nTo suppress or handle this error, implement \`handleEntryGeneratorMismatch\` in https://kit.svelte.dev/docs/configuration#prerender`
|
|
237
|
+
);
|
|
238
|
+
},
|
|
239
|
+
(input, keypath) => {
|
|
240
|
+
if (typeof input === 'function') return input;
|
|
241
|
+
if (['fail', 'warn', 'ignore'].includes(input)) return input;
|
|
242
|
+
throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
|
|
243
|
+
}
|
|
244
|
+
),
|
|
245
|
+
|
|
232
246
|
origin: validate('http://sveltekit-prerender', (input, keypath) => {
|
|
233
247
|
assert_string(input, keypath);
|
|
234
248
|
|
|
@@ -43,17 +43,6 @@ export function generate_manifest({ build_data, relative_path, routes }) {
|
|
|
43
43
|
})
|
|
44
44
|
);
|
|
45
45
|
|
|
46
|
-
/** @typedef {{ index: number, path: string }} LookupEntry */
|
|
47
|
-
/** @type {Map<import('types').PageNode, LookupEntry>} */
|
|
48
|
-
const bundled_nodes = new Map();
|
|
49
|
-
|
|
50
|
-
build_data.manifest_data.nodes.forEach((node, i) => {
|
|
51
|
-
bundled_nodes.set(node, {
|
|
52
|
-
path: join_relative(relative_path, `/nodes/${i}.js`),
|
|
53
|
-
index: i
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
46
|
/** @type {(path: string) => string} */
|
|
58
47
|
const loader = (path) => `() => import('${path}')`;
|
|
59
48
|
|
|
@@ -68,14 +57,10 @@ export function generate_manifest({ build_data, relative_path, routes }) {
|
|
|
68
57
|
function get_nodes(indexes) {
|
|
69
58
|
let string = indexes.map((n) => reindexed.get(n) ?? '').join(',');
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
string += ',';
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return `[${string}]`;
|
|
60
|
+
// since JavaScript ignores trailing commas, we need to insert a dummy
|
|
61
|
+
// comma so that the array has the correct length if the last item
|
|
62
|
+
// is undefined
|
|
63
|
+
return `[${string},]`;
|
|
79
64
|
}
|
|
80
65
|
|
|
81
66
|
// prettier-ignore
|
|
@@ -94,12 +79,12 @@ export function generate_manifest({ build_data, relative_path, routes }) {
|
|
|
94
79
|
],
|
|
95
80
|
routes: [
|
|
96
81
|
${routes.map(route => {
|
|
82
|
+
if (!route.page && !route.endpoint) return;
|
|
83
|
+
|
|
97
84
|
route.params.forEach(param => {
|
|
98
85
|
if (param.matcher) matchers.add(param.matcher);
|
|
99
86
|
});
|
|
100
87
|
|
|
101
|
-
if (!route.page && !route.endpoint) return;
|
|
102
|
-
|
|
103
88
|
return dedent`
|
|
104
89
|
{
|
|
105
90
|
id: ${s(route.id)},
|
|
@@ -2,7 +2,9 @@ import { join } from 'node:path';
|
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
3
|
import { get_option } from '../../utils/options.js';
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
validate_layout_exports,
|
|
6
|
+
validate_layout_server_exports,
|
|
7
|
+
validate_page_exports,
|
|
6
8
|
validate_page_server_exports,
|
|
7
9
|
validate_server_exports
|
|
8
10
|
} from '../../utils/exports.js';
|
|
@@ -10,6 +12,7 @@ import { load_config } from '../config/index.js';
|
|
|
10
12
|
import { forked } from '../../utils/fork.js';
|
|
11
13
|
import { should_polyfill } from '../../utils/platform.js';
|
|
12
14
|
import { installPolyfills } from '../../exports/node/polyfills.js';
|
|
15
|
+
import { resolve_entry } from '../../utils/routing.js';
|
|
13
16
|
|
|
14
17
|
export default forked(import.meta.url, analyse);
|
|
15
18
|
|
|
@@ -72,6 +75,8 @@ async function analyse({ manifest_path, env }) {
|
|
|
72
75
|
let prerender = undefined;
|
|
73
76
|
/** @type {any} */
|
|
74
77
|
let config = undefined;
|
|
78
|
+
/** @type {import('types').PrerenderEntryGenerator | undefined} */
|
|
79
|
+
let entries = undefined;
|
|
75
80
|
|
|
76
81
|
if (route.endpoint) {
|
|
77
82
|
const mod = await route.endpoint();
|
|
@@ -95,6 +100,7 @@ async function analyse({ manifest_path, env }) {
|
|
|
95
100
|
if (mod.OPTIONS) api_methods.push('OPTIONS');
|
|
96
101
|
|
|
97
102
|
config = mod.config;
|
|
103
|
+
entries = mod.entries;
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
if (route.page) {
|
|
@@ -109,8 +115,8 @@ async function analyse({ manifest_path, env }) {
|
|
|
109
115
|
|
|
110
116
|
for (const layout of layouts) {
|
|
111
117
|
if (layout) {
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
validate_layout_server_exports(layout.server, layout.server_id);
|
|
119
|
+
validate_layout_exports(layout.universal, layout.universal_id);
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
122
|
|
|
@@ -119,12 +125,13 @@ async function analyse({ manifest_path, env }) {
|
|
|
119
125
|
if (page.server?.actions) page_methods.push('POST');
|
|
120
126
|
|
|
121
127
|
validate_page_server_exports(page.server, page.server_id);
|
|
122
|
-
|
|
128
|
+
validate_page_exports(page.universal, page.universal_id);
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
prerender = get_option(nodes, 'prerender') ?? false;
|
|
126
132
|
|
|
127
133
|
config = get_config(nodes);
|
|
134
|
+
entries ??= get_option(nodes, 'entries');
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
metadata.routes.set(route.id, {
|
|
@@ -136,7 +143,9 @@ async function analyse({ manifest_path, env }) {
|
|
|
136
143
|
api: {
|
|
137
144
|
methods: api_methods
|
|
138
145
|
},
|
|
139
|
-
prerender
|
|
146
|
+
prerender,
|
|
147
|
+
entries:
|
|
148
|
+
entries && (await entries()).map((entry_object) => resolve_entry(route.id, entry_object))
|
|
140
149
|
});
|
|
141
150
|
}
|
|
142
151
|
|
|
@@ -127,6 +127,14 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
|
|
|
127
127
|
}
|
|
128
128
|
);
|
|
129
129
|
|
|
130
|
+
const handle_entry_generator_mismatch = normalise_error_handler(
|
|
131
|
+
log,
|
|
132
|
+
config.prerender.handleEntryGeneratorMismatch,
|
|
133
|
+
({ generatedFromId, entry, matchedId }) => {
|
|
134
|
+
return `The entries export from ${generatedFromId} generated entry ${entry}, which was matched by ${matchedId} - see the \`handleEntryGeneratorMismatch\` option in https://kit.svelte.dev/docs/configuration#prerender for more info.`;
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
|
|
130
138
|
const q = queue(config.prerender.concurrency);
|
|
131
139
|
|
|
132
140
|
/**
|
|
@@ -164,23 +172,25 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
|
|
|
164
172
|
* @param {string | null} referrer
|
|
165
173
|
* @param {string} decoded
|
|
166
174
|
* @param {string} [encoded]
|
|
175
|
+
* @param {string} [generated_from_id]
|
|
167
176
|
*/
|
|
168
|
-
function enqueue(referrer, decoded, encoded) {
|
|
177
|
+
function enqueue(referrer, decoded, encoded, generated_from_id) {
|
|
169
178
|
if (seen.has(decoded)) return;
|
|
170
179
|
seen.add(decoded);
|
|
171
180
|
|
|
172
181
|
const file = decoded.slice(config.paths.base.length + 1);
|
|
173
182
|
if (files.has(file)) return;
|
|
174
183
|
|
|
175
|
-
return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer));
|
|
184
|
+
return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer, generated_from_id));
|
|
176
185
|
}
|
|
177
186
|
|
|
178
187
|
/**
|
|
179
188
|
* @param {string} decoded
|
|
180
189
|
* @param {string} encoded
|
|
181
190
|
* @param {string?} referrer
|
|
191
|
+
* @param {string} [generated_from_id]
|
|
182
192
|
*/
|
|
183
|
-
async function visit(decoded, encoded, referrer) {
|
|
193
|
+
async function visit(decoded, encoded, referrer, generated_from_id) {
|
|
184
194
|
if (!decoded.startsWith(config.paths.base)) {
|
|
185
195
|
handle_http_error({ status: 404, path: decoded, referrer, referenceType: 'linked' });
|
|
186
196
|
return;
|
|
@@ -206,6 +216,20 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
|
|
|
206
216
|
}
|
|
207
217
|
});
|
|
208
218
|
|
|
219
|
+
const encoded_id = response.headers.get('x-sveltekit-routeid');
|
|
220
|
+
const decoded_id = encoded_id && decode_uri(encoded_id);
|
|
221
|
+
if (
|
|
222
|
+
decoded_id !== null &&
|
|
223
|
+
generated_from_id !== undefined &&
|
|
224
|
+
decoded_id !== generated_from_id
|
|
225
|
+
) {
|
|
226
|
+
handle_entry_generator_mismatch({
|
|
227
|
+
generatedFromId: generated_from_id,
|
|
228
|
+
entry: decoded,
|
|
229
|
+
matchedId: decoded_id
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
209
233
|
const body = Buffer.from(await response.arrayBuffer());
|
|
210
234
|
|
|
211
235
|
save('pages', response, body, decoded, encoded, referrer, 'linked');
|
|
@@ -378,9 +402,18 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
|
|
|
378
402
|
saved.set(file, dest);
|
|
379
403
|
}
|
|
380
404
|
|
|
405
|
+
/** @type {Array<{ id: string, entries: Array<string>}>} */
|
|
406
|
+
const route_level_entries = [];
|
|
407
|
+
for (const [id, { entries }] of metadata.routes.entries()) {
|
|
408
|
+
if (entries) {
|
|
409
|
+
route_level_entries.push({ id, entries });
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
381
413
|
if (
|
|
382
414
|
config.prerender.entries.length > 1 ||
|
|
383
415
|
config.prerender.entries[0] !== '*' ||
|
|
416
|
+
route_level_entries.length > 0 ||
|
|
384
417
|
prerender_map.size > 0
|
|
385
418
|
) {
|
|
386
419
|
// Only log if we're actually going to do something to not confuse users
|
|
@@ -401,6 +434,12 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
|
|
|
401
434
|
}
|
|
402
435
|
}
|
|
403
436
|
|
|
437
|
+
for (const { id, entries } of route_level_entries) {
|
|
438
|
+
for (const entry of entries) {
|
|
439
|
+
enqueue(null, config.paths.base + entry, undefined, id);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
404
443
|
await q.done();
|
|
405
444
|
|
|
406
445
|
// handle invalid fragment links
|
|
@@ -194,6 +194,12 @@ function update_types(config, routes, route, to_delete = new Set()) {
|
|
|
194
194
|
.join('; ')} }`
|
|
195
195
|
);
|
|
196
196
|
|
|
197
|
+
if (route.params.length > 0) {
|
|
198
|
+
exports.push(
|
|
199
|
+
`export type EntryGenerator = () => Promise<Array<RouteParams>> | Array<RouteParams>;`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
197
203
|
declarations.push(`type RouteId = '${route.id}';`);
|
|
198
204
|
|
|
199
205
|
// These could also be placed in our public types, but it would bloat them unnecessarily and we may want to change these in the future
|
|
@@ -21,7 +21,7 @@ export function sequence(...handlers) {
|
|
|
21
21
|
return handle({
|
|
22
22
|
event,
|
|
23
23
|
resolve: (event, options) => {
|
|
24
|
-
/** @
|
|
24
|
+
/** @type {import('types').ResolveOptions['transformPageChunk']} */
|
|
25
25
|
const transformPageChunk = async ({ html, done }) => {
|
|
26
26
|
if (options?.transformPageChunk) {
|
|
27
27
|
html = (await options.transformPageChunk({ html, done })) ?? '';
|
|
@@ -34,9 +34,21 @@ export function sequence(...handlers) {
|
|
|
34
34
|
return html;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
/** @type {import('types').ResolveOptions['filterSerializedResponseHeaders']} */
|
|
38
|
+
const filterSerializedResponseHeaders =
|
|
39
|
+
parent_options?.filterSerializedResponseHeaders ??
|
|
40
|
+
options?.filterSerializedResponseHeaders;
|
|
41
|
+
|
|
42
|
+
/** @type {import('types').ResolveOptions['preload']} */
|
|
43
|
+
const preload = parent_options?.preload ?? options?.preload;
|
|
44
|
+
|
|
37
45
|
return i < length - 1
|
|
38
|
-
? apply_handle(i + 1, event, {
|
|
39
|
-
|
|
46
|
+
? apply_handle(i + 1, event, {
|
|
47
|
+
transformPageChunk,
|
|
48
|
+
filterSerializedResponseHeaders,
|
|
49
|
+
preload
|
|
50
|
+
})
|
|
51
|
+
: resolve(event, { transformPageChunk, filterSerializedResponseHeaders, preload });
|
|
40
52
|
}
|
|
41
53
|
});
|
|
42
54
|
}
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import { mkdirp } from '../../../utils/filesystem.js';
|
|
3
3
|
import { find_deps, resolve_symlinks } from './utils.js';
|
|
4
4
|
import { s } from '../../../utils/misc.js';
|
|
5
|
+
import { normalizePath } from 'vite';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @param {string} out
|
|
@@ -48,12 +49,6 @@ export function build_server_nodes(out, kit, manifest_data, server_manifest, cli
|
|
|
48
49
|
const fonts = [];
|
|
49
50
|
|
|
50
51
|
if (node.component && client_manifest) {
|
|
51
|
-
const entry = find_deps(client_manifest, node.component, true);
|
|
52
|
-
|
|
53
|
-
imported.push(...entry.imports);
|
|
54
|
-
stylesheets.push(...entry.stylesheets);
|
|
55
|
-
fonts.push(...entry.fonts);
|
|
56
|
-
|
|
57
52
|
exports.push(
|
|
58
53
|
`export const component = async () => (await import('../${
|
|
59
54
|
resolve_symlinks(server_manifest, node.component).chunk.file
|
|
@@ -62,14 +57,6 @@ export function build_server_nodes(out, kit, manifest_data, server_manifest, cli
|
|
|
62
57
|
}
|
|
63
58
|
|
|
64
59
|
if (node.universal) {
|
|
65
|
-
if (client_manifest) {
|
|
66
|
-
const entry = find_deps(client_manifest, node.universal, true);
|
|
67
|
-
|
|
68
|
-
imported.push(...entry.imports);
|
|
69
|
-
stylesheets.push(...entry.stylesheets);
|
|
70
|
-
fonts.push(...entry.fonts);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
60
|
imports.push(`import * as universal from '../${server_manifest[node.universal].file}';`);
|
|
74
61
|
exports.push(`export { universal };`);
|
|
75
62
|
exports.push(`export const universal_id = ${s(node.universal)};`);
|
|
@@ -81,6 +68,18 @@ export function build_server_nodes(out, kit, manifest_data, server_manifest, cli
|
|
|
81
68
|
exports.push(`export const server_id = ${s(node.server)};`);
|
|
82
69
|
}
|
|
83
70
|
|
|
71
|
+
if (client_manifest && (node.universal || node.component)) {
|
|
72
|
+
const entry = find_deps(
|
|
73
|
+
client_manifest,
|
|
74
|
+
`${normalizePath(kit.outDir)}/generated/client-optimized/nodes/${i}.js`,
|
|
75
|
+
true
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
imported.push(...entry.imports);
|
|
79
|
+
stylesheets.push(...entry.stylesheets);
|
|
80
|
+
fonts.push(...entry.fonts);
|
|
81
|
+
}
|
|
82
|
+
|
|
84
83
|
exports.push(
|
|
85
84
|
`export const imports = ${s(imported)};`,
|
|
86
85
|
`export const stylesheets = ${s(stylesheets)};`,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { normalizePath } from 'vite';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Adds transitive JS and CSS dependencies to the js and css inputs.
|
|
@@ -72,7 +73,7 @@ export function find_deps(manifest, entry, add_dynamic_css) {
|
|
|
72
73
|
*/
|
|
73
74
|
export function resolve_symlinks(manifest, file) {
|
|
74
75
|
while (!manifest[file]) {
|
|
75
|
-
const next = path.relative('.', fs.realpathSync(file));
|
|
76
|
+
const next = normalizePath(path.relative('.', fs.realpathSync(file)));
|
|
76
77
|
if (next === file) throw new Error(`Could not find file "${file}" in Vite manifest`);
|
|
77
78
|
file = next;
|
|
78
79
|
}
|
|
@@ -116,7 +116,7 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
116
116
|
_: {
|
|
117
117
|
client: {
|
|
118
118
|
start: `${runtime_base}/client/start.js`,
|
|
119
|
-
app: `${svelte_config.kit.outDir}/generated/client/app.js`,
|
|
119
|
+
app: `${to_fs(svelte_config.kit.outDir)}/generated/client/app.js`,
|
|
120
120
|
imports: [],
|
|
121
121
|
stylesheets: [],
|
|
122
122
|
fonts: []
|
|
@@ -525,27 +525,6 @@ function kit({ svelte_config }) {
|
|
|
525
525
|
} else {
|
|
526
526
|
input['entry/start'] = `${runtime_directory}/client/start.js`;
|
|
527
527
|
input['entry/app'] = `${kit.outDir}/generated/client-optimized/app.js`;
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* @param {string | undefined} file
|
|
531
|
-
*/
|
|
532
|
-
function add_input(file) {
|
|
533
|
-
if (!file) return;
|
|
534
|
-
|
|
535
|
-
const resolved = path.resolve(file);
|
|
536
|
-
const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
|
|
537
|
-
|
|
538
|
-
const name = relative.startsWith('..')
|
|
539
|
-
? path.basename(file).replace(/^\+/, '')
|
|
540
|
-
: relative.replace(/(\\|\/)\+/g, '-').replace(/[\\/]/g, '-');
|
|
541
|
-
|
|
542
|
-
input[`entry/${name}`] = resolved;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
for (const node of manifest_data.nodes) {
|
|
546
|
-
add_input(node.component);
|
|
547
|
-
add_input(node.universal);
|
|
548
|
-
}
|
|
549
528
|
}
|
|
550
529
|
|
|
551
530
|
// see the kit.output.preloadStrategy option for details on why we have multiple options here
|
package/src/runtime/app/forms.js
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
import * as devalue from 'devalue';
|
|
2
|
-
import {
|
|
2
|
+
import { DEV } from 'esm-env';
|
|
3
|
+
import { client_method } from '../client/singletons.js';
|
|
3
4
|
import { invalidateAll } from './navigation.js';
|
|
4
|
-
import { BROWSER, DEV } from 'esm-env';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @param {string} name
|
|
8
|
-
*/
|
|
9
|
-
function guard(name) {
|
|
10
|
-
return () => {
|
|
11
|
-
throw new Error(`Cannot call ${name}(...) on the server`);
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
5
|
|
|
15
|
-
|
|
16
|
-
export const applyAction = BROWSER ? client.apply_action : guard('applyAction');
|
|
6
|
+
export const applyAction = client_method('apply_action');
|
|
17
7
|
|
|
18
8
|
/** @type {import('$app/forms').deserialize} */
|
|
19
9
|
export function deserialize(result) {
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export const
|
|
18
|
-
export const invalidateAll = BROWSER ? client.invalidateAll : guard('invalidateAll');
|
|
19
|
-
export const preloadData = BROWSER ? client.preload_data : guard('preloadData');
|
|
20
|
-
export const preloadCode = BROWSER ? client.preload_code : guard('preloadCode');
|
|
21
|
-
export const beforeNavigate = BROWSER ? client.before_navigate : () => {};
|
|
22
|
-
export const afterNavigate = BROWSER ? client.after_navigate : () => {};
|
|
1
|
+
import { client_method } from '../client/singletons.js';
|
|
2
|
+
|
|
3
|
+
export const disableScrollHandling = /* @__PURE__ */ client_method('disable_scroll_handling');
|
|
4
|
+
|
|
5
|
+
export const goto = /* @__PURE__ */ client_method('goto');
|
|
6
|
+
|
|
7
|
+
export const invalidate = /* @__PURE__ */ client_method('invalidate');
|
|
8
|
+
|
|
9
|
+
export const invalidateAll = /* @__PURE__ */ client_method('invalidate_all');
|
|
10
|
+
|
|
11
|
+
export const preloadData = /* @__PURE__ */ client_method('preload_data');
|
|
12
|
+
|
|
13
|
+
export const preloadCode = /* @__PURE__ */ client_method('preload_code');
|
|
14
|
+
|
|
15
|
+
export const beforeNavigate = /* @__PURE__ */ client_method('before_navigate');
|
|
16
|
+
|
|
17
|
+
export const afterNavigate = /* @__PURE__ */ client_method('after_navigate');
|
|
@@ -31,9 +31,9 @@ import { stores } from './singletons.js';
|
|
|
31
31
|
import { unwrap_promises } from '../../utils/promises.js';
|
|
32
32
|
import * as devalue from 'devalue';
|
|
33
33
|
import { INDEX_KEY, PRELOAD_PRIORITIES, SCROLL_KEY, SNAPSHOT_KEY } from './constants.js';
|
|
34
|
-
import {
|
|
34
|
+
import { validate_page_exports } from '../../utils/exports.js';
|
|
35
35
|
import { compact } from '../../utils/array.js';
|
|
36
|
-
import { validate_depends } from '../shared.js';
|
|
36
|
+
import { INVALIDATED_PARAM, validate_depends } from '../shared.js';
|
|
37
37
|
|
|
38
38
|
let errored = false;
|
|
39
39
|
|
|
@@ -428,7 +428,7 @@ export function create_client(app, target) {
|
|
|
428
428
|
const node = await loader();
|
|
429
429
|
|
|
430
430
|
if (DEV) {
|
|
431
|
-
|
|
431
|
+
validate_page_exports(node.universal);
|
|
432
432
|
}
|
|
433
433
|
|
|
434
434
|
if (node.universal?.load) {
|
|
@@ -1354,7 +1354,7 @@ export function create_client(app, target) {
|
|
|
1354
1354
|
return invalidate();
|
|
1355
1355
|
},
|
|
1356
1356
|
|
|
1357
|
-
|
|
1357
|
+
invalidate_all: () => {
|
|
1358
1358
|
force_invalidation = true;
|
|
1359
1359
|
return invalidate();
|
|
1360
1360
|
},
|
|
@@ -1775,13 +1775,10 @@ export function create_client(app, target) {
|
|
|
1775
1775
|
async function load_data(url, invalid) {
|
|
1776
1776
|
const data_url = new URL(url);
|
|
1777
1777
|
data_url.pathname = add_data_suffix(url.pathname);
|
|
1778
|
-
if (DEV && url.searchParams.has(
|
|
1779
|
-
throw new Error(
|
|
1778
|
+
if (DEV && url.searchParams.has(INVALIDATED_PARAM)) {
|
|
1779
|
+
throw new Error(`Cannot used reserved query parameter "${INVALIDATED_PARAM}"`);
|
|
1780
1780
|
}
|
|
1781
|
-
data_url.searchParams.append(
|
|
1782
|
-
'x-sveltekit-invalidated',
|
|
1783
|
-
invalid.map((x) => (x ? '1' : '')).join('_')
|
|
1784
|
-
);
|
|
1781
|
+
data_url.searchParams.append(INVALIDATED_PARAM, invalid.map((i) => (i ? '1' : '0')).join(''));
|
|
1785
1782
|
|
|
1786
1783
|
const res = await native_fetch(data_url.href);
|
|
1787
1784
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { writable } from 'svelte/store';
|
|
2
2
|
import { create_updated_store, notifiable_store } from './utils.js';
|
|
3
|
+
import { BROWSER } from 'esm-env';
|
|
3
4
|
|
|
4
5
|
/** @type {import('./types').Client} */
|
|
5
6
|
export let client;
|
|
@@ -13,6 +14,35 @@ export function init(opts) {
|
|
|
13
14
|
client = opts.client;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @template {keyof typeof client} T
|
|
19
|
+
* @param {T} key
|
|
20
|
+
* @returns {typeof client[T]}
|
|
21
|
+
*/
|
|
22
|
+
export function client_method(key) {
|
|
23
|
+
if (!BROWSER) {
|
|
24
|
+
if (key === 'before_navigate' || key === 'after_navigate') {
|
|
25
|
+
// @ts-expect-error doesn't recognize that both keys here return void so expects a async function
|
|
26
|
+
return () => {};
|
|
27
|
+
} else {
|
|
28
|
+
/** @type {Record<string, string>} */
|
|
29
|
+
const name_lookup = {
|
|
30
|
+
disable_scroll_handling: 'disableScrollHandling',
|
|
31
|
+
preload_data: 'preloadData',
|
|
32
|
+
preload_code: 'preloadCode',
|
|
33
|
+
invalidate_all: 'invalidateAll'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return () => {
|
|
37
|
+
throw new Error(`Cannot call ${name_lookup[key] ?? key}(...) on the server`);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
// @ts-expect-error
|
|
42
|
+
return (...args) => client[key](...args);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
16
46
|
export const stores = {
|
|
17
47
|
url: notifiable_store({}),
|
|
18
48
|
page: notifiable_store({}),
|
|
@@ -54,7 +54,7 @@ export interface Client {
|
|
|
54
54
|
disable_scroll_handling(): void;
|
|
55
55
|
goto: typeof goto;
|
|
56
56
|
invalidate: typeof invalidate;
|
|
57
|
-
|
|
57
|
+
invalidate_all: typeof invalidateAll;
|
|
58
58
|
preload_code: typeof preloadCode;
|
|
59
59
|
preload_data: typeof preloadData;
|
|
60
60
|
apply_action: typeof applyAction;
|
|
@@ -8,8 +8,6 @@ import { text } from '../../../exports/index.js';
|
|
|
8
8
|
import * as devalue from 'devalue';
|
|
9
9
|
import { create_async_iterator } from '../../../utils/streaming.js';
|
|
10
10
|
|
|
11
|
-
export const INVALIDATED_PARAM = 'x-sveltekit-invalidated';
|
|
12
|
-
|
|
13
11
|
const encoder = new TextEncoder();
|
|
14
12
|
|
|
15
13
|
/**
|
|
@@ -16,8 +16,6 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
|
|
|
16
16
|
return async (info, init) => {
|
|
17
17
|
const original_request = normalize_fetch_input(info, init, event.url);
|
|
18
18
|
|
|
19
|
-
const request_body = init?.body;
|
|
20
|
-
|
|
21
19
|
// some runtimes (e.g. Cloudflare) error if you access `request.mode`,
|
|
22
20
|
// annoyingly, so we need to read the value from the `init` object instead
|
|
23
21
|
let mode = (info instanceof Request ? info.mode : init?.mode) ?? 'cors';
|
|
@@ -111,15 +109,6 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
|
|
|
111
109
|
}
|
|
112
110
|
}
|
|
113
111
|
|
|
114
|
-
if (request_body && typeof request_body !== 'string' && !ArrayBuffer.isView(request_body)) {
|
|
115
|
-
// TODO is this still necessary? we just bail out below
|
|
116
|
-
// per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
|
|
117
|
-
// Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
|
|
118
|
-
// non-string bodies are irksome to deal with, but luckily aren't particularly useful
|
|
119
|
-
// in this context anyway, so we take the easy route and ban them
|
|
120
|
-
throw new Error('Request body must be a string or TypedArray');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
112
|
if (!request.headers.has('accept')) {
|
|
124
113
|
request.headers.set('accept', '*/*');
|
|
125
114
|
}
|
|
@@ -15,18 +15,21 @@ import {
|
|
|
15
15
|
strip_data_suffix
|
|
16
16
|
} from '../../utils/url.js';
|
|
17
17
|
import { exec } from '../../utils/routing.js';
|
|
18
|
-
import {
|
|
18
|
+
import { redirect_json_response, render_data } from './data/index.js';
|
|
19
19
|
import { add_cookies_to_headers, get_cookies } from './cookie.js';
|
|
20
20
|
import { create_fetch } from './fetch.js';
|
|
21
21
|
import { Redirect } from '../control.js';
|
|
22
22
|
import {
|
|
23
|
-
|
|
23
|
+
validate_layout_exports,
|
|
24
|
+
validate_layout_server_exports,
|
|
25
|
+
validate_page_exports,
|
|
24
26
|
validate_page_server_exports,
|
|
25
27
|
validate_server_exports
|
|
26
28
|
} from '../../utils/exports.js';
|
|
27
29
|
import { get_option } from '../../utils/options.js';
|
|
28
30
|
import { error, json, text } from '../../exports/index.js';
|
|
29
31
|
import { action_json_redirect, is_action_json_request } from './page/actions.js';
|
|
32
|
+
import { INVALIDATED_PARAM } from '../shared.js';
|
|
30
33
|
|
|
31
34
|
/* global __SVELTEKIT_ADAPTER_NAME__ */
|
|
32
35
|
|
|
@@ -94,7 +97,10 @@ export async function respond(request, options, manifest, state) {
|
|
|
94
97
|
if (is_data_request) {
|
|
95
98
|
decoded = strip_data_suffix(decoded) || '/';
|
|
96
99
|
url.pathname = strip_data_suffix(url.pathname) || '/';
|
|
97
|
-
invalidated_data_nodes = url.searchParams
|
|
100
|
+
invalidated_data_nodes = url.searchParams
|
|
101
|
+
.get(INVALIDATED_PARAM)
|
|
102
|
+
?.split('')
|
|
103
|
+
.map((node) => node === '1');
|
|
98
104
|
url.searchParams.delete(INVALIDATED_PARAM);
|
|
99
105
|
}
|
|
100
106
|
|
|
@@ -193,8 +199,11 @@ export async function respond(request, options, manifest, state) {
|
|
|
193
199
|
|
|
194
200
|
for (const layout of layouts) {
|
|
195
201
|
if (layout) {
|
|
196
|
-
|
|
197
|
-
|
|
202
|
+
validate_layout_server_exports(
|
|
203
|
+
layout.server,
|
|
204
|
+
/** @type {string} */ (layout.server_id)
|
|
205
|
+
);
|
|
206
|
+
validate_layout_exports(
|
|
198
207
|
layout.universal,
|
|
199
208
|
/** @type {string} */ (layout.universal_id)
|
|
200
209
|
);
|
|
@@ -203,7 +212,7 @@ export async function respond(request, options, manifest, state) {
|
|
|
203
212
|
|
|
204
213
|
if (page) {
|
|
205
214
|
validate_page_server_exports(page.server, /** @type {string} */ (page.server_id));
|
|
206
|
-
|
|
215
|
+
validate_page_exports(page.universal, /** @type {string} */ (page.universal_id));
|
|
207
216
|
}
|
|
208
217
|
}
|
|
209
218
|
|
package/src/runtime/shared.js
CHANGED
package/src/utils/exports.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @param {string
|
|
2
|
+
* @param {Set<string>} expected
|
|
3
3
|
*/
|
|
4
4
|
function validator(expected) {
|
|
5
|
-
const set = new Set(expected);
|
|
6
|
-
|
|
7
5
|
/**
|
|
8
6
|
* @param {any} module
|
|
9
7
|
* @param {string} [file]
|
|
@@ -12,11 +10,13 @@ function validator(expected) {
|
|
|
12
10
|
if (!module) return;
|
|
13
11
|
|
|
14
12
|
for (const key in module) {
|
|
15
|
-
if (key[0] === '_' ||
|
|
13
|
+
if (key[0] === '_' || expected.has(key)) continue; // key is valid in this module
|
|
14
|
+
|
|
15
|
+
const values = [...expected.values()];
|
|
16
16
|
|
|
17
17
|
const hint =
|
|
18
18
|
hint_for_supported_files(key, file?.slice(file.lastIndexOf('.'))) ??
|
|
19
|
-
`valid exports are ${
|
|
19
|
+
`valid exports are ${values.join(', ')}, or anything with a '_' prefix`;
|
|
20
20
|
|
|
21
21
|
throw new Error(`Invalid export '${key}'${file ? ` in ${file}` : ''} (${hint})`);
|
|
22
22
|
}
|
|
@@ -33,34 +33,45 @@ function validator(expected) {
|
|
|
33
33
|
function hint_for_supported_files(key, ext = '.js') {
|
|
34
34
|
let supported_files = [];
|
|
35
35
|
|
|
36
|
-
if (
|
|
36
|
+
if (valid_layout_exports.has(key)) {
|
|
37
|
+
supported_files.push(`+layout${ext}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (valid_page_exports.has(key)) {
|
|
37
41
|
supported_files.push(`+page${ext}`);
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
if (
|
|
44
|
+
if (valid_layout_server_exports.has(key)) {
|
|
45
|
+
supported_files.push(`+layout.server${ext}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (valid_page_server_exports.has(key)) {
|
|
41
49
|
supported_files.push(`+page.server${ext}`);
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
if (valid_server_exports.
|
|
52
|
+
if (valid_server_exports.has(key)) {
|
|
45
53
|
supported_files.push(`+server${ext}`);
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
if (supported_files.length > 0) {
|
|
49
|
-
return `'${key}' is a valid export in ${supported_files.join(
|
|
57
|
+
return `'${key}' is a valid export in ${supported_files.slice(0, -1).join(`, `)}${
|
|
58
|
+
supported_files.length > 1 ? ' or ' : ''
|
|
59
|
+
}${supported_files.at(-1)}`;
|
|
50
60
|
}
|
|
51
61
|
}
|
|
52
62
|
|
|
53
|
-
const
|
|
54
|
-
const valid_page_server_exports = [
|
|
63
|
+
const valid_layout_exports = new Set([
|
|
55
64
|
'load',
|
|
56
65
|
'prerender',
|
|
57
66
|
'csr',
|
|
58
67
|
'ssr',
|
|
59
|
-
'actions',
|
|
60
68
|
'trailingSlash',
|
|
61
69
|
'config'
|
|
62
|
-
];
|
|
63
|
-
const
|
|
70
|
+
]);
|
|
71
|
+
const valid_page_exports = new Set([...valid_layout_exports, 'entries']);
|
|
72
|
+
const valid_layout_server_exports = new Set([...valid_layout_exports, 'actions']);
|
|
73
|
+
const valid_page_server_exports = new Set([...valid_layout_server_exports, 'entries']);
|
|
74
|
+
const valid_server_exports = new Set([
|
|
64
75
|
'GET',
|
|
65
76
|
'POST',
|
|
66
77
|
'PATCH',
|
|
@@ -69,9 +80,12 @@ const valid_server_exports = [
|
|
|
69
80
|
'OPTIONS',
|
|
70
81
|
'prerender',
|
|
71
82
|
'trailingSlash',
|
|
72
|
-
'config'
|
|
73
|
-
|
|
83
|
+
'config',
|
|
84
|
+
'entries'
|
|
85
|
+
]);
|
|
74
86
|
|
|
75
|
-
export const
|
|
87
|
+
export const validate_layout_exports = validator(valid_layout_exports);
|
|
88
|
+
export const validate_page_exports = validator(valid_page_exports);
|
|
89
|
+
export const validate_layout_server_exports = validator(valid_layout_server_exports);
|
|
76
90
|
export const validate_page_server_exports = validator(valid_page_server_exports);
|
|
77
91
|
export const validate_server_exports = validator(valid_server_exports);
|
package/src/utils/options.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @template {'prerender' | 'ssr' | 'csr' | 'trailingSlash'} Option
|
|
3
|
-
* @template {
|
|
2
|
+
* @template {'prerender' | 'ssr' | 'csr' | 'trailingSlash' | 'entries'} Option
|
|
3
|
+
* @template {(import('types').SSRNode['universal'] | import('types').SSRNode['server'])[Option]} Value
|
|
4
4
|
*
|
|
5
5
|
* @param {Array<import('types').SSRNode | undefined>} nodes
|
|
6
6
|
* @param {Option} option
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export function get_option(nodes, option) {
|
|
11
11
|
return nodes.reduce((value, node) => {
|
|
12
|
-
return /** @type {
|
|
12
|
+
return /** @type {Value} TypeScript's too dumb to understand this */ (
|
|
13
13
|
node?.universal?.[option] ?? node?.server?.[option] ?? value
|
|
14
14
|
);
|
|
15
15
|
}, /** @type {Value | undefined} */ (undefined));
|
package/src/utils/routing.js
CHANGED
|
@@ -96,6 +96,61 @@ export function parse_route_id(id) {
|
|
|
96
96
|
return { pattern, params };
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
const basic_param_pattern = /\[(\[)?(?:\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Parses a route ID, then resolves it to a path by replacing parameters with actual values from `entry`.
|
|
103
|
+
* @param {string} id The route id
|
|
104
|
+
* @param {Record<string, string | undefined>} entry The entry meant to populate the route. For example, if the route is `/blog/[slug]`, the entry would be `{ slug: 'hello-world' }`
|
|
105
|
+
* @example
|
|
106
|
+
* ```js
|
|
107
|
+
* resolve_entry(`/blog/[slug]/[...somethingElse]`, { slug: 'hello-world', somethingElse: 'something/else' }); // `/blog/hello-world/something/else`
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export function resolve_entry(id, entry) {
|
|
111
|
+
const segments = get_route_segments(id);
|
|
112
|
+
return (
|
|
113
|
+
'/' +
|
|
114
|
+
segments
|
|
115
|
+
.map((segment) => {
|
|
116
|
+
const match = basic_param_pattern.exec(segment);
|
|
117
|
+
|
|
118
|
+
// static content -- i.e. not a param
|
|
119
|
+
if (!match) return segment;
|
|
120
|
+
|
|
121
|
+
const optional = !!match[1];
|
|
122
|
+
const name = match[2];
|
|
123
|
+
const param_value = entry[name];
|
|
124
|
+
|
|
125
|
+
// This is nested so TS correctly narrows the type
|
|
126
|
+
if (!param_value) {
|
|
127
|
+
if (optional) return '';
|
|
128
|
+
throw new Error(`Missing parameter '${name}' in route ${id}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (param_value.startsWith('/') || param_value.endsWith('/'))
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return param_value;
|
|
137
|
+
})
|
|
138
|
+
.filter(Boolean)
|
|
139
|
+
.join('/')
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const optional_param_regex = /\/\[\[\w+?(?:=\w+)?\]\]/;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Removes optional params from a route ID.
|
|
147
|
+
* @param {string} id
|
|
148
|
+
* @returns The route id with optional params removed
|
|
149
|
+
*/
|
|
150
|
+
export function remove_optional_params(id) {
|
|
151
|
+
return id.replace(optional_param_regex, '');
|
|
152
|
+
}
|
|
153
|
+
|
|
99
154
|
/**
|
|
100
155
|
* Returns `false` for `(group)` segments
|
|
101
156
|
* @param {string} segment
|
package/types/ambient.d.ts
CHANGED
|
@@ -359,6 +359,10 @@ declare module '@sveltejs/kit/hooks' {
|
|
|
359
359
|
|
|
360
360
|
/**
|
|
361
361
|
* A helper function for sequencing multiple `handle` calls in a middleware-like manner.
|
|
362
|
+
* The behavior for the `handle` options is as follows:
|
|
363
|
+
* - `transformPageChunk` is applied in reverse order and merged
|
|
364
|
+
* - `preload` is applied in forward order, the first option "wins" and no `preload` options after it are called
|
|
365
|
+
* - `filterSerializedResponseHeaders` behaves the same as `preload`
|
|
362
366
|
*
|
|
363
367
|
* ```js
|
|
364
368
|
* /// file: src/hooks.server.js
|
|
@@ -372,6 +376,10 @@ declare module '@sveltejs/kit/hooks' {
|
|
|
372
376
|
* // transforms are applied in reverse order
|
|
373
377
|
* console.log('first transform');
|
|
374
378
|
* return html;
|
|
379
|
+
* },
|
|
380
|
+
* preload: () => {
|
|
381
|
+
* // this one wins as it's the first defined in the chain
|
|
382
|
+
* console.log('first preload');
|
|
375
383
|
* }
|
|
376
384
|
* });
|
|
377
385
|
* console.log('first post-processing');
|
|
@@ -385,6 +393,13 @@ declare module '@sveltejs/kit/hooks' {
|
|
|
385
393
|
* transformPageChunk: ({ html }) => {
|
|
386
394
|
* console.log('second transform');
|
|
387
395
|
* return html;
|
|
396
|
+
* },
|
|
397
|
+
* preload: () => {
|
|
398
|
+
* console.log('second preload');
|
|
399
|
+
* },
|
|
400
|
+
* filterSerializedResponseHeaders: () => {
|
|
401
|
+
* // this one wins as it's the first defined in the chain
|
|
402
|
+
* console.log('second filterSerializedResponseHeaders');
|
|
388
403
|
* }
|
|
389
404
|
* });
|
|
390
405
|
* console.log('second post-processing');
|
|
@@ -398,7 +413,9 @@ declare module '@sveltejs/kit/hooks' {
|
|
|
398
413
|
*
|
|
399
414
|
* ```
|
|
400
415
|
* first pre-processing
|
|
416
|
+
* first preload
|
|
401
417
|
* second pre-processing
|
|
418
|
+
* second filterSerializedResponseHeaders
|
|
402
419
|
* second transform
|
|
403
420
|
* first transform
|
|
404
421
|
* second post-processing
|
package/types/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
Logger,
|
|
12
12
|
MaybePromise,
|
|
13
13
|
Prerendered,
|
|
14
|
+
PrerenderEntryGeneratorMismatchHandlerValue,
|
|
14
15
|
PrerenderHttpErrorHandlerValue,
|
|
15
16
|
PrerenderMissingIdHandlerValue,
|
|
16
17
|
PrerenderOption,
|
|
@@ -515,7 +516,7 @@ export interface KitConfig {
|
|
|
515
516
|
*/
|
|
516
517
|
handleHttpError?: PrerenderHttpErrorHandlerValue;
|
|
517
518
|
/**
|
|
518
|
-
* How to respond
|
|
519
|
+
* How to respond when hash links from one prerendered page to another don't correspond to an `id` on the destination page.
|
|
519
520
|
*
|
|
520
521
|
* - `'fail'` — fail the build
|
|
521
522
|
* - `'ignore'` - silently ignore the failure and continue
|
|
@@ -525,6 +526,17 @@ export interface KitConfig {
|
|
|
525
526
|
* @default "fail"
|
|
526
527
|
*/
|
|
527
528
|
handleMissingId?: PrerenderMissingIdHandlerValue;
|
|
529
|
+
/**
|
|
530
|
+
* How to respond when an entry generated by the `entries` export doesn't match the route it was generated from.
|
|
531
|
+
*
|
|
532
|
+
* - `'fail'` — fail the build
|
|
533
|
+
* - `'ignore'` - silently ignore the failure and continue
|
|
534
|
+
* - `'warn'` — continue, but print a warning
|
|
535
|
+
* - `(details) => void` — a custom error handler that takes a `details` object with `generatedFromId`, `entry`, `matchedId` and `message` properties. If you `throw` from this function, the build will fail
|
|
536
|
+
*
|
|
537
|
+
* @default "fail"
|
|
538
|
+
*/
|
|
539
|
+
handleEntryGeneratorMismatch?: PrerenderEntryGeneratorMismatchHandlerValue;
|
|
528
540
|
/**
|
|
529
541
|
* The value of `url.origin` during prerendering; useful if it is included in rendered content.
|
|
530
542
|
* @default "http://sveltekit-prerender"
|
|
@@ -557,16 +569,16 @@ export interface KitConfig {
|
|
|
557
569
|
* If SvelteKit encounters an error while loading the page and detects that a new version has been deployed (using the `name` specified here, which defaults to a timestamp of the build) it will fall back to traditional full-page navigation.
|
|
558
570
|
* Not all navigations will result in an error though, for example if the JavaScript for the next page is already loaded. If you still want to force a full-page navigation in these cases, use techniques such as setting the `pollInterval` and then using `beforeNavigate`:
|
|
559
571
|
* ```html
|
|
560
|
-
* /// +layout.svelte
|
|
572
|
+
* /// file: +layout.svelte
|
|
561
573
|
* <script>
|
|
562
|
-
*
|
|
563
|
-
*
|
|
574
|
+
* import { beforeNavigate } from '$app/navigation';
|
|
575
|
+
* import { updated } from '$app/stores';
|
|
564
576
|
*
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
577
|
+
* beforeNavigate(({ willUnload, to }) => {
|
|
578
|
+
* if ($updated && !willUnload && to?.url) {
|
|
579
|
+
* location.href = to.url.href;
|
|
580
|
+
* }
|
|
581
|
+
* });
|
|
570
582
|
* </script>
|
|
571
583
|
* ```
|
|
572
584
|
*
|
package/types/internal.d.ts
CHANGED
|
@@ -266,6 +266,7 @@ export interface ServerMetadataRoute {
|
|
|
266
266
|
};
|
|
267
267
|
methods: HttpMethod[];
|
|
268
268
|
prerender: PrerenderOption | undefined;
|
|
269
|
+
entries: Array<string> | undefined;
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
export interface ServerMetadata {
|
|
@@ -308,6 +309,7 @@ export interface SSRNode {
|
|
|
308
309
|
csr?: boolean;
|
|
309
310
|
trailingSlash?: TrailingSlash;
|
|
310
311
|
config?: any;
|
|
312
|
+
entries?: PrerenderEntryGenerator;
|
|
311
313
|
};
|
|
312
314
|
|
|
313
315
|
server: {
|
|
@@ -318,6 +320,7 @@ export interface SSRNode {
|
|
|
318
320
|
trailingSlash?: TrailingSlash;
|
|
319
321
|
actions?: Actions;
|
|
320
322
|
config?: any;
|
|
323
|
+
entries?: PrerenderEntryGenerator;
|
|
321
324
|
};
|
|
322
325
|
|
|
323
326
|
universal_id: string;
|
|
@@ -355,10 +358,13 @@ export interface PageNodeIndexes {
|
|
|
355
358
|
leaf: number;
|
|
356
359
|
}
|
|
357
360
|
|
|
361
|
+
export type PrerenderEntryGenerator = () => MaybePromise<Array<Record<string, string>>>;
|
|
362
|
+
|
|
358
363
|
export type SSREndpoint = Partial<Record<HttpMethod, RequestHandler>> & {
|
|
359
364
|
prerender?: PrerenderOption;
|
|
360
365
|
trailingSlash?: TrailingSlash;
|
|
361
366
|
config?: any;
|
|
367
|
+
entries?: PrerenderEntryGenerator;
|
|
362
368
|
};
|
|
363
369
|
|
|
364
370
|
export interface SSRRoute {
|
package/types/private.d.ts
CHANGED
|
@@ -205,8 +205,17 @@ export interface PrerenderMissingIdHandler {
|
|
|
205
205
|
(details: { path: string; id: string; referrers: string[]; message: string }): void;
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
export interface PrerenderEntryGeneratorMismatchHandler {
|
|
209
|
+
(details: { generatedFromId: string; entry: string; matchedId: string; message: string }): void;
|
|
210
|
+
}
|
|
211
|
+
|
|
208
212
|
export type PrerenderHttpErrorHandlerValue = 'fail' | 'warn' | 'ignore' | PrerenderHttpErrorHandler;
|
|
209
213
|
export type PrerenderMissingIdHandlerValue = 'fail' | 'warn' | 'ignore' | PrerenderMissingIdHandler;
|
|
214
|
+
export type PrerenderEntryGeneratorMismatchHandlerValue =
|
|
215
|
+
| 'fail'
|
|
216
|
+
| 'warn'
|
|
217
|
+
| 'ignore'
|
|
218
|
+
| PrerenderEntryGeneratorMismatchHandler;
|
|
210
219
|
|
|
211
220
|
export type PrerenderOption = boolean | 'auto';
|
|
212
221
|
|