@sveltejs/kit 1.9.3 → 1.11.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 +1 -1
- package/src/core/env.js +18 -9
- package/src/core/generate_manifest/index.js +34 -29
- package/src/core/postbuild/analyse.js +1 -9
- package/src/core/postbuild/crawl.js +14 -6
- package/src/core/postbuild/prerender.js +11 -7
- package/src/core/sync/utils.js +42 -5
- package/src/core/sync/write_client_manifest.js +55 -51
- package/src/core/sync/write_root.js +8 -10
- package/src/core/sync/write_types/index.js +4 -1
- package/src/exports/vite/build/build_service_worker.js +13 -8
- package/src/exports/vite/dev/index.js +8 -3
- package/src/exports/vite/index.js +52 -37
- package/src/exports/vite/preview/index.js +25 -2
- package/src/runtime/client/client.js +48 -24
- package/src/runtime/server/cookie.js +44 -40
- package/src/runtime/server/page/index.js +1 -26
- package/src/runtime/server/page/render.js +11 -11
- package/src/runtime/server/respond.js +1 -1
- package/src/utils/routing.js +1 -1
- package/src/utils/streaming.js +12 -5
- package/types/ambient.d.ts +5 -0
- package/types/index.d.ts +25 -2
package/package.json
CHANGED
package/src/core/env.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GENERATED_COMMENT } from '../constants.js';
|
|
2
|
+
import { dedent } from './sync/utils.js';
|
|
2
3
|
import { runtime_base } from './utils.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -50,10 +51,13 @@ export function create_dynamic_module(type, dev_values) {
|
|
|
50
51
|
export function create_static_types(id, env) {
|
|
51
52
|
const declarations = Object.keys(env[id])
|
|
52
53
|
.filter((k) => valid_identifier.test(k))
|
|
53
|
-
.map((k) =>
|
|
54
|
-
.join('\n');
|
|
54
|
+
.map((k) => `export const ${k}: string;`);
|
|
55
55
|
|
|
56
|
-
return `
|
|
56
|
+
return dedent`
|
|
57
|
+
declare module '$env/static/${id}' {
|
|
58
|
+
${declarations.join('\n')}
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
/**
|
|
@@ -65,19 +69,24 @@ export function create_static_types(id, env) {
|
|
|
65
69
|
export function create_dynamic_types(id, env, prefix) {
|
|
66
70
|
const properties = Object.keys(env[id])
|
|
67
71
|
.filter((k) => valid_identifier.test(k))
|
|
68
|
-
.map((k) =>
|
|
72
|
+
.map((k) => `${k}: string;`);
|
|
69
73
|
|
|
70
74
|
const prefixed = `[key: \`${prefix}\${string}\`]`;
|
|
71
75
|
|
|
72
76
|
if (id === 'private') {
|
|
73
|
-
properties.push(
|
|
74
|
-
properties.push(
|
|
77
|
+
properties.push(`${prefixed}: undefined;`);
|
|
78
|
+
properties.push(`[key: string]: string | undefined;`);
|
|
75
79
|
} else {
|
|
76
|
-
properties.push(
|
|
80
|
+
properties.push(`${prefixed}: string | undefined;`);
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
return dedent`
|
|
84
|
+
declare module '$env/dynamic/${id}' {
|
|
85
|
+
export const env: {
|
|
86
|
+
${properties.join('\n')}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
export const reserved = new Set([
|
|
@@ -3,6 +3,7 @@ import { get_mime_lookup } from '../utils.js';
|
|
|
3
3
|
import { resolve_symlinks } from '../../exports/vite/build/utils.js';
|
|
4
4
|
import { compact } from '../../utils/array.js';
|
|
5
5
|
import { join_relative } from '../../utils/filesystem.js';
|
|
6
|
+
import { dedent } from '../sync/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Generates the data used to write the server-side manifest.js file. This data is used in the Vite
|
|
@@ -80,37 +81,41 @@ export function generate_manifest({ build_data, relative_path, routes }) {
|
|
|
80
81
|
// prettier-ignore
|
|
81
82
|
// String representation of
|
|
82
83
|
/** @type {import('types').SSRManifest} */
|
|
83
|
-
return `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
return dedent`
|
|
85
|
+
{
|
|
86
|
+
appDir: ${s(build_data.app_dir)},
|
|
87
|
+
appPath: ${s(build_data.app_path)},
|
|
88
|
+
assets: new Set(${s(assets)}),
|
|
89
|
+
mimeTypes: ${s(get_mime_lookup(build_data.manifest_data))},
|
|
90
|
+
_: {
|
|
91
|
+
client: ${s(build_data.client)},
|
|
92
|
+
nodes: [
|
|
93
|
+
${(node_paths).map(loader).join(',\n')}
|
|
94
|
+
],
|
|
95
|
+
routes: [
|
|
96
|
+
${routes.map(route => {
|
|
97
|
+
route.params.forEach(param => {
|
|
98
|
+
if (param.matcher) matchers.add(param.matcher);
|
|
99
|
+
});
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
if (!route.page && !route.endpoint) return;
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
103
|
+
return dedent`
|
|
104
|
+
{
|
|
105
|
+
id: ${s(route.id)},
|
|
106
|
+
pattern: ${route.pattern},
|
|
107
|
+
params: ${s(route.params)},
|
|
108
|
+
page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${reindexed.get(route.page.leaf)} }` : 'null'},
|
|
109
|
+
endpoint: ${route.endpoint ? loader(join_relative(relative_path, resolve_symlinks(build_data.server_manifest, route.endpoint.file).chunk.file)) : 'null'}
|
|
110
|
+
}
|
|
111
|
+
`;
|
|
112
|
+
}).filter(Boolean).join(',\n')}
|
|
113
|
+
],
|
|
114
|
+
matchers: async () => {
|
|
115
|
+
${Array.from(matchers).map(type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')`).join('\n')}
|
|
116
|
+
return { ${Array.from(matchers).join(', ')} };
|
|
117
|
+
}
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
|
-
|
|
120
|
+
`;
|
|
116
121
|
}
|
|
@@ -122,15 +122,7 @@ async function analyse({ manifest_path, env }) {
|
|
|
122
122
|
validate_common_exports(page.universal, page.universal_id);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
prerender =
|
|
127
|
-
should_prerender === true ||
|
|
128
|
-
// Try prerendering if ssr is false and no server needed. Set it to 'auto' so that
|
|
129
|
-
// the route is not removed from the manifest, there could be a server load function.
|
|
130
|
-
// People can opt out of this behavior by explicitly setting prerender to false
|
|
131
|
-
(should_prerender !== false && get_option(nodes, 'ssr') === false && !page?.server?.actions
|
|
132
|
-
? 'auto'
|
|
133
|
-
: should_prerender ?? false);
|
|
125
|
+
prerender = get_option(nodes, 'prerender') ?? false;
|
|
134
126
|
|
|
135
127
|
config = get_config(nodes);
|
|
136
128
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolve } from '../../utils/url.js';
|
|
1
2
|
import { decode } from './entities.js';
|
|
2
3
|
|
|
3
4
|
const DOCTYPE = 'DOCTYPE';
|
|
@@ -12,8 +13,11 @@ const ATTRIBUTE_NAME = /[^\t\n\f />"'=]/;
|
|
|
12
13
|
|
|
13
14
|
const WHITESPACE = /[\s\n\r]/;
|
|
14
15
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} html
|
|
18
|
+
* @param {string} base
|
|
19
|
+
*/
|
|
20
|
+
export function crawl(html, base) {
|
|
17
21
|
/** @type {string[]} */
|
|
18
22
|
const ids = [];
|
|
19
23
|
|
|
@@ -157,7 +161,11 @@ export function crawl(html) {
|
|
|
157
161
|
value = decode(value);
|
|
158
162
|
|
|
159
163
|
if (name === 'href') {
|
|
160
|
-
|
|
164
|
+
if (tag === 'BASE') {
|
|
165
|
+
base = resolve(base, value);
|
|
166
|
+
} else {
|
|
167
|
+
href = resolve(base, value);
|
|
168
|
+
}
|
|
161
169
|
} else if (name === 'id') {
|
|
162
170
|
ids.push(value);
|
|
163
171
|
} else if (name === 'name') {
|
|
@@ -165,7 +173,7 @@ export function crawl(html) {
|
|
|
165
173
|
} else if (name === 'rel') {
|
|
166
174
|
rel = value;
|
|
167
175
|
} else if (name === 'src') {
|
|
168
|
-
if (value) hrefs.push(value);
|
|
176
|
+
if (value) hrefs.push(resolve(base, value));
|
|
169
177
|
} else if (name === 'srcset') {
|
|
170
178
|
const candidates = [];
|
|
171
179
|
let insideURL = true;
|
|
@@ -183,7 +191,7 @@ export function crawl(html) {
|
|
|
183
191
|
candidates.push(value);
|
|
184
192
|
for (const candidate of candidates) {
|
|
185
193
|
const src = candidate.split(WHITESPACE)[0];
|
|
186
|
-
if (src) hrefs.push(src);
|
|
194
|
+
if (src) hrefs.push(resolve(base, src));
|
|
187
195
|
}
|
|
188
196
|
}
|
|
189
197
|
} else {
|
|
@@ -195,7 +203,7 @@ export function crawl(html) {
|
|
|
195
203
|
}
|
|
196
204
|
|
|
197
205
|
if (href && !/\bexternal\b/i.test(rel)) {
|
|
198
|
-
hrefs.push(href);
|
|
206
|
+
hrefs.push(resolve(base, href));
|
|
199
207
|
}
|
|
200
208
|
}
|
|
201
209
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
4
|
import { installPolyfills } from '../../exports/node/polyfills.js';
|
|
@@ -144,6 +144,13 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
const files = new Set(walk(`${out}/client`).map(posixify));
|
|
147
|
+
|
|
148
|
+
const immutable = `${config.appDir}/immutable`;
|
|
149
|
+
if (existsSync(`${out}/server/${immutable}`)) {
|
|
150
|
+
for (const file of walk(`${out}/server/${immutable}`)) {
|
|
151
|
+
files.add(posixify(`${config.appDir}/immutable/${file}`));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
147
154
|
const seen = new Set();
|
|
148
155
|
const written = new Set();
|
|
149
156
|
|
|
@@ -240,17 +247,14 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
|
|
|
240
247
|
const headers = Object.fromEntries(response.headers);
|
|
241
248
|
|
|
242
249
|
if (config.prerender.crawl && headers['content-type'] === 'text/html') {
|
|
243
|
-
const { ids, hrefs } = crawl(body.toString());
|
|
250
|
+
const { ids, hrefs } = crawl(body.toString(), decoded);
|
|
244
251
|
|
|
245
252
|
actual_hashlinks.set(decoded, ids);
|
|
246
253
|
|
|
247
254
|
for (const href of hrefs) {
|
|
248
|
-
if (href
|
|
249
|
-
|
|
250
|
-
const resolved = resolve(encoded, href);
|
|
251
|
-
if (!is_root_relative(resolved)) continue;
|
|
255
|
+
if (!is_root_relative(href)) continue;
|
|
252
256
|
|
|
253
|
-
const { pathname, search, hash } = new URL(
|
|
257
|
+
const { pathname, search, hash } = new URL(href, 'http://localhost');
|
|
254
258
|
|
|
255
259
|
if (search) {
|
|
256
260
|
// TODO warn that query strings have no effect on statically-exported pages
|
package/src/core/sync/utils.js
CHANGED
|
@@ -25,9 +25,46 @@ export function write(file, code) {
|
|
|
25
25
|
fs.writeFileSync(file, code);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/** @
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
/** @type {WeakMap<TemplateStringsArray, { strings: string[], indents: string[] }>} */
|
|
29
|
+
const dedent_map = new WeakMap();
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Allows indenting template strings without the extra indentation ending up in the result.
|
|
33
|
+
* Still allows indentation of lines relative to one another in the template string.
|
|
34
|
+
* @param {TemplateStringsArray} strings
|
|
35
|
+
* @param {any[]} values
|
|
36
|
+
*/
|
|
37
|
+
export function dedent(strings, ...values) {
|
|
38
|
+
let dedented = dedent_map.get(strings);
|
|
39
|
+
|
|
40
|
+
if (!dedented) {
|
|
41
|
+
const indentation = /** @type {RegExpExecArray} */ (/\n?([ \t]*)/.exec(strings[0]))[1];
|
|
42
|
+
const pattern = new RegExp(`^${indentation}`, 'gm');
|
|
43
|
+
|
|
44
|
+
dedented = {
|
|
45
|
+
strings: strings.map((str) => str.replace(pattern, '')),
|
|
46
|
+
indents: []
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let current = '\n';
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
52
|
+
const string = dedented.strings[i];
|
|
53
|
+
const match = /\n([ \t]*)$/.exec(string);
|
|
54
|
+
|
|
55
|
+
if (match) current = match[0];
|
|
56
|
+
dedented.indents[i] = current;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
dedent_map.set(strings, dedented);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let str = dedented.strings[0];
|
|
63
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
64
|
+
str += String(values[i]).replace(/\n/g, dedented.indents[i]) + dedented.strings[i + 1];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
str = str.trim();
|
|
68
|
+
|
|
69
|
+
return str;
|
|
33
70
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { relative_path, resolve_entry } from '../../utils/filesystem.js';
|
|
2
2
|
import { s } from '../../utils/misc.js';
|
|
3
|
-
import {
|
|
3
|
+
import { dedent, write_if_changed } from './utils.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Writes the client manifest to disk. The manifest is used to power the router. It contains the
|
|
@@ -45,75 +45,79 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
|
|
|
45
45
|
write_if_changed(`${output}/nodes/${i}.js`, generate_node(node));
|
|
46
46
|
return `() => import('./nodes/${i}')`;
|
|
47
47
|
})
|
|
48
|
-
.join(',\n
|
|
48
|
+
.join(',\n');
|
|
49
49
|
|
|
50
50
|
const layouts_with_server_load = new Set();
|
|
51
51
|
|
|
52
|
-
const dictionary = `
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
52
|
+
const dictionary = dedent`
|
|
53
|
+
{
|
|
54
|
+
${manifest_data.routes
|
|
55
|
+
.map((route) => {
|
|
56
|
+
if (route.page) {
|
|
57
|
+
const errors = route.page.errors.slice(1).map((n) => n ?? '');
|
|
58
|
+
const layouts = route.page.layouts.slice(1).map((n) => n ?? '');
|
|
59
|
+
|
|
60
|
+
while (layouts.at(-1) === '') layouts.pop();
|
|
61
|
+
while (errors.at(-1) === '') errors.pop();
|
|
62
|
+
|
|
63
|
+
let leaf_has_server_load = false;
|
|
64
|
+
if (route.leaf) {
|
|
65
|
+
if (metadata) {
|
|
66
|
+
const i = /** @type {number} */ (indices.get(route.leaf));
|
|
67
|
+
|
|
68
|
+
leaf_has_server_load = metadata[i].has_server_load;
|
|
69
|
+
} else if (route.leaf.server) {
|
|
70
|
+
leaf_has_server_load = true;
|
|
71
|
+
}
|
|
70
72
|
}
|
|
71
|
-
}
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// Encode whether or not the route uses server data
|
|
75
|
+
// using the ones' complement, to save space
|
|
76
|
+
const array = [`${leaf_has_server_load ? '~' : ''}${route.page.leaf}`];
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
// Encode whether or not the layout uses server data.
|
|
79
|
+
// It's a different method compared to pages because layouts
|
|
80
|
+
// are reused across pages, so we save space by doing it this way.
|
|
81
|
+
route.page.layouts.forEach((layout) => {
|
|
82
|
+
if (layout == undefined) return;
|
|
82
83
|
|
|
83
|
-
|
|
84
|
+
let layout_has_server_load = false;
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
if (metadata) {
|
|
87
|
+
layout_has_server_load = metadata[layout].has_server_load;
|
|
88
|
+
} else if (manifest_data.nodes[layout].server) {
|
|
89
|
+
layout_has_server_load = true;
|
|
90
|
+
}
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
if (layout_has_server_load) {
|
|
93
|
+
layouts_with_server_load.add(layout);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
// only include non-root layout/error nodes if they exist
|
|
98
|
+
if (layouts.length > 0 || errors.length > 0) array.push(`[${layouts.join(',')}]`);
|
|
99
|
+
if (errors.length > 0) array.push(`[${errors.join(',')}]`);
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
return `${s(route.id)}: [${array.join(',')}]`;
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
.filter(Boolean)
|
|
105
|
+
.join(',\n')}
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
106
108
|
|
|
107
109
|
const hooks_file = resolve_entry(kit.files.hooks.client);
|
|
108
110
|
|
|
109
111
|
write_if_changed(
|
|
110
112
|
`${output}/app.js`,
|
|
111
|
-
|
|
113
|
+
dedent`
|
|
112
114
|
${hooks_file ? `import * as client_hooks from '${relative_path(output, hooks_file)}';` : ''}
|
|
113
115
|
|
|
114
116
|
export { matchers } from './matchers.js';
|
|
115
117
|
|
|
116
|
-
export const nodes = [
|
|
118
|
+
export const nodes = [
|
|
119
|
+
${nodes}
|
|
120
|
+
];
|
|
117
121
|
|
|
118
122
|
export const server_loads = [${[...layouts_with_server_load].join(',')}];
|
|
119
123
|
|
|
@@ -126,7 +130,7 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
|
|
|
126
130
|
};
|
|
127
131
|
|
|
128
132
|
export { default as root } from '../root.svelte';
|
|
129
|
-
`
|
|
133
|
+
`
|
|
130
134
|
);
|
|
131
135
|
|
|
132
136
|
// write matchers to a separate module so that we don't
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dedent, write_if_changed } from './utils.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @param {import('types').ManifestData} manifest_data
|
|
@@ -24,22 +24,20 @@ export function write_root(manifest_data, output) {
|
|
|
24
24
|
let pyramid = `<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} />`;
|
|
25
25
|
|
|
26
26
|
while (l--) {
|
|
27
|
-
pyramid = `
|
|
27
|
+
pyramid = dedent`
|
|
28
28
|
{#if constructors[${l + 1}]}
|
|
29
29
|
<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}}>
|
|
30
|
-
${pyramid
|
|
30
|
+
${pyramid}
|
|
31
31
|
</svelte:component>
|
|
32
32
|
{:else}
|
|
33
33
|
<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} />
|
|
34
34
|
{/if}
|
|
35
|
-
|
|
36
|
-
.replace(/^\t\t\t/gm, '')
|
|
37
|
-
.trim();
|
|
35
|
+
`;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
write_if_changed(
|
|
41
39
|
`${output}/root.svelte`,
|
|
42
|
-
|
|
40
|
+
dedent`
|
|
43
41
|
<!-- This file is generated by @sveltejs/kit — do not edit it! -->
|
|
44
42
|
<script>
|
|
45
43
|
import { setContext, afterUpdate, onMount } from 'svelte';
|
|
@@ -52,7 +50,7 @@ export function write_root(manifest_data, output) {
|
|
|
52
50
|
export let constructors;
|
|
53
51
|
export let components = [];
|
|
54
52
|
export let form;
|
|
55
|
-
${levels.map((l) => `export let data_${l} = null;`).join('\n
|
|
53
|
+
${levels.map((l) => `export let data_${l} = null;`).join('\n')}
|
|
56
54
|
|
|
57
55
|
if (!browser) {
|
|
58
56
|
setContext('__svelte__', stores);
|
|
@@ -78,7 +76,7 @@ export function write_root(manifest_data, output) {
|
|
|
78
76
|
});
|
|
79
77
|
</script>
|
|
80
78
|
|
|
81
|
-
${pyramid
|
|
79
|
+
${pyramid}
|
|
82
80
|
|
|
83
81
|
{#if mounted}
|
|
84
82
|
<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px">
|
|
@@ -87,6 +85,6 @@ export function write_root(manifest_data, output) {
|
|
|
87
85
|
{/if}
|
|
88
86
|
</div>
|
|
89
87
|
{/if}
|
|
90
|
-
`
|
|
88
|
+
`
|
|
91
89
|
);
|
|
92
90
|
}
|
|
@@ -592,7 +592,10 @@ export function tweak_types(content, is_server) {
|
|
|
592
592
|
});
|
|
593
593
|
}
|
|
594
594
|
|
|
595
|
-
if (
|
|
595
|
+
if (
|
|
596
|
+
ts.canHaveModifiers(node) &&
|
|
597
|
+
ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)
|
|
598
|
+
) {
|
|
596
599
|
if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) {
|
|
597
600
|
exports.set(node.name.text, node.name.text);
|
|
598
601
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import * as vite from 'vite';
|
|
3
|
+
import { dedent } from '../../../core/sync/utils.js';
|
|
3
4
|
import { s } from '../../../utils/misc.js';
|
|
4
5
|
import { get_config_aliases } from '../utils.js';
|
|
5
6
|
import { assets_base } from './utils.js';
|
|
@@ -32,30 +33,34 @@ export async function build_service_worker(
|
|
|
32
33
|
|
|
33
34
|
const service_worker = `${kit.outDir}/generated/service-worker.js`;
|
|
34
35
|
|
|
36
|
+
// in a service worker, `location` is the location of the service worker itself,
|
|
37
|
+
// which is guaranteed to be `<base>/service-worker.js`
|
|
38
|
+
const base = `location.pathname.split('/').slice(0, -1).join('/')`;
|
|
39
|
+
|
|
35
40
|
fs.writeFileSync(
|
|
36
41
|
service_worker,
|
|
37
|
-
`
|
|
42
|
+
dedent`
|
|
43
|
+
export const base = /*@__PURE__*/ ${base};
|
|
44
|
+
|
|
38
45
|
export const build = [
|
|
39
46
|
${Array.from(build)
|
|
40
|
-
.map((file) =>
|
|
41
|
-
.join(',\n
|
|
47
|
+
.map((file) => `base + ${s(`/${file}`)}`)
|
|
48
|
+
.join(',\n')}
|
|
42
49
|
];
|
|
43
50
|
|
|
44
51
|
export const files = [
|
|
45
52
|
${manifest_data.assets
|
|
46
53
|
.filter((asset) => kit.serviceWorker.files(asset.file))
|
|
47
|
-
.map((asset) =>
|
|
48
|
-
.join(',\n
|
|
54
|
+
.map((asset) => `base + ${s(`/${asset.file}`)}`)
|
|
55
|
+
.join(',\n')}
|
|
49
56
|
];
|
|
50
57
|
|
|
51
58
|
export const prerendered = [
|
|
52
|
-
${prerendered.paths.map((path) => s(path)).join(',\n
|
|
59
|
+
${prerendered.paths.map((path) => `base + ${s(path.replace(kit.paths.base, ''))}`).join(',\n')}
|
|
53
60
|
];
|
|
54
61
|
|
|
55
62
|
export const version = ${s(kit.version.name)};
|
|
56
63
|
`
|
|
57
|
-
.replace(/^\t{3}/gm, '')
|
|
58
|
-
.trim()
|
|
59
64
|
);
|
|
60
65
|
|
|
61
66
|
await vite.build({
|
|
@@ -175,15 +175,20 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
175
175
|
const styles = {};
|
|
176
176
|
|
|
177
177
|
for (const dep of deps) {
|
|
178
|
-
const
|
|
179
|
-
const query =
|
|
178
|
+
const url = new URL(dep.url, 'http://localhost/');
|
|
179
|
+
const query = url.searchParams;
|
|
180
180
|
|
|
181
181
|
if (
|
|
182
182
|
isCSSRequest(dep.file) ||
|
|
183
183
|
(query.has('svelte') && query.get('type') === 'style')
|
|
184
184
|
) {
|
|
185
|
+
// setting `?inline` to load CSS modules as css string
|
|
186
|
+
query.set('inline', '');
|
|
187
|
+
|
|
185
188
|
try {
|
|
186
|
-
const mod = await loud_ssr_load_module(
|
|
189
|
+
const mod = await loud_ssr_load_module(
|
|
190
|
+
`${url.pathname}${url.search}${url.hash}`
|
|
191
|
+
);
|
|
187
192
|
styles[dep.url] = mod.default;
|
|
188
193
|
} catch {
|
|
189
194
|
// this can happen with dynamically imported modules, I think
|
|
@@ -24,6 +24,7 @@ import prerender from '../../core/postbuild/prerender.js';
|
|
|
24
24
|
import analyse from '../../core/postbuild/analyse.js';
|
|
25
25
|
import { s } from '../../utils/misc.js';
|
|
26
26
|
import { hash } from '../../runtime/hash.js';
|
|
27
|
+
import { dedent } from '../../core/sync/utils.js';
|
|
27
28
|
|
|
28
29
|
export { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
29
30
|
|
|
@@ -334,7 +335,10 @@ function kit({ svelte_config }) {
|
|
|
334
335
|
|
|
335
336
|
async load(id, options) {
|
|
336
337
|
const browser = !options?.ssr;
|
|
337
|
-
|
|
338
|
+
|
|
339
|
+
const global = is_build
|
|
340
|
+
? `globalThis.__sveltekit_${version_hash}`
|
|
341
|
+
: `globalThis.__sveltekit_dev`;
|
|
338
342
|
|
|
339
343
|
if (options?.ssr === false && process.env.TEST !== 'true') {
|
|
340
344
|
const normalized_cwd = vite.normalizePath(cwd);
|
|
@@ -354,13 +358,16 @@ function kit({ svelte_config }) {
|
|
|
354
358
|
switch (id) {
|
|
355
359
|
case '\0$env/static/private':
|
|
356
360
|
return create_static_module('$env/static/private', env.private);
|
|
361
|
+
|
|
357
362
|
case '\0$env/static/public':
|
|
358
363
|
return create_static_module('$env/static/public', env.public);
|
|
364
|
+
|
|
359
365
|
case '\0$env/dynamic/private':
|
|
360
366
|
return create_dynamic_module(
|
|
361
367
|
'private',
|
|
362
368
|
vite_config_env.command === 'serve' ? env.private : undefined
|
|
363
369
|
);
|
|
370
|
+
|
|
364
371
|
case '\0$env/dynamic/public':
|
|
365
372
|
// populate `$env/dynamic/public` from `window`
|
|
366
373
|
if (browser) {
|
|
@@ -371,6 +378,7 @@ function kit({ svelte_config }) {
|
|
|
371
378
|
'public',
|
|
372
379
|
vite_config_env.command === 'serve' ? env.public : undefined
|
|
373
380
|
);
|
|
381
|
+
|
|
374
382
|
case '\0$service-worker':
|
|
375
383
|
return create_service_worker_module(svelte_config);
|
|
376
384
|
|
|
@@ -383,40 +391,47 @@ function kit({ svelte_config }) {
|
|
|
383
391
|
// for the sake of things like Vitest which may import this module
|
|
384
392
|
// outside the context of a page
|
|
385
393
|
if (browser) {
|
|
386
|
-
return `
|
|
387
|
-
export const
|
|
394
|
+
return dedent`
|
|
395
|
+
export const base = ${global}?.base ?? ${s(base)};
|
|
396
|
+
export const assets = ${global}?.assets ?? ${assets ? s(assets) : 'base'};
|
|
397
|
+
`;
|
|
388
398
|
}
|
|
389
399
|
|
|
390
|
-
return `
|
|
391
|
-
export let
|
|
400
|
+
return dedent`
|
|
401
|
+
export let base = ${s(base)};
|
|
402
|
+
export let assets = ${assets ? s(assets) : 'base'};
|
|
392
403
|
|
|
393
|
-
export const relative = ${svelte_config.kit.paths.relative};
|
|
404
|
+
export const relative = ${svelte_config.kit.paths.relative};
|
|
394
405
|
|
|
395
|
-
const initial = { base, assets };
|
|
406
|
+
const initial = { base, assets };
|
|
396
407
|
|
|
397
|
-
export function override(paths) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
408
|
+
export function override(paths) {
|
|
409
|
+
base = paths.base;
|
|
410
|
+
assets = paths.assets;
|
|
411
|
+
}
|
|
401
412
|
|
|
402
|
-
export function reset() {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
413
|
+
export function reset() {
|
|
414
|
+
base = initial.base;
|
|
415
|
+
assets = initial.assets;
|
|
416
|
+
}
|
|
406
417
|
|
|
407
|
-
/** @param {string} path */
|
|
408
|
-
export function set_assets(path) {
|
|
409
|
-
|
|
410
|
-
}
|
|
418
|
+
/** @param {string} path */
|
|
419
|
+
export function set_assets(path) {
|
|
420
|
+
assets = initial.assets = path;
|
|
421
|
+
}
|
|
422
|
+
`;
|
|
411
423
|
|
|
412
424
|
case '\0__sveltekit/environment':
|
|
413
425
|
const { version } = svelte_config.kit;
|
|
414
|
-
return `export const version = ${s(version.name)};
|
|
415
|
-
export let building = false;
|
|
416
426
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
427
|
+
return dedent`
|
|
428
|
+
export const version = ${s(version.name)};
|
|
429
|
+
export let building = false;
|
|
430
|
+
|
|
431
|
+
export function set_building() {
|
|
432
|
+
building = true;
|
|
433
|
+
}
|
|
434
|
+
`;
|
|
420
435
|
}
|
|
421
436
|
}
|
|
422
437
|
};
|
|
@@ -863,18 +878,18 @@ function find_overridden_config(config, resolved_config, enforced_config, path,
|
|
|
863
878
|
/**
|
|
864
879
|
* @param {import('types').ValidatedConfig} config
|
|
865
880
|
*/
|
|
866
|
-
const create_service_worker_module = (config) => `
|
|
867
|
-
if (typeof self === 'undefined' || self instanceof ServiceWorkerGlobalScope === false) {
|
|
868
|
-
|
|
869
|
-
}
|
|
881
|
+
const create_service_worker_module = (config) => dedent`
|
|
882
|
+
if (typeof self === 'undefined' || self instanceof ServiceWorkerGlobalScope === false) {
|
|
883
|
+
throw new Error('This module can only be imported inside a service worker');
|
|
884
|
+
}
|
|
870
885
|
|
|
871
|
-
export const build = [];
|
|
872
|
-
export const files = [
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
];
|
|
878
|
-
export const prerendered = [];
|
|
879
|
-
export const version = ${s(config.kit.version.name)};
|
|
886
|
+
export const build = [];
|
|
887
|
+
export const files = [
|
|
888
|
+
${create_assets(config)
|
|
889
|
+
.filter((asset) => config.kit.serviceWorker.files(asset.file))
|
|
890
|
+
.map((asset) => `${s(`${config.kit.paths.base}/${asset.file}`)}`)
|
|
891
|
+
.join(',\n')}
|
|
892
|
+
];
|
|
893
|
+
export const prerendered = [];
|
|
894
|
+
export const version = ${s(config.kit.version.name)};
|
|
880
895
|
`;
|
|
@@ -108,8 +108,31 @@ export async function preview(vite, vite_config, svelte_config) {
|
|
|
108
108
|
let prerendered = is_file(filename);
|
|
109
109
|
|
|
110
110
|
if (!prerendered) {
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
const has_trailing_slash = pathname.endsWith('/');
|
|
112
|
+
const html_filename = `${filename}${has_trailing_slash ? 'index.html' : '.html'}`;
|
|
113
|
+
|
|
114
|
+
let redirect;
|
|
115
|
+
|
|
116
|
+
if (is_file(html_filename)) {
|
|
117
|
+
filename = html_filename;
|
|
118
|
+
prerendered = true;
|
|
119
|
+
} else if (has_trailing_slash) {
|
|
120
|
+
if (is_file(filename.slice(0, -1) + '.html')) {
|
|
121
|
+
redirect = pathname.slice(0, -1);
|
|
122
|
+
}
|
|
123
|
+
} else if (is_file(filename + '/index.html')) {
|
|
124
|
+
redirect = pathname + '/';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (redirect) {
|
|
128
|
+
res.writeHead(307, {
|
|
129
|
+
location: redirect
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
res.end();
|
|
133
|
+
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
113
136
|
}
|
|
114
137
|
|
|
115
138
|
if (prerendered) {
|
|
@@ -177,6 +177,14 @@ export function create_client(app, target) {
|
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
function persist_state() {
|
|
181
|
+
update_scroll_positions(current_history_index);
|
|
182
|
+
storage.set(SCROLL_KEY, scroll_positions);
|
|
183
|
+
|
|
184
|
+
capture_snapshot(current_history_index);
|
|
185
|
+
storage.set(SNAPSHOT_KEY, snapshots);
|
|
186
|
+
}
|
|
187
|
+
|
|
180
188
|
/**
|
|
181
189
|
* @param {string | URL} url
|
|
182
190
|
* @param {{ noScroll?: boolean; replaceState?: boolean; keepFocus?: boolean; state?: any; invalidateAll?: boolean }} opts
|
|
@@ -379,17 +387,7 @@ export function create_client(app, target) {
|
|
|
379
387
|
// need to render the DOM before we can scroll to the rendered elements and do focus management
|
|
380
388
|
await tick();
|
|
381
389
|
|
|
382
|
-
|
|
383
|
-
// reset focus only if any manual focus management didn't override it
|
|
384
|
-
document.activeElement !== activeElement &&
|
|
385
|
-
// also refocus when activeElement is body already because the
|
|
386
|
-
// focus event might not have been fired on it yet
|
|
387
|
-
document.activeElement !== document.body;
|
|
388
|
-
|
|
389
|
-
if (!keepfocus && !changed_focus) {
|
|
390
|
-
await reset_focus();
|
|
391
|
-
}
|
|
392
|
-
|
|
390
|
+
// we reset scroll before dealing with focus, to avoid a flash of unscrolled content
|
|
393
391
|
if (autoscroll) {
|
|
394
392
|
const deep_linked =
|
|
395
393
|
url.hash && document.getElementById(decodeURIComponent(url.hash.slice(1)));
|
|
@@ -404,6 +402,17 @@ export function create_client(app, target) {
|
|
|
404
402
|
scrollTo(0, 0);
|
|
405
403
|
}
|
|
406
404
|
}
|
|
405
|
+
|
|
406
|
+
const changed_focus =
|
|
407
|
+
// reset focus only if any manual focus management didn't override it
|
|
408
|
+
document.activeElement !== activeElement &&
|
|
409
|
+
// also refocus when activeElement is body already because the
|
|
410
|
+
// focus event might not have been fired on it yet
|
|
411
|
+
document.activeElement !== document.body;
|
|
412
|
+
|
|
413
|
+
if (!keepfocus && !changed_focus) {
|
|
414
|
+
await reset_focus();
|
|
415
|
+
}
|
|
407
416
|
} else {
|
|
408
417
|
await tick();
|
|
409
418
|
}
|
|
@@ -1180,6 +1189,15 @@ export function create_client(app, target) {
|
|
|
1180
1189
|
route
|
|
1181
1190
|
});
|
|
1182
1191
|
}
|
|
1192
|
+
|
|
1193
|
+
if (__SVELTEKIT_DEV__) {
|
|
1194
|
+
console.error(
|
|
1195
|
+
'An error occurred while loading the page. This will cause a full page reload. (This message will only appear during development.)'
|
|
1196
|
+
);
|
|
1197
|
+
|
|
1198
|
+
debugger;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1183
1201
|
return await native_navigation(url);
|
|
1184
1202
|
}
|
|
1185
1203
|
|
|
@@ -1410,14 +1428,19 @@ export function create_client(app, target) {
|
|
|
1410
1428
|
goto(result.location, { invalidateAll: true }, []);
|
|
1411
1429
|
} else {
|
|
1412
1430
|
/** @type {Record<string, any>} */
|
|
1413
|
-
|
|
1414
|
-
|
|
1431
|
+
root.$set({
|
|
1432
|
+
// this brings Svelte's view of the world in line with SvelteKit's
|
|
1433
|
+
// after use:enhance reset the form....
|
|
1434
|
+
form: null,
|
|
1415
1435
|
page: { ...page, form: result.data, status: result.status }
|
|
1416
|
-
};
|
|
1417
|
-
|
|
1436
|
+
});
|
|
1437
|
+
|
|
1438
|
+
// ...so that setting the `form` prop takes effect and isn't ignored
|
|
1439
|
+
await tick();
|
|
1440
|
+
root.$set({ form: result.data });
|
|
1418
1441
|
|
|
1419
1442
|
if (result.type === 'success') {
|
|
1420
|
-
|
|
1443
|
+
reset_focus();
|
|
1421
1444
|
}
|
|
1422
1445
|
}
|
|
1423
1446
|
},
|
|
@@ -1432,6 +1455,8 @@ export function create_client(app, target) {
|
|
|
1432
1455
|
addEventListener('beforeunload', (e) => {
|
|
1433
1456
|
let should_block = false;
|
|
1434
1457
|
|
|
1458
|
+
persist_state();
|
|
1459
|
+
|
|
1435
1460
|
if (!navigating) {
|
|
1436
1461
|
// If we're navigating, beforeNavigate was already called. If we end up in here during navigation,
|
|
1437
1462
|
// it's due to an external or full-page-reload link, for which we don't want to call the hook again.
|
|
@@ -1461,11 +1486,7 @@ export function create_client(app, target) {
|
|
|
1461
1486
|
|
|
1462
1487
|
addEventListener('visibilitychange', () => {
|
|
1463
1488
|
if (document.visibilityState === 'hidden') {
|
|
1464
|
-
|
|
1465
|
-
storage.set(SCROLL_KEY, scroll_positions);
|
|
1466
|
-
|
|
1467
|
-
capture_snapshot(current_history_index);
|
|
1468
|
-
storage.set(SNAPSHOT_KEY, snapshots);
|
|
1489
|
+
persist_state();
|
|
1469
1490
|
}
|
|
1470
1491
|
});
|
|
1471
1492
|
|
|
@@ -1515,11 +1536,14 @@ export function create_client(app, target) {
|
|
|
1515
1536
|
|
|
1516
1537
|
// Ignore the following but fire beforeNavigate
|
|
1517
1538
|
if (external || options.reload) {
|
|
1518
|
-
|
|
1519
|
-
|
|
1539
|
+
if (before_navigate({ url, type: 'link' })) {
|
|
1540
|
+
// set `navigating` to `true` to prevent `beforeNavigate` callbacks
|
|
1541
|
+
// being called when the page unloads
|
|
1542
|
+
navigating = true;
|
|
1543
|
+
} else {
|
|
1520
1544
|
event.preventDefault();
|
|
1521
1545
|
}
|
|
1522
|
-
|
|
1546
|
+
|
|
1523
1547
|
return;
|
|
1524
1548
|
}
|
|
1525
1549
|
|
|
@@ -27,26 +27,6 @@ export function get_cookies(request, url, trailing_slash) {
|
|
|
27
27
|
// Emulate browser-behavior: if the cookie is set at '/foo/bar', its path is '/foo'
|
|
28
28
|
const default_path = normalized_url.split('/').slice(0, -1).join('/') || '/';
|
|
29
29
|
|
|
30
|
-
if (__SVELTEKIT_DEV__) {
|
|
31
|
-
// TODO this could theoretically be wrong if the cookie was set unencoded?
|
|
32
|
-
const initial_decoded_cookies = parse(header, { decode: decodeURIComponent });
|
|
33
|
-
// Remove all cookies that no longer exist according to the request
|
|
34
|
-
for (const name of Object.keys(cookie_paths)) {
|
|
35
|
-
cookie_paths[name] = new Set(
|
|
36
|
-
[...cookie_paths[name]].filter(
|
|
37
|
-
(path) => !path_matches(normalized_url, path) || name in initial_decoded_cookies
|
|
38
|
-
)
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
// Add all new cookies we might not have seen before
|
|
42
|
-
for (const name in initial_decoded_cookies) {
|
|
43
|
-
cookie_paths[name] = cookie_paths[name] ?? new Set();
|
|
44
|
-
if (![...cookie_paths[name]].some((path) => path_matches(normalized_url, path))) {
|
|
45
|
-
cookie_paths[name].add(default_path);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
30
|
/** @type {Record<string, import('./page/types').Cookie>} */
|
|
51
31
|
const new_cookies = {};
|
|
52
32
|
|
|
@@ -82,20 +62,43 @@ export function get_cookies(request, url, trailing_slash) {
|
|
|
82
62
|
const req_cookies = parse(header, { decode: decoder });
|
|
83
63
|
const cookie = req_cookies[name]; // the decoded string or undefined
|
|
84
64
|
|
|
85
|
-
if
|
|
86
|
-
|
|
65
|
+
// in development, if the cookie was set during this session with `cookies.set`,
|
|
66
|
+
// but at a different path, warn the user. (ignore cookies from request headers,
|
|
67
|
+
// since we don't know which path they were set at)
|
|
68
|
+
if (__SVELTEKIT_DEV__ && !cookie) {
|
|
69
|
+
const paths = Array.from(cookie_paths[name] ?? []).filter((path) => {
|
|
70
|
+
// we only care about paths that are _more_ specific than the current path
|
|
71
|
+
return path_matches(path, url.pathname) && path !== url.pathname;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (paths.length > 0) {
|
|
75
|
+
console.warn(
|
|
76
|
+
// prettier-ignore
|
|
77
|
+
`'${name}' cookie does not exist for ${url.pathname}, but was previously set at ${conjoin([...paths])}. Did you mean to set its 'path' to '/' instead?`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
87
80
|
}
|
|
88
81
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
82
|
+
return cookie;
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {import('cookie').CookieParseOptions} opts
|
|
87
|
+
*/
|
|
88
|
+
getAll(opts) {
|
|
89
|
+
const decoder = opts?.decode || decodeURIComponent;
|
|
90
|
+
const cookies = parse(header, { decode: decoder });
|
|
91
|
+
|
|
92
|
+
for (const c of Object.values(new_cookies)) {
|
|
93
|
+
if (
|
|
94
|
+
domain_matches(url.hostname, c.options.domain) &&
|
|
95
|
+
path_matches(url.pathname, c.options.path)
|
|
96
|
+
) {
|
|
97
|
+
cookies[c.name] = c.value;
|
|
98
|
+
}
|
|
98
99
|
}
|
|
100
|
+
|
|
101
|
+
return Object.entries(cookies).map(([name, value]) => ({ name, value }));
|
|
99
102
|
},
|
|
100
103
|
|
|
101
104
|
/**
|
|
@@ -122,18 +125,11 @@ export function get_cookies(request, url, trailing_slash) {
|
|
|
122
125
|
throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
|
|
123
126
|
}
|
|
124
127
|
|
|
125
|
-
cookie_paths[name]
|
|
128
|
+
cookie_paths[name] ??= new Set();
|
|
129
|
+
|
|
126
130
|
if (!value) {
|
|
127
|
-
if (!cookie_paths[name].has(path) && cookie_paths[name].size > 0) {
|
|
128
|
-
const paths = `'${Array.from(cookie_paths[name]).join("', '")}'`;
|
|
129
|
-
console.warn(
|
|
130
|
-
`Trying to delete cookie '${name}' at path '${path}', but a cookie with that name only exists at these paths: ${paths}.`
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
131
|
cookie_paths[name].delete(path);
|
|
134
132
|
} else {
|
|
135
|
-
// We could also emit a warning here if the cookie already exists at a different path,
|
|
136
|
-
// but that's more likely a false positive because it's valid to set the same name at different paths
|
|
137
133
|
cookie_paths[name].add(path);
|
|
138
134
|
}
|
|
139
135
|
}
|
|
@@ -236,3 +232,11 @@ export function add_cookies_to_headers(headers, cookies) {
|
|
|
236
232
|
headers.append('set-cookie', serialize(name, value, options));
|
|
237
233
|
}
|
|
238
234
|
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @param {string[]} array
|
|
238
|
+
*/
|
|
239
|
+
function conjoin(array) {
|
|
240
|
+
if (array.length <= 2) return array.join(' and ');
|
|
241
|
+
return `${array.slice(0, -1).join(', ')} and ${array.at(-1)}`;
|
|
242
|
+
}
|
|
@@ -79,38 +79,13 @@ export async function render_page(event, page, options, manifest, state, resolve
|
|
|
79
79
|
// it's crucial that we do this before returning the non-SSR response, otherwise
|
|
80
80
|
// SvelteKit will erroneously believe that the path has been prerendered,
|
|
81
81
|
// causing functions to be omitted from the manifesst generated later
|
|
82
|
-
const should_prerender = get_option(nodes, 'prerender');
|
|
83
|
-
|
|
82
|
+
const should_prerender = get_option(nodes, 'prerender') ?? false;
|
|
84
83
|
if (should_prerender) {
|
|
85
84
|
const mod = leaf_node.server;
|
|
86
85
|
if (mod?.actions) {
|
|
87
86
|
throw new Error('Cannot prerender pages with actions');
|
|
88
87
|
}
|
|
89
88
|
} else if (state.prerendering) {
|
|
90
|
-
// Try to render the shell when ssr is false and prerendering not explicitly disabled.
|
|
91
|
-
// People can opt out of this behavior by explicitly setting prerender to false.
|
|
92
|
-
if (
|
|
93
|
-
should_prerender !== false &&
|
|
94
|
-
get_option(nodes, 'ssr') === false &&
|
|
95
|
-
!leaf_node.server?.actions
|
|
96
|
-
) {
|
|
97
|
-
return await render_response({
|
|
98
|
-
branch: [],
|
|
99
|
-
fetched: [],
|
|
100
|
-
page_config: {
|
|
101
|
-
ssr: false,
|
|
102
|
-
csr: get_option(nodes, 'csr') ?? true
|
|
103
|
-
},
|
|
104
|
-
status,
|
|
105
|
-
error: null,
|
|
106
|
-
event,
|
|
107
|
-
options,
|
|
108
|
-
manifest,
|
|
109
|
-
state,
|
|
110
|
-
resolve_opts
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
89
|
// if the page isn't marked as prerenderable, then bail out at this point
|
|
115
90
|
return new Response(undefined, {
|
|
116
91
|
status: 204
|
|
@@ -235,20 +235,20 @@ export async function render_response({
|
|
|
235
235
|
for (const dep of stylesheets) {
|
|
236
236
|
const path = prefixed(dep);
|
|
237
237
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
238
|
+
const attributes = ['rel="stylesheet"'];
|
|
239
|
+
|
|
240
|
+
if (inline_styles.has(dep)) {
|
|
241
|
+
// don't load stylesheets that are already inlined
|
|
242
|
+
// include them in disabled state so that Vite can detect them and doesn't try to add them
|
|
243
|
+
attributes.push('disabled', 'media="(max-width: 0)"');
|
|
244
|
+
} else {
|
|
245
|
+
if (resolve_opts.preload({ type: 'css', path })) {
|
|
246
246
|
const preload_atts = ['rel="preload"', 'as="style"'];
|
|
247
247
|
link_header_preloads.add(`<${encodeURI(path)}>; ${preload_atts.join(';')}; nopush`);
|
|
248
248
|
}
|
|
249
|
-
|
|
250
|
-
head += `\n\t\t<link href="${path}" ${attributes.join(' ')}>`;
|
|
251
249
|
}
|
|
250
|
+
|
|
251
|
+
head += `\n\t\t<link href="${path}" ${attributes.join(' ')}>`;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
for (const dep of fonts) {
|
|
@@ -268,7 +268,7 @@ export async function render_response({
|
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
const global = `__sveltekit_${options.version_hash}`;
|
|
271
|
+
const global = __SVELTEKIT_DEV__ ? `__sveltekit_dev` : `__sveltekit_${options.version_hash}`;
|
|
272
272
|
|
|
273
273
|
const { data, chunks } = get_data(
|
|
274
274
|
event,
|
|
@@ -213,7 +213,7 @@ export async function respond(request, options, manifest, state) {
|
|
|
213
213
|
|
|
214
214
|
if (normalized !== url.pathname && !state.prerendering?.fallback) {
|
|
215
215
|
return new Response(undefined, {
|
|
216
|
-
status:
|
|
216
|
+
status: 308,
|
|
217
217
|
headers: {
|
|
218
218
|
'x-sveltekit-normalize': '1',
|
|
219
219
|
location:
|
package/src/utils/routing.js
CHANGED
|
@@ -157,7 +157,7 @@ export function exec(match, params, matchers) {
|
|
|
157
157
|
// and the next value is defined, otherwise the buffer will cause us to skip values
|
|
158
158
|
const next_param = params[i + 1];
|
|
159
159
|
const next_value = values[i + 1];
|
|
160
|
-
if (next_param && !next_param.rest && next_value) {
|
|
160
|
+
if (next_param && !next_param.rest && next_param.optional && next_value) {
|
|
161
161
|
buffered = 0;
|
|
162
162
|
}
|
|
163
163
|
continue;
|
package/src/utils/streaming.js
CHANGED
|
@@ -23,22 +23,29 @@ function defer() {
|
|
|
23
23
|
* }}
|
|
24
24
|
*/
|
|
25
25
|
export function create_async_iterator() {
|
|
26
|
-
let deferred = defer();
|
|
26
|
+
let deferred = [defer()];
|
|
27
27
|
|
|
28
28
|
return {
|
|
29
29
|
iterator: {
|
|
30
30
|
[Symbol.asyncIterator]() {
|
|
31
31
|
return {
|
|
32
|
-
next: () =>
|
|
32
|
+
next: async () => {
|
|
33
|
+
const next = await deferred[0].promise;
|
|
34
|
+
if (!next.done) deferred.shift();
|
|
35
|
+
return next;
|
|
36
|
+
}
|
|
33
37
|
};
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
push: (value) => {
|
|
37
|
-
deferred.fulfil({
|
|
38
|
-
|
|
41
|
+
deferred[deferred.length - 1].fulfil({
|
|
42
|
+
value,
|
|
43
|
+
done: false
|
|
44
|
+
});
|
|
45
|
+
deferred.push(defer());
|
|
39
46
|
},
|
|
40
47
|
done: () => {
|
|
41
|
-
deferred.fulfil({ done: true });
|
|
48
|
+
deferred[deferred.length - 1].fulfil({ done: true });
|
|
42
49
|
}
|
|
43
50
|
};
|
|
44
51
|
}
|
package/types/ambient.d.ts
CHANGED
|
@@ -326,6 +326,11 @@ declare module '$app/stores' {
|
|
|
326
326
|
* This module is only available to [service workers](https://kit.svelte.dev/docs/service-workers).
|
|
327
327
|
*/
|
|
328
328
|
declare module '$service-worker' {
|
|
329
|
+
/**
|
|
330
|
+
* The `base` path of the deployment. Typically this is equivalent to `config.kit.paths.base`, but it is calculated from `location.pathname` meaning that it will continue to work correctly if the site is deployed to a subdirectory.
|
|
331
|
+
* Note that there is a `base` but no `assets`, since service workers cannot be used if `config.kit.paths.assets` is specified.
|
|
332
|
+
*/
|
|
333
|
+
export const base: string;
|
|
329
334
|
/**
|
|
330
335
|
* An array of URL strings representing the files generated by Vite, suitable for caching with `cache.addAll(build)`.
|
|
331
336
|
* During development, this is an empty array.
|
package/types/index.d.ts
CHANGED
|
@@ -198,7 +198,13 @@ export interface Cookies {
|
|
|
198
198
|
get(name: string, opts?: import('cookie').CookieParseOptions): string | undefined;
|
|
199
199
|
|
|
200
200
|
/**
|
|
201
|
-
*
|
|
201
|
+
* Gets all cookies that were previously set with `cookies.set`, or from the request headers.
|
|
202
|
+
* @param opts the options, passed directily to `cookie.parse`. See documentation [here](https://github.com/jshttp/cookie#cookieparsestr-options)
|
|
203
|
+
*/
|
|
204
|
+
getAll(opts?: import('cookie').CookieParseOptions): Array<{ name: string; value: string }>;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Sets a cookie. This will add a `set-cookie` header to the response, but also make the cookie available via `cookies.get` or `cookies.getAll` during the current request.
|
|
202
208
|
*
|
|
203
209
|
* The `httpOnly` and `secure` options are `true` by default (except on http://localhost, where `secure` is `false`), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The `sameSite` option defaults to `lax`.
|
|
204
210
|
*
|
|
@@ -452,6 +458,8 @@ export interface KitConfig {
|
|
|
452
458
|
*
|
|
453
459
|
* If `true`, `base` and `assets` imported from `$app/paths` will be replaced with relative asset paths during server-side rendering, resulting in portable HTML.
|
|
454
460
|
* If `false`, `%sveltekit.assets%` and references to build artifacts will always be root-relative paths, unless `paths.assets` is an external URL
|
|
461
|
+
*
|
|
462
|
+
* If your app uses a `<base>` element, you should set this to `false`, otherwise asset URLs will incorrectly be resolved against the `<base>` URL rather than the current page.
|
|
455
463
|
* @default undefined
|
|
456
464
|
*/
|
|
457
465
|
relative?: boolean | undefined;
|
|
@@ -566,7 +574,22 @@ export interface KitConfig {
|
|
|
566
574
|
*/
|
|
567
575
|
version?: {
|
|
568
576
|
/**
|
|
569
|
-
* The current app version string. If specified, this must be deterministic (e.g. a commit ref rather than `Math.random()` or `Date.now().toString()`), otherwise defaults to a timestamp of the build
|
|
577
|
+
* The current app version string. If specified, this must be deterministic (e.g. a commit ref rather than `Math.random()` or `Date.now().toString()`), otherwise defaults to a timestamp of the build.
|
|
578
|
+
*
|
|
579
|
+
* For example, to use the current commit hash, you could do use `git rev-parse HEAD`:
|
|
580
|
+
*
|
|
581
|
+
* ```js
|
|
582
|
+
* /// file: svelte.config.js
|
|
583
|
+
* import * as child_process from 'node:child_process';
|
|
584
|
+
*
|
|
585
|
+
* export default {
|
|
586
|
+
* kit: {
|
|
587
|
+
* version: {
|
|
588
|
+
* name: child_process.execSync('git rev-parse HEAD').toString().trim()
|
|
589
|
+
* }
|
|
590
|
+
* }
|
|
591
|
+
* };
|
|
592
|
+
* ```
|
|
570
593
|
*/
|
|
571
594
|
name?: string;
|
|
572
595
|
/**
|