@sveltejs/kit 1.16.2 → 1.17.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 +5 -5
- package/src/core/postbuild/crawl.js +68 -43
- package/src/core/postbuild/prerender.js +6 -1
- package/src/exports/vite/dev/index.js +6 -9
- package/src/runtime/app/forms.js +46 -14
- package/src/runtime/client/client.js +3 -2
- package/src/runtime/server/cookie.js +35 -27
- package/src/runtime/server/fetch.js +3 -2
- package/src/runtime/server/respond.js +9 -2
- package/src/utils/fork.js +16 -20
- package/src/utils/routing.js +18 -24
- package/types/ambient.d.ts +22 -1
- package/types/index.d.ts +3 -0
- package/src/utils/unit_test.js +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "The fastest way to build Svelte apps",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"@sveltejs/vite-plugin-svelte": "^2.1.1",
|
|
15
15
|
"@types/cookie": "^0.5.1",
|
|
16
16
|
"cookie": "^0.5.0",
|
|
17
|
-
"devalue": "^4.3.
|
|
17
|
+
"devalue": "^4.3.1",
|
|
18
18
|
"esm-env": "^1.0.0",
|
|
19
19
|
"kleur": "^4.1.5",
|
|
20
20
|
"magic-string": "^0.30.0",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"svelte": "^3.56.0",
|
|
39
39
|
"svelte-preprocess": "^5.0.3",
|
|
40
40
|
"typescript": "^4.9.4",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
41
|
+
"vite": "^4.3.6",
|
|
42
|
+
"vitest": "^0.31.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"svelte": "^3.54.0",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"test:integration": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test",
|
|
90
90
|
"test:cross-platform:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:dev",
|
|
91
91
|
"test:cross-platform:build": "pnpm test:unit && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:build",
|
|
92
|
-
"test:unit": "
|
|
92
|
+
"test:unit": "vitest --config kit.vitest.config.js run",
|
|
93
93
|
"postinstall": "node postinstall.js"
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -13,6 +13,20 @@ const ATTRIBUTE_NAME = /[^\t\n\f />"'=]/;
|
|
|
13
13
|
|
|
14
14
|
const WHITESPACE = /[\s\n\r]/;
|
|
15
15
|
|
|
16
|
+
const CRAWLABLE_META_NAME_ATTRS = new Set([
|
|
17
|
+
'og:url',
|
|
18
|
+
'og:image',
|
|
19
|
+
'og:image:url',
|
|
20
|
+
'og:image:secure_url',
|
|
21
|
+
'og:video',
|
|
22
|
+
'og:video:url',
|
|
23
|
+
'og:video:secure_url',
|
|
24
|
+
'og:audio',
|
|
25
|
+
'og:audio:url',
|
|
26
|
+
'og:audio:secure_url',
|
|
27
|
+
'twitter:image'
|
|
28
|
+
]);
|
|
29
|
+
|
|
16
30
|
/**
|
|
17
31
|
* @param {string} html
|
|
18
32
|
* @param {string} base
|
|
@@ -81,6 +95,9 @@ export function crawl(html, base) {
|
|
|
81
95
|
|
|
82
96
|
const tag = html.slice(start, i).toUpperCase();
|
|
83
97
|
|
|
98
|
+
/** @type {Record<string, string>} */
|
|
99
|
+
const attributes = {};
|
|
100
|
+
|
|
84
101
|
if (tag === 'SCRIPT' || tag === 'STYLE') {
|
|
85
102
|
while (i < html.length) {
|
|
86
103
|
if (
|
|
@@ -95,9 +112,6 @@ export function crawl(html, base) {
|
|
|
95
112
|
}
|
|
96
113
|
}
|
|
97
114
|
|
|
98
|
-
let href = '';
|
|
99
|
-
let rel = '';
|
|
100
|
-
|
|
101
115
|
while (i < html.length) {
|
|
102
116
|
const start = i;
|
|
103
117
|
|
|
@@ -159,44 +173,7 @@ export function crawl(html, base) {
|
|
|
159
173
|
}
|
|
160
174
|
|
|
161
175
|
value = decode(value);
|
|
162
|
-
|
|
163
|
-
if (name === 'href') {
|
|
164
|
-
if (tag === 'BASE') {
|
|
165
|
-
base = resolve(base, value);
|
|
166
|
-
} else {
|
|
167
|
-
href = resolve(base, value);
|
|
168
|
-
}
|
|
169
|
-
} else if (name === 'id') {
|
|
170
|
-
ids.push(value);
|
|
171
|
-
} else if (name === 'name') {
|
|
172
|
-
if (tag === 'A') ids.push(value);
|
|
173
|
-
} else if (name === 'rel') {
|
|
174
|
-
rel = value;
|
|
175
|
-
} else if (name === 'src') {
|
|
176
|
-
if (value) hrefs.push(resolve(base, value));
|
|
177
|
-
} else if (name === 'srcset') {
|
|
178
|
-
const candidates = [];
|
|
179
|
-
let insideURL = true;
|
|
180
|
-
value = value.trim();
|
|
181
|
-
for (let i = 0; i < value.length; i++) {
|
|
182
|
-
if (
|
|
183
|
-
value[i] === ',' &&
|
|
184
|
-
(!insideURL || (insideURL && WHITESPACE.test(value[i + 1])))
|
|
185
|
-
) {
|
|
186
|
-
candidates.push(value.slice(0, i));
|
|
187
|
-
value = value.substring(i + 1).trim();
|
|
188
|
-
i = 0;
|
|
189
|
-
insideURL = true;
|
|
190
|
-
} else if (WHITESPACE.test(value[i])) {
|
|
191
|
-
insideURL = false;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
candidates.push(value);
|
|
195
|
-
for (const candidate of candidates) {
|
|
196
|
-
const src = candidate.split(WHITESPACE)[0];
|
|
197
|
-
if (src) hrefs.push(resolve(base, src));
|
|
198
|
-
}
|
|
199
|
-
}
|
|
176
|
+
attributes[name] = value;
|
|
200
177
|
} else {
|
|
201
178
|
i -= 1;
|
|
202
179
|
}
|
|
@@ -205,8 +182,56 @@ export function crawl(html, base) {
|
|
|
205
182
|
i += 1;
|
|
206
183
|
}
|
|
207
184
|
|
|
208
|
-
|
|
209
|
-
|
|
185
|
+
const { href, id, name, property, rel, src, srcset, content } = attributes;
|
|
186
|
+
|
|
187
|
+
if (href) {
|
|
188
|
+
if (tag === 'BASE') {
|
|
189
|
+
base = resolve(base, href);
|
|
190
|
+
} else if (!rel || !/\bexternal\b/i.test(rel)) {
|
|
191
|
+
hrefs.push(resolve(base, href));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (id) {
|
|
196
|
+
ids.push(id);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (name && tag === 'A') {
|
|
200
|
+
ids.push(name);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (src) {
|
|
204
|
+
hrefs.push(resolve(base, src));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (srcset) {
|
|
208
|
+
let value = srcset;
|
|
209
|
+
const candidates = [];
|
|
210
|
+
let insideURL = true;
|
|
211
|
+
value = value.trim();
|
|
212
|
+
for (let i = 0; i < value.length; i++) {
|
|
213
|
+
if (value[i] === ',' && (!insideURL || (insideURL && WHITESPACE.test(value[i + 1])))) {
|
|
214
|
+
candidates.push(value.slice(0, i));
|
|
215
|
+
value = value.substring(i + 1).trim();
|
|
216
|
+
i = 0;
|
|
217
|
+
insideURL = true;
|
|
218
|
+
} else if (WHITESPACE.test(value[i])) {
|
|
219
|
+
insideURL = false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
candidates.push(value);
|
|
223
|
+
for (const candidate of candidates) {
|
|
224
|
+
const src = candidate.split(WHITESPACE)[0];
|
|
225
|
+
if (src) hrefs.push(resolve(base, src));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (tag === 'META' && content) {
|
|
230
|
+
const attr = name ?? property;
|
|
231
|
+
|
|
232
|
+
if (attr && CRAWLABLE_META_NAME_ATTRS.has(attr)) {
|
|
233
|
+
hrefs.push(resolve(base, content));
|
|
234
|
+
}
|
|
210
235
|
}
|
|
211
236
|
}
|
|
212
237
|
}
|
|
@@ -12,6 +12,7 @@ import { get_route_segments } from '../../utils/routing.js';
|
|
|
12
12
|
import { queue } from './queue.js';
|
|
13
13
|
import { crawl } from './crawl.js';
|
|
14
14
|
import { forked } from '../../utils/fork.js';
|
|
15
|
+
import * as devalue from 'devalue';
|
|
15
16
|
|
|
16
17
|
export default forked(import.meta.url, prerender);
|
|
17
18
|
|
|
@@ -340,7 +341,11 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
|
|
|
340
341
|
|
|
341
342
|
writeFileSync(
|
|
342
343
|
dest,
|
|
343
|
-
`<
|
|
344
|
+
`<script>location.href=${devalue.uneval(
|
|
345
|
+
location
|
|
346
|
+
)};</script><meta http-equiv="refresh" content=${escape_html_attr(
|
|
347
|
+
`0;url=${location}`
|
|
348
|
+
)}>`
|
|
344
349
|
);
|
|
345
350
|
|
|
346
351
|
written.add(file);
|
|
@@ -176,20 +176,17 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
176
176
|
const styles = {};
|
|
177
177
|
|
|
178
178
|
for (const dep of deps) {
|
|
179
|
-
const url = new URL(dep.url, '
|
|
179
|
+
const url = new URL(dep.url, 'dummy:/');
|
|
180
180
|
const query = url.searchParams;
|
|
181
181
|
|
|
182
182
|
if (
|
|
183
|
-
isCSSRequest(dep.file) ||
|
|
184
|
-
|
|
183
|
+
(isCSSRequest(dep.file) ||
|
|
184
|
+
(query.has('svelte') && query.get('type') === 'style')) &&
|
|
185
|
+
!(query.has('raw') || query.has('url') || query.has('inline'))
|
|
185
186
|
) {
|
|
186
|
-
// setting `?inline` to load CSS modules as css string
|
|
187
|
-
query.set('inline', '');
|
|
188
|
-
|
|
189
187
|
try {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
);
|
|
188
|
+
query.set('inline', '');
|
|
189
|
+
const mod = await vite.ssrLoadModule(`${url.pathname}${url.search}${url.hash}`);
|
|
193
190
|
styles[dep.url] = mod.default;
|
|
194
191
|
} catch {
|
|
195
192
|
// this can happen with dynamically imported modules, I think
|
package/src/runtime/app/forms.js
CHANGED
|
@@ -14,12 +14,26 @@ export function deserialize(result) {
|
|
|
14
14
|
return parsed;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} old_name
|
|
19
|
+
* @param {string} new_name
|
|
20
|
+
* @param {string} call_location
|
|
21
|
+
* @returns void
|
|
22
|
+
*/
|
|
23
|
+
function warn_on_access(old_name, new_name, call_location) {
|
|
24
|
+
if (!DEV) return;
|
|
25
|
+
// TODO 2.0: Remove this code
|
|
26
|
+
console.warn(
|
|
27
|
+
`\`${old_name}\` has been deprecated in favor of \`${new_name}\`. \`${old_name}\` will be removed in a future version. (Called from ${call_location})`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
17
31
|
/** @type {import('$app/forms').enhance} */
|
|
18
|
-
export function enhance(
|
|
32
|
+
export function enhance(form_element, submit = () => {}) {
|
|
19
33
|
if (
|
|
20
34
|
DEV &&
|
|
21
|
-
/** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(
|
|
22
|
-
'post'
|
|
35
|
+
/** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(form_element))
|
|
36
|
+
.method !== 'post'
|
|
23
37
|
) {
|
|
24
38
|
throw new Error('use:enhance can only be used on <form> fields with method="POST"');
|
|
25
39
|
}
|
|
@@ -35,7 +49,7 @@ export function enhance(form, submit = () => {}) {
|
|
|
35
49
|
if (result.type === 'success') {
|
|
36
50
|
if (reset !== false) {
|
|
37
51
|
// We call reset from the prototype to avoid DOM clobbering
|
|
38
|
-
HTMLFormElement.prototype.reset.call(
|
|
52
|
+
HTMLFormElement.prototype.reset.call(form_element);
|
|
39
53
|
}
|
|
40
54
|
await invalidateAll();
|
|
41
55
|
}
|
|
@@ -60,14 +74,15 @@ export function enhance(form, submit = () => {}) {
|
|
|
60
74
|
// We do cloneNode for avoid DOM clobbering - https://github.com/sveltejs/kit/issues/7593
|
|
61
75
|
event.submitter?.hasAttribute('formaction')
|
|
62
76
|
? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formAction
|
|
63
|
-
: /** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(
|
|
77
|
+
: /** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(form_element))
|
|
78
|
+
.action
|
|
64
79
|
);
|
|
65
80
|
|
|
66
|
-
const
|
|
81
|
+
const form_data = new FormData(form_element);
|
|
67
82
|
|
|
68
83
|
const submitter_name = event.submitter?.getAttribute('name');
|
|
69
84
|
if (submitter_name) {
|
|
70
|
-
|
|
85
|
+
form_data.append(submitter_name, event.submitter?.getAttribute('value') ?? '');
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
const controller = new AbortController();
|
|
@@ -75,13 +90,22 @@ export function enhance(form, submit = () => {}) {
|
|
|
75
90
|
let cancelled = false;
|
|
76
91
|
const cancel = () => (cancelled = true);
|
|
77
92
|
|
|
93
|
+
// TODO 2.0: Remove `data` and `form`
|
|
78
94
|
const callback =
|
|
79
95
|
(await submit({
|
|
80
96
|
action,
|
|
81
97
|
cancel,
|
|
82
98
|
controller,
|
|
83
|
-
data
|
|
84
|
-
|
|
99
|
+
get data() {
|
|
100
|
+
warn_on_access('data', 'formData', 'use:enhance submit function');
|
|
101
|
+
return form_data;
|
|
102
|
+
},
|
|
103
|
+
formData: form_data,
|
|
104
|
+
get form() {
|
|
105
|
+
warn_on_access('form', 'formElement', 'use:enhance submit function');
|
|
106
|
+
return form_element;
|
|
107
|
+
},
|
|
108
|
+
formElement: form_element,
|
|
85
109
|
submitter: event.submitter
|
|
86
110
|
})) ?? fallback_callback;
|
|
87
111
|
if (cancelled) return;
|
|
@@ -97,7 +121,7 @@ export function enhance(form, submit = () => {}) {
|
|
|
97
121
|
'x-sveltekit-action': 'true'
|
|
98
122
|
},
|
|
99
123
|
cache: 'no-store',
|
|
100
|
-
body:
|
|
124
|
+
body: form_data,
|
|
101
125
|
signal: controller.signal
|
|
102
126
|
});
|
|
103
127
|
|
|
@@ -110,8 +134,16 @@ export function enhance(form, submit = () => {}) {
|
|
|
110
134
|
|
|
111
135
|
callback({
|
|
112
136
|
action,
|
|
113
|
-
data
|
|
114
|
-
|
|
137
|
+
get data() {
|
|
138
|
+
warn_on_access('data', 'formData', 'callback returned from use:enhance submit function');
|
|
139
|
+
return form_data;
|
|
140
|
+
},
|
|
141
|
+
formData: form_data,
|
|
142
|
+
get form() {
|
|
143
|
+
warn_on_access('form', 'formElement', 'callback returned from use:enhance submit function');
|
|
144
|
+
return form_element;
|
|
145
|
+
},
|
|
146
|
+
formElement: form_element,
|
|
115
147
|
update: (opts) => fallback_callback({ action, result, reset: opts?.reset }),
|
|
116
148
|
// @ts-expect-error generic constraints stuff we don't care about
|
|
117
149
|
result
|
|
@@ -119,12 +151,12 @@ export function enhance(form, submit = () => {}) {
|
|
|
119
151
|
}
|
|
120
152
|
|
|
121
153
|
// @ts-expect-error
|
|
122
|
-
HTMLFormElement.prototype.addEventListener.call(
|
|
154
|
+
HTMLFormElement.prototype.addEventListener.call(form_element, 'submit', handle_submit);
|
|
123
155
|
|
|
124
156
|
return {
|
|
125
157
|
destroy() {
|
|
126
158
|
// @ts-expect-error
|
|
127
|
-
HTMLFormElement.prototype.removeEventListener.call(
|
|
159
|
+
HTMLFormElement.prototype.removeEventListener.call(form_element, 'submit', handle_submit);
|
|
128
160
|
}
|
|
129
161
|
};
|
|
130
162
|
}
|
|
@@ -272,7 +272,7 @@ export function create_client(app, target) {
|
|
|
272
272
|
|
|
273
273
|
/** @param {import('./types').NavigationFinished} result */
|
|
274
274
|
function initialize(result) {
|
|
275
|
-
if (DEV && document.querySelector('vite-error-overlay')) return;
|
|
275
|
+
if (DEV && result.state.error && document.querySelector('vite-error-overlay')) return;
|
|
276
276
|
|
|
277
277
|
current = result.state;
|
|
278
278
|
|
|
@@ -1908,7 +1908,8 @@ function reset_focus() {
|
|
|
1908
1908
|
const tabindex = root.getAttribute('tabindex');
|
|
1909
1909
|
|
|
1910
1910
|
root.tabIndex = -1;
|
|
1911
|
-
|
|
1911
|
+
// @ts-expect-error
|
|
1912
|
+
root.focus({ preventScroll: true, focusVisible: false });
|
|
1912
1913
|
|
|
1913
1914
|
// restore `tabindex` as to prevent `root` from stealing input from elements
|
|
1914
1915
|
if (tabindex !== null) {
|
|
@@ -107,32 +107,7 @@ export function get_cookies(request, url, trailing_slash) {
|
|
|
107
107
|
* @param {import('cookie').CookieSerializeOptions} opts
|
|
108
108
|
*/
|
|
109
109
|
set(name, value, opts = {}) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
new_cookies[name] = {
|
|
113
|
-
name,
|
|
114
|
-
value,
|
|
115
|
-
options: {
|
|
116
|
-
...defaults,
|
|
117
|
-
...opts,
|
|
118
|
-
path
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
if (__SVELTEKIT_DEV__) {
|
|
123
|
-
const serialized = serialize(name, value, new_cookies[name].options);
|
|
124
|
-
if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) {
|
|
125
|
-
throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
cookie_paths[name] ??= new Set();
|
|
129
|
-
|
|
130
|
-
if (!value) {
|
|
131
|
-
cookie_paths[name].delete(path);
|
|
132
|
-
} else {
|
|
133
|
-
cookie_paths[name].add(path);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
110
|
+
set_internal(name, value, { ...defaults, ...opts });
|
|
136
111
|
},
|
|
137
112
|
|
|
138
113
|
/**
|
|
@@ -193,7 +168,40 @@ export function get_cookies(request, url, trailing_slash) {
|
|
|
193
168
|
.join('; ');
|
|
194
169
|
}
|
|
195
170
|
|
|
196
|
-
|
|
171
|
+
/**
|
|
172
|
+
* @param {string} name
|
|
173
|
+
* @param {string} value
|
|
174
|
+
* @param {import('cookie').CookieSerializeOptions} opts
|
|
175
|
+
*/
|
|
176
|
+
function set_internal(name, value, opts) {
|
|
177
|
+
let path = opts.path ?? default_path;
|
|
178
|
+
|
|
179
|
+
new_cookies[name] = {
|
|
180
|
+
name,
|
|
181
|
+
value,
|
|
182
|
+
options: {
|
|
183
|
+
...opts,
|
|
184
|
+
path
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
if (__SVELTEKIT_DEV__) {
|
|
189
|
+
const serialized = serialize(name, value, new_cookies[name].options);
|
|
190
|
+
if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) {
|
|
191
|
+
throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
cookie_paths[name] ??= new Set();
|
|
195
|
+
|
|
196
|
+
if (!value) {
|
|
197
|
+
cookie_paths[name].delete(path);
|
|
198
|
+
} else {
|
|
199
|
+
cookie_paths[name].add(path);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return { cookies, new_cookies, get_cookie_header, set_internal };
|
|
197
205
|
}
|
|
198
206
|
|
|
199
207
|
/**
|
|
@@ -9,10 +9,11 @@ import * as paths from '__sveltekit/paths';
|
|
|
9
9
|
* manifest: import('types').SSRManifest;
|
|
10
10
|
* state: import('types').SSRState;
|
|
11
11
|
* get_cookie_header: (url: URL, header: string | null) => string;
|
|
12
|
+
* set_internal: (name: string, value: string, opts: import('cookie').CookieSerializeOptions) => void;
|
|
12
13
|
* }} opts
|
|
13
14
|
* @returns {typeof fetch}
|
|
14
15
|
*/
|
|
15
|
-
export function create_fetch({ event, options, manifest, state, get_cookie_header }) {
|
|
16
|
+
export function create_fetch({ event, options, manifest, state, get_cookie_header, set_internal }) {
|
|
16
17
|
return async (info, init) => {
|
|
17
18
|
const original_request = normalize_fetch_input(info, init, event.url);
|
|
18
19
|
|
|
@@ -131,7 +132,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
|
|
|
131
132
|
const { name, value, ...options } = set_cookie_parser.parseString(str);
|
|
132
133
|
|
|
133
134
|
// options.sameSite is string, something more specific is required - type cast is safe
|
|
134
|
-
|
|
135
|
+
set_internal(
|
|
135
136
|
name,
|
|
136
137
|
value,
|
|
137
138
|
/** @type {import('cookie').CookieSerializeOptions} */ (options)
|
|
@@ -244,7 +244,7 @@ export async function respond(request, options, manifest, state) {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
const { cookies, new_cookies, get_cookie_header } = get_cookies(
|
|
247
|
+
const { cookies, new_cookies, get_cookie_header, set_internal } = get_cookies(
|
|
248
248
|
request,
|
|
249
249
|
url,
|
|
250
250
|
trailing_slash ?? 'never'
|
|
@@ -252,7 +252,14 @@ export async function respond(request, options, manifest, state) {
|
|
|
252
252
|
|
|
253
253
|
cookies_to_add = new_cookies;
|
|
254
254
|
event.cookies = cookies;
|
|
255
|
-
event.fetch = create_fetch({
|
|
255
|
+
event.fetch = create_fetch({
|
|
256
|
+
event,
|
|
257
|
+
options,
|
|
258
|
+
manifest,
|
|
259
|
+
state,
|
|
260
|
+
get_cookie_header,
|
|
261
|
+
set_internal
|
|
262
|
+
});
|
|
256
263
|
|
|
257
264
|
if (state.prerendering && !state.prerendering.fallback) disable_search(url);
|
|
258
265
|
|
package/src/utils/fork.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url';
|
|
2
|
-
import
|
|
2
|
+
import { Worker, parentPort } from 'node:worker_threads';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Runs a task in a subprocess so any dangling stuff gets killed upon completion.
|
|
@@ -11,23 +11,21 @@ import child_process from 'node:child_process';
|
|
|
11
11
|
* @returns {(opts: T) => Promise<U>} A function that when called starts the subprocess
|
|
12
12
|
*/
|
|
13
13
|
export function forked(module, callback) {
|
|
14
|
-
if (process.env.SVELTEKIT_FORK &&
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
process.on(
|
|
14
|
+
if (process.env.SVELTEKIT_FORK && parentPort) {
|
|
15
|
+
parentPort.on(
|
|
18
16
|
'message',
|
|
19
17
|
/** @param {any} data */ async (data) => {
|
|
20
18
|
if (data?.type === 'args' && data.module === module) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
}
|
|
19
|
+
parentPort?.postMessage({
|
|
20
|
+
type: 'result',
|
|
21
|
+
module,
|
|
22
|
+
payload: await callback(data.payload)
|
|
23
|
+
});
|
|
28
24
|
}
|
|
29
25
|
}
|
|
30
26
|
);
|
|
27
|
+
|
|
28
|
+
parentPort.postMessage({ type: 'ready', module });
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
/**
|
|
@@ -36,20 +34,18 @@ export function forked(module, callback) {
|
|
|
36
34
|
*/
|
|
37
35
|
const fn = function (opts) {
|
|
38
36
|
return new Promise((fulfil, reject) => {
|
|
39
|
-
const
|
|
40
|
-
stdio: 'inherit',
|
|
37
|
+
const worker = new Worker(fileURLToPath(module), {
|
|
41
38
|
env: {
|
|
42
39
|
...process.env,
|
|
43
40
|
SVELTEKIT_FORK: 'true'
|
|
44
|
-
}
|
|
45
|
-
serialization: 'advanced'
|
|
41
|
+
}
|
|
46
42
|
});
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
worker.on(
|
|
49
45
|
'message',
|
|
50
46
|
/** @param {any} data */ (data) => {
|
|
51
47
|
if (data?.type === 'ready' && data.module === module) {
|
|
52
|
-
|
|
48
|
+
worker.postMessage({
|
|
53
49
|
type: 'args',
|
|
54
50
|
module,
|
|
55
51
|
payload: opts
|
|
@@ -57,13 +53,13 @@ export function forked(module, callback) {
|
|
|
57
53
|
}
|
|
58
54
|
|
|
59
55
|
if (data?.type === 'result' && data.module === module) {
|
|
60
|
-
|
|
56
|
+
worker.terminate();
|
|
61
57
|
fulfil(data.payload);
|
|
62
58
|
}
|
|
63
59
|
}
|
|
64
60
|
);
|
|
65
61
|
|
|
66
|
-
|
|
62
|
+
worker.on('exit', (code) => {
|
|
67
63
|
if (code) {
|
|
68
64
|
reject(new Error(`Failed with code ${code}`));
|
|
69
65
|
}
|
package/src/utils/routing.js
CHANGED
|
@@ -96,7 +96,7 @@ export function parse_route_id(id) {
|
|
|
96
96
|
return { pattern, params };
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
const basic_param_pattern = /\[(\[)?(?:\.\.\.)?(\w+?)(?:=(\w+))?\]\]
|
|
99
|
+
const basic_param_pattern = /\[(\[)?(?:\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
102
|
* Parses a route ID, then resolves it to a path by replacing parameters with actual values from `entry`.
|
|
@@ -112,29 +112,23 @@ export function resolve_entry(id, entry) {
|
|
|
112
112
|
return (
|
|
113
113
|
'/' +
|
|
114
114
|
segments
|
|
115
|
-
.map((segment) =>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
throw new Error(
|
|
133
|
-
`Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
return param_value;
|
|
137
|
-
})
|
|
115
|
+
.map((segment) =>
|
|
116
|
+
segment.replace(basic_param_pattern, (_, optional, name) => {
|
|
117
|
+
const param_value = entry[name];
|
|
118
|
+
|
|
119
|
+
// This is nested so TS correctly narrows the type
|
|
120
|
+
if (!param_value) {
|
|
121
|
+
if (optional) return '';
|
|
122
|
+
throw new Error(`Missing parameter '${name}' in route ${id}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (param_value.startsWith('/') || param_value.endsWith('/'))
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
|
|
128
|
+
);
|
|
129
|
+
return param_value;
|
|
130
|
+
})
|
|
131
|
+
)
|
|
138
132
|
.filter(Boolean)
|
|
139
133
|
.join('/')
|
|
140
134
|
);
|
package/types/ambient.d.ts
CHANGED
|
@@ -80,15 +80,36 @@ declare module '$app/forms' {
|
|
|
80
80
|
Invalid extends Record<string, unknown> | undefined = Record<string, any>
|
|
81
81
|
> = (input: {
|
|
82
82
|
action: URL;
|
|
83
|
+
/**
|
|
84
|
+
* use `formData` instead of `data`
|
|
85
|
+
* @deprecated
|
|
86
|
+
*/
|
|
83
87
|
data: FormData;
|
|
88
|
+
formData: FormData;
|
|
89
|
+
/**
|
|
90
|
+
* use `formElement` instead of `form`
|
|
91
|
+
* @deprecated
|
|
92
|
+
*/
|
|
84
93
|
form: HTMLFormElement;
|
|
94
|
+
formElement: HTMLFormElement;
|
|
85
95
|
controller: AbortController;
|
|
86
96
|
cancel(): void;
|
|
87
97
|
submitter: HTMLElement | null;
|
|
88
98
|
}) => MaybePromise<
|
|
89
99
|
| void
|
|
90
100
|
| ((opts: {
|
|
101
|
+
/**
|
|
102
|
+
* use `formData` instead of `data`
|
|
103
|
+
* @deprecated
|
|
104
|
+
*/
|
|
105
|
+
data: FormData;
|
|
106
|
+
formData: FormData;
|
|
107
|
+
/**
|
|
108
|
+
* use `formElement` instead of `form`
|
|
109
|
+
* @deprecated
|
|
110
|
+
*/
|
|
91
111
|
form: HTMLFormElement;
|
|
112
|
+
formElement: HTMLFormElement;
|
|
92
113
|
action: URL;
|
|
93
114
|
result: ActionResult<Success, Invalid>;
|
|
94
115
|
/**
|
|
@@ -108,7 +129,7 @@ declare module '$app/forms' {
|
|
|
108
129
|
Success extends Record<string, unknown> | undefined = Record<string, any>,
|
|
109
130
|
Invalid extends Record<string, unknown> | undefined = Record<string, any>
|
|
110
131
|
>(
|
|
111
|
-
|
|
132
|
+
formElement: HTMLFormElement,
|
|
112
133
|
/**
|
|
113
134
|
* Called upon submission with the given FormData and the `action` that should be triggered.
|
|
114
135
|
* If `cancel` is called, the form will not be submitted.
|
package/types/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
UniqueInterface
|
|
21
21
|
} from './private.js';
|
|
22
22
|
import { BuildData, SSRNodeLoader, SSRRoute, ValidatedConfig } from './internal.js';
|
|
23
|
+
import type { PluginOptions } from '@sveltejs/vite-plugin-svelte';
|
|
23
24
|
|
|
24
25
|
export { PrerenderOption } from './private.js';
|
|
25
26
|
|
|
@@ -186,6 +187,8 @@ export interface Config {
|
|
|
186
187
|
};
|
|
187
188
|
/** Preprocessor options, if any. Preprocessing can alternatively also be done through Vite's preprocessor capabilities. */
|
|
188
189
|
preprocess?: any;
|
|
190
|
+
/** `vite-plugin-svelte` plugin options. */
|
|
191
|
+
vitePlugin?: PluginOptions;
|
|
189
192
|
/** Any additional options required by tooling that integrates with Svelte. */
|
|
190
193
|
[key: string]: any;
|
|
191
194
|
}
|
package/src/utils/unit_test.js
DELETED