@sveltejs/kit 3.0.0-next.1 → 3.0.0-next.3

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.
Files changed (45) hide show
  1. package/package.json +2 -2
  2. package/src/core/postbuild/analyse.js +0 -8
  3. package/src/core/postbuild/prerender.js +2 -0
  4. package/src/core/sync/create_manifest_data/index.js +24 -1
  5. package/src/core/sync/write_env.js +2 -1
  6. package/src/exports/internal/env.js +1 -1
  7. package/src/exports/public.d.ts +1 -1
  8. package/src/exports/vite/build/build_server.js +47 -58
  9. package/src/exports/vite/build/remote.js +18 -11
  10. package/src/exports/vite/build/utils.js +0 -8
  11. package/src/exports/vite/index.js +221 -217
  12. package/src/exports/vite/static_analysis/index.js +2 -4
  13. package/src/exports/vite/static_analysis/types.d.ts +14 -0
  14. package/src/exports/vite/utils.js +1 -12
  15. package/src/runtime/app/server/remote/command.js +0 -3
  16. package/src/runtime/app/server/remote/form.js +18 -13
  17. package/src/runtime/app/server/remote/prerender.js +28 -34
  18. package/src/runtime/app/server/remote/query.js +105 -94
  19. package/src/runtime/app/server/remote/requested.js +14 -10
  20. package/src/runtime/app/server/remote/shared.js +25 -19
  21. package/src/runtime/client/client.js +19 -13
  22. package/src/runtime/client/ndjson.js +6 -33
  23. package/src/runtime/client/remote-functions/command.svelte.js +7 -32
  24. package/src/runtime/client/remote-functions/form.svelte.js +62 -82
  25. package/src/runtime/client/remote-functions/prerender.svelte.js +14 -6
  26. package/src/runtime/client/remote-functions/query/index.js +6 -14
  27. package/src/runtime/client/remote-functions/query/instance.svelte.js +20 -0
  28. package/src/runtime/client/remote-functions/query/proxy.js +3 -3
  29. package/src/runtime/client/remote-functions/query-batch.svelte.js +59 -68
  30. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +21 -6
  31. package/src/runtime/client/remote-functions/query-live/iterator.js +36 -55
  32. package/src/runtime/client/remote-functions/shared.svelte.js +76 -59
  33. package/src/runtime/client/sse.js +32 -0
  34. package/src/runtime/client/stream.js +38 -0
  35. package/src/runtime/server/page/render.js +23 -80
  36. package/src/runtime/server/page/server_routing.js +20 -15
  37. package/src/runtime/server/remote.js +296 -204
  38. package/src/runtime/server/respond.js +4 -2
  39. package/src/runtime/shared.js +83 -13
  40. package/src/types/global-private.d.ts +3 -3
  41. package/src/types/internal.d.ts +54 -35
  42. package/src/utils/error.js +12 -0
  43. package/src/version.js +1 -1
  44. package/types/index.d.ts +17 -7
  45. package/types/index.d.ts.map +5 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "3.0.0-next.1",
3
+ "version": "3.0.0-next.3",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -37,7 +37,7 @@
37
37
  "dts-buddy": "^0.8.0",
38
38
  "jsdom": "^29.1.1",
39
39
  "rolldown": "^1.0.0-rc.6",
40
- "svelte": "^5.55.7",
40
+ "svelte": "^5.56.3",
41
41
  "svelte-preprocess": "^6.0.5",
42
42
  "typescript": "~6.0.0",
43
43
  "vite": "^8.0.10",
@@ -10,7 +10,6 @@ import { has_server_load, resolve_route } from '../../utils/routing.js';
10
10
  import { check_feature } from '../../utils/features.js';
11
11
  import { createReadableStream } from '@sveltejs/kit/node';
12
12
  import { PageNodes } from '../../utils/page_nodes.js';
13
- import { build_server_nodes } from '../../exports/vite/build/build_server.js';
14
13
 
15
14
  export default forked(import.meta.url, analyse);
16
15
 
@@ -22,9 +21,7 @@ export default forked(import.meta.url, analyse);
22
21
  * server_manifest: import('vite').Manifest;
23
22
  * tracked_features: Record<string, string[]>;
24
23
  * env: Record<string, string>;
25
- * out: string;
26
24
  * remotes: RemoteChunk[];
27
- * root: string;
28
25
  * vite_config_file: string | undefined;
29
26
  * }} opts
30
27
  */
@@ -35,9 +32,7 @@ async function analyse({
35
32
  server_manifest,
36
33
  tracked_features,
37
34
  env,
38
- out,
39
35
  remotes,
40
- root,
41
36
  vite_config_file
42
37
  }) {
43
38
  /** @type {import('@sveltejs/kit').SSRManifest} */
@@ -67,9 +62,6 @@ async function analyse({
67
62
  internal.set_manifest(manifest);
68
63
  internal.set_read_implementation((file) => createReadableStream(`${server_root}/server/${file}`));
69
64
 
70
- // first, build server nodes without the client manifest so we can analyse it
71
- build_server_nodes(out, config, manifest_data, server_manifest, null, null, null, root);
72
-
73
65
  /** @type {import('types').ServerMetadata} */
74
66
  const metadata = {
75
67
  nodes: [],
@@ -210,6 +210,8 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
210
210
 
211
211
  const seen = new Set();
212
212
  const written = new Set();
213
+
214
+ /** @type {Map<string, Promise<any>>} */
213
215
  const remote_responses = new Map();
214
216
 
215
217
  /** @type {Map<string, Set<string>>} */
@@ -32,6 +32,7 @@ export default function create_manifest_data({
32
32
  const matchers = create_matchers(config, cwd);
33
33
  const { nodes, routes } = create_routes_and_nodes(cwd, config, fallback);
34
34
 
35
+ // validate matcher names used in parameterised routes
35
36
  for (const route of routes) {
36
37
  for (const param of route.params) {
37
38
  if (param.matcher && !matchers[param.matcher]) {
@@ -50,6 +51,7 @@ export default function create_manifest_data({
50
51
  }
51
52
 
52
53
  /**
54
+ * Returns a list of files in the `static` directory.
53
55
  * @param {import('types').ValidatedConfig} config
54
56
  */
55
57
  export function create_assets(config) {
@@ -130,6 +132,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
130
132
  /** @type {import('types').PageNode[]} */
131
133
  const nodes = [];
132
134
 
135
+ // create route data by processing files in `src/routes`
133
136
  if (fs.existsSync(config.kit.files.routes)) {
134
137
  /**
135
138
  * @param {number} depth
@@ -388,6 +391,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
388
391
 
389
392
  prevent_conflicts(routes);
390
393
 
394
+ // fallback root layout and root error components
391
395
  const root = routes[0];
392
396
 
393
397
  if (!root.layout?.component) {
@@ -396,10 +400,11 @@ function create_routes_and_nodes(cwd, config, fallback) {
396
400
  }
397
401
 
398
402
  if (!root.error?.component) {
399
- if (!root.error) root.error = { depth: 0 };
403
+ if (!root.error) root.error = { depth: 0, parent: root.layout };
400
404
  root.error.component = posixify(path.relative(cwd, `${fallback}/error.svelte`));
401
405
  }
402
406
 
407
+ // populate the page nodes list
403
408
  // we do layouts/errors first as they are more likely to be reused,
404
409
  // and smaller indexes take fewer bytes. also, this guarantees that
405
410
  // the default error/layout are 0/1
@@ -421,6 +426,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
421
426
 
422
427
  const node_analyser = create_node_analyser(cwd);
423
428
 
429
+ // add the related layout, page, and error nodes for a route
424
430
  for (const route of routes) {
425
431
  if (!route.leaf) continue;
426
432
 
@@ -465,6 +471,22 @@ function create_routes_and_nodes(cwd, config, fallback) {
465
471
  }
466
472
  }
467
473
 
474
+ // add parents to error nodes so that we can compute which page options apply to them
475
+ for (const route of routes) {
476
+ if (!route.error) continue;
477
+
478
+ /** @type {import('types').RouteData | null} */
479
+ let current_route = route;
480
+ while (current_route) {
481
+ if (current_route.layout) {
482
+ route.error.parent = current_route.layout;
483
+ break;
484
+ }
485
+ current_route = current_route.parent;
486
+ }
487
+ }
488
+
489
+ // compute the final page options for each page node
468
490
  for (const node of nodes) {
469
491
  node.page_options = node_analyser.get_page_options(node);
470
492
  }
@@ -482,6 +504,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
482
504
  }
483
505
 
484
506
  /**
507
+ * Determine if and how the file is relevant to the routing system.
485
508
  * @param {string} project_relative
486
509
  * @param {string} file
487
510
  * @param {string[]} component_extensions
@@ -2,6 +2,7 @@
2
2
  import path from 'node:path';
3
3
  import { create_explicit_env_types } from '../env.js';
4
4
  import { write_if_changed } from './utils.js';
5
+ import { posixify } from '../../utils/os.js';
5
6
 
6
7
  const DOCS = '// See https://svelte.dev/docs/kit/environment-variables for more information';
7
8
 
@@ -18,7 +19,7 @@ export function write_env(kit, entry, env_config) {
18
19
  const out = path.join(kit.outDir, 'env.d.ts');
19
20
 
20
21
  if (entry && env_config) {
21
- const relative = path.relative(kit.outDir, entry);
22
+ const relative = posixify(path.relative(kit.outDir, entry));
22
23
  content.push(
23
24
  `// This file is generated from ${relative}.\n${DOCS}`,
24
25
  create_explicit_env_types(env_config, relative, 'private'),
@@ -1,7 +1,7 @@
1
1
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
2
  /** @import { EnvVarConfig } from '@sveltejs/kit' */
3
3
 
4
- import { stackless } from '../vite/utils.js';
4
+ import { stackless } from '../../utils/error.js';
5
5
 
6
6
  const MISSING = {
7
7
  message: `Value is missing. If it is optional, add a Standard Schema validator declaring it as such.`
@@ -1655,7 +1655,7 @@ export interface SSRManifest {
1655
1655
 
1656
1656
  /** private fields */
1657
1657
  _: {
1658
- client: NonNullable<BuildData['client']>;
1658
+ client: BuildData['client'];
1659
1659
  nodes: SSRNodeLoader[];
1660
1660
  /** hashed filename -> import to that file */
1661
1661
  remotes: Record<string, () => Promise<any>>;
@@ -16,39 +16,16 @@ import { fix_css_urls } from '../../../utils/css.js';
16
16
  import { escape_for_interpolation } from '../../../utils/escape.js';
17
17
 
18
18
  /**
19
- * @overload Build without the client manifest so we can analyse the nodes.
20
19
  * @param {string} out
21
20
  * @param {ValidatedKitConfig} kit
22
21
  * @param {ManifestData} manifest_data
23
22
  * @param {Manifest} server_manifest
24
- * @param {null} client_manifest
25
- * @param {null} assets_path
26
- * @param {null} client_chunks
27
- * @param {string} root
28
- * @returns {void}
29
- */
30
- /**
31
- * @overload Or build with the client manifest
32
- * @param {string} out
33
- * @param {ValidatedKitConfig} kit
34
- * @param {ManifestData} manifest_data
35
- * @param {Manifest} server_manifest
36
- * @param {Manifest} client_manifest
23
+ * @param {Manifest | null} client_manifest
37
24
  * @param {string} assets_path
38
- * @param {Rolldown.RolldownOutput['output']} client_chunks
25
+ * @param {(Rolldown.OutputAsset | Rolldown.OutputChunk)[]} chunks
39
26
  * @param {string} root
40
27
  * @returns {void}
41
28
  */
42
- /**
43
- * @param {string} out
44
- * @param {ValidatedKitConfig} kit
45
- * @param {ManifestData} manifest_data
46
- * @param {Manifest} server_manifest
47
- * @param {Manifest | null} client_manifest
48
- * @param {string | null} assets_path
49
- * @param {Rolldown.RolldownOutput['output'] | null} client_chunks
50
- * @param {string} root
51
- */
52
29
  export function build_server_nodes(
53
30
  out,
54
31
  kit,
@@ -56,7 +33,7 @@ export function build_server_nodes(
56
33
  server_manifest,
57
34
  client_manifest,
58
35
  assets_path,
59
- client_chunks,
36
+ chunks,
60
37
  root
61
38
  ) {
62
39
  mkdirp(`${out}/server/nodes`);
@@ -75,8 +52,8 @@ export function build_server_nodes(
75
52
  */
76
53
  let prepare_css_for_inlining = (css) => s(css);
77
54
 
78
- if (client_chunks && kit.inlineStyleThreshold > 0 && kit.output.bundleStrategy === 'split') {
79
- for (const chunk of client_chunks) {
55
+ if (chunks && kit.inlineStyleThreshold > 0 && kit.output.bundleStrategy === 'split') {
56
+ for (const chunk of chunks) {
80
57
  if (chunk.type !== 'asset' || !chunk.fileName.endsWith('.css')) {
81
58
  continue;
82
59
  }
@@ -132,6 +109,9 @@ export function build_server_nodes(
132
109
  }
133
110
  }
134
111
 
112
+ /** path to the `.svelte-kit` directory */
113
+ const out_dir = normalizePath(kit.outDir);
114
+
135
115
  for (let i = 0; i < manifest_data.nodes.length; i++) {
136
116
  const node = manifest_data.nodes[i];
137
117
 
@@ -155,7 +135,11 @@ export function build_server_nodes(
155
135
  /** @type {Set<string>} */
156
136
  const eager_assets = new Set();
157
137
 
158
- if (node.component && client_manifest) {
138
+ const uses_server_component = node.child_pages
139
+ ? node.child_pages.some((child) => child.page_options?.ssr !== false)
140
+ : node.page_options?.ssr !== false;
141
+
142
+ if (node.component && uses_server_component) {
159
143
  exports.push(
160
144
  'let component_cache;',
161
145
  `export const component = async () => component_cache ??= (await import('../${
@@ -185,18 +169,7 @@ export function build_server_nodes(
185
169
  exports.push(`export const server_id = ${s(node.server)};`);
186
170
  }
187
171
 
188
- if (
189
- client_manifest &&
190
- (node.universal || node.component) &&
191
- kit.output.bundleStrategy === 'split'
192
- ) {
193
- const entry_path = `${normalizePath(kit.outDir)}/generated/client-optimized/nodes/${i}.js`;
194
- const entry = find_deps(client_manifest, entry_path, true, root);
195
-
196
- // Eagerly load client stylesheets and fonts imported by the SSR-ed page to avoid FOUC.
197
- // However, if it is not used during SSR (not present in the server manifest),
198
- // then it can be lazily loaded in the browser.
199
-
172
+ if ((node.universal || node.component) && kit.output.bundleStrategy === 'split') {
200
173
  /** @type {AssetDependencies | undefined} */
201
174
  let component;
202
175
  if (node.component) {
@@ -209,25 +182,41 @@ export function build_server_nodes(
209
182
  universal = find_deps(server_manifest, node.universal, true, root);
210
183
  }
211
184
 
212
- /** @type {Set<string>} */
213
- const eager_css = new Set();
185
+ if (client_manifest) {
186
+ const entry_path = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
187
+ const entry = find_deps(client_manifest, entry_path, true, root);
214
188
 
215
- entry.stylesheet_map.forEach((value, filepath) => {
216
- // pages and layouts are renamed to node indexes when optimised for the client
217
- // so we use the original filename instead to check against the server manifest
218
- if (filepath === entry_path) {
219
- filepath = node.component ?? filepath;
220
- }
189
+ // Eagerly load client stylesheets and fonts imported by the SSR-ed page to avoid FOUC.
190
+ // However, if it is not used during SSR (not present in the server manifest),
191
+ // then it can be lazily loaded in the browser.
221
192
 
222
- if (component?.stylesheet_map.has(filepath) || universal?.stylesheet_map.has(filepath)) {
223
- value.css.forEach((file) => eager_css.add(file));
224
- value.assets.forEach((file) => eager_assets.add(file));
225
- }
226
- });
193
+ /** @type {Set<string>} */
194
+ const eager_css = new Set();
195
+
196
+ entry.stylesheet_map.forEach((value, filepath) => {
197
+ // pages and layouts are renamed to node indexes when optimised for the client
198
+ // so we use the original filename instead to check against the server manifest
199
+ if (filepath === entry_path) {
200
+ filepath = node.component ?? filepath;
201
+ }
202
+
203
+ if (component?.stylesheet_map.has(filepath) || universal?.stylesheet_map.has(filepath)) {
204
+ value.css.forEach((file) => eager_css.add(file));
205
+ value.assets.forEach((file) => eager_assets.add(file));
206
+ }
207
+ });
227
208
 
228
- imported = entry.imports;
229
- stylesheets = Array.from(eager_css);
230
- fonts = filter_fonts(Array.from(eager_assets));
209
+ imported = entry.imports;
210
+ stylesheets = Array.from(eager_css);
211
+ fonts = filter_fonts(Array.from(eager_assets));
212
+ } else {
213
+ for (const entry of [component, universal]) {
214
+ if (!entry) continue;
215
+ imported.push(...entry.imports);
216
+ stylesheets.push(...entry.stylesheets);
217
+ fonts.push(...entry.fonts);
218
+ }
219
+ }
231
220
  }
232
221
 
233
222
  exports.push(
@@ -244,7 +233,7 @@ export function build_server_nodes(
244
233
 
245
234
  // Keep track of Vite asset filenames so that we avoid touching unrelated ones
246
235
  // when adjusting the inlined CSS
247
- if (stylesheets_to_inline.size && assets_path && eager_assets.size) {
236
+ if (stylesheets_to_inline.size && eager_assets.size) {
248
237
  vite_assets = new Set(
249
238
  Array.from(eager_assets).map((asset) => {
250
239
  return decodeURIComponent(asset.replace(`${assets_path}/`, ''));
@@ -13,7 +13,7 @@ import { posixify } from '../../../utils/os.js';
13
13
  * @param {Array<{ hash: string, file: string }>} remotes
14
14
  * @param {ServerMetadata} metadata
15
15
  * @param {string} cwd
16
- * @param {Rolldown.RolldownOutput} server_bundle
16
+ * @param {(Rolldown.OutputAsset | Rolldown.OutputChunk)[]} server_chunks
17
17
  * @param {NonNullable<import('vitest/config').ViteUserConfig['build']>['sourcemap']} sourcemap
18
18
  */
19
19
  export async function treeshake_prerendered_remotes(
@@ -22,11 +22,14 @@ export async function treeshake_prerendered_remotes(
22
22
  remotes,
23
23
  metadata,
24
24
  cwd,
25
- server_bundle,
25
+ server_chunks,
26
26
  sourcemap
27
27
  ) {
28
28
  if (remotes.length === 0) return;
29
29
 
30
+ /** @type {string[]} */
31
+ const chunk_paths = [];
32
+
30
33
  for (const remote of remotes) {
31
34
  const exports_map = metadata.remotes.get(remote.hash);
32
35
  if (!exports_map) continue;
@@ -45,7 +48,7 @@ export async function treeshake_prerendered_remotes(
45
48
  // remove file extension
46
49
  const remote_filename = path.basename(remote.file).split('.').slice(0, -1).join('.');
47
50
 
48
- const remote_chunk = Object.values(server_bundle).find((chunk) => {
51
+ const remote_chunk = server_chunks.find((chunk) => {
49
52
  return chunk.name === remote_filename;
50
53
  });
51
54
 
@@ -88,8 +91,13 @@ export async function treeshake_prerendered_remotes(
88
91
 
89
92
  const stubbed = modified_code.toString();
90
93
  fs.writeFileSync(chunk_path, stubbed);
94
+ chunk_paths.push(chunk_path);
95
+ }
96
+
97
+ if (!chunk_paths.length) return;
91
98
 
92
- const bundle = /** @type {import('vite').Rolldown.RolldownOutput} */ (
99
+ for (const chunk_path of chunk_paths) {
100
+ const bundle = /** @type {Rolldown.RolldownOutput} */ (
93
101
  await vite.build({
94
102
  configFile: false,
95
103
  build: {
@@ -97,7 +105,7 @@ export async function treeshake_prerendered_remotes(
97
105
  ssr: true,
98
106
  target: 'esnext',
99
107
  sourcemap,
100
- rollupOptions: {
108
+ rolldownOptions: {
101
109
  // avoid resolving imports
102
110
  external: (id) => !id.endsWith(chunk_path),
103
111
  input: {
@@ -108,14 +116,13 @@ export async function treeshake_prerendered_remotes(
108
116
  })
109
117
  );
110
118
 
111
- const chunk = bundle.output.find(
112
- (output) => output.type === 'chunk' && output.name === 'treeshaken'
113
- );
114
- if (chunk && chunk.type === 'chunk') {
115
- fs.writeFileSync(chunk_path, chunk.code);
119
+ for (const output of bundle.output) {
120
+ if (output.type !== 'chunk' || output.name !== 'treeshaken') return;
121
+
122
+ fs.writeFileSync(chunk_path, output.code);
116
123
 
117
124
  const chunk_sourcemap = bundle.output.find(
118
- (output) => output.type === 'asset' && output.fileName === chunk.fileName + '.map'
125
+ (o) => o.type === 'asset' && o.fileName === output.fileName + '.map'
119
126
  );
120
127
  if (chunk_sourcemap && chunk_sourcemap.type === 'asset') {
121
128
  fs.writeFileSync(chunk_path + '.map', chunk_sourcemap.source);
@@ -124,14 +124,6 @@ export function filter_fonts(assets) {
124
124
  return assets.filter((asset) => /\.(woff2?|ttf|otf)$/.test(asset));
125
125
  }
126
126
 
127
- /**
128
- * @param {import('types').ValidatedKitConfig} config
129
- * @returns {string}
130
- */
131
- export function assets_base(config) {
132
- return (config.paths.assets || config.paths.base || '.') + '/';
133
- }
134
-
135
127
  /**
136
128
  * Writes a function with arguments used by a template literal.
137
129
  * This helps us store strings in a module and inject values at runtime.