@sveltejs/kit 3.0.0-next.23 → 3.0.0-next.25

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 (105) hide show
  1. package/package.json +51 -8
  2. package/src/cli.js +3 -3
  3. package/src/constants.js +19 -1
  4. package/src/core/adapt/builder.js +3 -5
  5. package/src/core/env.js +137 -182
  6. package/src/core/postbuild/analyse.js +9 -4
  7. package/src/core/postbuild/prerender.js +5 -6
  8. package/src/core/sync/create_manifest_data/index.js +29 -33
  9. package/src/core/sync/sync.js +23 -40
  10. package/src/core/sync/write_app_manifest.js +83 -0
  11. package/src/core/sync/write_client_manifest.js +6 -4
  12. package/src/core/sync/write_server.js +12 -22
  13. package/src/core/sync/write_types/index.js +2 -2
  14. package/src/core/utils.js +10 -28
  15. package/src/exports/adapter.js +22 -0
  16. package/src/exports/hooks/public.d.ts +9 -1
  17. package/src/exports/node/index.js +77 -5
  18. package/src/exports/public.d.ts +1 -1
  19. package/src/exports/vite/build/build_server.js +3 -3
  20. package/src/exports/vite/build/service-worker.js +98 -0
  21. package/src/exports/vite/build/utils.js +48 -4
  22. package/src/exports/vite/dev/index.js +28 -25
  23. package/src/exports/vite/index.js +94 -747
  24. package/src/exports/vite/module_ids.js +1 -11
  25. package/src/exports/vite/plugins/env-vars.js +159 -0
  26. package/src/exports/vite/plugins/guard.js +205 -0
  27. package/src/exports/vite/preview/index.js +6 -4
  28. package/src/exports/vite/utils.js +99 -4
  29. package/src/runtime/app/env/{types.d.ts → client.js} +9 -4
  30. package/src/runtime/app/env/index.js +1 -2
  31. package/src/runtime/app/env/private/index.js +1 -0
  32. package/src/runtime/app/env/public/client.js +1 -1
  33. package/src/runtime/app/env/public/server.js +1 -1
  34. package/src/runtime/app/env/{internal.js → server.js} +4 -4
  35. package/src/runtime/app/forms/client.js +263 -0
  36. package/src/runtime/app/forms/index.js +1 -263
  37. package/src/runtime/app/forms/public.d.ts +1 -1
  38. package/src/runtime/app/forms/server.js +6 -0
  39. package/src/runtime/app/forms/shared.js +34 -0
  40. package/src/runtime/app/manifest/index.js +1 -1
  41. package/src/runtime/app/navigation/client.js +15 -0
  42. package/src/runtime/app/navigation/index.js +1 -15
  43. package/src/runtime/app/navigation/public.d.ts +1 -1
  44. package/src/runtime/app/navigation/server.js +15 -0
  45. package/src/runtime/app/paths/server.js +1 -1
  46. package/src/runtime/app/server/remote/form.js +11 -1
  47. package/src/runtime/app/server/remote/prerender.js +1 -2
  48. package/src/runtime/app/server/remote/query.js +3 -3
  49. package/src/runtime/app/state/client.svelte.js +207 -0
  50. package/src/runtime/app/state/index.js +1 -66
  51. package/src/runtime/app/state/public.d.ts +1 -1
  52. package/src/runtime/client/bundle.js +1 -1
  53. package/src/runtime/client/client.js +210 -278
  54. package/src/runtime/client/fetcher.js +18 -8
  55. package/src/runtime/client/remote-functions/form.svelte.js +9 -6
  56. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  57. package/src/runtime/client/remote-functions/query-live/iterator.js +1 -1
  58. package/src/runtime/client/remote-functions/shared.svelte.js +2 -2
  59. package/src/runtime/client/snapshots.js +1 -3
  60. package/src/runtime/client/stream.js +27 -20
  61. package/src/runtime/client/utils.js +7 -3
  62. package/src/runtime/components/root.svelte +5 -5
  63. package/src/runtime/env/dynamic/private.js +1 -1
  64. package/src/runtime/env/static/private.js +1 -1
  65. package/src/runtime/error-chain.js +54 -0
  66. package/src/runtime/form-utils.js +55 -87
  67. package/src/runtime/invalid-import.js +1 -0
  68. package/src/runtime/props.svelte.js +1 -1
  69. package/src/runtime/server/data/index.js +12 -27
  70. package/src/runtime/server/endpoint.js +8 -3
  71. package/src/runtime/server/env_module.js +1 -1
  72. package/src/runtime/server/errors.js +9 -12
  73. package/src/runtime/server/fetch.js +14 -16
  74. package/src/runtime/server/index.js +30 -27
  75. package/src/runtime/server/internal.js +34 -4
  76. package/src/runtime/server/page/actions.js +77 -124
  77. package/src/runtime/server/page/data_serializer.js +6 -10
  78. package/src/runtime/server/page/index.js +45 -82
  79. package/src/runtime/server/page/render.js +34 -52
  80. package/src/runtime/server/page/respond_with_error.js +7 -9
  81. package/src/runtime/server/remote-functions.js +138 -166
  82. package/src/runtime/server/respond.js +75 -30
  83. package/src/runtime/server/state.js +1 -0
  84. package/src/runtime/server/utils.js +0 -7
  85. package/src/runtime/utils.js +41 -0
  86. package/src/telemetry.js +1 -33
  87. package/src/types/ambient-private.d.ts +9 -17
  88. package/src/types/global-private.d.ts +8 -0
  89. package/src/types/internal.d.ts +27 -15
  90. package/src/types/private.d.ts +1 -1
  91. package/src/utils/exports.js +1 -0
  92. package/src/utils/filesystem.js +10 -22
  93. package/src/utils/functions.js +10 -0
  94. package/src/utils/routing.js +0 -11
  95. package/src/utils/streaming.js +5 -15
  96. package/src/utils/url.js +0 -11
  97. package/src/version.js +1 -1
  98. package/types/index.d.ts +54 -29
  99. package/types/index.d.ts.map +10 -5
  100. package/src/runtime/app/env/private.js +0 -1
  101. package/src/runtime/app/env/standard-schema.d.ts +0 -0
  102. package/src/runtime/app/state/client.js +0 -63
  103. package/src/runtime/client/state.svelte.js +0 -98
  104. package/src/runtime/server/ambient.d.ts +0 -4
  105. package/src/utils/path.js +0 -23
@@ -116,12 +116,13 @@ function get_raw_body(req, body_size_limit) {
116
116
  /**
117
117
  * @param {{
118
118
  * request: import('http').IncomingMessage;
119
+ * response?: import('http').ServerResponse;
119
120
  * base: string;
120
121
  * bodySizeLimit?: number;
121
122
  * }} options
122
123
  * @returns {Request}
123
124
  */
124
- export function getRequest({ request, base, bodySizeLimit }) {
125
+ export function getRequest({ request, response, base, bodySizeLimit }) {
125
126
  let headers = /** @type {Record<string, string>} */ (request.headers);
126
127
  if (request.httpVersionMajor >= 2) {
127
128
  // the Request constructor rejects headers with ':' in the name
@@ -144,6 +145,15 @@ export function getRequest({ request, base, bodySizeLimit }) {
144
145
  }
145
146
  });
146
147
 
148
+ // `readableAborted` stays false once the request has been fully read (or drained),
149
+ // so a client disconnect must also be detected on the response side. `writableEnded`
150
+ // rather than `writableFinished` because HTTP/2 marks cancelled streams as finished
151
+ response?.once('close', () => {
152
+ if (!response.writableEnded) {
153
+ controller.abort();
154
+ }
155
+ });
156
+
147
157
  return new Request(base + request.url, {
148
158
  // @ts-expect-error
149
159
  duplex: 'half',
@@ -214,14 +224,14 @@ export function setResponse(res, response) {
214
224
  }
215
225
  }
216
226
 
217
- res.writeHead(response.status);
218
-
219
227
  if (!response.body) {
228
+ res.writeHead(response.status);
220
229
  res.end();
221
230
  return;
222
231
  }
223
232
 
224
233
  if (response.body.locked) {
234
+ res.writeHead(response.status);
225
235
  res.end(
226
236
  'Fatal error: Response body is locked. ' +
227
237
  "This can happen when the response was already read (for example through 'response.json()' or 'response.text()')."
@@ -249,11 +259,73 @@ export function setResponse(res, response) {
249
259
  res.on('close', cancel);
250
260
  res.on('error', cancel);
251
261
 
252
- void next();
262
+ /** @type {Uint8Array<ArrayBuffer>[]} */
263
+ const buffered = [];
264
+
265
+ /** @type {ReturnType<typeof reader.read> | null} */
266
+ let pending = null;
267
+
268
+ void probe();
269
+
270
+ // a fixed body (a string, buffer or blob, however constructed) settles all its
271
+ // reads before the next macrotask, so it can be measured and sent with a
272
+ // `content-length`; a genuine stream leaves a read pending and only has its
273
+ // headers delayed by a single tick
274
+ async function probe() {
275
+ try {
276
+ /** @type {Promise<undefined>} */
277
+ const deadline = new Promise((fulfil) => setImmediate(() => fulfil(undefined)));
278
+
279
+ while (buffered.length < 2) {
280
+ pending = reader.read();
281
+ const result = await Promise.race([pending, deadline]);
282
+
283
+ if (!result) break; // deadline hit — treat the body as a stream
284
+
285
+ pending = null;
286
+
287
+ if (result.done) {
288
+ // a `content-length` next to a `transfer-encoding` would be invalid
289
+ if (!res.hasHeader('content-length') && !res.hasHeader('transfer-encoding')) {
290
+ res.setHeader(
291
+ 'content-length',
292
+ buffered.reduce((total, chunk) => total + chunk.byteLength, 0)
293
+ );
294
+ }
295
+ break;
296
+ }
297
+
298
+ buffered.push(result.value);
299
+ }
300
+
301
+ if (res.destroyed) return;
302
+
303
+ res.writeHead(response.status);
304
+ await next();
305
+ } catch (error) {
306
+ if (!res.headersSent) res.writeHead(response.status);
307
+ cancel(error instanceof Error ? error : new Error(String(error)));
308
+ }
309
+ }
310
+
253
311
  async function next() {
254
312
  try {
255
313
  for (;;) {
256
- const { done, value } = await reader.read();
314
+ /** @type {Awaited<ReturnType<typeof reader.read>>} */
315
+ let result;
316
+ if (buffered.length > 0) {
317
+ result = {
318
+ done: false,
319
+ value: /** @type {Uint8Array<ArrayBuffer>} */ (buffered.shift())
320
+ };
321
+ } else if (pending) {
322
+ result = await pending;
323
+ pending = null;
324
+ } else {
325
+ result = await reader.read();
326
+ }
327
+
328
+ const { done, value } = result;
257
329
 
258
330
  if (done) break;
259
331
 
@@ -165,7 +165,7 @@ export interface Builder {
165
165
  /**
166
166
  * Generate a server-side manifest to initialise the SvelteKit [server](https://svelte.dev/docs/kit/@sveltejs-kit#Server) with.
167
167
  * @param opts
168
- * @param opts.relativePath A relative path to the base directory of the server build output
168
+ * @param opts.relativePath A relative path to the base directory of the server build output
169
169
  */
170
170
  generateManifest: (opts: { relativePath: string; routes?: RouteDefinition[] }) => string;
171
171
 
@@ -128,7 +128,7 @@ export function build_server_nodes(
128
128
  /** @type {string[]} */
129
129
  let stylesheets = [];
130
130
 
131
- /** @type {string[]} */
131
+ /** @type {import('types').FontDependency[]} */
132
132
  let fonts = [];
133
133
 
134
134
  /** @type {Set<string>} */
@@ -187,7 +187,7 @@ export function build_server_nodes(
187
187
  }
188
188
 
189
189
  if (client_manifest) {
190
- const entry_path = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
190
+ const entry_path = `${out_dir}/generated/build/client-optimized/nodes/${i}.js`;
191
191
  const entry = find_deps(client_manifest, entry_path, true, root);
192
192
 
193
193
  // Eagerly load client stylesheets and fonts imported by the SSR-ed page to avoid FOUC.
@@ -212,7 +212,7 @@ export function build_server_nodes(
212
212
 
213
213
  imported = entry.imports;
214
214
  stylesheets = Array.from(eager_css);
215
- fonts = filter_fonts(Array.from(eager_assets));
215
+ fonts = filter_fonts(Array.from(eager_assets), client_manifest, root);
216
216
  } else {
217
217
  for (const entry of [component, universal]) {
218
218
  if (!entry) continue;
@@ -0,0 +1,98 @@
1
+ /** @import { ValidatedConfig } from 'types' */
2
+ /** @import { Plugin, UserConfig } from 'vite' */
3
+ import { runtime_directory } from '../../../core/utils.js';
4
+ import { warn_overridden_config } from '../utils.js';
5
+
6
+ /**
7
+ * @param {ValidatedConfig} kit
8
+ * @param {() => { service_worker_entry_file: string | null; kit_global: string; out: string; initial_config: UserConfig; }} get_config
9
+ * @returns {Plugin}
10
+ */
11
+ export function plugin_service_worker_build(kit, get_config) {
12
+ return {
13
+ name: 'vite-plugin-sveltekit-service-worker',
14
+
15
+ config(config) {
16
+ const { service_worker_entry_file, kit_global, out, initial_config } = get_config();
17
+
18
+ if (!service_worker_entry_file) return;
19
+
20
+ if (kit.paths.assets) {
21
+ throw new Error('Cannot use service worker alongside config.paths.assets');
22
+ }
23
+
24
+ const user_service_worker_output_config =
25
+ config.environments?.serviceWorker?.build?.rolldownOptions?.output;
26
+
27
+ /** @type {UserConfig} */
28
+ const new_config = {
29
+ environments: {
30
+ serviceWorker: {
31
+ define: {
32
+ __SVELTEKIT_PAYLOAD__: kit_global
33
+ },
34
+ build: {
35
+ modulePreload: false,
36
+ rolldownOptions: {
37
+ external: [`${kit.paths.base}/${kit.appDir}/env.js`],
38
+ input: {
39
+ 'service-worker': service_worker_entry_file
40
+ },
41
+ output: {
42
+ format: 'es',
43
+ entryFileNames: 'service-worker.js',
44
+ assetFileNames: `${kit.appDir}/immutable/assets/[name].[hash][extname]`,
45
+ codeSplitting:
46
+ (Array.isArray(user_service_worker_output_config)
47
+ ? user_service_worker_output_config[0].codeSplitting
48
+ : user_service_worker_output_config?.codeSplitting) ?? false
49
+ }
50
+ },
51
+ outDir: `${out}/client`,
52
+ minify: initial_config.build?.minify,
53
+ // avoid overwriting the client build Vite manifest
54
+ manifest: '.vite/service-worker-manifest.json'
55
+ },
56
+ consumer: 'client'
57
+ }
58
+ }
59
+ };
60
+
61
+ warn_overridden_config(config, new_config);
62
+
63
+ return new_config;
64
+ },
65
+
66
+ // our serviceWorker environment only exists when building because Vite only
67
+ // supports the default client environment during development (for now)
68
+ applyToEnvironment(environment) {
69
+ return environment.name === 'serviceWorker';
70
+ },
71
+
72
+ generateBundle(_, bundle) {
73
+ const invalid_modules = new Set();
74
+ const modules = new Map([
75
+ [`${runtime_directory}/app/forms/index.js`, '$app/forms'],
76
+ [`${runtime_directory}/app/navigation/index.js`, '$app/navigation'],
77
+ [`${runtime_directory}/app/state/index.js`, '$app/state']
78
+ ]);
79
+
80
+ for (const output of Object.values(bundle)) {
81
+ if (output.type !== 'chunk') continue;
82
+
83
+ for (const id of output.moduleIds) {
84
+ const module = modules.get(id);
85
+ if (module) invalid_modules.add(module);
86
+ }
87
+ }
88
+
89
+ if (invalid_modules.size > 0) {
90
+ throw new Error(
91
+ `Cannot import ${Array.from(modules.values())
92
+ .filter((module) => invalid_modules.has(module))
93
+ .join(', ')} into service-worker code.`
94
+ );
95
+ }
96
+ }
97
+ };
98
+ }
@@ -94,7 +94,7 @@ export function find_deps(manifest, entry, add_dynamic_css, root) {
94
94
  imports: Array.from(imports),
95
95
  stylesheets: Array.from(stylesheets),
96
96
  // TODO do we need this separately?
97
- fonts: filter_fonts(assets),
97
+ fonts: filter_fonts(assets, manifest, root),
98
98
  stylesheet_map
99
99
  };
100
100
  }
@@ -116,12 +116,56 @@ export function resolve_symlinks(manifest, file, root) {
116
116
  return { chunk, file };
117
117
  }
118
118
 
119
+ /** @type {WeakMap<import('vite').Manifest, Map<string, string>>} */
120
+ const source_maps = new WeakMap();
121
+
119
122
  /**
120
123
  * @param {string[]} assets
121
- * @returns {string[]}
124
+ * @param {import('vite').Manifest} manifest
125
+ * @param {string} root
126
+ * @returns {import('types').FontDependency[]}
122
127
  */
123
- export function filter_fonts(assets) {
124
- return assets.filter((asset) => /\.(woff2?|ttf|otf)$/.test(asset));
128
+ export function filter_fonts(assets, manifest, root) {
129
+ let sources = source_maps.get(manifest);
130
+
131
+ if (!sources) {
132
+ sources = new Map();
133
+ source_maps.set(manifest, sources);
134
+
135
+ // identical files are emitted once but can have several sources — keep the first
136
+ for (const key of Object.keys(manifest).sort()) {
137
+ const { file, src } = manifest[key];
138
+ if (src && !sources.has(file)) {
139
+ sources.set(file, src);
140
+ }
141
+ }
142
+ }
143
+
144
+ return assets
145
+ .filter((asset) => /\.(woff2?|ttf|otf)$/.test(asset))
146
+ .map((file) => {
147
+ const src = sources.get(file) ?? file;
148
+ const marker = '/node_modules/';
149
+ const index = src.lastIndexOf(marker);
150
+
151
+ // Vite follows package manager symlinks when generating the manifest. Prefer the
152
+ // project-local node_modules path when it points to the same file.
153
+ if (index !== -1) {
154
+ const filename = `node_modules/${src.slice(index + marker.length)}`;
155
+ const source = path.resolve(root, src);
156
+ const unresolved = path.resolve(root, filename);
157
+
158
+ if (
159
+ fs.existsSync(source) &&
160
+ fs.existsSync(unresolved) &&
161
+ fs.realpathSync(unresolved) === fs.realpathSync(source)
162
+ ) {
163
+ return { file, filename };
164
+ }
165
+ }
166
+
167
+ return { file, filename: normalizePath(path.relative(root, path.resolve(root, src))) };
168
+ });
125
169
  }
126
170
 
127
171
  /**
@@ -25,6 +25,9 @@ import { SCHEME } from '../../../utils/url.js';
25
25
  import { check_feature } from '../../../utils/features.js';
26
26
  import { escape_html } from '../../../utils/escape.js';
27
27
  import { get_runner } from '../../../runner.js';
28
+ import { write_server } from '../../../core/sync/write_server.js';
29
+ import { write_tsconfig } from '../../../core/sync/write_tsconfig/index.js';
30
+ import create_manifest_data from '../../../core/sync/create_manifest_data/index.js';
28
31
 
29
32
  // vite-specifc queries that we should skip handling for css urls
30
33
  const vite_css_query_regex = /(?:\?|&)(?:raw|url|inline)(?:&|$)/;
@@ -74,7 +77,7 @@ export async function dev(
74
77
  return fetch(info, init);
75
78
  };
76
79
 
77
- sync.init(svelte_config, root);
80
+ write_tsconfig(svelte_config, root);
78
81
 
79
82
  /** @type {ManifestData} */
80
83
  let manifest_data;
@@ -86,6 +89,20 @@ export async function dev(
86
89
 
87
90
  const runner = get_runner(vite, vite_dev_server);
88
91
 
92
+ /**
93
+ * Log a response to the console, routed through Vite's logger so that it
94
+ * respects the configured `logLevel` and any `customLogger`
95
+ * @param {number} status
96
+ * @param {string} log
97
+ */
98
+ function log_dev_response(status, log) {
99
+ if (status < 400) {
100
+ vite_dev_server.config.logger.info(log);
101
+ } else {
102
+ vite_dev_server.config.logger.error(log);
103
+ }
104
+ }
105
+
89
106
  /**
90
107
  * @param {string} url
91
108
  * @returns {Promise<Record<string, any>>}
@@ -135,7 +152,8 @@ export async function dev(
135
152
 
136
153
  async function update_manifest() {
137
154
  try {
138
- ({ manifest_data } = sync.create(svelte_config, root));
155
+ manifest_data = create_manifest_data(svelte_config, root);
156
+ sync.create(svelte_config, root, manifest_data, false);
139
157
  set_manifest_data(manifest_data);
140
158
 
141
159
  await load_and_validate_params({
@@ -172,7 +190,7 @@ export async function dev(
172
190
  _: {
173
191
  client: {
174
192
  start: `${get_runtime_base(root)}/client/entry.js`,
175
- app: `${to_fs(svelte_config.outDir)}/generated/client/app.js`,
193
+ app: `${to_fs(svelte_config.outDir)}/generated/dev/client/app.js`,
176
194
  imports: [],
177
195
  stylesheets: [],
178
196
  fonts: [],
@@ -182,7 +200,7 @@ export async function dev(
182
200
  ? undefined
183
201
  : manifest_data.nodes.map((node, i) => {
184
202
  if (node.component || node.universal) {
185
- return `${svelte_config.paths.base}${to_fs(svelte_config.outDir)}/generated/client/nodes/${i}.js`;
203
+ return `${svelte_config.paths.base}${to_fs(svelte_config.outDir)}/generated/dev/client/nodes/${i}.js`;
186
204
  }
187
205
  }),
188
206
  // `css` is not necessary in dev, as the JS file from `nodes` will reference the CSS file
@@ -450,7 +468,7 @@ export async function dev(
450
468
  file.startsWith(serviceWorker) ||
451
469
  file.startsWith(hooks.server)
452
470
  ) {
453
- sync.server(svelte_config, root);
471
+ write_server(svelte_config, path.join(svelte_config.outDir, 'generated', 'dev'), root);
454
472
  }
455
473
  });
456
474
 
@@ -576,7 +594,7 @@ export async function dev(
576
594
  await runner.import(`${get_runtime_base(root)}/server/index.js`)
577
595
  );
578
596
 
579
- const { set_fix_stack_trace, log_response } = await runner.import(
597
+ const { set_fix_stack_trace, format_response } = await runner.import(
580
598
  `${get_runtime_base(root)}/server/internal.js`
581
599
  );
582
600
  set_fix_stack_trace(fix_stack_trace);
@@ -593,7 +611,8 @@ export async function dev(
593
611
 
594
612
  const request = getRequest({
595
613
  base,
596
- request: req
614
+ request: req,
615
+ response: res
597
616
  });
598
617
 
599
618
  if (manifest_error) {
@@ -642,11 +661,11 @@ export async function dev(
642
661
  if (rendered.status === 404) {
643
662
  // @ts-expect-error
644
663
  serve_static_middleware.handle(req, res, () => {
645
- log_response(rendered.status, request);
664
+ log_dev_response(rendered.status, format_response(rendered.status, request));
646
665
  setResponse(res, rendered);
647
666
  });
648
667
  } else {
649
- log_response(rendered.status, request);
668
+ log_dev_response(rendered.status, format_response(rendered.status, request));
650
669
  setResponse(res, rendered);
651
670
  }
652
671
  } catch (e) {
@@ -736,19 +755,3 @@ function has_correct_case(file, assets) {
736
755
 
737
756
  return false;
738
757
  }
739
-
740
- /**
741
- * Invalidates a module in all environments.
742
- * @param {ViteDevServer} server
743
- * @param {string} id
744
- * @returns {void}
745
- */
746
- export function invalidate_module(server, id) {
747
- for (const environment in server.environments) {
748
- const module = server.environments[environment].moduleGraph.getModuleById(id);
749
- if (module) {
750
- server.environments[environment].moduleGraph.invalidateModule(module);
751
- void server.environments[environment].reloadModule(module);
752
- }
753
- }
754
- }