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

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 (59) hide show
  1. package/package.json +5 -7
  2. package/src/core/adapt/builder.js +127 -31
  3. package/src/core/adapt/index.js +3 -0
  4. package/src/core/config/index.js +4 -7
  5. package/src/core/env.js +36 -8
  6. package/src/core/generate_manifest/index.js +42 -43
  7. package/src/core/postbuild/analyse.js +4 -4
  8. package/src/core/postbuild/fallback.js +1 -1
  9. package/src/core/postbuild/prerender.js +2 -2
  10. package/src/core/sync/create_manifest_data/index.js +0 -1
  11. package/src/core/sync/write_app_manifest.js +3 -2
  12. package/src/exports/public.d.ts +100 -48
  13. package/src/exports/vite/build/index.js +1173 -0
  14. package/src/exports/vite/build/remote.js +4 -4
  15. package/src/exports/vite/dev/generate_manifest.js +308 -0
  16. package/src/exports/vite/dev/index.js +28 -284
  17. package/src/exports/vite/index.js +117 -1308
  18. package/src/exports/vite/plugins/env-vars.js +11 -34
  19. package/src/exports/vite/plugins/guard.js +19 -7
  20. package/src/exports/vite/plugins/remote.js +237 -0
  21. package/src/exports/vite/preview/index.js +8 -8
  22. package/src/exports/vite/static_analysis/index.js +15 -15
  23. package/src/exports/vite/utils.js +1 -1
  24. package/src/runner.js +4 -3
  25. package/src/runtime/app/paths/server.js +2 -2
  26. package/src/runtime/app/server/index.js +3 -3
  27. package/src/runtime/app/server/public.d.ts +114 -57
  28. package/src/runtime/app/server/remote/requested.js +31 -15
  29. package/src/runtime/app/state/client.svelte.js +3 -1
  30. package/src/runtime/client/client.js +39 -194
  31. package/src/runtime/client/fetcher.js +6 -6
  32. package/src/runtime/client/focus.js +120 -0
  33. package/src/runtime/client/remote-functions/command.svelte.js +20 -9
  34. package/src/runtime/client/remote-functions/form.svelte.js +12 -7
  35. package/src/runtime/client/remote-functions/query/instance.svelte.js +19 -5
  36. package/src/runtime/client/remote-functions/shared.svelte.js +22 -1
  37. package/src/runtime/client/scroll.js +39 -0
  38. package/src/runtime/client/utils.js +28 -0
  39. package/src/runtime/form-utils.js +243 -245
  40. package/src/runtime/server/data/index.js +3 -10
  41. package/src/runtime/server/fetch.js +13 -15
  42. package/src/runtime/server/index.js +8 -7
  43. package/src/runtime/server/internal.js +1 -2
  44. package/src/runtime/server/page/index.js +10 -15
  45. package/src/runtime/server/page/load_data.js +2 -2
  46. package/src/runtime/server/page/render.js +8 -13
  47. package/src/runtime/server/page/respond_with_error.js +4 -5
  48. package/src/runtime/server/page/server_routing.js +21 -27
  49. package/src/runtime/server/remote-functions.js +14 -14
  50. package/src/runtime/server/respond.js +17 -40
  51. package/src/runtime/server/state.js +1 -0
  52. package/src/runtime/server/utils.js +4 -4
  53. package/src/runtime/shared.js +9 -0
  54. package/src/types/ambient-private.d.ts +1 -2
  55. package/src/types/internal.d.ts +49 -6
  56. package/src/utils/filesystem.js +43 -27
  57. package/src/version.js +1 -1
  58. package/types/index.d.ts +215 -92
  59. package/types/index.d.ts.map +6 -2
@@ -0,0 +1,1173 @@
1
+ /** @import { TopLevelFilterExpression } from '@rolldown/pluginutils' */
2
+ /** @import { EnvVarConfig } from '@sveltejs/kit/env' */
3
+ /** @import { BuildData, ManifestData, Prerendered, ServerMetadata, RemoteChunk, ValidatedConfig } from 'types' */
4
+ /** @import { Manifest, Plugin, ResolvedConfig, Rolldown, UserConfig } from 'vite' */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import process from 'node:process';
8
+ import { styleText } from 'node:util';
9
+ import { code, include } from '@rolldown/pluginutils';
10
+ import { build_server_nodes } from './build_server.js';
11
+ import { treeshake_prerendered_remotes } from './remote.js';
12
+ import { find_deps, resolve_symlinks } from './utils.js';
13
+ import { warn_overridden_config } from '../utils.js';
14
+ import { adapt } from '../../../core/adapt/index.js';
15
+ import { generate_manifest } from '../../../core/generate_manifest/index.js';
16
+ import analyse from '../../../core/postbuild/analyse.js';
17
+ import prerender from '../../../core/postbuild/prerender.js';
18
+ import * as sync from '../../../core/sync/sync.js';
19
+ import create_manifest_data from '../../../core/sync/create_manifest_data/index.js';
20
+ import { get_manifest_routes } from '../../../core/sync/write_app_manifest.js';
21
+ import { write_client_manifest } from '../../../core/sync/write_client_manifest.js';
22
+ import { logger, runtime_directory } from '../../../core/utils.js';
23
+ import { compact } from '../../../utils/array.js';
24
+ import { copy, read, resolve_entry, walk } from '../../../utils/filesystem.js';
25
+ import { load_and_validate_params } from '../../../utils/params.js';
26
+ import { posixify } from '../../../utils/os.js';
27
+ import { stackless } from '../../../utils/error.js';
28
+ import { s } from '../../../utils/misc.js';
29
+
30
+ /**
31
+ * @typedef {object} Config
32
+ * @property {typeof import('vite')} vite
33
+ * @property {UserConfig} initial_config
34
+ * @property {string} root
35
+ * @property {string} global_name
36
+ * @property {string} kit_global
37
+ * @property {string | null} service_worker_entry_file
38
+ * @property {(relative_path: string) => boolean} sourcemapIgnoreList
39
+ */
40
+
41
+ /**
42
+ * @param {ValidatedConfig} kit
43
+ * @param {() => Config} get_config
44
+ * @param {(metadata: ServerMetadata) => void} set_build_metadata
45
+ * @param {(data: ManifestData) => void} set_manifest_data
46
+ * @param {() => Record<string, EnvVarConfig<any>> | null} get_explicit_env_config
47
+ * @param {() => ({ remotes: RemoteChunk[]; remote_original_by_hash: Map<string, string> })} get_remote_metadata
48
+ * @param {(fn: () => Promise<void>) => void} set_finalise
49
+ * @returns {Plugin}
50
+ */
51
+ export function plugin_compile(
52
+ kit,
53
+ get_config,
54
+ set_build_metadata,
55
+ set_manifest_data,
56
+ get_explicit_env_config,
57
+ get_remote_metadata,
58
+ set_finalise
59
+ ) {
60
+ /** @type {typeof import('vite')} */
61
+ let vite;
62
+ /** @type {UserConfig} */
63
+ let initial_config;
64
+ /** @type {string} */
65
+ let root;
66
+ /** @type {string} */
67
+ let global_name;
68
+ /** @type {string | null} */
69
+ let service_worker_entry_file;
70
+
71
+ /** @type {string} */
72
+ let out_dir;
73
+ /** @type {string} */
74
+ let out;
75
+ /** @type {Record<string, string>} */
76
+ let env;
77
+ /** @type {ManifestData} */
78
+ let manifest_data;
79
+
80
+ /** @type {ResolvedConfig} */
81
+ let vite_config;
82
+
83
+ /** @type {Map<string, Rolldown.RolldownOutput['output']>} */
84
+ const watch_build_output = new Map();
85
+ /**
86
+ * A map showing which features (such as `$app/server:read`) are defined
87
+ * in which chunks, so that we can later determine which routes use which features
88
+ * @type {Record<string, string[]>}
89
+ */
90
+ let tracked_features = {};
91
+ /** @type {Manifest} */
92
+ let vite_server_manifest;
93
+ /** @type {Manifest | null} */
94
+ let vite_client_manifest = null;
95
+ /** @type {Prerendered} */
96
+ let prerendered;
97
+
98
+ return {
99
+ name: 'vite-plugin-sveltekit-compile',
100
+
101
+ apply: 'build',
102
+
103
+ /**
104
+ * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
105
+ * @see https://vitejs.dev/guide/api-plugin.html#config
106
+ */
107
+ config: {
108
+ // avoids overwriting the base setting that's also set by Vitest
109
+ order: 'pre',
110
+ handler(config, config_env) {
111
+ let kit_global;
112
+ let sourcemapIgnoreList;
113
+
114
+ ({
115
+ root,
116
+ global_name,
117
+ kit_global,
118
+ vite,
119
+ initial_config,
120
+ service_worker_entry_file,
121
+ sourcemapIgnoreList
122
+ } = get_config());
123
+
124
+ const app_immutable = `${kit.appDir}/immutable`;
125
+
126
+ out_dir = posixify(kit.outDir);
127
+ out = `${out_dir}/output`;
128
+
129
+ env = vite.loadEnv(config_env.mode, kit.env.dir, '');
130
+
131
+ /** @type {Record<string, string>} */
132
+ const server_input = {
133
+ index: `${runtime_directory}/server/index.js`,
134
+ internal: `<sveltekit:generated>/server.js`,
135
+ env: '<sveltekit:generated>/env/config.js',
136
+ ['remote-entry']: `${runtime_directory}/app/server/remote/index.js`
137
+ };
138
+
139
+ manifest_data = create_manifest_data(kit, root);
140
+ set_manifest_data(manifest_data);
141
+ sync.all(kit, root, manifest_data);
142
+
143
+ // add entry points for every endpoint...
144
+ manifest_data.routes.forEach((route) => {
145
+ if (route.endpoint) {
146
+ const resolved = path.resolve(root, route.endpoint.file);
147
+ const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
148
+ const name = posixify(path.join('entries/endpoints', relative.replace(/\.js$/, '')));
149
+ server_input[name] = resolved;
150
+ }
151
+ });
152
+
153
+ // ...and every component used by pages...
154
+ manifest_data.nodes.forEach((node) => {
155
+ for (const file of [node.component, node.universal, node.server]) {
156
+ if (file) {
157
+ const resolved = path.resolve(root, file);
158
+ const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
159
+
160
+ const name = relative.startsWith('..')
161
+ ? posixify(path.join('entries/fallbacks', path.basename(file)))
162
+ : posixify(path.join('entries/pages', relative.replace(/\.js$/, '')));
163
+ server_input[name] = resolved;
164
+ }
165
+ }
166
+ });
167
+
168
+ // ...and the params file
169
+ if (manifest_data.params) {
170
+ server_input['entries/params'] = path.resolve(root, manifest_data.params);
171
+ }
172
+
173
+ // ...and the hooks files
174
+ if (manifest_data.hooks.server) {
175
+ server_input['entries/hooks.server'] = path.resolve(root, manifest_data.hooks.server);
176
+ }
177
+ if (manifest_data.hooks.universal) {
178
+ server_input['entries/hooks.universal'] = path.resolve(
179
+ root,
180
+ manifest_data.hooks.universal
181
+ );
182
+ }
183
+
184
+ // ...and the server instrumentation file
185
+ const server_instrumentation = resolve_entry(
186
+ path.join(kit.files.src, 'instrumentation.server')
187
+ );
188
+ if (server_instrumentation) {
189
+ if (kit.adapter && !kit.adapter.supports?.instrumentation?.()) {
190
+ throw new Error(`${server_instrumentation} is unsupported in ${kit.adapter.name}.`);
191
+ }
192
+ server_input['instrumentation.server'] = server_instrumentation;
193
+ }
194
+
195
+ /** @type {Record<string, string>} */
196
+ const client_input = {};
197
+
198
+ if (kit.output.bundleStrategy !== 'split') {
199
+ client_input['bundle'] = `${runtime_directory}/client/bundle.js`;
200
+ } else {
201
+ client_input['entry/start'] = `${runtime_directory}/client/entry.js`;
202
+ client_input['entry/payload'] = `${runtime_directory}/client/payload.js`;
203
+ client_input['entry/app'] = `${out_dir}/generated/build/client-optimized/app.js`;
204
+ manifest_data.nodes.forEach((node, i) => {
205
+ if (node.component || node.universal) {
206
+ client_input[`nodes/${i}`] =
207
+ `${out_dir}/generated/build/client-optimized/nodes/${i}.js`;
208
+ }
209
+ });
210
+ }
211
+
212
+ const inline = kit.output.bundleStrategy === 'inline';
213
+
214
+ /** @type {string} */
215
+ const base = (kit.paths.assets || kit.paths.base) + '/';
216
+ const root_to_assets = app_immutable + '/assets/';
217
+ const assets_to_root =
218
+ app_immutable
219
+ .split('/')
220
+ .map(() => '..')
221
+ .join('/') + '/../';
222
+
223
+ const relative = kit.paths.relative !== false || !!kit.paths.assets;
224
+
225
+ /** @satisfies {UserConfig} */
226
+ const new_config = {
227
+ // Affects how Vite loads JS assets on the client.
228
+ // If the initial HTML we render uses an absolute path for assets,
229
+ // the additional chunks Vite loads must also use an absolute path.
230
+ // Otherwise, you end up with additional chunks being loaded relative
231
+ // to the current chunk rather than the root.
232
+ base: relative ? './' : base,
233
+ build: {
234
+ cssCodeSplit: !inline,
235
+ cssMinify: initial_config.build?.minify == null ? true : !!initial_config.build.minify,
236
+ manifest: true,
237
+ rolldownOptions: {
238
+ output: {
239
+ name: `${global_name}.app`,
240
+ assetFileNames: `${app_immutable}/assets/[name].[hash][extname]`,
241
+ hoistTransitiveImports: false,
242
+ sourcemapIgnoreList
243
+ },
244
+ preserveEntrySignatures: 'strict',
245
+ onwarn(warning, handler) {
246
+ if (
247
+ warning.code === 'IMPORT_IS_UNDEFINED' &&
248
+ warning.id === `${out_dir}/generated/build/client-optimized/app.js`
249
+ ) {
250
+ // ignore e.g. undefined `handleError` hook when
251
+ // referencing `client_hooks.handleError`
252
+ return;
253
+ }
254
+
255
+ handler(warning);
256
+ },
257
+ watch: {
258
+ exclude: [
259
+ // Ignore all siblings of config.outDir/generated
260
+ `${out_dir}/generated/**`
261
+ ]
262
+ }
263
+ },
264
+ emptyOutDir: false,
265
+ ssrEmitAssets: true
266
+ },
267
+ worker: {
268
+ rolldownOptions: {
269
+ output: {
270
+ entryFileNames: `${app_immutable}/workers/[name]-[hash].js`,
271
+ chunkFileNames: `${app_immutable}/workers/chunks/[hash].js`,
272
+ assetFileNames: `${app_immutable}/workers/assets/[name]-[hash][extname]`,
273
+ hoistTransitiveImports: false
274
+ }
275
+ }
276
+ },
277
+ builder: {
278
+ sharedConfigBuild: true,
279
+ sharedPlugins: true
280
+ },
281
+ environments: {
282
+ ssr: {
283
+ build: {
284
+ copyPublicDir: false,
285
+ outDir: `${out}/server`,
286
+ target: 'node22',
287
+ rolldownOptions: {
288
+ input: server_input,
289
+ output: {
290
+ entryFileNames: '[name].js',
291
+ chunkFileNames: 'chunks/[name].js'
292
+ }
293
+ }
294
+ },
295
+ // these are stubs that will be replaced after the initial server build
296
+ define: {
297
+ __SVELTEKIT_HAS_SERVER_LOAD__: 'true',
298
+ __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true',
299
+ __SVELTEKIT_PAYLOAD__: '{}'
300
+ }
301
+ },
302
+ client: {
303
+ build: {
304
+ outDir: `${out}/client`,
305
+ rolldownOptions: {
306
+ input: inline ? client_input['bundle'] : client_input,
307
+ output: {
308
+ format: inline ? 'iife' : 'esm',
309
+ entryFileNames: `${app_immutable}/[name].[hash].js`,
310
+ chunkFileNames: (/** @type {Rolldown.PreRenderedChunk} */ chunk_info) => {
311
+ // The manifest data chunk gets a fixed (non-hashed) filename so
312
+ // that importers' content hashes are stable regardless of the
313
+ // manifest content — this breaks the content-hash feedback loop
314
+ if (chunk_info.name === 'sveltekit-manifest') {
315
+ return `${kit.appDir}/manifest.js`;
316
+ }
317
+ return `${app_immutable}/chunks/[hash].js`;
318
+ },
319
+ codeSplitting:
320
+ kit.output.bundleStrategy === 'split'
321
+ ? {
322
+ groups: [
323
+ {
324
+ name: 'sveltekit-manifest',
325
+ test: '<sveltekit:generated>/app-manifest.js'
326
+ }
327
+ ]
328
+ }
329
+ : false
330
+ },
331
+ // This silences Rolldown warnings about not supporting `import.meta`
332
+ // for the `iife` output format. We don't care because it's
333
+ // only used in development and will be treeshaken away
334
+ transform: inline
335
+ ? {
336
+ define: {
337
+ 'import.meta': '{}'
338
+ }
339
+ }
340
+ : undefined
341
+ }
342
+ },
343
+ define: {
344
+ __SVELTEKIT_PAYLOAD__:
345
+ kit.output.bundleStrategy !== 'split' ? kit_global : 'undefined'
346
+ }
347
+ }
348
+ },
349
+ experimental: {
350
+ // Allows us to use relative paths in as many places as we can
351
+ renderBuiltUrl(filename, { ssr, hostType }) {
352
+ if (hostType === 'js') {
353
+ // SSR builds should use an absolute path in JS modules to
354
+ // match the default Vite behaviour
355
+ if (ssr) return base + filename;
356
+
357
+ // We could always use a relative asset base path here, but it's better for performance not to.
358
+ // E.g. Vite generates `new URL('/asset.png', import.meta).href` for a relative path vs just '/asset.png'.
359
+ // That's larger and takes longer to run and also causes an HTML diff between SSR and client
360
+ // causing us to do a more expensive hydration check.
361
+ return { relative };
362
+ }
363
+
364
+ if (!relative) return;
365
+
366
+ // ensure assets loaded by CSS files are loaded relative to the
367
+ // CSS file rather than the default of relative to the root
368
+
369
+ // _app/immutable/assets files
370
+ if (filename.startsWith(root_to_assets)) {
371
+ return `./${filename.slice(root_to_assets.length)}`;
372
+ }
373
+
374
+ // static dir files
375
+ return assets_to_root + filename;
376
+ }
377
+ }
378
+ };
379
+
380
+ warn_overridden_config(config, new_config);
381
+
382
+ return new_config;
383
+ }
384
+ },
385
+
386
+ configResolved(resolved_config) {
387
+ vite_config = resolved_config;
388
+ },
389
+
390
+ applyToEnvironment(environment) {
391
+ return environment.name !== 'serviceWorker';
392
+ },
393
+
394
+ renderChunk: {
395
+ // composable filters are not accepted type-wise but still work during build
396
+ // see https://github.com/vitejs/rolldown-vite/issues/605
397
+ filter: /** @type {any} */ (
398
+ /** @satisfies {TopLevelFilterExpression[]} */ ([include(code('__SVELTEKIT_TRACK__'))])
399
+ ),
400
+ handler(code, chunk) {
401
+ // composable filters only work during build so we still need this guard for dev
402
+ // see https://github.com/vitejs/rolldown-vite/issues/605
403
+ if (code.includes('__SVELTEKIT_TRACK__')) {
404
+ return {
405
+ // Rolldown changes our single quotes to double quotes so we need it in the regex too
406
+ code: code.replace(/__SVELTEKIT_TRACK__\(['"](.+?)['"]\)/g, (_, label) => {
407
+ (tracked_features[chunk.name + '.js'] ??= []).push(label);
408
+ // put extra whitespace at the end of the comment to preserve the source size and avoid interfering with source maps
409
+ return `/* track ${label} */`;
410
+ }),
411
+ map: null // TODO we may need to generate a sourcemap in future
412
+ };
413
+ }
414
+ }
415
+ },
416
+
417
+ generateBundle(_options, bundle) {
418
+ // a watched build returns a watcher rather than the build output from
419
+ // `builder.build` so we need to retrieve it from the generateBundle hook
420
+ if (this.meta.watchMode) {
421
+ watch_build_output.set(
422
+ this.environment.name,
423
+ /** @type {Rolldown.RolldownOutput['output']} */ (Object.values(bundle))
424
+ );
425
+ }
426
+
427
+ if (this.environment.config.consumer !== 'client') return;
428
+
429
+ this.emitFile({
430
+ type: 'asset',
431
+ fileName: `${kit.appDir}/version.json`,
432
+ source: s({ version: kit.version.name })
433
+ });
434
+ },
435
+
436
+ async buildApp(builder) {
437
+ fs.rmSync(out, { force: true, recursive: true });
438
+ fs.mkdirSync(out, { recursive: true });
439
+
440
+ const verbose = builder.config.logLevel === 'info' || builder.config.logLevel === undefined;
441
+ const log = logger({ verbose });
442
+
443
+ /** @type {(() => Promise<void>) | null} */
444
+ let finalise;
445
+
446
+ let ssr_build = await builder.build(builder.environments.ssr);
447
+
448
+ /** @param {Rolldown.RolldownOutput['output']} server_chunks */
449
+ const process_ssr_build = async (server_chunks) => {
450
+ // Replace manifest placeholders in SSR output. `assets` and `routes`
451
+ // are known from `manifest_data`. `immutable` and `prerendered` are not
452
+ // known yet — they get sentinel strings that are replaced after
453
+ // the client build and after prerendering respectively.
454
+ replace_manifest_placeholder_variables(server_chunks, `${out}/server`, {
455
+ assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
456
+ routes: get_manifest_routes(manifest_data.routes)
457
+ });
458
+
459
+ vite_server_manifest = /** @type {Manifest} */ (
460
+ JSON.parse(read(`${out}/server/.vite/manifest.json`))
461
+ );
462
+
463
+ const manifest_path = `${out}/server/manifest-full.js`;
464
+ const assets_path = `${kit.appDir}/immutable/assets`;
465
+ const workers_path = `${kit.appDir}/immutable/workers`;
466
+
467
+ /** @type {BuildData} */
468
+ const build_data = {
469
+ app_dir: kit.appDir,
470
+ app_path: `${kit.paths.base.slice(1)}${kit.paths.base ? '/' : ''}${kit.appDir}`,
471
+ manifest_data,
472
+ out_dir: out,
473
+ service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
474
+ client: null,
475
+ server_manifest: vite_server_manifest
476
+ };
477
+
478
+ const { remotes, remote_original_by_hash } = get_remote_metadata();
479
+
480
+ fs.writeFileSync(
481
+ manifest_path,
482
+ `export const manifest = ${generate_manifest({
483
+ build_data,
484
+ prerendered: [],
485
+ relative_path: '.',
486
+ routes: manifest_data.routes,
487
+ remotes,
488
+ root
489
+ })};\n`
490
+ );
491
+
492
+ // first, build server nodes without the client manifest so we can analyse it
493
+ build_server_nodes(
494
+ out,
495
+ kit,
496
+ manifest_data,
497
+ vite_server_manifest,
498
+ null,
499
+ assets_path,
500
+ server_chunks,
501
+ root
502
+ );
503
+
504
+ log.info('Analysing routes');
505
+
506
+ const { metadata } = await analyse({
507
+ hash: kit.router.type === 'hash',
508
+ manifest_path,
509
+ manifest_data,
510
+ server_manifest: vite_server_manifest,
511
+ tracked_features,
512
+ env,
513
+ remotes,
514
+ vite_config_file: vite_config.configFile
515
+ });
516
+ set_build_metadata(metadata);
517
+
518
+ log.info('Building app');
519
+
520
+ /** @type {Record<string, EnvVarConfig<any>> | null} */
521
+ const explicit_env_config = get_explicit_env_config();
522
+ /** @type {Array<{ path: string }> | null} */
523
+ let immutable = null;
524
+
525
+ const server_assets = `${out}/server/${assets_path}`;
526
+ const client_assets = `${out}/client/${assets_path}`;
527
+
528
+ const skip_client_build = manifest_data.nodes.every(
529
+ (node) => node.page_options?.csr === false
530
+ );
531
+
532
+ if (
533
+ !skip_client_build &&
534
+ kit.csp.directives['require-trusted-types-for']?.includes('script') &&
535
+ !kit.csp.directives['trusted-types']?.includes('svelte-trusted-html')
536
+ ) {
537
+ throw new Error(
538
+ "The `csp.directives['trusted-types']` option must include 'svelte-trusted-html' unless all pages have `csr: false`"
539
+ );
540
+ }
541
+
542
+ if (skip_client_build) {
543
+ copy(server_assets, client_assets);
544
+ copy(`${out}/server/${workers_path}`, `${out}/client/${workers_path}`);
545
+ copy(kit.files.assets, `${out}/client`);
546
+ } else {
547
+ // ...and build the client
548
+ write_client_manifest(
549
+ kit,
550
+ manifest_data,
551
+ `${out_dir}/generated/build/client-optimized`,
552
+ root,
553
+ metadata.nodes
554
+ );
555
+
556
+ // Through the finished analysis we can now check if any node has server or universal load functions
557
+ const nodes = Object.values(metadata.nodes);
558
+ const has_server_load = nodes.some((node) => node.has_server_load);
559
+ const has_universal_load = nodes.some((node) => node.has_universal_load);
560
+
561
+ if (builder.environments.client.config.define) {
562
+ builder.environments.client.config.define.__SVELTEKIT_HAS_SERVER_LOAD__ =
563
+ s(has_server_load);
564
+ builder.environments.client.config.define.__SVELTEKIT_HAS_UNIVERSAL_LOAD__ =
565
+ s(has_universal_load);
566
+ }
567
+
568
+ const client_build = await builder.build(builder.environments.client);
569
+ const client_chunks = await normalise_build(
570
+ builder.environments.client.name,
571
+ client_build,
572
+ watch_build_output
573
+ );
574
+
575
+ // We use `build.ssrEmitAssets` so that asset URLs created from
576
+ // imports in server-only modules correspond to files in the build,
577
+ // but we don't want to copy over CSS imports as these are already
578
+ // accounted for in the client bundle. In most cases it would be
579
+ // a no-op, but for SSR builds `url(...)` paths are handled
580
+ // differently (relative for client, absolute for server)
581
+ // resulting in different hashes, and thus duplication
582
+ const ssr_stylesheets = new Set(
583
+ Object.values(vite_server_manifest)
584
+ .map((chunk) => chunk.css ?? [])
585
+ .flat()
586
+ );
587
+
588
+ if (fs.existsSync(server_assets)) {
589
+ for (const file of fs.readdirSync(server_assets)) {
590
+ const src = `${server_assets}/${file}`;
591
+ const dest = `${client_assets}/${file}`;
592
+
593
+ if (fs.existsSync(dest) || ssr_stylesheets.has(`${assets_path}/${file}`)) {
594
+ continue;
595
+ }
596
+
597
+ copy(src, dest);
598
+ }
599
+ }
600
+
601
+ // worker files emitted by the server build (e.g. `?worker&url` imports
602
+ // in server-only modules) must be served to the client, too
603
+ copy(`${out}/server/${workers_path}`, `${out}/client/${workers_path}`);
604
+
605
+ vite_client_manifest = /** @type {Manifest} */ (
606
+ JSON.parse(read(`${out}/client/.vite/manifest.json`))
607
+ );
608
+
609
+ /**
610
+ * @param {string} entry
611
+ * @param {boolean} [add_dynamic_css]
612
+ */
613
+ const deps_of = (entry, add_dynamic_css = false) =>
614
+ find_deps(
615
+ /** @type {Manifest} */ (vite_client_manifest),
616
+ posixify(path.relative(root, entry)),
617
+ add_dynamic_css,
618
+ root
619
+ );
620
+
621
+ // the inline bundle and stylesheet are deleted further down, after
622
+ // being inlined into the page, so they must not appear in `immutable`
623
+ /** @type {Set<string>} */
624
+ const inlined = new Set();
625
+ /** @type {Rolldown.OutputAsset | undefined} */
626
+ let inline_style;
627
+
628
+ if (kit.output.bundleStrategy === 'inline') {
629
+ inline_style = /** @type {Rolldown.OutputAsset | undefined} */ (
630
+ client_chunks.find(
631
+ (chunk) =>
632
+ chunk.type === 'asset' &&
633
+ chunk.names.length === 1 &&
634
+ chunk.names[0] === 'style.css'
635
+ )
636
+ );
637
+
638
+ inlined.add(deps_of(`${runtime_directory}/client/bundle.js`).file);
639
+ if (inline_style) inlined.add(inline_style.fileName);
640
+ }
641
+
642
+ // Replace manifest placeholders in client output. `immutable` is
643
+ // computed from the Vite client manifest, `assets` and `routes`
644
+ // from `manifest_data`. `prerendered` is left as a placeholder
645
+ // for now — it's replaced after prerendering completes.
646
+ immutable = collect_immutable(vite_client_manifest, kit.appDir, inlined);
647
+
648
+ replace_manifest_placeholder_variables(client_chunks, `${out}/client`, {
649
+ immutable,
650
+ assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
651
+ routes: get_manifest_routes(manifest_data.routes)
652
+ });
653
+
654
+ // Now that the client build is done, replace the `build` sentinel
655
+ // in the SSR output with the real build files
656
+ replace_manifest_placeholder_strings(`${out}/server`, { immutable });
657
+
658
+ const has_explicit_dynamic_public_env = Object.values(explicit_env_config ?? {}).some(
659
+ (variable) => variable.public && !variable.static
660
+ );
661
+
662
+ // the app only depends on runtime public env if it imports `$app/env/public`
663
+ // *and* at least one public env var is actually dynamic (non-static)
664
+ const uses_env_dynamic_public =
665
+ has_explicit_dynamic_public_env &&
666
+ client_chunks.some(
667
+ (chunk) =>
668
+ chunk.type === 'chunk' &&
669
+ chunk.modules[
670
+ posixify(fs.realpathSync(`${out_dir}/generated/build/env/public/client.js`))
671
+ ]
672
+ );
673
+
674
+ if (kit.output.bundleStrategy === 'split') {
675
+ const start_entry = posixify(
676
+ path.relative(root, `${runtime_directory}/client/entry.js`)
677
+ );
678
+ const start = find_deps(vite_client_manifest, start_entry, false, root);
679
+ const runtime_entry = resolve_symlinks(vite_client_manifest, start_entry, root).chunk
680
+ .dynamicImports?.[0]; // client/entry.js dynamically imports client/client-entry.js
681
+ if (!runtime_entry) throw new Error('Could not find the client runtime chunk');
682
+ const runtime = find_deps(vite_client_manifest, runtime_entry, false, root);
683
+ const app = deps_of(`${out_dir}/generated/build/client-optimized/app.js`);
684
+
685
+ build_data.client = {
686
+ start: start.file,
687
+ app: app.file,
688
+ imports: Array.from(
689
+ new Set([
690
+ ...start.imports,
691
+ runtime.file,
692
+ ...runtime.imports,
693
+ app.file,
694
+ ...app.imports
695
+ ])
696
+ ),
697
+ stylesheets: [...start.stylesheets, ...runtime.stylesheets, ...app.stylesheets],
698
+ fonts: [...start.fonts, ...runtime.fonts, ...app.fonts],
699
+ uses_env_dynamic_public
700
+ };
701
+
702
+ // In case of server-side route resolution, we create a purpose-built route manifest that is
703
+ // similar to that on the client, with as much information computed upfront so that we
704
+ // don't need to include any code of the actual routes in the server bundle.
705
+ if (kit.router.resolution === 'server') {
706
+ const nodes = manifest_data.nodes.map((node, i) => {
707
+ if (node.component || node.universal) {
708
+ const entry = `${out_dir}/generated/build/client-optimized/nodes/${i}.js`;
709
+ const deps = deps_of(entry, true);
710
+ const file = resolve_symlinks(
711
+ /** @type {Manifest} */ (vite_client_manifest),
712
+ `${out_dir}/generated/build/client-optimized/nodes/${i}.js`,
713
+ root
714
+ ).chunk.file;
715
+
716
+ return { file, css: deps.stylesheets };
717
+ }
718
+ });
719
+ build_data.client.nodes = nodes.map((node) => node?.file);
720
+ build_data.client.css = nodes.map((node) => node?.css);
721
+
722
+ build_data.client.routes = compact(
723
+ manifest_data.routes.map((route) => {
724
+ if (!route.page) return;
725
+
726
+ return {
727
+ id: route.id,
728
+ pattern: route.pattern,
729
+ params: route.params,
730
+ layouts: route.page.layouts.map((l) =>
731
+ l !== undefined ? [metadata.nodes[l].has_server_load, l] : undefined
732
+ ),
733
+ errors: route.page.errors,
734
+ leaf: [metadata.nodes[route.page.leaf].has_server_load, route.page.leaf]
735
+ };
736
+ })
737
+ );
738
+ }
739
+ } else {
740
+ const start = deps_of(`${runtime_directory}/client/bundle.js`);
741
+
742
+ build_data.client = {
743
+ start: start.file,
744
+ imports: start.imports,
745
+ stylesheets: start.stylesheets,
746
+ fonts: start.fonts,
747
+ uses_env_dynamic_public
748
+ };
749
+
750
+ if (kit.output.bundleStrategy === 'inline') {
751
+ build_data.client.inline = {
752
+ script: read(`${out}/client/${start.file}`),
753
+ style: /** @type {string | undefined} */ (inline_style?.source)
754
+ };
755
+
756
+ // the bundle and stylesheet are inlined into the page, so the
757
+ // emitted files are never loaded
758
+ fs.unlinkSync(`${out}/client/${start.file}`);
759
+ fs.rmSync(`${out}/client/${start.file}.map`, { force: true });
760
+ if (inline_style) fs.unlinkSync(`${out}/client/${inline_style.fileName}`);
761
+ }
762
+ }
763
+
764
+ // regenerate manifest now that we have client entry...
765
+ fs.writeFileSync(
766
+ manifest_path,
767
+ `export const manifest = ${generate_manifest({
768
+ build_data,
769
+ prerendered: [],
770
+ relative_path: '.',
771
+ routes: manifest_data.routes,
772
+ remotes,
773
+ root
774
+ })};\n`
775
+ );
776
+
777
+ // regenerate nodes with the client manifest...
778
+ build_server_nodes(
779
+ out,
780
+ kit,
781
+ manifest_data,
782
+ vite_server_manifest,
783
+ vite_client_manifest,
784
+ assets_path,
785
+ client_chunks,
786
+ root
787
+ );
788
+ }
789
+
790
+ // ...and prerender
791
+ let prerender_results;
792
+ try {
793
+ prerender_results = await prerender({
794
+ hash: kit.router.type === 'hash',
795
+ out,
796
+ manifest_path,
797
+ metadata,
798
+ verbose,
799
+ env,
800
+ vite_config_file: vite_config.configFile,
801
+ is_tty: process.stdout.isTTY
802
+ });
803
+
804
+ // this silly hack is necessary to ensure that stderr from prerender is flushed before we continue
805
+ await new Promise((f) => setTimeout(f, 0));
806
+ } catch (e) {
807
+ if (e instanceof Error && e.message === '__handled__') {
808
+ // error details are already logged inside `prerender`, don't duplicate them
809
+ throw stackless('Prerendering failed');
810
+ } else {
811
+ // Unforeseen error, rethrow as-is
812
+ throw e;
813
+ }
814
+ }
815
+
816
+ prerendered = prerender_results.prerendered;
817
+
818
+ // Replace the `prerendered` sentinel in both SSR and client output
819
+ // with the real prerendered paths. The other sentinels (`build`)
820
+ // were already replaced after the client build.
821
+ const prerendered_paths = prerendered.paths.map((p) => {
822
+ return { path: p.replace(kit.paths.base, '').slice(1) };
823
+ });
824
+
825
+ replace_manifest_placeholder_strings(`${out}/server`, { prerendered: prerendered_paths });
826
+ replace_manifest_placeholder_strings(`${out}/client`, { prerendered: prerendered_paths });
827
+
828
+ // For `inline` strategy, the entry file was deleted and read into
829
+ // `build_data.client.inline.script` — replace the sentinel there too
830
+ if (build_data.client?.inline?.script) {
831
+ build_data.client.inline.script = build_data.client.inline.script.replaceAll(
832
+ '"__sveltekit_manifest_prerendered__"',
833
+ JSON.stringify(prerendered_paths)
834
+ );
835
+ }
836
+
837
+ // generate a new manifest that doesn't include prerendered pages
838
+ fs.writeFileSync(
839
+ `${out}/server/manifest.js`,
840
+ `export const manifest = ${generate_manifest({
841
+ build_data,
842
+ prerendered: prerendered.paths,
843
+ relative_path: '.',
844
+ routes: manifest_data.routes.filter(
845
+ (route) => prerender_results.prerender_map.get(route.id) !== true
846
+ ),
847
+ remotes,
848
+ root
849
+ })};\n`
850
+ );
851
+
852
+ await treeshake_prerendered_remotes(
853
+ vite,
854
+ out,
855
+ remotes,
856
+ remote_original_by_hash,
857
+ metadata,
858
+ process.cwd(),
859
+ server_chunks,
860
+ vite_config.build.sourcemap
861
+ );
862
+
863
+ // defer until after other buildApp hooks have run
864
+ finalise = async () => {
865
+ /** @type {typeof import('$app/manifest')} */
866
+ const app_manifest = {
867
+ assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
868
+ immutable: immutable ?? [],
869
+ prerendered: prerendered_paths,
870
+ routes: get_manifest_routes(manifest_data.routes)
871
+ };
872
+
873
+ // defer creating the service worker to avoid other plugins from
874
+ // overwriting it if they run a client environment build
875
+ if (service_worker_entry_file) {
876
+ log.info('Building service worker');
877
+
878
+ // Add defines for `$app/manifest`
879
+ builder.environments.serviceWorker.config.define = {
880
+ ...builder.environments.serviceWorker.config.define,
881
+
882
+ __SVELTEKIT_MANIFEST_ASSETS__: s(app_manifest.assets),
883
+ __SVELTEKIT_MANIFEST_IMMUTABLE__: s(app_manifest.immutable),
884
+ __SVELTEKIT_MANIFEST_PRERENDERED__: s(app_manifest.prerendered),
885
+ __SVELTEKIT_MANIFEST_ROUTES__: s(app_manifest.routes)
886
+ };
887
+
888
+ // we have to overwrite this because it can't be configured per environment in the config hook
889
+ builder.environments.serviceWorker.config.experimental.renderBuiltUrl = (filename) => {
890
+ return {
891
+ runtime: `new URL(${JSON.stringify(filename)}, location.href).pathname`
892
+ };
893
+ };
894
+
895
+ const service_worker_build = await builder.build(builder.environments.serviceWorker);
896
+ await normalise_build(
897
+ builder.environments.serviceWorker.name,
898
+ service_worker_build,
899
+ watch_build_output
900
+ );
901
+ }
902
+
903
+ console.log(
904
+ `\nRun ${styleText(['bold', 'cyan'], 'npm run preview')} to preview your production build locally.`
905
+ );
906
+
907
+ if (kit.adapter) {
908
+ await adapt(
909
+ kit,
910
+ build_data,
911
+ metadata,
912
+ prerendered,
913
+ prerender_results.prerender_map,
914
+ app_manifest,
915
+ log,
916
+ remotes,
917
+ vite_config,
918
+ explicit_env_config
919
+ );
920
+ } else {
921
+ log.warn('\nNo adapter specified');
922
+
923
+ const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
924
+ console.log(
925
+ `See ${link} to learn how to configure your app to run on the platform of your choosing`
926
+ );
927
+ }
928
+ };
929
+ set_finalise(finalise);
930
+ };
931
+
932
+ // `vite build`
933
+ ssr_build = Array.isArray(ssr_build) ? ssr_build[0] : ssr_build;
934
+ if ('output' in ssr_build) {
935
+ await load_and_validate_params({
936
+ routes: manifest_data.routes,
937
+ params_path: manifest_data.params,
938
+ root
939
+ });
940
+
941
+ return await process_ssr_build(ssr_build.output);
942
+ }
943
+
944
+ // `vite build --watch`
945
+ let rebuild = false;
946
+
947
+ const before_ssr_build_rerun = async () => {
948
+ rebuild = true;
949
+
950
+ // these are set once per plugin initialisation or when the config hook
951
+ // runs. However, those don't re-run during watch mode. So, we need to
952
+ // re-initialise them manually here
953
+ manifest_data = create_manifest_data(kit, root);
954
+ set_manifest_data(manifest_data);
955
+ sync.all(kit, root, manifest_data);
956
+
957
+ tracked_features = {};
958
+
959
+ finalise = null;
960
+
961
+ fs.mkdirSync(out, { recursive: true });
962
+
963
+ await load_and_validate_params({
964
+ routes: manifest_data.routes,
965
+ params_path: manifest_data.params,
966
+ root
967
+ });
968
+ };
969
+
970
+ ssr_build.on('change', before_ssr_build_rerun);
971
+ ssr_build.on('restart', before_ssr_build_rerun);
972
+
973
+ /** @type {PromiseWithResolvers<void>} */
974
+ const task = Promise.withResolvers();
975
+
976
+ ssr_build.on('event', async (event) => {
977
+ if (event.code === 'ERROR') {
978
+ return task.reject();
979
+ }
980
+
981
+ if (event.code === 'BUNDLE_END') {
982
+ try {
983
+ await process_ssr_build(
984
+ /** @type {Rolldown.RolldownOutput['output']} */ (
985
+ watch_build_output.get(builder.environments.ssr.name)
986
+ )
987
+ );
988
+ // buildApp hooks don't rerun in watch mode so we need to run
989
+ // the deferred steps here on subsequent builds
990
+ if (rebuild) await finalise?.();
991
+ } catch (e) {
992
+ return task.reject(e);
993
+ } finally {
994
+ await event.result.close();
995
+ }
996
+ return task.resolve();
997
+ }
998
+ });
999
+
1000
+ await task.promise;
1001
+ }
1002
+ };
1003
+ }
1004
+
1005
+ /**
1006
+ * @param {() => Promise<void>} finalise
1007
+ * @returns {Plugin}
1008
+ */
1009
+ export function plugin_adapter(finalise) {
1010
+ return {
1011
+ name: 'vite-plugin-sveltekit-adapter',
1012
+ apply: 'build',
1013
+ buildApp: {
1014
+ // this will run after any buildApp hooks provided by other Vite plugins
1015
+ // see https://vite.dev/guide/api-environment-frameworks#environments-during-build
1016
+ order: 'post',
1017
+ async handler() {
1018
+ await finalise();
1019
+ }
1020
+ }
1021
+ };
1022
+ }
1023
+
1024
+ /**
1025
+ * Collects the content-hashed files of a Vite manifest, in the shape of
1026
+ * `$app/manifest`'s `immutable` export.
1027
+ *
1028
+ * @param {Manifest} manifest
1029
+ * @param {string} app_dir
1030
+ * @param {Set<string>} inlined files deleted from the output after being inlined into the page
1031
+ * @returns {Array<{ path: string }>}
1032
+ */
1033
+ const collect_immutable = (manifest, app_dir, inlined) => {
1034
+ const prefix = `${app_dir}/immutable`;
1035
+
1036
+ /** @type {Set<string>} */
1037
+ const files = new Set();
1038
+
1039
+ /** @param {string} file */
1040
+ const add = (file) => {
1041
+ if (file.startsWith(prefix) && !inlined.has(file)) files.add(file);
1042
+ };
1043
+
1044
+ for (const key in manifest) {
1045
+ const { file, css, assets } = manifest[key];
1046
+ add(file);
1047
+ if (css) for (let i = 0; i < css.length; i++) add(css[i]);
1048
+ if (assets) for (let i = 0; i < assets.length; i++) add(assets[i]);
1049
+ }
1050
+
1051
+ return Array.from(files, (path) => ({ path }));
1052
+ };
1053
+
1054
+ /**
1055
+ * Replaces manifest data placeholder identifiers in output chunks with real
1056
+ * values, or strings that are valid JS (so thecode doesn't crash during prerendering)
1057
+ * but findable on disk for later replacement by `replace_manifest_placeholder_strings`.
1058
+ *
1059
+ * @param {Rolldown.RolldownOutput['output']} chunks
1060
+ * @param {string} output_dir
1061
+ * @param {{
1062
+ * immutable?: Array<{ path: string }>;
1063
+ * assets?: Array<{ path: string }>;
1064
+ * prerendered?: Array<{ path: string }>;
1065
+ * routes?: Array<{ id: string; page: boolean; endpoint: boolean }>;
1066
+ * }} values
1067
+ */
1068
+ const replace_manifest_placeholder_variables = (chunks, output_dir, values) => {
1069
+ /** @type {Record<string, string>} */
1070
+ const replacements = {
1071
+ __SVELTEKIT_MANIFEST_IMMUTABLE__: JSON.stringify(
1072
+ values.immutable ?? '__sveltekit_manifest_build__'
1073
+ ),
1074
+ __SVELTEKIT_MANIFEST_ASSETS__: JSON.stringify(values.assets ?? '__sveltekit_manifest_files__'),
1075
+ __SVELTEKIT_MANIFEST_PRERENDERED__: JSON.stringify(
1076
+ values.prerendered ?? '__sveltekit_manifest_prerendered__'
1077
+ ),
1078
+ __SVELTEKIT_MANIFEST_ROUTES__: JSON.stringify(values.routes ?? '__sveltekit_manifest_routes__')
1079
+ };
1080
+
1081
+ for (const chunk of chunks) {
1082
+ if (chunk.type !== 'chunk') continue;
1083
+ if (!chunk.code.includes('__SVELTEKIT_MANIFEST_')) continue;
1084
+
1085
+ let code = chunk.code;
1086
+
1087
+ for (const [identifier, replacement] of Object.entries(replacements)) {
1088
+ code = code.replaceAll(identifier, replacement);
1089
+ }
1090
+
1091
+ const file_path = `${output_dir}/${chunk.fileName}`;
1092
+ fs.writeFileSync(file_path, code);
1093
+ }
1094
+ };
1095
+
1096
+ /**
1097
+ * Replaces manifest sentinel strings in files on disk with real values.
1098
+ * This is used for values that weren't known when the first replacement
1099
+ * pass ran (e.g. `build` wasn't known until after the client build,
1100
+ * `prerendered` wasn't known until after prerendering).
1101
+ *
1102
+ * @param {string} dir Directory to scan for .js files
1103
+ * @param {{
1104
+ * immutable?: Array<{ path: string }>;
1105
+ * prerendered?: Array<{ path: string }>;
1106
+ * }} values
1107
+ */
1108
+ const replace_manifest_placeholder_strings = (dir, values) => {
1109
+ /** @type {Array<[string, string]>} */
1110
+ const replacements = [];
1111
+
1112
+ if (values.immutable !== undefined) {
1113
+ replacements.push(['__sveltekit_manifest_build__', JSON.stringify(values.immutable)]);
1114
+ }
1115
+ if (values.prerendered !== undefined) {
1116
+ replacements.push(['__sveltekit_manifest_prerendered__', JSON.stringify(values.prerendered)]);
1117
+ }
1118
+
1119
+ for (const file of walk(dir)) {
1120
+ if (!file.endsWith('.js')) continue;
1121
+
1122
+ const file_path = `${dir}/${file}`;
1123
+ let code = read(file_path);
1124
+ let changed = false;
1125
+
1126
+ for (const [sentinel, replacement] of replacements) {
1127
+ if (code.includes(sentinel)) {
1128
+ code = code.replaceAll(`"${sentinel}"`, replacement);
1129
+ changed = true;
1130
+ }
1131
+ }
1132
+
1133
+ if (changed) {
1134
+ fs.writeFileSync(file_path, code);
1135
+ }
1136
+ }
1137
+ };
1138
+
1139
+ /**
1140
+ * Normalises the build output to a consistent format, handling watch mode and multiple environments
1141
+ * @param {string} name The name of the environment
1142
+ * @param {Rolldown.RolldownOutput | Rolldown.RolldownOutput[] | Rolldown.RolldownWatcher} build The return value of builder.build
1143
+ * @param {Map<string, Rolldown.RolldownOutput['output']>} build_output_map
1144
+ * @returns {Promise<Rolldown.RolldownOutput['output']>}
1145
+ */
1146
+ async function normalise_build(name, build, build_output_map) {
1147
+ if ('output' in build) {
1148
+ return build.output;
1149
+ }
1150
+
1151
+ if (Array.isArray(build)) {
1152
+ return build[0].output;
1153
+ }
1154
+
1155
+ /** @type {PromiseWithResolvers<void>} */
1156
+ const bundling = Promise.withResolvers();
1157
+
1158
+ build.on('event', async (event) => {
1159
+ if (event.code === 'ERROR') {
1160
+ await build.close();
1161
+ return bundling.reject(event.error);
1162
+ }
1163
+
1164
+ if (event.code === 'BUNDLE_END') {
1165
+ await build.close();
1166
+ return bundling.resolve();
1167
+ }
1168
+ });
1169
+
1170
+ await bundling.promise;
1171
+
1172
+ return /** @type {Rolldown.RolldownOutput['output']} */ (build_output_map.get(name));
1173
+ }