@sveltejs/kit 2.16.0 → 2.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 -3
- package/src/core/adapt/builder.js +2 -0
- package/src/core/config/index.js +16 -1
- package/src/core/config/options.js +2 -1
- package/src/core/generate_manifest/index.js +14 -5
- package/src/core/postbuild/analyse.js +2 -2
- package/src/core/sync/write_client_manifest.js +33 -17
- package/src/core/sync/write_server.js +2 -1
- package/src/exports/public.d.ts +29 -6
- package/src/exports/vite/build/build_server.js +5 -0
- package/src/exports/vite/dev/index.js +29 -1
- package/src/exports/vite/index.js +51 -5
- package/src/runtime/app/state/index.js +11 -0
- package/src/runtime/client/client.js +80 -46
- package/src/runtime/client/parse.js +20 -0
- package/src/runtime/client/types.d.ts +16 -1
- package/src/runtime/client/utils.js +6 -0
- package/src/runtime/pathname.js +54 -0
- package/src/runtime/server/cookie.js +2 -1
- package/src/runtime/server/endpoint.js +0 -5
- package/src/runtime/server/fetch.js +12 -0
- package/src/runtime/server/page/index.js +1 -1
- package/src/runtime/server/page/render.js +25 -3
- package/src/runtime/server/page/server_routing.js +110 -0
- package/src/runtime/server/respond.js +45 -26
- package/src/runtime/server/validate-headers.js +64 -0
- package/src/runtime/utils.js +21 -0
- package/src/types/ambient-private.d.ts +1 -0
- package/src/types/global-private.d.ts +2 -0
- package/src/types/internal.d.ts +47 -9
- package/src/utils/filesystem.js +4 -3
- package/src/utils/routing.js +8 -0
- package/src/utils/url.js +0 -23
- package/src/version.js +1 -1
- package/types/index.d.ts +72 -13
- package/types/index.d.ts.map +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.0",
|
|
4
4
|
"description": "SvelteKit is the fastest way to build Svelte apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"svelte": "^5.2.9",
|
|
42
42
|
"svelte-preprocess": "^6.0.0",
|
|
43
43
|
"typescript": "^5.3.3",
|
|
44
|
-
"vite": "^6.0.
|
|
45
|
-
"vitest": "^
|
|
44
|
+
"vite": "^6.0.11",
|
|
45
|
+
"vitest": "^3.0.1"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0",
|
|
@@ -96,6 +96,8 @@
|
|
|
96
96
|
"test:integration": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test",
|
|
97
97
|
"test:cross-platform:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:dev",
|
|
98
98
|
"test:cross-platform:build": "pnpm test:unit && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:build",
|
|
99
|
+
"test:server-side-route-resolution:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:server-side-route-resolution:dev",
|
|
100
|
+
"test:server-side-route-resolution:build": "pnpm test:unit && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:server-side-route-resolution:build",
|
|
99
101
|
"test:unit": "vitest --config kit.vitest.config.js run",
|
|
100
102
|
"generate:version": "node scripts/generate-version.js",
|
|
101
103
|
"generate:types": "node scripts/generate-dts.js"
|
|
@@ -138,6 +138,7 @@ export function create_builder({
|
|
|
138
138
|
generateManifest: ({ relativePath }) =>
|
|
139
139
|
generate_manifest({
|
|
140
140
|
build_data,
|
|
141
|
+
prerendered: [],
|
|
141
142
|
relative_path: relativePath,
|
|
142
143
|
routes: Array.from(filtered)
|
|
143
144
|
})
|
|
@@ -185,6 +186,7 @@ export function create_builder({
|
|
|
185
186
|
generateManifest({ relativePath, routes: subset }) {
|
|
186
187
|
return generate_manifest({
|
|
187
188
|
build_data,
|
|
189
|
+
prerendered: prerendered.paths,
|
|
188
190
|
relative_path: relativePath,
|
|
189
191
|
routes: subset
|
|
190
192
|
? subset.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route)))
|
package/src/core/config/index.js
CHANGED
|
@@ -115,5 +115,20 @@ export function validate_config(config) {
|
|
|
115
115
|
);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
const validated = options(config, 'config');
|
|
119
|
+
|
|
120
|
+
if (validated.kit.router.resolution === 'server') {
|
|
121
|
+
if (validated.kit.router.type === 'hash') {
|
|
122
|
+
throw new Error(
|
|
123
|
+
"The `router.resolution` option cannot be 'server' if `router.type` is 'hash'"
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (validated.kit.output.bundleStrategy !== 'split') {
|
|
127
|
+
throw new Error(
|
|
128
|
+
"The `router.resolution` option cannot be 'server' if `output.bundleStrategy` is 'inline' or 'single'"
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return validated;
|
|
119
134
|
}
|
|
@@ -8,27 +8,31 @@ import { compact } from '../../utils/array.js';
|
|
|
8
8
|
import { join_relative } from '../../utils/filesystem.js';
|
|
9
9
|
import { dedent } from '../sync/utils.js';
|
|
10
10
|
import { find_server_assets } from './find_server_assets.js';
|
|
11
|
+
import { uneval } from 'devalue';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Generates the data used to write the server-side manifest.js file. This data is used in the Vite
|
|
14
15
|
* build process, to power routing, etc.
|
|
15
16
|
* @param {{
|
|
16
17
|
* build_data: import('types').BuildData;
|
|
18
|
+
* prerendered: string[];
|
|
17
19
|
* relative_path: string;
|
|
18
20
|
* routes: import('types').RouteData[];
|
|
19
21
|
* }} opts
|
|
20
22
|
*/
|
|
21
|
-
export function generate_manifest({ build_data, relative_path, routes }) {
|
|
23
|
+
export function generate_manifest({ build_data, prerendered, relative_path, routes }) {
|
|
22
24
|
/**
|
|
23
25
|
* @type {Map<any, number>} The new index of each node in the filtered nodes array
|
|
24
26
|
*/
|
|
25
27
|
const reindexed = new Map();
|
|
26
28
|
/**
|
|
27
29
|
* All nodes actually used in the routes definition (prerendered routes are omitted).
|
|
28
|
-
*
|
|
30
|
+
* If `routes` is empty, it means that this manifest is only used for server-side resolution
|
|
31
|
+
* and the root layout/error is therefore not needed.
|
|
32
|
+
* Else, root layout/error is always included as they are needed for 404 and root errors.
|
|
29
33
|
* @type {Set<any>}
|
|
30
34
|
*/
|
|
31
|
-
const used_nodes = new Set([0, 1]);
|
|
35
|
+
const used_nodes = new Set(routes.length > 0 ? [0, 1] : []);
|
|
32
36
|
|
|
33
37
|
const server_assets = find_server_assets(build_data, routes);
|
|
34
38
|
|
|
@@ -57,7 +61,11 @@ export function generate_manifest({ build_data, relative_path, routes }) {
|
|
|
57
61
|
assets.push(build_data.service_worker);
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
// In case of server side route resolution, we need to include all matchers. Prerendered routes are not part
|
|
65
|
+
// of the server manifest, and they could reference matchers that then would not be included.
|
|
66
|
+
const matchers = new Set(
|
|
67
|
+
build_data.client?.nodes ? Object.keys(build_data.manifest_data.matchers) : undefined
|
|
68
|
+
);
|
|
61
69
|
|
|
62
70
|
/** @param {Array<number | undefined>} indexes */
|
|
63
71
|
function get_nodes(indexes) {
|
|
@@ -90,7 +98,7 @@ export function generate_manifest({ build_data, relative_path, routes }) {
|
|
|
90
98
|
assets: new Set(${s(assets)}),
|
|
91
99
|
mimeTypes: ${s(mime_types)},
|
|
92
100
|
_: {
|
|
93
|
-
client: ${
|
|
101
|
+
client: ${uneval(build_data.client)},
|
|
94
102
|
nodes: [
|
|
95
103
|
${(node_paths).map(loader).join(',\n')}
|
|
96
104
|
],
|
|
@@ -113,6 +121,7 @@ export function generate_manifest({ build_data, relative_path, routes }) {
|
|
|
113
121
|
`;
|
|
114
122
|
}).filter(Boolean).join(',\n')}
|
|
115
123
|
],
|
|
124
|
+
prerendered_routes: new Set(${s(prerendered)}),
|
|
116
125
|
matchers: async () => {
|
|
117
126
|
${Array.from(
|
|
118
127
|
matchers,
|
|
@@ -13,7 +13,7 @@ import { forked } from '../../utils/fork.js';
|
|
|
13
13
|
import { installPolyfills } from '../../exports/node/polyfills.js';
|
|
14
14
|
import { ENDPOINT_METHODS } from '../../constants.js';
|
|
15
15
|
import { filter_private_env, filter_public_env } from '../../utils/env.js';
|
|
16
|
-
import { resolve_route } from '../../utils/routing.js';
|
|
16
|
+
import { has_server_load, resolve_route } from '../../utils/routing.js';
|
|
17
17
|
import { get_page_config } from '../../utils/route_config.js';
|
|
18
18
|
import { check_feature } from '../../utils/features.js';
|
|
19
19
|
import { createReadableStream } from '@sveltejs/kit/node';
|
|
@@ -88,7 +88,7 @@ async function analyse({
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
metadata.nodes[node.index] = {
|
|
91
|
-
has_server_load: node
|
|
91
|
+
has_server_load: has_server_load(node)
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -10,9 +10,11 @@ import colors from 'kleur';
|
|
|
10
10
|
* @param {import('types').ValidatedKitConfig} kit
|
|
11
11
|
* @param {import('types').ManifestData} manifest_data
|
|
12
12
|
* @param {string} output
|
|
13
|
-
* @param {
|
|
13
|
+
* @param {import('types').ServerMetadata['nodes']} [metadata] If this is omitted, we have to assume that all routes with a `+layout/page.server.js` file have a server load function
|
|
14
14
|
*/
|
|
15
15
|
export function write_client_manifest(kit, manifest_data, output, metadata) {
|
|
16
|
+
const client_routing = kit.router.resolution === 'client';
|
|
17
|
+
|
|
16
18
|
/**
|
|
17
19
|
* Creates a module that exports a `CSRPageNode`
|
|
18
20
|
* @param {import('types').PageNode} node
|
|
@@ -47,11 +49,14 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
|
|
|
47
49
|
write_if_changed(`${output}/nodes/${i}.js`, generate_node(node));
|
|
48
50
|
return `() => import('./nodes/${i}')`;
|
|
49
51
|
})
|
|
52
|
+
// If route resolution happens on the server, we only need the root layout and root error page
|
|
53
|
+
// upfront, the rest is loaded on demand as the user navigates the app
|
|
54
|
+
.slice(0, client_routing ? manifest_data.nodes.length : 2)
|
|
50
55
|
.join(',\n');
|
|
51
56
|
|
|
52
57
|
const layouts_with_server_load = new Set();
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
let dictionary = dedent`
|
|
55
60
|
{
|
|
56
61
|
${manifest_data.routes
|
|
57
62
|
.map((route) => {
|
|
@@ -108,6 +113,13 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
|
|
|
108
113
|
}
|
|
109
114
|
`;
|
|
110
115
|
|
|
116
|
+
if (!client_routing) {
|
|
117
|
+
dictionary = '{}';
|
|
118
|
+
const root_layout = layouts_with_server_load.has(0);
|
|
119
|
+
layouts_with_server_load.clear();
|
|
120
|
+
if (root_layout) layouts_with_server_load.add(0);
|
|
121
|
+
}
|
|
122
|
+
|
|
111
123
|
const client_hooks_file = resolve_entry(kit.files.hooks.client);
|
|
112
124
|
const universal_hooks_file = resolve_entry(kit.files.hooks.universal);
|
|
113
125
|
|
|
@@ -123,6 +135,8 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
|
|
|
123
135
|
);
|
|
124
136
|
}
|
|
125
137
|
|
|
138
|
+
// Stringified version of
|
|
139
|
+
/** @type {import('../../runtime/client/types.js').SvelteKitApp} */
|
|
126
140
|
write_if_changed(
|
|
127
141
|
`${output}/app.js`,
|
|
128
142
|
dedent`
|
|
@@ -137,7 +151,7 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
|
|
|
137
151
|
: ''
|
|
138
152
|
}
|
|
139
153
|
|
|
140
|
-
export { matchers } from './matchers.js';
|
|
154
|
+
${client_routing ? "export { matchers } from './matchers.js';" : 'export const matchers = {};'}
|
|
141
155
|
|
|
142
156
|
export const nodes = [
|
|
143
157
|
${nodes}
|
|
@@ -158,7 +172,7 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
|
|
|
158
172
|
|
|
159
173
|
export const decoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.decode]));
|
|
160
174
|
|
|
161
|
-
export const hash = ${
|
|
175
|
+
export const hash = ${s(kit.router.type === 'hash')};
|
|
162
176
|
|
|
163
177
|
export const decode = (type, value) => decoders[type](value);
|
|
164
178
|
|
|
@@ -166,21 +180,23 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
|
|
|
166
180
|
`
|
|
167
181
|
);
|
|
168
182
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
183
|
+
if (client_routing) {
|
|
184
|
+
// write matchers to a separate module so that we don't
|
|
185
|
+
// need to worry about name conflicts
|
|
186
|
+
const imports = [];
|
|
187
|
+
const matchers = [];
|
|
173
188
|
|
|
174
|
-
|
|
175
|
-
|
|
189
|
+
for (const key in manifest_data.matchers) {
|
|
190
|
+
const src = manifest_data.matchers[key];
|
|
176
191
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
192
|
+
imports.push(`import { match as ${key} } from ${s(relative_path(output, src))};`);
|
|
193
|
+
matchers.push(key);
|
|
194
|
+
}
|
|
180
195
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
196
|
+
const module = imports.length
|
|
197
|
+
? `${imports.join('\n')}\n\nexport const matchers = { ${matchers.join(', ')} };`
|
|
198
|
+
: 'export const matchers = {};';
|
|
184
199
|
|
|
185
|
-
|
|
200
|
+
write_if_changed(`${output}/matchers.js`, module);
|
|
201
|
+
}
|
|
186
202
|
}
|
|
@@ -35,7 +35,6 @@ import { set_manifest, set_read_implementation } from '__sveltekit/server';
|
|
|
35
35
|
import { set_private_env, set_public_env, set_safe_public_env } from '${runtime_directory}/shared-server.js';
|
|
36
36
|
|
|
37
37
|
export const options = {
|
|
38
|
-
app_dir: ${s(config.kit.appDir)},
|
|
39
38
|
app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
|
|
40
39
|
csp: ${s(config.kit.csp)},
|
|
41
40
|
csrf_check_origin: ${s(config.kit.csrf.checkOrigin)},
|
|
@@ -118,6 +117,8 @@ export function write_server(config, output) {
|
|
|
118
117
|
return posixify(path.relative(`${output}/server`, file));
|
|
119
118
|
}
|
|
120
119
|
|
|
120
|
+
// Contains the stringified version of
|
|
121
|
+
/** @type {import('types').SSROptions} */
|
|
121
122
|
write_if_changed(
|
|
122
123
|
`${output}/server/internal.js`,
|
|
123
124
|
server_template({
|
package/src/exports/public.d.ts
CHANGED
|
@@ -659,6 +659,28 @@ export interface KitConfig {
|
|
|
659
659
|
* @since 2.14.0
|
|
660
660
|
*/
|
|
661
661
|
type?: 'pathname' | 'hash';
|
|
662
|
+
/**
|
|
663
|
+
* How to determine which route to load when navigating to a new page.
|
|
664
|
+
*
|
|
665
|
+
* By default, SvelteKit will serve a route manifest to the browser.
|
|
666
|
+
* When navigating, this manifest is used (along with the `reroute` hook, if it exists) to determine which components to load and which `load` functions to run.
|
|
667
|
+
* Because everything happens on the client, this decision can be made immediately. The drawback is that the manifest needs to be
|
|
668
|
+
* loaded and parsed before the first navigation can happen, which may have an impact if your app contains many routes.
|
|
669
|
+
*
|
|
670
|
+
* Alternatively, SvelteKit can determine the route on the server. This means that for every navigation to a path that has not yet been visited, the server will be asked to determine the route.
|
|
671
|
+
* This has several advantages:
|
|
672
|
+
* - The client does not need to load the routing manifest upfront, which can lead to faster initial page loads
|
|
673
|
+
* - The list of routes is hidden from public view
|
|
674
|
+
* - The server has an opportunity to intercept each navigation (for example through a middleware), enabling (for example) A/B testing opaque to SvelteKit
|
|
675
|
+
|
|
676
|
+
* The drawback is that for unvisited paths, resolution will take slightly longer (though this is mitigated by [preloading](https://svelte.dev/docs/kit/link-options#data-sveltekit-preload-data)).
|
|
677
|
+
*
|
|
678
|
+
* > [!NOTE] When using server-side route resolution and prerendering, the resolution is prerendered along with the route itself.
|
|
679
|
+
*
|
|
680
|
+
* @default "client"
|
|
681
|
+
* @since 2.17.0
|
|
682
|
+
*/
|
|
683
|
+
resolution?: 'client' | 'server';
|
|
662
684
|
};
|
|
663
685
|
serviceWorker?: {
|
|
664
686
|
/**
|
|
@@ -1088,31 +1110,31 @@ export interface AfterNavigate extends Omit<Navigation, 'type'> {
|
|
|
1088
1110
|
}
|
|
1089
1111
|
|
|
1090
1112
|
/**
|
|
1091
|
-
* The shape of the `$page` store
|
|
1113
|
+
* The shape of the [`page`](https://svelte.dev/docs/kit/$app-state#page) reactive object and the [`$page`](https://svelte.dev/docs/kit/$app-stores) store.
|
|
1092
1114
|
*/
|
|
1093
1115
|
export interface Page<
|
|
1094
1116
|
Params extends Record<string, string> = Record<string, string>,
|
|
1095
1117
|
RouteId extends string | null = string | null
|
|
1096
1118
|
> {
|
|
1097
1119
|
/**
|
|
1098
|
-
* The URL of the current page
|
|
1120
|
+
* The URL of the current page.
|
|
1099
1121
|
*/
|
|
1100
1122
|
url: URL;
|
|
1101
1123
|
/**
|
|
1102
|
-
* The parameters of the current page - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object
|
|
1124
|
+
* The parameters of the current page - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
|
|
1103
1125
|
*/
|
|
1104
1126
|
params: Params;
|
|
1105
1127
|
/**
|
|
1106
|
-
* Info about the current route
|
|
1128
|
+
* Info about the current route.
|
|
1107
1129
|
*/
|
|
1108
1130
|
route: {
|
|
1109
1131
|
/**
|
|
1110
|
-
* The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]
|
|
1132
|
+
* The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`.
|
|
1111
1133
|
*/
|
|
1112
1134
|
id: RouteId;
|
|
1113
1135
|
};
|
|
1114
1136
|
/**
|
|
1115
|
-
*
|
|
1137
|
+
* HTTP status code of the current page.
|
|
1116
1138
|
*/
|
|
1117
1139
|
status: number;
|
|
1118
1140
|
/**
|
|
@@ -1297,6 +1319,7 @@ export interface SSRManifest {
|
|
|
1297
1319
|
client: NonNullable<BuildData['client']>;
|
|
1298
1320
|
nodes: SSRNodeLoader[];
|
|
1299
1321
|
routes: SSRRoute[];
|
|
1322
|
+
prerendered_routes: Set<string>;
|
|
1300
1323
|
matchers: () => Promise<Record<string, ParamMatcher>>;
|
|
1301
1324
|
/** A `[file]: size` map of all assets imported by server code */
|
|
1302
1325
|
server_assets: Record<string, number>;
|
|
@@ -47,7 +47,12 @@ export function build_server_nodes(out, kit, manifest_data, server_manifest, cli
|
|
|
47
47
|
css.filter(asset => client_stylesheets.has(asset.fileName))
|
|
48
48
|
.forEach((asset) => {
|
|
49
49
|
if (asset.source.length < kit.inlineStyleThreshold) {
|
|
50
|
+
// We know that the names for entry points are numbers.
|
|
50
51
|
const [index] = basename(asset.fileName).split('.');
|
|
52
|
+
// There can also be other CSS files from shared components
|
|
53
|
+
// for example, which we need to ignore here.
|
|
54
|
+
if (isNaN(+index)) return;
|
|
55
|
+
|
|
51
56
|
const server_stylesheet = server_stylesheets.get(+index);
|
|
52
57
|
const file = `${out}/server/stylesheets/${index}.js`;
|
|
53
58
|
|
|
@@ -136,7 +136,34 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
136
136
|
imports: [],
|
|
137
137
|
stylesheets: [],
|
|
138
138
|
fonts: [],
|
|
139
|
-
uses_env_dynamic_public: true
|
|
139
|
+
uses_env_dynamic_public: true,
|
|
140
|
+
nodes:
|
|
141
|
+
svelte_config.kit.router.resolution === 'client'
|
|
142
|
+
? undefined
|
|
143
|
+
: manifest_data.nodes.map((node, i) => {
|
|
144
|
+
if (node.component || node.universal) {
|
|
145
|
+
return `${svelte_config.kit.paths.base}${to_fs(svelte_config.kit.outDir)}/generated/client/nodes/${i}.js`;
|
|
146
|
+
}
|
|
147
|
+
}),
|
|
148
|
+
routes:
|
|
149
|
+
svelte_config.kit.router.resolution === 'client'
|
|
150
|
+
? undefined
|
|
151
|
+
: compact(
|
|
152
|
+
manifest_data.routes.map((route) => {
|
|
153
|
+
if (!route.page) return;
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
id: route.id,
|
|
157
|
+
pattern: route.pattern,
|
|
158
|
+
params: route.params,
|
|
159
|
+
layouts: route.page.layouts.map((l) =>
|
|
160
|
+
l !== undefined ? [!!manifest_data.nodes[l].server, l] : undefined
|
|
161
|
+
),
|
|
162
|
+
errors: route.page.errors,
|
|
163
|
+
leaf: [!!manifest_data.nodes[route.page.leaf].server, route.page.leaf]
|
|
164
|
+
};
|
|
165
|
+
})
|
|
166
|
+
)
|
|
140
167
|
},
|
|
141
168
|
server_assets: new Proxy(
|
|
142
169
|
{},
|
|
@@ -222,6 +249,7 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
222
249
|
return result;
|
|
223
250
|
};
|
|
224
251
|
}),
|
|
252
|
+
prerendered_routes: new Set(),
|
|
225
253
|
routes: compact(
|
|
226
254
|
manifest_data.routes.map((route) => {
|
|
227
255
|
if (!route.page && !route.endpoint) return null;
|
|
@@ -13,7 +13,7 @@ import { load_config } from '../../core/config/index.js';
|
|
|
13
13
|
import { generate_manifest } from '../../core/generate_manifest/index.js';
|
|
14
14
|
import { build_server_nodes } from './build/build_server.js';
|
|
15
15
|
import { build_service_worker } from './build/build_service_worker.js';
|
|
16
|
-
import { assets_base, find_deps } from './build/utils.js';
|
|
16
|
+
import { assets_base, find_deps, resolve_symlinks } from './build/utils.js';
|
|
17
17
|
import { dev } from './dev/index.js';
|
|
18
18
|
import { is_illegal, module_guard } from './graph_analysis/index.js';
|
|
19
19
|
import { preview } from './preview/index.js';
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
sveltekit_server
|
|
36
36
|
} from './module_ids.js';
|
|
37
37
|
import { resolve_peer_dependency } from '../../utils/import.js';
|
|
38
|
+
import { compact } from '../../utils/array.js';
|
|
38
39
|
|
|
39
40
|
const cwd = process.cwd();
|
|
40
41
|
|
|
@@ -319,7 +320,8 @@ async function kit({ svelte_config }) {
|
|
|
319
320
|
__SVELTEKIT_APP_VERSION_FILE__: s(`${kit.appDir}/version.json`),
|
|
320
321
|
__SVELTEKIT_APP_VERSION_POLL_INTERVAL__: s(kit.version.pollInterval),
|
|
321
322
|
__SVELTEKIT_DEV__: 'false',
|
|
322
|
-
__SVELTEKIT_EMBEDDED__: kit.embedded ? 'true' : 'false'
|
|
323
|
+
__SVELTEKIT_EMBEDDED__: kit.embedded ? 'true' : 'false',
|
|
324
|
+
__SVELTEKIT_CLIENT_ROUTING__: kit.router.resolution === 'client' ? 'true' : 'false'
|
|
323
325
|
};
|
|
324
326
|
|
|
325
327
|
if (!secondary_build_started) {
|
|
@@ -329,7 +331,8 @@ async function kit({ svelte_config }) {
|
|
|
329
331
|
new_config.define = {
|
|
330
332
|
__SVELTEKIT_APP_VERSION_POLL_INTERVAL__: '0',
|
|
331
333
|
__SVELTEKIT_DEV__: 'true',
|
|
332
|
-
__SVELTEKIT_EMBEDDED__: kit.embedded ? 'true' : 'false'
|
|
334
|
+
__SVELTEKIT_EMBEDDED__: kit.embedded ? 'true' : 'false',
|
|
335
|
+
__SVELTEKIT_CLIENT_ROUTING__: kit.router.resolution === 'client' ? 'true' : 'false'
|
|
333
336
|
};
|
|
334
337
|
|
|
335
338
|
// These Kit dependencies are packaged as CommonJS, which means they must always be externalized.
|
|
@@ -421,15 +424,22 @@ async function kit({ svelte_config }) {
|
|
|
421
424
|
|
|
422
425
|
const illegal_module = strip_virtual_prefix(relative);
|
|
423
426
|
|
|
427
|
+
const error_prefix = `Cannot import ${illegal_module} into client-side code. This could leak sensitive information.`;
|
|
428
|
+
const error_suffix = `
|
|
429
|
+
Tips:
|
|
430
|
+
- To resolve this error, ensure that no exports from ${illegal_module} are used, even transitively, in client-side code.
|
|
431
|
+
- If you're only using the import as a type, change it to \`import type\`.
|
|
432
|
+
- If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`;
|
|
433
|
+
|
|
424
434
|
if (import_map.has(illegal_module)) {
|
|
425
435
|
const importer = path.relative(
|
|
426
436
|
cwd,
|
|
427
437
|
/** @type {string} */ (import_map.get(illegal_module))
|
|
428
438
|
);
|
|
429
|
-
throw new Error(
|
|
439
|
+
throw new Error(`${error_prefix}\nImported by: ${importer}.${error_suffix}`);
|
|
430
440
|
}
|
|
431
441
|
|
|
432
|
-
throw new Error(
|
|
442
|
+
throw new Error(`${error_prefix}${error_suffix}`);
|
|
433
443
|
}
|
|
434
444
|
}
|
|
435
445
|
|
|
@@ -472,12 +482,14 @@ async function kit({ svelte_config }) {
|
|
|
472
482
|
return dedent`
|
|
473
483
|
export const base = ${global}?.base ?? ${s(base)};
|
|
474
484
|
export const assets = ${global}?.assets ?? ${assets ? s(assets) : 'base'};
|
|
485
|
+
export const app_dir = ${s(kit.appDir)};
|
|
475
486
|
`;
|
|
476
487
|
}
|
|
477
488
|
|
|
478
489
|
return dedent`
|
|
479
490
|
export let base = ${s(base)};
|
|
480
491
|
export let assets = ${assets ? s(assets) : 'base'};
|
|
492
|
+
export const app_dir = ${s(kit.appDir)};
|
|
481
493
|
|
|
482
494
|
export const relative = ${svelte_config.kit.paths.relative};
|
|
483
495
|
|
|
@@ -788,6 +800,7 @@ async function kit({ svelte_config }) {
|
|
|
788
800
|
manifest_path,
|
|
789
801
|
`export const manifest = ${generate_manifest({
|
|
790
802
|
build_data,
|
|
803
|
+
prerendered: [],
|
|
791
804
|
relative_path: '.',
|
|
792
805
|
routes: manifest_data.routes
|
|
793
806
|
})};\n`
|
|
@@ -870,6 +883,37 @@ async function kit({ svelte_config }) {
|
|
|
870
883
|
(chunk) => chunk.type === 'chunk' && chunk.modules[env_dynamic_public]
|
|
871
884
|
)
|
|
872
885
|
};
|
|
886
|
+
|
|
887
|
+
// In case of server-side route resolution, we create a purpose-built route manifest that is
|
|
888
|
+
// similar to that on the client, with as much information computed upfront so that we
|
|
889
|
+
// don't need to include any code of the actual routes in the server bundle.
|
|
890
|
+
if (svelte_config.kit.router.resolution === 'server') {
|
|
891
|
+
build_data.client.nodes = manifest_data.nodes.map((node, i) => {
|
|
892
|
+
if (node.component || node.universal) {
|
|
893
|
+
return resolve_symlinks(
|
|
894
|
+
client_manifest,
|
|
895
|
+
`${kit.outDir}/generated/client-optimized/nodes/${i}.js`
|
|
896
|
+
).chunk.file;
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
build_data.client.routes = compact(
|
|
901
|
+
manifest_data.routes.map((route) => {
|
|
902
|
+
if (!route.page) return;
|
|
903
|
+
|
|
904
|
+
return {
|
|
905
|
+
id: route.id,
|
|
906
|
+
pattern: route.pattern,
|
|
907
|
+
params: route.params,
|
|
908
|
+
layouts: route.page.layouts.map((l) =>
|
|
909
|
+
l !== undefined ? [metadata.nodes[l].has_server_load, l] : undefined
|
|
910
|
+
),
|
|
911
|
+
errors: route.page.errors,
|
|
912
|
+
leaf: [metadata.nodes[route.page.leaf].has_server_load, route.page.leaf]
|
|
913
|
+
};
|
|
914
|
+
})
|
|
915
|
+
);
|
|
916
|
+
}
|
|
873
917
|
} else {
|
|
874
918
|
const start = deps_of(`${runtime_directory}/client/bundle.js`);
|
|
875
919
|
|
|
@@ -910,6 +954,7 @@ async function kit({ svelte_config }) {
|
|
|
910
954
|
manifest_path,
|
|
911
955
|
`export const manifest = ${generate_manifest({
|
|
912
956
|
build_data,
|
|
957
|
+
prerendered: [],
|
|
913
958
|
relative_path: '.',
|
|
914
959
|
routes: manifest_data.routes
|
|
915
960
|
})};\n`
|
|
@@ -941,6 +986,7 @@ async function kit({ svelte_config }) {
|
|
|
941
986
|
`${out}/server/manifest.js`,
|
|
942
987
|
`export const manifest = ${generate_manifest({
|
|
943
988
|
build_data,
|
|
989
|
+
prerendered: prerendered.paths,
|
|
944
990
|
relative_path: '.',
|
|
945
991
|
routes: manifest_data.routes.filter((route) => prerender_map.get(route.id) !== true)
|
|
946
992
|
})};\n`
|
|
@@ -32,6 +32,17 @@ import { BROWSER } from 'esm-env';
|
|
|
32
32
|
* {/if}
|
|
33
33
|
* ```
|
|
34
34
|
*
|
|
35
|
+
* Changes to `page` are available exclusively with runes. (The legacy reactivity syntax will not reflect any changes)
|
|
36
|
+
*
|
|
37
|
+
* ```svelte
|
|
38
|
+
* <!--- file: +page.svelte --->
|
|
39
|
+
* <script>
|
|
40
|
+
* import { page } from '$app/state';
|
|
41
|
+
* const id = $derived(page.params.id); // This will correctly update id for usage on this page
|
|
42
|
+
* $: badId = page.params.id; // Do not use; will never update after initial load
|
|
43
|
+
* </script>
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
35
46
|
* On the server, values can only be read during rendering (in other words _not_ in e.g. `load` functions). In the browser, the values can be read at any time.
|
|
36
47
|
*
|
|
37
48
|
* @type {import('@sveltejs/kit').Page}
|