@sveltejs/kit 2.56.1 → 2.57.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/core/config/index.js +22 -2
- package/src/core/config/options.js +20 -4
- package/src/core/postbuild/prerender.js +2 -1
- package/src/core/sync/utils.js +8 -1
- package/src/core/utils.js +1 -2
- package/src/exports/index.js +1 -1
- package/src/exports/internal/index.js +9 -0
- package/src/exports/node/index.js +19 -6
- package/src/exports/public.d.ts +37 -20
- package/src/exports/vite/build/build_service_worker.js +1 -1
- package/src/exports/vite/build/remote.js +126 -0
- package/src/exports/vite/dev/index.js +21 -8
- package/src/exports/vite/index.js +35 -22
- package/src/exports/vite/preview/index.js +5 -1
- package/src/exports/vite/utils.js +18 -0
- package/src/runtime/app/forms.js +2 -1
- package/src/runtime/app/server/remote/prerender.js +2 -1
- package/src/runtime/app/server/remote/query.js +2 -4
- package/src/runtime/app/server/remote/requested.js +3 -2
- package/src/runtime/app/server/remote/shared.js +2 -1
- package/src/runtime/client/client.js +8 -9
- package/src/runtime/client/fetcher.js +2 -1
- package/src/runtime/client/remote-functions/form.svelte.js +10 -5
- package/src/runtime/client/remote-functions/query.svelte.js +3 -2
- package/src/runtime/client/utils.js +2 -1
- package/src/runtime/form-utils.js +2 -2
- package/src/runtime/server/fetch.js +4 -3
- package/src/runtime/server/index.js +3 -2
- package/src/runtime/server/page/index.js +3 -2
- package/src/runtime/server/page/load_data.js +4 -3
- package/src/runtime/server/page/render.js +7 -1
- package/src/runtime/server/respond.js +12 -25
- package/src/utils/functions.js +2 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +40 -22
- package/types/index.d.ts.map +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.57.1",
|
|
4
4
|
"description": "SvelteKit is the fastest way to build Svelte apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@opentelemetry/api": "^1.0.0",
|
|
36
|
-
"@playwright/test": "1.
|
|
36
|
+
"@playwright/test": "^1.59.1",
|
|
37
37
|
"@sveltejs/vite-plugin-svelte": "^6.0.0-next.3",
|
|
38
38
|
"@types/connect": "^3.4.38",
|
|
39
39
|
"@types/node": "^18.19.119",
|
package/src/core/config/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import process from 'node:process';
|
|
4
4
|
import * as url from 'node:url';
|
|
5
5
|
import options from './options.js';
|
|
6
|
+
import { resolve_entry } from '../../utils/filesystem.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Loads the template (src/app.html by default) and validates that it has the
|
|
@@ -96,7 +97,7 @@ export async function load_config({ cwd = process.cwd() } = {}) {
|
|
|
96
97
|
* @returns {import('types').ValidatedConfig}
|
|
97
98
|
*/
|
|
98
99
|
function process_config(config, { cwd = process.cwd() } = {}) {
|
|
99
|
-
const validated = validate_config(config);
|
|
100
|
+
const validated = validate_config(config, cwd);
|
|
100
101
|
|
|
101
102
|
validated.kit.outDir = path.resolve(cwd, validated.kit.outDir);
|
|
102
103
|
|
|
@@ -116,15 +117,17 @@ function process_config(config, { cwd = process.cwd() } = {}) {
|
|
|
116
117
|
|
|
117
118
|
/**
|
|
118
119
|
* @param {import('@sveltejs/kit').Config} config
|
|
120
|
+
* @param {string} [cwd]
|
|
119
121
|
* @returns {import('types').ValidatedConfig}
|
|
120
122
|
*/
|
|
121
|
-
export function validate_config(config) {
|
|
123
|
+
export function validate_config(config, cwd = process.cwd()) {
|
|
122
124
|
if (typeof config !== 'object') {
|
|
123
125
|
throw new Error(
|
|
124
126
|
'The Svelte config file must have a configuration object as its default export. See https://svelte.dev/docs/kit/configuration'
|
|
125
127
|
);
|
|
126
128
|
}
|
|
127
129
|
|
|
130
|
+
/** @type {import('types').ValidatedConfig} */
|
|
128
131
|
const validated = options(config, 'config');
|
|
129
132
|
const files = validated.kit.files;
|
|
130
133
|
|
|
@@ -151,5 +154,22 @@ export function validate_config(config) {
|
|
|
151
154
|
}
|
|
152
155
|
}
|
|
153
156
|
|
|
157
|
+
if (validated.kit.csp?.directives?.['require-trusted-types-for']?.includes('script')) {
|
|
158
|
+
if (!validated.kit.csp?.directives?.['trusted-types']?.includes('svelte-trusted-html')) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
"The `csp.directives['trusted-types']` option must include 'svelte-trusted-html'"
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
if (
|
|
164
|
+
validated.kit.serviceWorker?.register &&
|
|
165
|
+
resolve_entry(path.resolve(cwd, validated.kit.files.serviceWorker)) &&
|
|
166
|
+
!validated.kit.csp?.directives?.['trusted-types']?.includes('sveltekit-trusted-url')
|
|
167
|
+
) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
"The `csp.directives['trusted-types']` option must include 'sveltekit-trusted-url' when `serviceWorker.register` is true"
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
154
174
|
return validated;
|
|
155
175
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
/** @import { Validator } from './types.js' */
|
|
2
|
+
|
|
1
3
|
import process from 'node:process';
|
|
2
4
|
import colors from 'kleur';
|
|
3
|
-
|
|
4
|
-
/** @typedef {import('./types.js').Validator} Validator */
|
|
5
|
+
import { supportsTrustedTypes } from '../sync/utils.js';
|
|
5
6
|
|
|
6
7
|
const directives = object({
|
|
7
8
|
'child-src': string_array(),
|
|
@@ -28,8 +29,14 @@ const directives = object({
|
|
|
28
29
|
'navigate-to': string_array(),
|
|
29
30
|
'report-uri': string_array(),
|
|
30
31
|
'report-to': string_array(),
|
|
31
|
-
'require-trusted-types-for':
|
|
32
|
-
|
|
32
|
+
'require-trusted-types-for': validate(undefined, (input, keypath) => {
|
|
33
|
+
assert_trusted_types_supported(keypath);
|
|
34
|
+
return string_array()(input, keypath);
|
|
35
|
+
}),
|
|
36
|
+
'trusted-types': validate(undefined, (input, keypath) => {
|
|
37
|
+
assert_trusted_types_supported(keypath);
|
|
38
|
+
return string_array()(input, keypath);
|
|
39
|
+
}),
|
|
33
40
|
'upgrade-insecure-requests': boolean(false),
|
|
34
41
|
'require-sri-for': string_array(),
|
|
35
42
|
'block-all-mixed-content': boolean(false),
|
|
@@ -486,4 +493,13 @@ function assert_string(input, keypath) {
|
|
|
486
493
|
}
|
|
487
494
|
}
|
|
488
495
|
|
|
496
|
+
/** @param {string} keypath */
|
|
497
|
+
function assert_trusted_types_supported(keypath) {
|
|
498
|
+
if (!supportsTrustedTypes()) {
|
|
499
|
+
throw new Error(
|
|
500
|
+
`${keypath} is not supported by your version of Svelte. Please upgrade to Svelte 5.51.0 or later to use this directive.`
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
489
505
|
export default options;
|
|
@@ -3,6 +3,7 @@ import { dirname, join } from 'node:path';
|
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
4
|
import { installPolyfills } from '../../exports/node/polyfills.js';
|
|
5
5
|
import { mkdirp, posixify, walk } from '../../utils/filesystem.js';
|
|
6
|
+
import { noop } from '../../utils/functions.js';
|
|
6
7
|
import { decode_uri, is_root_relative, resolve } from '../../utils/url.js';
|
|
7
8
|
import { escape_html } from '../../utils/escape.js';
|
|
8
9
|
import { logger } from '../utils.js';
|
|
@@ -69,7 +70,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) {
|
|
|
69
70
|
log.error(format(details));
|
|
70
71
|
};
|
|
71
72
|
case 'ignore':
|
|
72
|
-
return
|
|
73
|
+
return noop;
|
|
73
74
|
default:
|
|
74
75
|
// @ts-expect-error TS thinks T might be of a different kind, but it's not
|
|
75
76
|
return (details) => input({ ...details, message: format(details) });
|
package/src/core/sync/utils.js
CHANGED
|
@@ -6,6 +6,8 @@ import { import_peer } from '../../utils/import.js';
|
|
|
6
6
|
/** @type {{ VERSION: string }} */
|
|
7
7
|
const { VERSION } = await import_peer('svelte/compiler');
|
|
8
8
|
|
|
9
|
+
const [MAJOR, MINOR] = VERSION.split('.').map(Number);
|
|
10
|
+
|
|
9
11
|
/** @type {Map<string, string>} */
|
|
10
12
|
const previous_contents = new Map();
|
|
11
13
|
|
|
@@ -74,5 +76,10 @@ export function dedent(strings, ...values) {
|
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
export function isSvelte5Plus() {
|
|
77
|
-
return
|
|
79
|
+
return MAJOR >= 5;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// TODO 3.0 remove this once we can bump the peerDep range
|
|
83
|
+
export function supportsTrustedTypes() {
|
|
84
|
+
return (MAJOR === 5 && MINOR >= 51) || MAJOR > 5;
|
|
78
85
|
}
|
package/src/core/utils.js
CHANGED
|
@@ -4,6 +4,7 @@ import process from 'node:process';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import colors from 'kleur';
|
|
6
6
|
import { posixify, to_fs } from '../utils/filesystem.js';
|
|
7
|
+
import { noop } from '../utils/functions.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Resolved path of the `runtime` directory
|
|
@@ -24,8 +25,6 @@ export const runtime_base = runtime_directory.startsWith(process.cwd())
|
|
|
24
25
|
? `/${path.relative('.', runtime_directory)}`
|
|
25
26
|
: to_fs(runtime_directory);
|
|
26
27
|
|
|
27
|
-
function noop() {}
|
|
28
|
-
|
|
29
28
|
/** @param {{ verbose: boolean }} opts */
|
|
30
29
|
export function logger({ verbose }) {
|
|
31
30
|
/** @type {import('types').Logger} */
|
package/src/exports/index.js
CHANGED
|
@@ -106,7 +106,7 @@ export function isHttpError(e, status) {
|
|
|
106
106
|
* @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number)} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages). Must be in the range 300-308.
|
|
107
107
|
* @param {string | URL} location The location to redirect to.
|
|
108
108
|
* @throws {Redirect} This error instructs SvelteKit to redirect to the specified location.
|
|
109
|
-
* @throws {Error} If the provided status is invalid.
|
|
109
|
+
* @throws {Error} If the provided status is invalid or the location cannot be used as a header value.
|
|
110
110
|
* @return {never}
|
|
111
111
|
*/
|
|
112
112
|
export function redirect(status, location) {
|
|
@@ -27,6 +27,15 @@ export class Redirect {
|
|
|
27
27
|
* @param {string} location
|
|
28
28
|
*/
|
|
29
29
|
constructor(status, location) {
|
|
30
|
+
try {
|
|
31
|
+
new Headers({ location });
|
|
32
|
+
} catch {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Invalid redirect location ${JSON.stringify(location)}: ` +
|
|
35
|
+
'this string contains characters that cannot be used in HTTP headers'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
this.status = status;
|
|
31
40
|
this.location = location;
|
|
32
41
|
}
|
|
@@ -2,6 +2,7 @@ import { createReadStream } from 'node:fs';
|
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
3
|
import * as set_cookie_parser from 'set-cookie-parser';
|
|
4
4
|
import { SvelteKitError } from '../internal/index.js';
|
|
5
|
+
import { noop } from '../../utils/functions.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @param {import('http').IncomingMessage} req
|
|
@@ -15,10 +16,11 @@ function get_raw_body(req, body_size_limit) {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const content_length = Number(h['content-length']);
|
|
19
|
+
const has_content_length = Number.isFinite(content_length);
|
|
18
20
|
|
|
19
21
|
// check if no request body
|
|
20
22
|
if (
|
|
21
|
-
(req.httpVersionMajor === 1 &&
|
|
23
|
+
(req.httpVersionMajor === 1 && !has_content_length && h['transfer-encoding'] == null) ||
|
|
22
24
|
content_length === 0
|
|
23
25
|
) {
|
|
24
26
|
return null;
|
|
@@ -35,7 +37,7 @@ function get_raw_body(req, body_size_limit) {
|
|
|
35
37
|
|
|
36
38
|
return new ReadableStream({
|
|
37
39
|
start(controller) {
|
|
38
|
-
if (body_size_limit !== undefined && content_length > body_size_limit) {
|
|
40
|
+
if (body_size_limit !== undefined && has_content_length && content_length > body_size_limit) {
|
|
39
41
|
let message = `Content-length of ${content_length} exceeds limit of ${body_size_limit} bytes.`;
|
|
40
42
|
|
|
41
43
|
if (body_size_limit === 0) {
|
|
@@ -64,11 +66,22 @@ function get_raw_body(req, body_size_limit) {
|
|
|
64
66
|
if (cancelled) return;
|
|
65
67
|
|
|
66
68
|
size += chunk.length;
|
|
67
|
-
|
|
69
|
+
|
|
70
|
+
if (body_size_limit !== undefined && size > body_size_limit) {
|
|
71
|
+
cancelled = true;
|
|
72
|
+
|
|
73
|
+
const message = `request body size exceeded BODY_SIZE_LIMIT of ${body_size_limit}`;
|
|
74
|
+
|
|
75
|
+
const error = new SvelteKitError(413, 'Payload Too Large', message);
|
|
76
|
+
controller.error(error);
|
|
77
|
+
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (has_content_length && size > content_length) {
|
|
68
82
|
cancelled = true;
|
|
69
83
|
|
|
70
|
-
const
|
|
71
|
-
const message = `request body size exceeded ${constraint} of ${content_length}`;
|
|
84
|
+
const message = `request body size exceeded content-length of ${content_length}`;
|
|
72
85
|
|
|
73
86
|
const error = new SvelteKitError(413, 'Payload Too Large', message);
|
|
74
87
|
controller.error(error);
|
|
@@ -200,7 +213,7 @@ export async function setResponse(res, response) {
|
|
|
200
213
|
|
|
201
214
|
// If the reader has already been interrupted with an error earlier,
|
|
202
215
|
// then it will appear here, it is useless, but it needs to be catch.
|
|
203
|
-
reader.cancel(error).catch(
|
|
216
|
+
reader.cancel(error).catch(noop);
|
|
204
217
|
if (error) res.destroy(error);
|
|
205
218
|
};
|
|
206
219
|
|
package/src/exports/public.d.ts
CHANGED
|
@@ -1893,28 +1893,35 @@ type InputElementProps<T extends keyof InputTypeMap> = T extends 'checkbox' | 'r
|
|
|
1893
1893
|
get files(): FileList | null;
|
|
1894
1894
|
set files(v: FileList | null);
|
|
1895
1895
|
}
|
|
1896
|
-
: T extends 'select'
|
|
1896
|
+
: T extends 'select'
|
|
1897
1897
|
? {
|
|
1898
1898
|
name: string;
|
|
1899
|
-
multiple: T extends 'select' ? false : true;
|
|
1900
1899
|
'aria-invalid': boolean | 'false' | 'true' | undefined;
|
|
1901
|
-
get value(): string
|
|
1902
|
-
set value(v: string
|
|
1900
|
+
get value(): string;
|
|
1901
|
+
set value(v: string);
|
|
1903
1902
|
}
|
|
1904
|
-
: T extends '
|
|
1903
|
+
: T extends 'select multiple'
|
|
1905
1904
|
? {
|
|
1906
1905
|
name: string;
|
|
1906
|
+
multiple: true;
|
|
1907
1907
|
'aria-invalid': boolean | 'false' | 'true' | undefined;
|
|
1908
|
-
get value(): string
|
|
1909
|
-
set value(v: string
|
|
1908
|
+
get value(): string[];
|
|
1909
|
+
set value(v: string[]);
|
|
1910
1910
|
}
|
|
1911
|
-
:
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1911
|
+
: T extends 'text'
|
|
1912
|
+
? {
|
|
1913
|
+
name: string;
|
|
1914
|
+
'aria-invalid': boolean | 'false' | 'true' | undefined;
|
|
1915
|
+
get value(): string | number;
|
|
1916
|
+
set value(v: string | number);
|
|
1917
|
+
}
|
|
1918
|
+
: {
|
|
1919
|
+
name: string;
|
|
1920
|
+
type: T;
|
|
1921
|
+
'aria-invalid': boolean | 'false' | 'true' | undefined;
|
|
1922
|
+
get value(): string | number;
|
|
1923
|
+
set value(v: string | number);
|
|
1924
|
+
};
|
|
1918
1925
|
|
|
1919
1926
|
type RemoteFormFieldMethods<T> = {
|
|
1920
1927
|
/** The values that will be submitted */
|
|
@@ -1925,6 +1932,15 @@ type RemoteFormFieldMethods<T> = {
|
|
|
1925
1932
|
issues(): RemoteFormIssue[] | undefined;
|
|
1926
1933
|
};
|
|
1927
1934
|
|
|
1935
|
+
// These two types use "T extends unknown ? .. : .." to distribute over unions.
|
|
1936
|
+
// Example: if "type T = A | b" then "keyof T" only contains keys that both A and B have, with "KeysOfUnion<T>" we get the keys of both A and B
|
|
1937
|
+
type KeysOfUnion<T> = T extends unknown ? keyof T : never;
|
|
1938
|
+
type ValueOfUnionKey<T, K extends PropertyKey> = T extends unknown
|
|
1939
|
+
? K extends keyof T
|
|
1940
|
+
? T[K]
|
|
1941
|
+
: never
|
|
1942
|
+
: never;
|
|
1943
|
+
|
|
1928
1944
|
export type RemoteFormFieldValue = string | string[] | number | boolean | File | File[];
|
|
1929
1945
|
|
|
1930
1946
|
type AsArgs<Type extends keyof InputTypeMap, Value> = Type extends 'checkbox'
|
|
@@ -1997,14 +2013,15 @@ export type RemoteFormFields<T> =
|
|
|
1997
2013
|
? RecursiveFormFields
|
|
1998
2014
|
: NonNullable<T> extends string | number | boolean | File
|
|
1999
2015
|
? RemoteFormField<NonNullable<T>>
|
|
2000
|
-
:
|
|
2016
|
+
: // [T] is used to prevent distributing over union, only the last condition should distribute over unions
|
|
2017
|
+
[T] extends [string[] | File[]]
|
|
2001
2018
|
? RemoteFormField<T> & { [K in number]: RemoteFormField<T[number]> }
|
|
2002
|
-
: T extends Array<infer U>
|
|
2019
|
+
: [T] extends [Array<infer U>]
|
|
2003
2020
|
? RemoteFormFieldContainer<T> & {
|
|
2004
2021
|
[K in number]: RemoteFormFields<U>;
|
|
2005
2022
|
}
|
|
2006
2023
|
: RemoteFormFieldContainer<T> & {
|
|
2007
|
-
[K in
|
|
2024
|
+
[K in KeysOfUnion<T>]-?: RemoteFormFields<ValueOfUnionKey<T, K>>;
|
|
2008
2025
|
};
|
|
2009
2026
|
|
|
2010
2027
|
// By breaking this out into its own type, we avoid the TS recursion depth limit
|
|
@@ -2075,10 +2092,10 @@ export type RemoteForm<Input extends RemoteFormInput | void, Output> = {
|
|
|
2075
2092
|
callback: (opts: {
|
|
2076
2093
|
form: HTMLFormElement;
|
|
2077
2094
|
data: Input;
|
|
2078
|
-
submit: () => Promise<
|
|
2079
|
-
updates: (...updates: RemoteQueryUpdate[]) => Promise<
|
|
2095
|
+
submit: () => Promise<boolean> & {
|
|
2096
|
+
updates: (...updates: RemoteQueryUpdate[]) => Promise<boolean>;
|
|
2080
2097
|
};
|
|
2081
|
-
}) => void
|
|
2098
|
+
}) => void
|
|
2082
2099
|
): {
|
|
2083
2100
|
method: 'POST';
|
|
2084
2101
|
action: string;
|
|
@@ -110,7 +110,7 @@ export async function build_service_worker(
|
|
|
110
110
|
// .mjs so that esbuild doesn't incorrectly inject `export` https://github.com/vitejs/vite/issues/15379
|
|
111
111
|
entryFileNames: `service-worker.${is_rolldown ? 'js' : 'mjs'}`,
|
|
112
112
|
assetFileNames: `${kit.appDir}/immutable/assets/[name].[hash][extname]`,
|
|
113
|
-
inlineDynamicImports: !is_rolldown
|
|
113
|
+
inlineDynamicImports: !is_rolldown ? true : undefined
|
|
114
114
|
}
|
|
115
115
|
},
|
|
116
116
|
outDir: `${out}/client`,
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** @import { ServerMetadata } from 'types' */
|
|
2
|
+
/** @import { Rollup } from 'vite' */
|
|
3
|
+
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { Parser } from 'acorn';
|
|
7
|
+
import MagicString from 'magic-string';
|
|
8
|
+
import { posixify } from '../../../utils/filesystem.js';
|
|
9
|
+
import { import_peer } from '../../../utils/import.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} out
|
|
13
|
+
* @param {Array<{ hash: string, file: string }>} remotes
|
|
14
|
+
* @param {ServerMetadata} metadata
|
|
15
|
+
* @param {string} cwd
|
|
16
|
+
* @param {Rollup.OutputBundle} server_bundle
|
|
17
|
+
* @param {NonNullable<import('vitest/config').ViteUserConfig['build']>['sourcemap']} sourcemap
|
|
18
|
+
*/
|
|
19
|
+
export async function treeshake_prerendered_remotes(
|
|
20
|
+
out,
|
|
21
|
+
remotes,
|
|
22
|
+
metadata,
|
|
23
|
+
cwd,
|
|
24
|
+
server_bundle,
|
|
25
|
+
sourcemap
|
|
26
|
+
) {
|
|
27
|
+
if (remotes.length === 0) return;
|
|
28
|
+
|
|
29
|
+
const vite = /** @type {typeof import('vite')} */ (await import_peer('vite'));
|
|
30
|
+
|
|
31
|
+
for (const remote of remotes) {
|
|
32
|
+
const exports_map = metadata.remotes.get(remote.hash);
|
|
33
|
+
if (!exports_map) continue;
|
|
34
|
+
|
|
35
|
+
/** @type {string[]} */
|
|
36
|
+
const dynamic = [];
|
|
37
|
+
/** @type {string[]} */
|
|
38
|
+
const prerendered = [];
|
|
39
|
+
|
|
40
|
+
for (const [name, value] of exports_map) {
|
|
41
|
+
(value.dynamic ? dynamic : prerendered).push(name);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (prerendered.length === 0) continue; // nothing to treeshake
|
|
45
|
+
|
|
46
|
+
// remove file extension
|
|
47
|
+
const remote_filename = path.basename(remote.file).split('.').slice(0, -1).join('.');
|
|
48
|
+
|
|
49
|
+
const remote_chunk = Object.values(server_bundle).find((chunk) => {
|
|
50
|
+
return chunk.name === remote_filename;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (!remote_chunk) continue;
|
|
54
|
+
|
|
55
|
+
const chunk_path = posixify(path.relative(cwd, `${out}/server/${remote_chunk.fileName}`));
|
|
56
|
+
|
|
57
|
+
const code = fs.readFileSync(chunk_path, 'utf-8');
|
|
58
|
+
const parsed = Parser.parse(code, { sourceType: 'module', ecmaVersion: 'latest' });
|
|
59
|
+
const modified_code = new MagicString(code);
|
|
60
|
+
|
|
61
|
+
for (const fn of prerendered) {
|
|
62
|
+
for (const node of parsed.body) {
|
|
63
|
+
const declaration =
|
|
64
|
+
node.type === 'ExportNamedDeclaration'
|
|
65
|
+
? node.declaration
|
|
66
|
+
: node.type === 'VariableDeclaration'
|
|
67
|
+
? node
|
|
68
|
+
: null;
|
|
69
|
+
|
|
70
|
+
if (!declaration || declaration.type !== 'VariableDeclaration') continue;
|
|
71
|
+
|
|
72
|
+
for (const declarator of declaration.declarations) {
|
|
73
|
+
if (declarator.id.type === 'Identifier' && declarator.id.name === fn) {
|
|
74
|
+
modified_code.overwrite(
|
|
75
|
+
node.start,
|
|
76
|
+
node.end,
|
|
77
|
+
`const ${fn} = prerender('unchecked', () => { throw new Error('Unexpectedly called prerender function. Did you forget to set { dynamic: true } ?') });`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const node of parsed.body) {
|
|
85
|
+
if (node.type === 'ExportDefaultDeclaration') {
|
|
86
|
+
modified_code.remove(node.start, node.end);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const stubbed = modified_code.toString();
|
|
91
|
+
fs.writeFileSync(chunk_path, stubbed);
|
|
92
|
+
|
|
93
|
+
const bundle = /** @type {import('vite').Rollup.RollupOutput} */ (
|
|
94
|
+
await vite.build({
|
|
95
|
+
configFile: false,
|
|
96
|
+
build: {
|
|
97
|
+
write: false,
|
|
98
|
+
ssr: true,
|
|
99
|
+
target: 'esnext',
|
|
100
|
+
sourcemap,
|
|
101
|
+
rollupOptions: {
|
|
102
|
+
// avoid resolving imports
|
|
103
|
+
external: (id) => !id.endsWith(chunk_path),
|
|
104
|
+
input: {
|
|
105
|
+
treeshaken: chunk_path
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const chunk = bundle.output.find(
|
|
113
|
+
(output) => output.type === 'chunk' && output.name === 'treeshaken'
|
|
114
|
+
);
|
|
115
|
+
if (chunk && chunk.type === 'chunk') {
|
|
116
|
+
fs.writeFileSync(chunk_path, chunk.code);
|
|
117
|
+
|
|
118
|
+
const chunk_sourcemap = bundle.output.find(
|
|
119
|
+
(output) => output.type === 'asset' && output.fileName === chunk.fileName + '.map'
|
|
120
|
+
);
|
|
121
|
+
if (chunk_sourcemap && chunk_sourcemap.type === 'asset') {
|
|
122
|
+
fs.writeFileSync(chunk_path + '.map', chunk_sourcemap.source);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** @import { RequestEvent } from '@sveltejs/kit' */
|
|
2
|
+
/** @import { PrerenderOption, UniversalNode } from 'types' */
|
|
1
3
|
import fs from 'node:fs';
|
|
2
4
|
import path from 'node:path';
|
|
3
5
|
import process from 'node:process';
|
|
@@ -15,7 +17,7 @@ import { SVELTE_KIT_ASSETS } from '../../../constants.js';
|
|
|
15
17
|
import * as sync from '../../../core/sync/sync.js';
|
|
16
18
|
import { get_mime_lookup, runtime_base } from '../../../core/utils.js';
|
|
17
19
|
import { compact } from '../../../utils/array.js';
|
|
18
|
-
import { not_found } from '../utils.js';
|
|
20
|
+
import { is_chrome_devtools_request, not_found } from '../utils.js';
|
|
19
21
|
import { SCHEME } from '../../../utils/url.js';
|
|
20
22
|
import { check_feature } from '../../../utils/features.js';
|
|
21
23
|
import { escape_html } from '../../../utils/escape.js';
|
|
@@ -34,13 +36,19 @@ const vite_css_query_regex = /(?:\?|&)(?:raw|url|inline)(?:&|$)/;
|
|
|
34
36
|
export async function dev(vite, vite_config, svelte_config, get_remotes) {
|
|
35
37
|
installPolyfills();
|
|
36
38
|
|
|
39
|
+
/** @type {AsyncLocalStorage<{ event: RequestEvent, config: any, prerender: PrerenderOption }>} */
|
|
37
40
|
const async_local_storage = new AsyncLocalStorage();
|
|
38
41
|
|
|
39
42
|
globalThis.__SVELTEKIT_TRACK__ = (label) => {
|
|
40
43
|
const context = async_local_storage.getStore();
|
|
41
44
|
if (!context || context.prerender === true) return;
|
|
42
45
|
|
|
43
|
-
check_feature(
|
|
46
|
+
check_feature(
|
|
47
|
+
/** @type {string} */ (context.event.route.id),
|
|
48
|
+
context.config,
|
|
49
|
+
label,
|
|
50
|
+
svelte_config.kit.adapter
|
|
51
|
+
);
|
|
44
52
|
};
|
|
45
53
|
|
|
46
54
|
const fetch = globalThis.fetch;
|
|
@@ -68,7 +76,8 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
|
|
|
68
76
|
async function loud_ssr_load_module(url) {
|
|
69
77
|
try {
|
|
70
78
|
return await vite.ssrLoadModule(url, { fixStacktrace: true });
|
|
71
|
-
} catch (/** @type {
|
|
79
|
+
} catch (/** @type {unknown} */ e) {
|
|
80
|
+
const err = /** @type {import('rollup').RollupError} */ (e);
|
|
72
81
|
const msg = buildErrorMessage(err, [colors.red(`Internal server error: ${err.message}`)]);
|
|
73
82
|
|
|
74
83
|
if (!vite.config.logger.hasErrorLogged(err)) {
|
|
@@ -77,13 +86,13 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
|
|
|
77
86
|
|
|
78
87
|
vite.ws.send({
|
|
79
88
|
type: 'error',
|
|
80
|
-
err: {
|
|
89
|
+
err: /** @type {import('vite').ErrorPayload['err']} */ ({
|
|
81
90
|
...err,
|
|
82
91
|
// these properties are non-enumerable and will
|
|
83
92
|
// not be serialized unless we explicitly include them
|
|
84
93
|
message: err.message,
|
|
85
|
-
stack: err.stack
|
|
86
|
-
}
|
|
94
|
+
stack: err.stack ?? ''
|
|
95
|
+
})
|
|
87
96
|
});
|
|
88
97
|
|
|
89
98
|
throw err;
|
|
@@ -204,7 +213,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
|
|
|
204
213
|
|
|
205
214
|
if (node.universal) {
|
|
206
215
|
if (node.page_options?.ssr === false) {
|
|
207
|
-
result.universal = node.page_options;
|
|
216
|
+
result.universal = /** @type {UniversalNode} */ (node.page_options);
|
|
208
217
|
} else {
|
|
209
218
|
// TODO: explain why the file was loaded on the server if we fail to load it
|
|
210
219
|
const { module, module_node } = await resolve(node.universal);
|
|
@@ -437,7 +446,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
|
|
|
437
446
|
return () => {
|
|
438
447
|
const serve_static_middleware = vite.middlewares.stack.find(
|
|
439
448
|
(middleware) =>
|
|
440
|
-
/** @type {
|
|
449
|
+
/** @type {Function} */ (middleware.handle).name === 'viteServeStaticMiddleware'
|
|
441
450
|
);
|
|
442
451
|
|
|
443
452
|
// Vite will give a 403 on URLs like /test, /static, and /package.json preventing us from
|
|
@@ -467,6 +476,10 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
|
|
|
467
476
|
return;
|
|
468
477
|
}
|
|
469
478
|
|
|
479
|
+
if (is_chrome_devtools_request(decoded, res)) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
470
483
|
if (!decoded.startsWith(svelte_config.kit.paths.base)) {
|
|
471
484
|
return not_found(req, res, svelte_config.kit.paths.base);
|
|
472
485
|
}
|