@sveltejs/kit 1.3.10 → 1.5.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 +3 -3
- package/src/constants.js +4 -2
- package/src/core/adapt/builder.js +57 -29
- package/src/core/adapt/index.js +2 -6
- package/src/core/postbuild/analyse.js +25 -1
- package/src/core/postbuild/crawl.js +2 -2
- package/src/core/postbuild/fallback.js +1 -4
- package/src/core/postbuild/prerender.js +0 -2
- package/src/core/sync/write_root.js +6 -5
- package/src/core/sync/write_server.js +2 -3
- package/src/core/sync/write_types/index.js +19 -16
- package/src/exports/vite/dev/index.js +3 -6
- package/src/exports/vite/index.js +19 -7
- package/src/exports/vite/preview/index.js +2 -2
- package/src/runtime/app/paths.js +1 -1
- package/src/runtime/client/client.js +86 -25
- package/src/runtime/client/constants.js +1 -0
- package/src/runtime/client/session-storage.js +25 -0
- package/src/runtime/client/start.js +5 -9
- package/src/runtime/client/utils.js +2 -1
- package/src/runtime/server/endpoint.js +4 -1
- package/src/runtime/server/fetch.js +1 -1
- package/src/runtime/server/page/index.js +1 -1
- package/src/runtime/server/page/render.js +4 -3
- package/src/runtime/server/page/respond_with_error.js +1 -1
- package/src/runtime/server/respond.js +7 -6
- package/src/runtime/server/utils.js +1 -19
- package/src/runtime/shared.js +2 -8
- package/src/utils/exports.js +6 -3
- package/src/utils/options.js +16 -0
- package/types/ambient.d.ts +8 -0
- package/types/index.d.ts +30 -5
- package/types/internal.d.ts +4 -2
- package/types/private.d.ts +7 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/sveltejs/kit",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"set-cookie-parser": "^2.5.1",
|
|
23
23
|
"sirv": "^2.0.2",
|
|
24
24
|
"tiny-glob": "^0.2.9",
|
|
25
|
-
"undici": "5.
|
|
25
|
+
"undici": "5.18.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@playwright/test": "^1.29.2",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"lint": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore && eslint src/**",
|
|
84
84
|
"check": "tsc",
|
|
85
85
|
"check:all": "tsc && pnpm -r --filter=\"./**\" check",
|
|
86
|
-
"format": "
|
|
86
|
+
"format": "prettier --write . --config ../../.prettierrc --ignore-path .gitignore",
|
|
87
87
|
"test": "pnpm test:unit && pnpm test:integration",
|
|
88
88
|
"test:integration": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test",
|
|
89
89
|
"test:cross-platform:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:dev",
|
package/src/constants.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* A fake asset path used in `vite dev` and `vite preview`, so that we can
|
|
3
|
+
* serve local assets while verifying that requests are correctly prefixed
|
|
4
|
+
*/
|
|
3
5
|
export const SVELTE_KIT_ASSETS = '/_svelte_kit_assets';
|
|
4
6
|
|
|
5
7
|
export const GENERATED_COMMENT = '// this file is generated — do not edit it\n';
|
|
@@ -18,13 +18,54 @@ const pipe = promisify(pipeline);
|
|
|
18
18
|
* config: import('types').ValidatedConfig;
|
|
19
19
|
* build_data: import('types').BuildData;
|
|
20
20
|
* server_metadata: import('types').ServerMetadata;
|
|
21
|
-
*
|
|
21
|
+
* route_data: import('types').RouteData[];
|
|
22
22
|
* prerendered: import('types').Prerendered;
|
|
23
|
+
* prerender_map: import('types').PrerenderMap;
|
|
23
24
|
* log: import('types').Logger;
|
|
24
25
|
* }} opts
|
|
25
26
|
* @returns {import('types').Builder}
|
|
26
27
|
*/
|
|
27
|
-
export function create_builder({
|
|
28
|
+
export function create_builder({
|
|
29
|
+
config,
|
|
30
|
+
build_data,
|
|
31
|
+
server_metadata,
|
|
32
|
+
route_data,
|
|
33
|
+
prerendered,
|
|
34
|
+
prerender_map,
|
|
35
|
+
log
|
|
36
|
+
}) {
|
|
37
|
+
/** @type {Map<import('types').RouteDefinition, import('types').RouteData>} */
|
|
38
|
+
const lookup = new Map();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Rather than exposing the internal `RouteData` type, which is subject to change,
|
|
42
|
+
* we expose a stable type that adapters can use to group/filter routes
|
|
43
|
+
*/
|
|
44
|
+
const routes = route_data.map((route) => {
|
|
45
|
+
const methods =
|
|
46
|
+
/** @type {import('types').HttpMethod[]} */
|
|
47
|
+
(server_metadata.routes.get(route.id)?.methods);
|
|
48
|
+
const config = server_metadata.routes.get(route.id)?.config;
|
|
49
|
+
|
|
50
|
+
/** @type {import('types').RouteDefinition} */
|
|
51
|
+
const facade = {
|
|
52
|
+
id: route.id,
|
|
53
|
+
segments: get_route_segments(route.id).map((segment) => ({
|
|
54
|
+
dynamic: segment.includes('['),
|
|
55
|
+
rest: segment.includes('[...'),
|
|
56
|
+
content: segment
|
|
57
|
+
})),
|
|
58
|
+
pattern: route.pattern,
|
|
59
|
+
prerender: prerender_map.get(route.id) ?? false,
|
|
60
|
+
methods,
|
|
61
|
+
config
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
lookup.set(facade, route);
|
|
65
|
+
|
|
66
|
+
return facade;
|
|
67
|
+
});
|
|
68
|
+
|
|
28
69
|
return {
|
|
29
70
|
log,
|
|
30
71
|
rimraf,
|
|
@@ -33,6 +74,7 @@ export function create_builder({ config, build_data, server_metadata, routes, pr
|
|
|
33
74
|
|
|
34
75
|
config,
|
|
35
76
|
prerendered,
|
|
77
|
+
routes,
|
|
36
78
|
|
|
37
79
|
async compress(directory) {
|
|
38
80
|
if (!existsSync(directory)) {
|
|
@@ -52,29 +94,12 @@ export function create_builder({ config, build_data, server_metadata, routes, pr
|
|
|
52
94
|
},
|
|
53
95
|
|
|
54
96
|
async createEntries(fn) {
|
|
55
|
-
/** @type {import('types').RouteDefinition[]} */
|
|
56
|
-
const facades = routes.map((route) => {
|
|
57
|
-
const methods =
|
|
58
|
-
/** @type {import('types').HttpMethod[]} */
|
|
59
|
-
(server_metadata.routes.get(route.id)?.methods);
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
id: route.id,
|
|
63
|
-
segments: get_route_segments(route.id).map((segment) => ({
|
|
64
|
-
dynamic: segment.includes('['),
|
|
65
|
-
rest: segment.includes('[...'),
|
|
66
|
-
content: segment
|
|
67
|
-
})),
|
|
68
|
-
pattern: route.pattern,
|
|
69
|
-
methods
|
|
70
|
-
};
|
|
71
|
-
});
|
|
72
|
-
|
|
73
97
|
const seen = new Set();
|
|
74
98
|
|
|
75
|
-
for (let i = 0; i <
|
|
76
|
-
const route =
|
|
77
|
-
|
|
99
|
+
for (let i = 0; i < route_data.length; i += 1) {
|
|
100
|
+
const route = route_data[i];
|
|
101
|
+
if (prerender_map.get(route.id) === true) continue;
|
|
102
|
+
const { id, filter, complete } = fn(routes[i]);
|
|
78
103
|
|
|
79
104
|
if (seen.has(id)) continue;
|
|
80
105
|
seen.add(id);
|
|
@@ -82,9 +107,10 @@ export function create_builder({ config, build_data, server_metadata, routes, pr
|
|
|
82
107
|
const group = [route];
|
|
83
108
|
|
|
84
109
|
// figure out which lower priority routes should be considered fallbacks
|
|
85
|
-
for (let j = i + 1; j <
|
|
86
|
-
if (
|
|
87
|
-
|
|
110
|
+
for (let j = i + 1; j < route_data.length; j += 1) {
|
|
111
|
+
if (prerender_map.get(routes[j].id) === true) continue;
|
|
112
|
+
if (filter(routes[j])) {
|
|
113
|
+
group.push(route_data[j]);
|
|
88
114
|
}
|
|
89
115
|
}
|
|
90
116
|
|
|
@@ -95,7 +121,7 @@ export function create_builder({ config, build_data, server_metadata, routes, pr
|
|
|
95
121
|
// TODO is this still necessary, given the new way of doing things?
|
|
96
122
|
filtered.forEach((route) => {
|
|
97
123
|
if (route.page) {
|
|
98
|
-
const endpoint =
|
|
124
|
+
const endpoint = route_data.find((candidate) => candidate.id === route.id + '.json');
|
|
99
125
|
|
|
100
126
|
if (endpoint) {
|
|
101
127
|
filtered.add(endpoint);
|
|
@@ -143,11 +169,13 @@ export function create_builder({ config, build_data, server_metadata, routes, pr
|
|
|
143
169
|
});
|
|
144
170
|
},
|
|
145
171
|
|
|
146
|
-
generateManifest: ({ relativePath }) => {
|
|
172
|
+
generateManifest: ({ relativePath, routes: subset }) => {
|
|
147
173
|
return generate_manifest({
|
|
148
174
|
build_data,
|
|
149
175
|
relative_path: relativePath,
|
|
150
|
-
routes
|
|
176
|
+
routes: subset
|
|
177
|
+
? subset.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route)))
|
|
178
|
+
: route_data
|
|
151
179
|
});
|
|
152
180
|
},
|
|
153
181
|
|
package/src/core/adapt/index.js
CHANGED
|
@@ -18,13 +18,9 @@ export async function adapt(config, build_data, server_metadata, prerendered, pr
|
|
|
18
18
|
config,
|
|
19
19
|
build_data,
|
|
20
20
|
server_metadata,
|
|
21
|
-
|
|
22
|
-
if (!route.page && !route.endpoint) return false;
|
|
23
|
-
|
|
24
|
-
const prerender = prerender_map.get(route.id);
|
|
25
|
-
return prerender === false || prerender === undefined || prerender === 'auto';
|
|
26
|
-
}),
|
|
21
|
+
route_data: build_data.manifest_data.routes.filter((route) => route.page || route.endpoint),
|
|
27
22
|
prerendered,
|
|
23
|
+
prerender_map,
|
|
28
24
|
log
|
|
29
25
|
});
|
|
30
26
|
await adapt(builder);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
|
-
import { get_option } from '../../
|
|
3
|
+
import { get_option } from '../../utils/options.js';
|
|
4
4
|
import {
|
|
5
5
|
validate_common_exports,
|
|
6
6
|
validate_page_server_exports,
|
|
@@ -67,6 +67,8 @@ async function analyse({ manifest_path, env }) {
|
|
|
67
67
|
|
|
68
68
|
/** @type {import('types').PrerenderOption | undefined} */
|
|
69
69
|
let prerender = undefined;
|
|
70
|
+
/** @type {any} */
|
|
71
|
+
let config = undefined;
|
|
70
72
|
|
|
71
73
|
if (route.endpoint) {
|
|
72
74
|
const mod = await route.endpoint();
|
|
@@ -123,13 +125,35 @@ async function analyse({ manifest_path, env }) {
|
|
|
123
125
|
(should_prerender !== false && get_option(nodes, 'ssr') === false && !page?.server?.actions
|
|
124
126
|
? 'auto'
|
|
125
127
|
: should_prerender ?? false);
|
|
128
|
+
|
|
129
|
+
config = get_config(nodes);
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
metadata.routes.set(route.id, {
|
|
129
133
|
prerender,
|
|
134
|
+
config,
|
|
130
135
|
methods: Array.from(methods)
|
|
131
136
|
});
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
return metadata;
|
|
135
140
|
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Do a shallow merge (first level) of the config object
|
|
144
|
+
* @param {Array<import('types').SSRNode | undefined>} nodes
|
|
145
|
+
*/
|
|
146
|
+
function get_config(nodes) {
|
|
147
|
+
let current = {};
|
|
148
|
+
for (const node of nodes) {
|
|
149
|
+
const config = node?.universal?.config ?? node?.server?.config;
|
|
150
|
+
if (config) {
|
|
151
|
+
current = {
|
|
152
|
+
...current,
|
|
153
|
+
...config
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return Object.keys(current).length ? current : undefined;
|
|
159
|
+
}
|
|
@@ -165,7 +165,7 @@ export function crawl(html) {
|
|
|
165
165
|
} else if (name === 'rel') {
|
|
166
166
|
rel = value;
|
|
167
167
|
} else if (name === 'src') {
|
|
168
|
-
hrefs.push(value);
|
|
168
|
+
if (value) hrefs.push(value);
|
|
169
169
|
} else if (name === 'srcset') {
|
|
170
170
|
const candidates = [];
|
|
171
171
|
let insideURL = true;
|
|
@@ -183,7 +183,7 @@ export function crawl(html) {
|
|
|
183
183
|
candidates.push(value);
|
|
184
184
|
for (const candidate of candidates) {
|
|
185
185
|
const src = candidate.split(WHITESPACE)[0];
|
|
186
|
-
hrefs.push(src);
|
|
186
|
+
if (src) hrefs.push(src);
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
} else {
|
|
@@ -15,9 +15,7 @@ installPolyfills();
|
|
|
15
15
|
const server_root = join(config.outDir, 'output');
|
|
16
16
|
|
|
17
17
|
/** @type {import('types').ServerInternalModule} */
|
|
18
|
-
const { set_building
|
|
19
|
-
pathToFileURL(`${server_root}/server/internal.js`).href
|
|
20
|
-
);
|
|
18
|
+
const { set_building } = await import(pathToFileURL(`${server_root}/server/internal.js`).href);
|
|
21
19
|
|
|
22
20
|
/** @type {import('types').ServerModule} */
|
|
23
21
|
const { Server } = await import(pathToFileURL(`${server_root}/server/index.js`).href);
|
|
@@ -26,7 +24,6 @@ const { Server } = await import(pathToFileURL(`${server_root}/server/index.js`).
|
|
|
26
24
|
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
|
|
27
25
|
|
|
28
26
|
set_building(true);
|
|
29
|
-
set_paths(config.paths);
|
|
30
27
|
|
|
31
28
|
const server = new Server(manifest);
|
|
32
29
|
await server.init({ env: JSON.parse(env) });
|
|
@@ -100,8 +100,6 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
|
|
|
100
100
|
/** @type {Map<string, string>} */
|
|
101
101
|
const saved = new Map();
|
|
102
102
|
|
|
103
|
-
internal.set_paths(config.paths);
|
|
104
|
-
|
|
105
103
|
const server = new Server(manifest);
|
|
106
104
|
await server.init({ env });
|
|
107
105
|
|
|
@@ -21,16 +21,16 @@ export function write_root(manifest_data, output) {
|
|
|
21
21
|
|
|
22
22
|
let l = max_depth;
|
|
23
23
|
|
|
24
|
-
let pyramid = `<svelte:component this={components[${l}]} data={data_${l}} {form} />`;
|
|
24
|
+
let pyramid = `<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} />`;
|
|
25
25
|
|
|
26
26
|
while (l--) {
|
|
27
27
|
pyramid = `
|
|
28
|
-
{#if
|
|
29
|
-
<svelte:component this={components[${l}]} data={data_${l}}>
|
|
28
|
+
{#if constructors[${l + 1}]}
|
|
29
|
+
<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}}>
|
|
30
30
|
${pyramid.replace(/\n/g, '\n\t\t\t\t\t')}
|
|
31
31
|
</svelte:component>
|
|
32
32
|
{:else}
|
|
33
|
-
<svelte:component this={components[${l}]} data={data_${l}} {form} />
|
|
33
|
+
<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} />
|
|
34
34
|
{/if}
|
|
35
35
|
`
|
|
36
36
|
.replace(/^\t\t\t/gm, '')
|
|
@@ -49,7 +49,8 @@ export function write_root(manifest_data, output) {
|
|
|
49
49
|
export let stores;
|
|
50
50
|
export let page;
|
|
51
51
|
|
|
52
|
-
export let
|
|
52
|
+
export let constructors;
|
|
53
|
+
export let components = [];
|
|
53
54
|
export let form;
|
|
54
55
|
${levels.map((l) => `export let data_${l} = null;`).join('\n\t\t\t\t')}
|
|
55
56
|
|
|
@@ -25,9 +25,8 @@ const server_template = ({
|
|
|
25
25
|
error_page
|
|
26
26
|
}) => `
|
|
27
27
|
import root from '../root.svelte';
|
|
28
|
-
import {
|
|
28
|
+
import { set_assets, set_building, set_private_env, set_public_env, set_version } from '${runtime_directory}/shared.js';
|
|
29
29
|
|
|
30
|
-
set_paths(${s(config.kit.paths)});
|
|
31
30
|
set_version(${s(config.kit.version.name)});
|
|
32
31
|
|
|
33
32
|
export const options = {
|
|
@@ -58,7 +57,7 @@ export function get_hooks() {
|
|
|
58
57
|
return ${hooks ? `import(${s(hooks)})` : '{}'};
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
export {
|
|
60
|
+
export { set_assets, set_building, set_private_env, set_public_env };
|
|
62
61
|
`;
|
|
63
62
|
|
|
64
63
|
// TODO need to re-run this whenever src/app.html or src/error.html are
|
|
@@ -198,23 +198,26 @@ function update_types(config, routes, route, to_delete = new Set()) {
|
|
|
198
198
|
|
|
199
199
|
// 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
|
|
200
200
|
if (route.layout || route.leaf) {
|
|
201
|
-
// If T extends the empty object, void is also allowed as a return type
|
|
202
|
-
declarations.push(`type MaybeWithVoid<T> = {} extends T ? T | void : T;`);
|
|
203
|
-
// Returns the key of the object whose values are required.
|
|
204
201
|
declarations.push(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
202
|
+
// If T extends the empty object, void is also allowed as a return type
|
|
203
|
+
`type MaybeWithVoid<T> = {} extends T ? T | void : T;`,
|
|
204
|
+
|
|
205
|
+
// Returns the key of the object whose values are required.
|
|
206
|
+
`export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];`,
|
|
207
|
+
|
|
208
|
+
// Helper type to get the correct output type for load functions. It should be passed the parent type to check what types from App.PageData are still required.
|
|
209
|
+
// If none, void is also allowed as a return type.
|
|
210
|
+
`type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>`,
|
|
211
|
+
|
|
212
|
+
// null & {} == null, we need to prevent that in some situations
|
|
213
|
+
`type EnsureDefined<T> = T extends null | undefined ? {} : T;`,
|
|
214
|
+
|
|
215
|
+
// Takes a union type and returns a union type where each type also has all properties
|
|
216
|
+
// of all possible types (typed as undefined), making accessing them more ergonomic
|
|
217
|
+
`type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;`,
|
|
218
|
+
|
|
219
|
+
// Re-export `Snapshot` from @sveltejs/kit — in future we could use this to infer <T> from the return type of `snapshot.capture`
|
|
220
|
+
`export type Snapshot<T = any> = Kit.Snapshot<T>;`
|
|
218
221
|
);
|
|
219
222
|
}
|
|
220
223
|
|
|
@@ -438,20 +438,17 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
438
438
|
return;
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
-
// we have to import `Server` before calling `
|
|
441
|
+
// we have to import `Server` before calling `set_assets`
|
|
442
442
|
const { Server } = /** @type {import('types').ServerModule} */ (
|
|
443
443
|
await vite.ssrLoadModule(`${runtime_base}/server/index.js`)
|
|
444
444
|
);
|
|
445
445
|
|
|
446
|
-
const {
|
|
446
|
+
const { set_assets, set_version, set_fix_stack_trace } =
|
|
447
447
|
/** @type {import('types').ServerInternalModule} */ (
|
|
448
448
|
await vite.ssrLoadModule(`${runtime_base}/shared.js`)
|
|
449
449
|
);
|
|
450
450
|
|
|
451
|
-
|
|
452
|
-
base: svelte_config.kit.paths.base,
|
|
453
|
-
assets
|
|
454
|
-
});
|
|
451
|
+
set_assets(assets);
|
|
455
452
|
|
|
456
453
|
set_version(svelte_config.kit.version.name);
|
|
457
454
|
|
|
@@ -22,6 +22,7 @@ import { get_config_aliases, get_env } from './utils.js';
|
|
|
22
22
|
import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
|
|
23
23
|
import prerender from '../../core/postbuild/prerender.js';
|
|
24
24
|
import analyse from '../../core/postbuild/analyse.js';
|
|
25
|
+
import { s } from '../../utils/misc.js';
|
|
25
26
|
|
|
26
27
|
export { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
27
28
|
|
|
@@ -253,9 +254,9 @@ function kit({ svelte_config }) {
|
|
|
253
254
|
new_config.build.ssr = !secondary_build_started;
|
|
254
255
|
|
|
255
256
|
new_config.define = {
|
|
256
|
-
__SVELTEKIT_ADAPTER_NAME__:
|
|
257
|
-
__SVELTEKIT_APP_VERSION_FILE__:
|
|
258
|
-
__SVELTEKIT_APP_VERSION_POLL_INTERVAL__:
|
|
257
|
+
__SVELTEKIT_ADAPTER_NAME__: s(kit.adapter?.name),
|
|
258
|
+
__SVELTEKIT_APP_VERSION_FILE__: s(`${kit.appDir}/version.json`),
|
|
259
|
+
__SVELTEKIT_APP_VERSION_POLL_INTERVAL__: s(kit.version.pollInterval),
|
|
259
260
|
__SVELTEKIT_DEV__: 'false',
|
|
260
261
|
__SVELTEKIT_EMBEDDED__: kit.embedded ? 'true' : 'false'
|
|
261
262
|
};
|
|
@@ -315,7 +316,9 @@ function kit({ svelte_config }) {
|
|
|
315
316
|
|
|
316
317
|
async resolveId(id) {
|
|
317
318
|
// treat $env/static/[public|private] as virtual
|
|
318
|
-
if (id.startsWith('$env/') || id === '$service-worker')
|
|
319
|
+
if (id.startsWith('$env/') || id === '$internal/paths' || id === '$service-worker') {
|
|
320
|
+
return `\0${id}`;
|
|
321
|
+
}
|
|
319
322
|
},
|
|
320
323
|
|
|
321
324
|
async load(id, options) {
|
|
@@ -351,6 +354,15 @@ function kit({ svelte_config }) {
|
|
|
351
354
|
);
|
|
352
355
|
case '\0$service-worker':
|
|
353
356
|
return create_service_worker_module(svelte_config);
|
|
357
|
+
case '\0$internal/paths':
|
|
358
|
+
const { assets, base } = svelte_config.kit.paths;
|
|
359
|
+
return `export const base = ${s(base)};
|
|
360
|
+
export let assets = ${s(assets)};
|
|
361
|
+
|
|
362
|
+
/** @param {string} path */
|
|
363
|
+
export function set_assets(path) {
|
|
364
|
+
assets = path;
|
|
365
|
+
}`;
|
|
354
366
|
}
|
|
355
367
|
}
|
|
356
368
|
};
|
|
@@ -552,7 +564,7 @@ function kit({ svelte_config }) {
|
|
|
552
564
|
this.emitFile({
|
|
553
565
|
type: 'asset',
|
|
554
566
|
fileName: `${kit.appDir}/version.json`,
|
|
555
|
-
source:
|
|
567
|
+
source: s({ version: kit.version.name })
|
|
556
568
|
});
|
|
557
569
|
},
|
|
558
570
|
|
|
@@ -788,9 +800,9 @@ export const build = [];
|
|
|
788
800
|
export const files = [
|
|
789
801
|
${create_assets(config)
|
|
790
802
|
.filter((asset) => config.kit.serviceWorker.files(asset.file))
|
|
791
|
-
.map((asset) => `${
|
|
803
|
+
.map((asset) => `${s(`${config.kit.paths.base}/${asset.file}`)}`)
|
|
792
804
|
.join(',\n\t\t\t\t')}
|
|
793
805
|
];
|
|
794
806
|
export const prerendered = [];
|
|
795
|
-
export const version = ${
|
|
807
|
+
export const version = ${s(config.kit.version.name)};
|
|
796
808
|
`;
|
|
@@ -37,14 +37,14 @@ export async function preview(vite, vite_config, svelte_config) {
|
|
|
37
37
|
const dir = join(svelte_config.kit.outDir, 'output/server');
|
|
38
38
|
|
|
39
39
|
/** @type {import('types').ServerInternalModule} */
|
|
40
|
-
const {
|
|
40
|
+
const { set_assets } = await import(pathToFileURL(join(dir, 'internal.js')).href);
|
|
41
41
|
|
|
42
42
|
/** @type {import('types').ServerModule} */
|
|
43
43
|
const { Server } = await import(pathToFileURL(join(dir, 'index.js')).href);
|
|
44
44
|
|
|
45
45
|
const { manifest } = await import(pathToFileURL(join(dir, 'manifest.js')).href);
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
set_assets(assets);
|
|
48
48
|
|
|
49
49
|
const server = new Server(manifest);
|
|
50
50
|
await server.init({
|
package/src/runtime/app/paths.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { base, assets } from '
|
|
1
|
+
export { base, assets } from '$internal/paths';
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
is_external_url,
|
|
16
16
|
scroll_state
|
|
17
17
|
} from './utils.js';
|
|
18
|
+
import * as storage from './session-storage.js';
|
|
18
19
|
import {
|
|
19
20
|
lock_fetch,
|
|
20
21
|
unlock_fetch,
|
|
@@ -26,11 +27,12 @@ import { parse } from './parse.js';
|
|
|
26
27
|
|
|
27
28
|
import Root from '__GENERATED__/root.svelte';
|
|
28
29
|
import { nodes, server_loads, dictionary, matchers, hooks } from '__CLIENT__/manifest.js';
|
|
30
|
+
import { base } from '$internal/paths';
|
|
29
31
|
import { HttpError, Redirect } from '../control.js';
|
|
30
32
|
import { stores } from './singletons.js';
|
|
31
33
|
import { unwrap_promises } from '../../utils/promises.js';
|
|
32
34
|
import * as devalue from 'devalue';
|
|
33
|
-
import { INDEX_KEY, PRELOAD_PRIORITIES, SCROLL_KEY } from './constants.js';
|
|
35
|
+
import { INDEX_KEY, PRELOAD_PRIORITIES, SCROLL_KEY, SNAPSHOT_KEY } from './constants.js';
|
|
34
36
|
import { validate_common_exports } from '../../utils/exports.js';
|
|
35
37
|
import { compact } from '../../utils/array.js';
|
|
36
38
|
|
|
@@ -51,12 +53,10 @@ default_error_loader();
|
|
|
51
53
|
|
|
52
54
|
/** @typedef {{ x: number, y: number }} ScrollPosition */
|
|
53
55
|
/** @type {Record<number, ScrollPosition>} */
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// do nothing
|
|
59
|
-
}
|
|
56
|
+
const scroll_positions = storage.get(SCROLL_KEY) ?? {};
|
|
57
|
+
|
|
58
|
+
/** @type {Record<string, any[]>} */
|
|
59
|
+
const snapshots = storage.get(SNAPSHOT_KEY) ?? {};
|
|
60
60
|
|
|
61
61
|
/** @param {number} index */
|
|
62
62
|
function update_scroll_positions(index) {
|
|
@@ -66,15 +66,22 @@ function update_scroll_positions(index) {
|
|
|
66
66
|
/**
|
|
67
67
|
* @param {{
|
|
68
68
|
* target: HTMLElement;
|
|
69
|
-
* base: string;
|
|
70
69
|
* }} opts
|
|
71
70
|
* @returns {import('./types').Client}
|
|
72
71
|
*/
|
|
73
|
-
export function create_client({ target
|
|
72
|
+
export function create_client({ target }) {
|
|
74
73
|
const container = __SVELTEKIT_EMBEDDED__ ? target : document.documentElement;
|
|
75
74
|
/** @type {Array<((url: URL) => boolean)>} */
|
|
76
75
|
const invalidated = [];
|
|
77
76
|
|
|
77
|
+
/**
|
|
78
|
+
* An array of the `+layout.svelte` and `+page.svelte` component instances
|
|
79
|
+
* that currently live on the page — used for capturing and restoring snapshots.
|
|
80
|
+
* It's updated/manipulated through `bind:this` in `Root.svelte`.
|
|
81
|
+
* @type {import('svelte').SvelteComponent[]}
|
|
82
|
+
*/
|
|
83
|
+
const components = [];
|
|
84
|
+
|
|
78
85
|
/** @type {{id: string, promise: Promise<import('./types').NavigationResult>} | null} */
|
|
79
86
|
let load_cache = null;
|
|
80
87
|
|
|
@@ -158,6 +165,20 @@ export function create_client({ target, base }) {
|
|
|
158
165
|
await update(intent, url, []);
|
|
159
166
|
}
|
|
160
167
|
|
|
168
|
+
/** @param {number} index */
|
|
169
|
+
function capture_snapshot(index) {
|
|
170
|
+
if (components.some((c) => c?.snapshot)) {
|
|
171
|
+
snapshots[index] = components.map((c) => c?.snapshot?.capture());
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** @param {number} index */
|
|
176
|
+
function restore_snapshot(index) {
|
|
177
|
+
snapshots[index]?.forEach((value, i) => {
|
|
178
|
+
components[i]?.snapshot?.restore(value);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
161
182
|
/**
|
|
162
183
|
* @param {string | URL} url
|
|
163
184
|
* @param {{ noScroll?: boolean; replaceState?: boolean; keepFocus?: boolean; state?: any; invalidateAll?: boolean }} opts
|
|
@@ -238,11 +259,20 @@ export function create_client({ target, base }) {
|
|
|
238
259
|
* @param {import('./types').NavigationIntent | undefined} intent
|
|
239
260
|
* @param {URL} url
|
|
240
261
|
* @param {string[]} redirect_chain
|
|
262
|
+
* @param {number} [previous_history_index]
|
|
241
263
|
* @param {{hash?: string, scroll: { x: number, y: number } | null, keepfocus: boolean, details: { replaceState: boolean, state: any } | null}} [opts]
|
|
242
264
|
* @param {{}} [nav_token] To distinguish between different navigation events and determine the latest. Needed for example for redirects to keep the original token
|
|
243
265
|
* @param {() => void} [callback]
|
|
244
266
|
*/
|
|
245
|
-
async function update(
|
|
267
|
+
async function update(
|
|
268
|
+
intent,
|
|
269
|
+
url,
|
|
270
|
+
redirect_chain,
|
|
271
|
+
previous_history_index,
|
|
272
|
+
opts,
|
|
273
|
+
nav_token = {},
|
|
274
|
+
callback
|
|
275
|
+
) {
|
|
246
276
|
token = nav_token;
|
|
247
277
|
let navigation_result = intent && (await load_route(intent));
|
|
248
278
|
|
|
@@ -301,11 +331,28 @@ export function create_client({ target, base }) {
|
|
|
301
331
|
|
|
302
332
|
updating = true;
|
|
303
333
|
|
|
334
|
+
// `previous_history_index` will be undefined for invalidation
|
|
335
|
+
if (previous_history_index) {
|
|
336
|
+
update_scroll_positions(previous_history_index);
|
|
337
|
+
capture_snapshot(previous_history_index);
|
|
338
|
+
}
|
|
339
|
+
|
|
304
340
|
if (opts && opts.details) {
|
|
305
341
|
const { details } = opts;
|
|
306
342
|
const change = details.replaceState ? 0 : 1;
|
|
307
343
|
details.state[INDEX_KEY] = current_history_index += change;
|
|
308
344
|
history[details.replaceState ? 'replaceState' : 'pushState'](details.state, '', url);
|
|
345
|
+
|
|
346
|
+
if (!details.replaceState) {
|
|
347
|
+
// if we navigated back, then pushed a new state, we can
|
|
348
|
+
// release memory by pruning the scroll/snapshot lookup
|
|
349
|
+
let i = current_history_index + 1;
|
|
350
|
+
while (snapshots[i] || scroll_positions[i]) {
|
|
351
|
+
delete snapshots[i];
|
|
352
|
+
delete scroll_positions[i];
|
|
353
|
+
i += 1;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
309
356
|
}
|
|
310
357
|
|
|
311
358
|
// reset preload synchronously after the history state has been set to avoid race conditions
|
|
@@ -384,10 +431,12 @@ export function create_client({ target, base }) {
|
|
|
384
431
|
|
|
385
432
|
root = new Root({
|
|
386
433
|
target,
|
|
387
|
-
props: { ...result.props, stores },
|
|
434
|
+
props: { ...result.props, stores, components },
|
|
388
435
|
hydrate: true
|
|
389
436
|
});
|
|
390
437
|
|
|
438
|
+
restore_snapshot(current_history_index);
|
|
439
|
+
|
|
391
440
|
/** @type {import('types').AfterNavigate} */
|
|
392
441
|
const navigation = {
|
|
393
442
|
from: null,
|
|
@@ -445,7 +494,7 @@ export function create_client({ target, base }) {
|
|
|
445
494
|
},
|
|
446
495
|
props: {
|
|
447
496
|
// @ts-ignore Somehow it's getting SvelteComponent and SvelteComponentDev mixed up
|
|
448
|
-
|
|
497
|
+
constructors: compact(branch).map((branch_node) => branch_node.node.component)
|
|
449
498
|
}
|
|
450
499
|
};
|
|
451
500
|
|
|
@@ -594,12 +643,17 @@ export function create_client({ target, base }) {
|
|
|
594
643
|
}
|
|
595
644
|
|
|
596
645
|
// we must fixup relative urls so they are resolved from the target page
|
|
597
|
-
const resolved = new URL(requested, url)
|
|
598
|
-
depends(resolved);
|
|
646
|
+
const resolved = new URL(requested, url);
|
|
647
|
+
depends(resolved.href);
|
|
648
|
+
|
|
649
|
+
// match ssr serialized data url, which is important to find cached responses
|
|
650
|
+
if (resolved.origin === url.origin) {
|
|
651
|
+
requested = resolved.href.slice(url.origin.length);
|
|
652
|
+
}
|
|
599
653
|
|
|
600
654
|
// prerendered pages may be served from any origin, so `initial_fetch` urls shouldn't be resolved
|
|
601
655
|
return started
|
|
602
|
-
? subsequent_fetch(requested, resolved, init)
|
|
656
|
+
? subsequent_fetch(requested, resolved.href, init)
|
|
603
657
|
: initial_fetch(requested, init);
|
|
604
658
|
},
|
|
605
659
|
setHeaders: () => {}, // noop
|
|
@@ -745,7 +799,7 @@ export function create_client({ target, base }) {
|
|
|
745
799
|
server_data = await load_data(url, invalid_server_nodes);
|
|
746
800
|
} catch (error) {
|
|
747
801
|
return load_root_error_page({
|
|
748
|
-
status: 500,
|
|
802
|
+
status: error instanceof HttpError ? error.status : 500,
|
|
749
803
|
error: await handle_error(error, { url, params, route: { id: route.id } }),
|
|
750
804
|
url,
|
|
751
805
|
route
|
|
@@ -1086,7 +1140,8 @@ export function create_client({ target, base }) {
|
|
|
1086
1140
|
return;
|
|
1087
1141
|
}
|
|
1088
1142
|
|
|
1089
|
-
|
|
1143
|
+
// store this before calling `accepted()`, which may change the index
|
|
1144
|
+
const previous_history_index = current_history_index;
|
|
1090
1145
|
|
|
1091
1146
|
accepted();
|
|
1092
1147
|
|
|
@@ -1100,6 +1155,7 @@ export function create_client({ target, base }) {
|
|
|
1100
1155
|
intent,
|
|
1101
1156
|
url,
|
|
1102
1157
|
redirect_chain,
|
|
1158
|
+
previous_history_index,
|
|
1103
1159
|
{
|
|
1104
1160
|
scroll,
|
|
1105
1161
|
keepfocus,
|
|
@@ -1380,12 +1436,10 @@ export function create_client({ target, base }) {
|
|
|
1380
1436
|
addEventListener('visibilitychange', () => {
|
|
1381
1437
|
if (document.visibilityState === 'hidden') {
|
|
1382
1438
|
update_scroll_positions(current_history_index);
|
|
1439
|
+
storage.set(SCROLL_KEY, scroll_positions);
|
|
1383
1440
|
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
} catch {
|
|
1387
|
-
// do nothing
|
|
1388
|
-
}
|
|
1441
|
+
capture_snapshot(current_history_index);
|
|
1442
|
+
storage.set(SNAPSHOT_KEY, snapshots);
|
|
1389
1443
|
}
|
|
1390
1444
|
});
|
|
1391
1445
|
|
|
@@ -1532,7 +1586,7 @@ export function create_client({ target, base }) {
|
|
|
1532
1586
|
});
|
|
1533
1587
|
});
|
|
1534
1588
|
|
|
1535
|
-
addEventListener('popstate', (event) => {
|
|
1589
|
+
addEventListener('popstate', async (event) => {
|
|
1536
1590
|
if (event.state?.[INDEX_KEY]) {
|
|
1537
1591
|
// if a popstate-driven navigation is cancelled, we need to counteract it
|
|
1538
1592
|
// with history.go, which means we end up back here, hence this check
|
|
@@ -1550,8 +1604,9 @@ export function create_client({ target, base }) {
|
|
|
1550
1604
|
}
|
|
1551
1605
|
|
|
1552
1606
|
const delta = event.state[INDEX_KEY] - current_history_index;
|
|
1607
|
+
let blocked = false;
|
|
1553
1608
|
|
|
1554
|
-
navigate({
|
|
1609
|
+
await navigate({
|
|
1555
1610
|
url: new URL(location.href),
|
|
1556
1611
|
scroll,
|
|
1557
1612
|
keepfocus: false,
|
|
@@ -1562,10 +1617,15 @@ export function create_client({ target, base }) {
|
|
|
1562
1617
|
},
|
|
1563
1618
|
blocked: () => {
|
|
1564
1619
|
history.go(-delta);
|
|
1620
|
+
blocked = true;
|
|
1565
1621
|
},
|
|
1566
1622
|
type: 'popstate',
|
|
1567
1623
|
delta
|
|
1568
1624
|
});
|
|
1625
|
+
|
|
1626
|
+
if (!blocked) {
|
|
1627
|
+
restore_snapshot(current_history_index);
|
|
1628
|
+
}
|
|
1569
1629
|
}
|
|
1570
1630
|
});
|
|
1571
1631
|
|
|
@@ -1693,7 +1753,8 @@ async function load_data(url, invalid) {
|
|
|
1693
1753
|
|
|
1694
1754
|
if (!res.ok) {
|
|
1695
1755
|
// error message is a JSON-stringified string which devalue can't handle at the top level
|
|
1696
|
-
|
|
1756
|
+
// turn it into a HttpError to not call handleError on the client again (was already handled on the server)
|
|
1757
|
+
throw new HttpError(res.status, data);
|
|
1697
1758
|
}
|
|
1698
1759
|
|
|
1699
1760
|
// revive devalue-flattened data
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a value from `sessionStorage`
|
|
3
|
+
* @param {string} key
|
|
4
|
+
*/
|
|
5
|
+
export function get(key) {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(sessionStorage[key]);
|
|
8
|
+
} catch {
|
|
9
|
+
// do nothing
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Write a value to `sessionStorage`
|
|
15
|
+
* @param {string} key
|
|
16
|
+
* @param {any} value
|
|
17
|
+
*/
|
|
18
|
+
export function set(key, value) {
|
|
19
|
+
const json = JSON.stringify(value);
|
|
20
|
+
try {
|
|
21
|
+
sessionStorage[key] = json;
|
|
22
|
+
} catch {
|
|
23
|
+
// do nothing
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import { DEV } from 'esm-env';
|
|
2
2
|
import { create_client } from './client.js';
|
|
3
3
|
import { init } from './singletons.js';
|
|
4
|
-
import {
|
|
4
|
+
import { set_assets, set_version, set_public_env } from '../shared.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @param {{
|
|
8
|
+
* assets: string;
|
|
8
9
|
* env: Record<string, string>;
|
|
9
10
|
* hydrate: Parameters<import('./types').Client['_hydrate']>[0];
|
|
10
|
-
* paths: {
|
|
11
|
-
* assets: string;
|
|
12
|
-
* base: string;
|
|
13
|
-
* },
|
|
14
11
|
* target: HTMLElement;
|
|
15
12
|
* version: string;
|
|
16
13
|
* }} opts
|
|
17
14
|
*/
|
|
18
|
-
export async function start({ env, hydrate,
|
|
15
|
+
export async function start({ assets, env, hydrate, target, version }) {
|
|
19
16
|
set_public_env(env);
|
|
20
|
-
|
|
17
|
+
set_assets(assets);
|
|
21
18
|
set_version(version);
|
|
22
19
|
|
|
23
20
|
if (DEV && target === document.body) {
|
|
@@ -27,8 +24,7 @@ export async function start({ env, hydrate, paths, target, version }) {
|
|
|
27
24
|
}
|
|
28
25
|
|
|
29
26
|
const client = create_client({
|
|
30
|
-
target
|
|
31
|
-
base: paths.base
|
|
27
|
+
target
|
|
32
28
|
});
|
|
33
29
|
|
|
34
30
|
init({ client });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BROWSER, DEV } from 'esm-env';
|
|
2
2
|
import { writable } from 'svelte/store';
|
|
3
|
-
import { assets
|
|
3
|
+
import { assets } from '$internal/paths';
|
|
4
|
+
import { version } from '../shared.js';
|
|
4
5
|
import { PRELOAD_PRIORITIES } from './constants.js';
|
|
5
6
|
|
|
6
7
|
/* global __SVELTEKIT_APP_VERSION_FILE__, __SVELTEKIT_APP_VERSION_POLL_INTERVAL__ */
|
|
@@ -4,11 +4,12 @@ import { method_not_allowed } from './utils.js';
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @param {import('types').RequestEvent} event
|
|
7
|
+
* @param {import('types').SSRRoute} route
|
|
7
8
|
* @param {import('types').SSREndpoint} mod
|
|
8
9
|
* @param {import('types').SSRState} state
|
|
9
10
|
* @returns {Promise<Response>}
|
|
10
11
|
*/
|
|
11
|
-
export async function render_endpoint(event, mod, state) {
|
|
12
|
+
export async function render_endpoint(event, route, mod, state) {
|
|
12
13
|
const method = /** @type {import('types').HttpMethod} */ (event.request.method);
|
|
13
14
|
|
|
14
15
|
let handler = mod[method];
|
|
@@ -38,6 +39,8 @@ export async function render_endpoint(event, mod, state) {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
state.initiator = route;
|
|
43
|
+
|
|
41
44
|
try {
|
|
42
45
|
const response = await handler(
|
|
43
46
|
/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
|
|
@@ -4,7 +4,6 @@ import { normalize_error } from '../../../utils/error.js';
|
|
|
4
4
|
import { add_data_suffix } from '../../../utils/url.js';
|
|
5
5
|
import { HttpError, Redirect } from '../../control.js';
|
|
6
6
|
import {
|
|
7
|
-
get_option,
|
|
8
7
|
redirect_response,
|
|
9
8
|
static_error_page,
|
|
10
9
|
handle_error_and_jsonify,
|
|
@@ -19,6 +18,7 @@ import {
|
|
|
19
18
|
import { load_data, load_server_data } from './load_data.js';
|
|
20
19
|
import { render_response } from './render.js';
|
|
21
20
|
import { respond_with_error } from './respond_with_error.js';
|
|
21
|
+
import { get_option } from '../../../utils/options.js';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* @param {import('types').RequestEvent} event
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import * as devalue from 'devalue';
|
|
2
2
|
import { readable, writable } from 'svelte/store';
|
|
3
3
|
import { DEV } from 'esm-env';
|
|
4
|
+
import { assets, base } from '$internal/paths';
|
|
4
5
|
import { hash } from '../../hash.js';
|
|
5
6
|
import { serialize_data } from './serialize_data.js';
|
|
6
7
|
import { s } from '../../../utils/misc.js';
|
|
7
8
|
import { Csp } from './csp.js';
|
|
8
9
|
import { uneval_action_response } from './actions.js';
|
|
9
10
|
import { clarify_devalue_error } from '../utils.js';
|
|
10
|
-
import {
|
|
11
|
+
import { version, public_env } from '../../shared.js';
|
|
11
12
|
import { text } from '../../../exports/index.js';
|
|
12
13
|
|
|
13
14
|
// TODO rename this function/module
|
|
@@ -89,7 +90,7 @@ export async function render_response({
|
|
|
89
90
|
navigating: writable(null),
|
|
90
91
|
updated
|
|
91
92
|
},
|
|
92
|
-
|
|
93
|
+
constructors: await Promise.all(branch.map(({ node }) => node.component())),
|
|
93
94
|
form: form_value
|
|
94
95
|
};
|
|
95
96
|
|
|
@@ -265,8 +266,8 @@ export async function render_response({
|
|
|
265
266
|
|
|
266
267
|
if (page_config.csr) {
|
|
267
268
|
const opts = [
|
|
269
|
+
`assets: ${s(assets)}`,
|
|
268
270
|
`env: ${s(public_env)}`,
|
|
269
|
-
`paths: ${s({ assets, base })}`,
|
|
270
271
|
`target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode`,
|
|
271
272
|
`version: ${s(version)}`
|
|
272
273
|
];
|
|
@@ -2,11 +2,11 @@ import { render_response } from './render.js';
|
|
|
2
2
|
import { load_data, load_server_data } from './load_data.js';
|
|
3
3
|
import {
|
|
4
4
|
handle_error_and_jsonify,
|
|
5
|
-
get_option,
|
|
6
5
|
static_error_page,
|
|
7
6
|
redirect_response,
|
|
8
7
|
GENERIC_ERROR
|
|
9
8
|
} from '../utils.js';
|
|
9
|
+
import { get_option } from '../../../utils/options.js';
|
|
10
10
|
import { HttpError, Redirect } from '../../control.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { DEV } from 'esm-env';
|
|
2
|
+
import { base } from '$internal/paths';
|
|
2
3
|
import { is_endpoint_request, render_endpoint } from './endpoint.js';
|
|
3
4
|
import { render_page } from './page/index.js';
|
|
4
5
|
import { render_response } from './page/render.js';
|
|
5
6
|
import { respond_with_error } from './page/respond_with_error.js';
|
|
6
7
|
import { is_form_content_type } from '../../utils/http.js';
|
|
7
|
-
import { GENERIC_ERROR,
|
|
8
|
+
import { GENERIC_ERROR, handle_fatal_error, redirect_response } from './utils.js';
|
|
8
9
|
import {
|
|
9
10
|
decode_pathname,
|
|
10
11
|
decode_params,
|
|
@@ -23,8 +24,8 @@ import {
|
|
|
23
24
|
validate_page_server_exports,
|
|
24
25
|
validate_server_exports
|
|
25
26
|
} from '../../utils/exports.js';
|
|
27
|
+
import { get_option } from '../../utils/options.js';
|
|
26
28
|
import { error, json, text } from '../../exports/index.js';
|
|
27
|
-
import * as paths from '../shared.js';
|
|
28
29
|
|
|
29
30
|
/* global __SVELTEKIT_ADAPTER_NAME__ */
|
|
30
31
|
|
|
@@ -70,11 +71,11 @@ export async function respond(request, options, manifest, state) {
|
|
|
70
71
|
/** @type {Record<string, string>} */
|
|
71
72
|
let params = {};
|
|
72
73
|
|
|
73
|
-
if (
|
|
74
|
-
if (!decoded.startsWith(
|
|
74
|
+
if (base && !state.prerendering?.fallback) {
|
|
75
|
+
if (!decoded.startsWith(base)) {
|
|
75
76
|
return text('Not found', { status: 404 });
|
|
76
77
|
}
|
|
77
|
-
decoded = decoded.slice(
|
|
78
|
+
decoded = decoded.slice(base.length) || '/';
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
const is_data_request = has_data_suffix(decoded);
|
|
@@ -357,7 +358,7 @@ export async function respond(request, options, manifest, state) {
|
|
|
357
358
|
trailing_slash ?? 'never'
|
|
358
359
|
);
|
|
359
360
|
} else if (route.endpoint && (!route.page || is_endpoint_request(event))) {
|
|
360
|
-
response = await render_endpoint(event, await route.endpoint(), state);
|
|
361
|
+
response = await render_endpoint(event, route, await route.endpoint(), state);
|
|
361
362
|
} else if (route.page) {
|
|
362
363
|
response = await render_page(
|
|
363
364
|
event,
|
|
@@ -2,7 +2,6 @@ import * as devalue from 'devalue';
|
|
|
2
2
|
import { json, text } from '../../exports/index.js';
|
|
3
3
|
import { coalesce_to_error } from '../../utils/error.js';
|
|
4
4
|
import { negotiate } from '../../utils/http.js';
|
|
5
|
-
import { has_data_suffix } from '../../utils/url.js';
|
|
6
5
|
import { HttpError } from '../control.js';
|
|
7
6
|
import { fix_stack_trace } from '../shared.js';
|
|
8
7
|
|
|
@@ -51,23 +50,6 @@ export function allowed_methods(mod) {
|
|
|
51
50
|
return allowed;
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
/**
|
|
55
|
-
* @template {'prerender' | 'ssr' | 'csr' | 'trailingSlash'} Option
|
|
56
|
-
* @template {Option extends 'prerender' ? import('types').PrerenderOption : Option extends 'trailingSlash' ? import('types').TrailingSlash : boolean} Value
|
|
57
|
-
*
|
|
58
|
-
* @param {Array<import('types').SSRNode | undefined>} nodes
|
|
59
|
-
* @param {Option} option
|
|
60
|
-
*
|
|
61
|
-
* @returns {Value | undefined}
|
|
62
|
-
*/
|
|
63
|
-
export function get_option(nodes, option) {
|
|
64
|
-
return nodes.reduce((value, node) => {
|
|
65
|
-
return /** @type {any} TypeScript's too dumb to understand this */ (
|
|
66
|
-
node?.universal?.[option] ?? node?.server?.[option] ?? value
|
|
67
|
-
);
|
|
68
|
-
}, /** @type {Value | undefined} */ (undefined));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
53
|
/**
|
|
72
54
|
* Return as a response that renders the error.html
|
|
73
55
|
*
|
|
@@ -98,7 +80,7 @@ export async function handle_fatal_error(event, options, error) {
|
|
|
98
80
|
'text/html'
|
|
99
81
|
]);
|
|
100
82
|
|
|
101
|
-
if (
|
|
83
|
+
if (event.isDataRequest || type === 'application/json') {
|
|
102
84
|
return json(body, {
|
|
103
85
|
status
|
|
104
86
|
});
|
package/src/runtime/shared.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
1
|
+
export { set_assets } from '$internal/paths';
|
|
2
|
+
|
|
3
3
|
export let building = false;
|
|
4
4
|
export let version = '';
|
|
5
5
|
|
|
@@ -12,12 +12,6 @@ export let public_env = {};
|
|
|
12
12
|
/** @param {string} stack */
|
|
13
13
|
export let fix_stack_trace = (stack) => stack;
|
|
14
14
|
|
|
15
|
-
/** @param {{ base: string, assets: string }} paths */
|
|
16
|
-
export function set_paths(paths) {
|
|
17
|
-
base = paths.base;
|
|
18
|
-
assets = paths.assets || base;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
15
|
/** @param {boolean} value */
|
|
22
16
|
export function set_building(value) {
|
|
23
17
|
building = value;
|
package/src/utils/exports.js
CHANGED
|
@@ -31,7 +31,8 @@ export const validate_common_exports = validator([
|
|
|
31
31
|
'prerender',
|
|
32
32
|
'csr',
|
|
33
33
|
'ssr',
|
|
34
|
-
'trailingSlash'
|
|
34
|
+
'trailingSlash',
|
|
35
|
+
'config'
|
|
35
36
|
]);
|
|
36
37
|
|
|
37
38
|
export const validate_page_server_exports = validator([
|
|
@@ -40,7 +41,8 @@ export const validate_page_server_exports = validator([
|
|
|
40
41
|
'csr',
|
|
41
42
|
'ssr',
|
|
42
43
|
'actions',
|
|
43
|
-
'trailingSlash'
|
|
44
|
+
'trailingSlash',
|
|
45
|
+
'config'
|
|
44
46
|
]);
|
|
45
47
|
|
|
46
48
|
export const validate_server_exports = validator([
|
|
@@ -50,5 +52,6 @@ export const validate_server_exports = validator([
|
|
|
50
52
|
'PUT',
|
|
51
53
|
'DELETE',
|
|
52
54
|
'prerender',
|
|
53
|
-
'trailingSlash'
|
|
55
|
+
'trailingSlash',
|
|
56
|
+
'config'
|
|
54
57
|
]);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template {'prerender' | 'ssr' | 'csr' | 'trailingSlash'} Option
|
|
3
|
+
* @template {Option extends 'prerender' ? import('types').PrerenderOption : Option extends 'trailingSlash' ? import('types').TrailingSlash : boolean} Value
|
|
4
|
+
*
|
|
5
|
+
* @param {Array<import('types').SSRNode | undefined>} nodes
|
|
6
|
+
* @param {Option} option
|
|
7
|
+
*
|
|
8
|
+
* @returns {Value | undefined}
|
|
9
|
+
*/
|
|
10
|
+
export function get_option(nodes, option) {
|
|
11
|
+
return nodes.reduce((value, node) => {
|
|
12
|
+
return /** @type {any} TypeScript's too dumb to understand this */ (
|
|
13
|
+
node?.universal?.[option] ?? node?.server?.[option] ?? value
|
|
14
|
+
);
|
|
15
|
+
}, /** @type {Value | undefined} */ (undefined));
|
|
16
|
+
}
|
package/types/ambient.d.ts
CHANGED
|
@@ -170,6 +170,7 @@ declare module '$app/navigation' {
|
|
|
170
170
|
export function disableScrollHandling(): void;
|
|
171
171
|
/**
|
|
172
172
|
* Returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified `url`.
|
|
173
|
+
* For external URLs, use `window.location = url` instead of calling `goto(url)`.
|
|
173
174
|
*
|
|
174
175
|
* @param url Where to navigate to. Note that if you've set [`config.kit.paths.base`](https://kit.svelte.dev/docs/configuration#paths) and the URL is root-relative, you need to prepend the base path if you want to navigate within the app.
|
|
175
176
|
* @param opts Options related to the navigation
|
|
@@ -431,3 +432,10 @@ declare module '@sveltejs/kit/vite' {
|
|
|
431
432
|
export function sveltekit(): Promise<Plugin[]>;
|
|
432
433
|
export { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
433
434
|
}
|
|
435
|
+
|
|
436
|
+
/** Internal version of $app/paths */
|
|
437
|
+
declare module '$internal/paths' {
|
|
438
|
+
export const base: `/${string}`;
|
|
439
|
+
export let assets: `https://${string}` | `http://${string}`;
|
|
440
|
+
export function set_assets(path: string): void;
|
|
441
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -7,13 +7,15 @@ import { CompileOptions } from 'svelte/types/compiler/interfaces';
|
|
|
7
7
|
import {
|
|
8
8
|
AdapterEntry,
|
|
9
9
|
CspDirectives,
|
|
10
|
+
HttpMethod,
|
|
10
11
|
Logger,
|
|
11
12
|
MaybePromise,
|
|
12
13
|
Prerendered,
|
|
13
14
|
PrerenderHttpErrorHandlerValue,
|
|
14
15
|
PrerenderMissingIdHandlerValue,
|
|
16
|
+
PrerenderOption,
|
|
15
17
|
RequestOptions,
|
|
16
|
-
|
|
18
|
+
RouteSegment,
|
|
17
19
|
UniqueInterface
|
|
18
20
|
} from './private.js';
|
|
19
21
|
import { SSRNodeLoader, SSRRoute, ValidatedConfig } from './internal.js';
|
|
@@ -50,9 +52,11 @@ export type AwaitedProperties<input extends Record<string, any> | void> =
|
|
|
50
52
|
? OptionalUnion<AwaitedPropertiesUnion<input>>
|
|
51
53
|
: AwaitedPropertiesUnion<input>;
|
|
52
54
|
|
|
53
|
-
export type AwaitedActions<T extends Record<string, (...args: any) => any>> =
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
export type AwaitedActions<T extends Record<string, (...args: any) => any>> = OptionalUnion<
|
|
56
|
+
{
|
|
57
|
+
[Key in keyof T]: UnpackValidationError<Awaited<ReturnType<T[Key]>>>;
|
|
58
|
+
}[keyof T]
|
|
59
|
+
>;
|
|
56
60
|
|
|
57
61
|
// Takes a union type and returns a union type where each type also has all properties
|
|
58
62
|
// of all possible types (typed as undefined), making accessing them more ergonomic
|
|
@@ -83,10 +87,13 @@ export interface Builder {
|
|
|
83
87
|
config: ValidatedConfig;
|
|
84
88
|
/** Information about prerendered pages and assets, if any. */
|
|
85
89
|
prerendered: Prerendered;
|
|
90
|
+
/** An array of dynamic (not prerendered) routes */
|
|
91
|
+
routes: RouteDefinition[];
|
|
86
92
|
|
|
87
93
|
/**
|
|
88
94
|
* Create separate functions that map to one or more routes of your app.
|
|
89
95
|
* @param fn A function that groups a set of routes into an entry point
|
|
96
|
+
* @deprecated Use `builder.routes` instead
|
|
90
97
|
*/
|
|
91
98
|
createEntries(fn: (route: RouteDefinition) => AdapterEntry): Promise<void>;
|
|
92
99
|
|
|
@@ -99,7 +106,7 @@ export interface Builder {
|
|
|
99
106
|
* Generate a server-side manifest to initialise the SvelteKit [server](https://kit.svelte.dev/docs/types#public-types-server) with.
|
|
100
107
|
* @param opts a relative path to the base directory of the app and optionally in which format (esm or cjs) the manifest should be generated
|
|
101
108
|
*/
|
|
102
|
-
generateManifest(opts: { relativePath: string }): string;
|
|
109
|
+
generateManifest(opts: { relativePath: string; routes?: RouteDefinition[] }): string;
|
|
103
110
|
|
|
104
111
|
/**
|
|
105
112
|
* Resolve a path to the `name` directory inside `outDir`, e.g. `/path/to/.svelte-kit/my-adapter`.
|
|
@@ -453,6 +460,7 @@ export interface KitConfig {
|
|
|
453
460
|
* - `(details) => void` — a custom error handler that takes a `details` object with `status`, `path`, `referrer`, `referenceType` and `message` properties. If you `throw` from this function, the build will fail
|
|
454
461
|
*
|
|
455
462
|
* ```js
|
|
463
|
+
* /// file: svelte.config.js
|
|
456
464
|
* /// type: import('@sveltejs/kit').Config
|
|
457
465
|
* const config = {
|
|
458
466
|
* kit: {
|
|
@@ -956,6 +964,15 @@ export interface ResolveOptions {
|
|
|
956
964
|
preload?(input: { type: 'font' | 'css' | 'js' | 'asset'; path: string }): boolean;
|
|
957
965
|
}
|
|
958
966
|
|
|
967
|
+
export interface RouteDefinition<Config = any> {
|
|
968
|
+
id: string;
|
|
969
|
+
pattern: RegExp;
|
|
970
|
+
prerender: PrerenderOption;
|
|
971
|
+
segments: RouteSegment[];
|
|
972
|
+
methods: HttpMethod[];
|
|
973
|
+
config: Config;
|
|
974
|
+
}
|
|
975
|
+
|
|
959
976
|
export class Server {
|
|
960
977
|
constructor(manifest: SSRManifest);
|
|
961
978
|
init(options: ServerInitOptions): Promise<void>;
|
|
@@ -1189,3 +1206,11 @@ export interface SubmitFunction<
|
|
|
1189
1206
|
}) => void)
|
|
1190
1207
|
>;
|
|
1191
1208
|
}
|
|
1209
|
+
|
|
1210
|
+
/**
|
|
1211
|
+
* The type of `export const snapshot` exported from a page or layout component.
|
|
1212
|
+
*/
|
|
1213
|
+
export interface Snapshot<T = any> {
|
|
1214
|
+
capture: () => T;
|
|
1215
|
+
restore: (snapshot: T) => void;
|
|
1216
|
+
}
|
package/types/internal.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { OutputChunk } from 'rollup';
|
|
2
1
|
import { SvelteComponent } from 'svelte/internal';
|
|
3
2
|
import {
|
|
4
3
|
Config,
|
|
@@ -30,7 +29,7 @@ export interface ServerModule {
|
|
|
30
29
|
|
|
31
30
|
export interface ServerInternalModule {
|
|
32
31
|
set_building(building: boolean): void;
|
|
33
|
-
|
|
32
|
+
set_assets(path: string): void;
|
|
34
33
|
set_private_env(environment: Record<string, string>): void;
|
|
35
34
|
set_public_env(environment: Record<string, string>): void;
|
|
36
35
|
set_version(version: string): void;
|
|
@@ -240,6 +239,7 @@ export interface ServerMetadata {
|
|
|
240
239
|
{
|
|
241
240
|
prerender: PrerenderOption | undefined;
|
|
242
241
|
methods: HttpMethod[];
|
|
242
|
+
config: any;
|
|
243
243
|
}
|
|
244
244
|
>;
|
|
245
245
|
}
|
|
@@ -280,6 +280,7 @@ export interface SSRNode {
|
|
|
280
280
|
ssr?: boolean;
|
|
281
281
|
csr?: boolean;
|
|
282
282
|
trailingSlash?: TrailingSlash;
|
|
283
|
+
config?: any;
|
|
283
284
|
};
|
|
284
285
|
|
|
285
286
|
server: {
|
|
@@ -289,6 +290,7 @@ export interface SSRNode {
|
|
|
289
290
|
csr?: boolean;
|
|
290
291
|
trailingSlash?: TrailingSlash;
|
|
291
292
|
actions?: Actions;
|
|
293
|
+
config?: any;
|
|
292
294
|
};
|
|
293
295
|
|
|
294
296
|
// store this in dev so we can print serialization errors
|
package/types/private.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// but which cannot be imported from `@sveltejs/kit`. Care should
|
|
3
3
|
// be taken to avoid breaking changes when editing this file
|
|
4
4
|
|
|
5
|
+
import { RouteDefinition } from './index.js';
|
|
6
|
+
|
|
5
7
|
export interface AdapterEntry {
|
|
6
8
|
/**
|
|
7
9
|
* A string that uniquely identifies an HTTP service (e.g. serverless function) and is used for deduplication.
|
|
@@ -12,8 +14,11 @@ export interface AdapterEntry {
|
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* A function that compares the candidate route with the current route to determine
|
|
15
|
-
* if it should be
|
|
16
|
-
*
|
|
17
|
+
* if it should be grouped with the current route.
|
|
18
|
+
*
|
|
19
|
+
* Use cases:
|
|
20
|
+
* - Fallback pages: `/foo/[c]` is a fallback for `/foo/a-[b]`, and `/[...catchall]` is a fallback for all routes
|
|
21
|
+
* - Grouping routes that share a common `config`: `/foo` should be deployed to the edge, `/bar` and `/baz` should be deployed to a serverless function
|
|
17
22
|
*/
|
|
18
23
|
filter(route: RouteDefinition): boolean;
|
|
19
24
|
|
|
@@ -212,13 +217,6 @@ export interface RequestOptions {
|
|
|
212
217
|
platform?: App.Platform;
|
|
213
218
|
}
|
|
214
219
|
|
|
215
|
-
export interface RouteDefinition {
|
|
216
|
-
id: string;
|
|
217
|
-
pattern: RegExp;
|
|
218
|
-
segments: RouteSegment[];
|
|
219
|
-
methods: HttpMethod[];
|
|
220
|
-
}
|
|
221
|
-
|
|
222
220
|
export interface RouteSegment {
|
|
223
221
|
content: string;
|
|
224
222
|
dynamic: boolean;
|