@sveltejs/kit 3.0.0-next.24 → 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 (71) hide show
  1. package/package.json +16 -10
  2. package/src/constants.js +6 -0
  3. package/src/core/adapt/builder.js +128 -33
  4. package/src/core/adapt/index.js +3 -0
  5. package/src/core/config/index.js +4 -7
  6. package/src/core/env.js +39 -14
  7. package/src/core/generate_manifest/index.js +42 -43
  8. package/src/core/postbuild/analyse.js +4 -4
  9. package/src/core/postbuild/fallback.js +1 -1
  10. package/src/core/postbuild/prerender.js +4 -4
  11. package/src/core/sync/create_manifest_data/index.js +0 -1
  12. package/src/core/sync/write_app_manifest.js +3 -2
  13. package/src/core/sync/write_server.js +3 -17
  14. package/src/core/utils.js +10 -0
  15. package/src/exports/adapter.js +22 -0
  16. package/src/exports/public.d.ts +101 -49
  17. package/src/exports/vite/build/index.js +1173 -0
  18. package/src/exports/vite/build/remote.js +4 -4
  19. package/src/exports/vite/build/service-worker.js +98 -0
  20. package/src/exports/vite/dev/generate_manifest.js +308 -0
  21. package/src/exports/vite/dev/index.js +40 -282
  22. package/src/exports/vite/index.js +156 -1717
  23. package/src/exports/vite/plugins/env-vars.js +53 -34
  24. package/src/exports/vite/plugins/guard.js +217 -0
  25. package/src/exports/vite/plugins/remote.js +237 -0
  26. package/src/exports/vite/preview/index.js +8 -8
  27. package/src/exports/vite/static_analysis/index.js +15 -15
  28. package/src/exports/vite/utils.js +98 -1
  29. package/src/runner.js +4 -3
  30. package/src/runtime/app/paths/server.js +2 -2
  31. package/src/runtime/app/server/index.js +3 -3
  32. package/src/runtime/app/server/public.d.ts +114 -57
  33. package/src/runtime/app/server/remote/query.js +2 -2
  34. package/src/runtime/app/server/remote/requested.js +31 -15
  35. package/src/runtime/app/state/client.svelte.js +3 -1
  36. package/src/runtime/client/client.js +43 -198
  37. package/src/runtime/client/fetcher.js +6 -6
  38. package/src/runtime/client/focus.js +120 -0
  39. package/src/runtime/client/remote-functions/command.svelte.js +20 -9
  40. package/src/runtime/client/remote-functions/form.svelte.js +12 -7
  41. package/src/runtime/client/remote-functions/query/instance.svelte.js +19 -5
  42. package/src/runtime/client/remote-functions/shared.svelte.js +22 -1
  43. package/src/runtime/client/scroll.js +39 -0
  44. package/src/runtime/client/utils.js +28 -0
  45. package/src/runtime/form-utils.js +254 -264
  46. package/src/runtime/server/data/index.js +14 -36
  47. package/src/runtime/server/errors.js +7 -10
  48. package/src/runtime/server/fetch.js +21 -25
  49. package/src/runtime/server/index.js +35 -31
  50. package/src/runtime/server/internal.js +34 -5
  51. package/src/runtime/server/page/actions.js +6 -8
  52. package/src/runtime/server/page/data_serializer.js +6 -10
  53. package/src/runtime/server/page/index.js +18 -28
  54. package/src/runtime/server/page/load_data.js +2 -2
  55. package/src/runtime/server/page/render.js +22 -40
  56. package/src/runtime/server/page/respond_with_error.js +10 -13
  57. package/src/runtime/server/page/server_routing.js +21 -27
  58. package/src/runtime/server/remote-functions.js +136 -136
  59. package/src/runtime/server/respond.js +66 -64
  60. package/src/runtime/server/state.js +2 -0
  61. package/src/runtime/server/utils.js +4 -11
  62. package/src/runtime/shared.js +9 -0
  63. package/src/runtime/utils.js +41 -0
  64. package/src/types/ambient-private.d.ts +1 -2
  65. package/src/types/global-private.d.ts +8 -0
  66. package/src/types/internal.d.ts +56 -17
  67. package/src/utils/filesystem.js +44 -28
  68. package/src/utils/streaming.js +5 -15
  69. package/src/version.js +1 -1
  70. package/types/index.d.ts +235 -93
  71. package/types/index.d.ts.map +9 -2
@@ -1,104 +1,82 @@
1
- /** @import { TopLevelFilterExpression } from '@rolldown/pluginutils' */
2
1
  /** @import { EnvVarConfig } from '@sveltejs/kit/env' */
3
2
  /** @import { Options } from '@sveltejs/vite-plugin-svelte' */
4
3
  /** @import { PreprocessorGroup } from 'svelte/compiler' */
5
- /** @import { BuildData, ManifestData, Prerendered, RemoteChunk, RemoteInternals, ServerMetadata, ValidatedConfig } from 'types' */
6
- /** @import { Manifest, Plugin, ResolvedConfig, Rolldown, UserConfig, ViteDevServer } from 'vite' */
4
+ /** @import { ManifestData, RemoteChunk, ServerMetadata, ValidatedConfig } from 'types' */
5
+ /** @import { CorsOptions, Plugin, ResolvedConfig, Rolldown, UserConfig } from 'vite' */
7
6
  import fs from 'node:fs';
8
7
  import path from 'node:path';
9
8
  import process from 'node:process';
10
9
  import { styleText } from 'node:util';
10
+ import * as vite from 'vite';
11
11
 
12
- import {
13
- and,
14
- code,
15
- exactRegex,
16
- importerId,
17
- include,
18
- not,
19
- prefixRegex
20
- } from '@rolldown/pluginutils';
21
- import MagicString from 'magic-string';
22
-
23
- import { copy, read, resolve_entry } from '../../utils/filesystem.js';
12
+ import { resolve_entry } from '../../utils/filesystem.js';
24
13
  import { posixify } from '../../utils/os.js';
25
14
  import { to_fs } from '../../utils/vite.js';
26
- import { create_exported_declarations } from '../../core/env.js';
27
- import * as sync from '../../core/sync/sync.js';
28
- import { load_and_validate_params } from '../../utils/params.js';
29
- import { runtime_directory, logger } from '../../core/utils.js';
30
- import { generate_manifest } from '../../core/generate_manifest/index.js';
31
- import { build_server_nodes } from './build/build_server.js';
32
- import { find_deps, resolve_symlinks } from './build/utils.js';
15
+ import { runtime_directory, logger, get_global_name } from '../../core/utils.js';
33
16
  import { dev } from './dev/index.js';
34
17
  import { preview } from './preview/index.js';
35
18
  import {
36
- error_for_missing_config,
19
+ enforced_config,
37
20
  get_config_aliases,
38
21
  is_remote_module,
39
- normalize_id,
40
22
  remote_module_pattern,
41
- server_only_directory_pattern,
42
- server_only_module_pattern
23
+ warn_overridden_config
43
24
  } from './utils.js';
44
25
  import { stackless } from '../../utils/error.js';
45
- import { adapt } from '../../core/adapt/index.js';
46
- import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
47
- import prerender from '../../core/postbuild/prerender.js';
48
- import analyse from '../../core/postbuild/analyse.js';
49
26
  import { s } from '../../utils/misc.js';
50
- import { hash } from '../../utils/hash.js';
51
27
  import { dedent } from '../../core/sync/utils.js';
52
28
  import create_manifest_data from '../../core/sync/create_manifest_data/index.js';
53
29
  import { get_import_aliases, get_hash_import_keys } from '../../utils/imports.js';
54
- import { app_env_private, app_server } from './module_ids.js';
55
30
  import { import_peer } from '../../utils/import.js';
56
- import { compact } from '../../utils/array.js';
57
31
  import { should_ignore, has_children } from './static_analysis/utils.js';
58
32
  import { process_config, split_config, validate_config } from '../../core/config/index.js';
59
- import { treeshake_prerendered_remotes } from './build/remote.js';
60
- import { get_runner } from '../../runner.js';
61
- import { plugin_env_vars } from './plugins/env-vars.js';
62
- import { get_manifest_routes, write_app_manifest } from '../../core/sync/write_app_manifest.js';
63
-
64
- /** @type {import('./types.js').EnforcedConfig} */
65
- const enforced_config = {
66
- appType: true,
67
- base: true,
68
- build: {
69
- cssCodeSplit: true,
70
- emptyOutDir: true,
71
- lib: {
72
- entry: true,
73
- name: true,
74
- formats: true
75
- },
76
- manifest: true,
77
- outDir: true,
78
- rolldownOptions: {
79
- input: true,
80
- output: {
81
- format: true,
82
- entryFileNames: true,
83
- chunkFileNames: true,
84
- assetFileNames: true
85
- },
86
- preserveEntrySignatures: true
87
- },
88
- ssr: true
89
- },
90
- publicDir: true,
91
- resolve: {
92
- alias: {
93
- $app: true,
94
- $env: true,
95
- '<sveltekit:generated>': true
96
- }
97
- }
98
- };
33
+ import { plugin_env_vars, plugin_service_worker_env_vars } from './plugins/env-vars.js';
34
+ import { plugin_guard } from './plugins/guard.js';
35
+ import { plugin_remote, plugin_remote_guard } from './plugins/remote.js';
36
+ import { write_app_manifest } from '../../core/sync/write_app_manifest.js';
37
+ import { plugin_service_worker_build } from './build/service-worker.js';
38
+ import { plugin_adapter, plugin_compile } from './build/index.js';
99
39
 
100
40
  const options_regex = /(export\s+const\s+(prerender|csr|ssr|trailingSlash))\s*=/s;
101
41
 
42
+ /**
43
+ * Resolves the CORS config for dev and preview servers.
44
+ * SvelteKit needs `preflightContinue: true` so that OPTIONS requests for
45
+ * `+server.js` endpoints aren't intercepted by Vite's CORS middleware.
46
+ * If the user has explicitly set values that prevent this, we warn them
47
+ * and preserve their settings.
48
+ * @param {CorsOptions | boolean | undefined} user_cors
49
+ * @param {'server.cors' | 'preview.cors'} key
50
+ * @param {boolean} warn Whether to emit a warning when the user's settings prevent OPTIONS handlers from working. Only relevant for the dev/preview servers, not `vite build`.
51
+ * @returns {CorsOptions | undefined}
52
+ */
53
+ function resolve_cors(user_cors, key, warn) {
54
+ // `preview.cors` falls back to the resolved `server.cors`, so emitting a value here when
55
+ // the user hasn't set one is what drops Vite's `defaultAllowedOrigins` restriction
56
+ if (user_cors === undefined) {
57
+ return key === 'server.cors' ? { preflightContinue: true } : undefined;
58
+ }
59
+
60
+ // with `cors: false` Vite installs no CORS middleware, so OPTIONS handlers already work
61
+ if (user_cors === false) return undefined;
62
+
63
+ if (typeof user_cors === 'object' && user_cors !== null) {
64
+ if (user_cors.preflightContinue === undefined) return { preflightContinue: true };
65
+ if (user_cors.preflightContinue) return undefined;
66
+ }
67
+
68
+ if (warn) {
69
+ console.warn(
70
+ styleText(
71
+ ['yellow', 'bold'],
72
+ `OPTIONS request handlers will not work unless \`${key}.preflightContinue\` is set to \`true\``
73
+ )
74
+ );
75
+ }
76
+
77
+ return undefined;
78
+ }
79
+
102
80
  const removed_modules = [
103
81
  {
104
82
  name: '$lib',
@@ -260,9 +238,6 @@ function resolve_root(vite_config) {
260
238
  * @return {Plugin[]}
261
239
  */
262
240
  function kit({ svelte_config }) {
263
- /** @type {typeof import('vite')} */
264
- let vite;
265
-
266
241
  /**
267
242
  * The posix-ified root of the project based on the Vite configuration.
268
243
  * @type {string}
@@ -276,49 +251,26 @@ function kit({ svelte_config }) {
276
251
  /** @type {string} The base directory for the Vite builds */
277
252
  let out;
278
253
 
279
- /** @type {string} */
280
- let version_hash;
281
-
282
- /** @type {ResolvedConfig} */
283
- let vite_config;
284
-
285
254
  /** @type {boolean} */
286
255
  let is_build;
287
256
 
288
- /** @type {Record<string, string>} */
289
- let env;
290
-
291
257
  /** @type {ManifestData} */
292
258
  let manifest_data;
293
259
 
294
- /** @type {ServerMetadata | null} set at build time once analysis has finished */
295
- let build_metadata = null;
296
-
297
260
  /** @type {UserConfig} */
298
261
  let initial_config;
299
262
 
300
263
  /** @type {string | null} */
301
264
  let service_worker_entry_file;
302
- /** @type {string} */
303
- let normalized_cwd;
304
265
  /** @type {Array<{ alias: string, path: string }>} */
305
266
  let normalized_aliases;
306
- /** @type {string} */
307
- let normalized_node_modules;
308
- /** @type {string} */
309
- let normalized_routes;
310
- /** @type {string} */
311
- let normalized_assets;
312
- /**
313
- * A map showing which features (such as `$app/server:read`) are defined
314
- * in which chunks, so that we can later determine which routes use which features
315
- * @type {Record<string, string[]>}
316
- */
317
- let tracked_features = {};
318
267
 
319
268
  const sourcemapIgnoreList = /** @param {string} relative_path */ (relative_path) =>
320
269
  relative_path.includes('node_modules') || relative_path.includes(kit.outDir);
321
270
 
271
+ /** @type {string} the `__sveltekit_xxx` name, without `globalThis.` */
272
+ let global_name;
273
+
322
274
  /** @type {string} name for `globalThis.__sveltekit_xxx` */
323
275
  let kit_global;
324
276
 
@@ -376,7 +328,7 @@ function kit({ svelte_config }) {
376
328
  */
377
329
  config: {
378
330
  order: 'pre',
379
- async handler(config, config_env) {
331
+ handler(config, config_env) {
380
332
  initial_config = config;
381
333
  is_build = config_env.command === 'build';
382
334
 
@@ -384,21 +336,13 @@ function kit({ svelte_config }) {
384
336
  out_dir = posixify(kit.outDir);
385
337
  out = `${out_dir}/output`;
386
338
 
387
- version_hash = hash(kit.version.name);
388
-
389
- kit_global = is_build
390
- ? `globalThis.__sveltekit_${version_hash}`
391
- : 'globalThis.__sveltekit_dev';
392
-
393
- vite = await import_peer('vite', root);
339
+ global_name = get_global_name(kit.version.name, !is_build);
340
+ kit_global = `globalThis.${global_name}`;
394
341
 
395
- env = vite.loadEnv(config_env.mode, kit.env.dir, '');
342
+ service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
343
+ service_worker_entry_file &&= posixify(service_worker_entry_file);
396
344
 
397
- normalized_cwd = vite.normalizePath(root);
398
345
  normalized_aliases = get_import_aliases(root, vite.normalizePath.bind(vite));
399
- normalized_node_modules = vite.normalizePath(path.resolve(root, 'node_modules'));
400
- normalized_routes = vite.normalizePath(path.resolve(root, kit.files.routes));
401
- normalized_assets = vite.normalizePath(path.resolve(root, kit.files.assets));
402
346
 
403
347
  // Add `#`-prefixed import keys to the enforced config so users are warned
404
348
  // if they try to set them in their Vite config's resolve.alias
@@ -438,6 +382,7 @@ function kit({ svelte_config }) {
438
382
  // dev and preview config can be shared
439
383
  /** @type {UserConfig} */
440
384
  const new_config = {
385
+ appType: 'custom',
441
386
  environments: {
442
387
  ssr: {
443
388
  build: {
@@ -458,7 +403,7 @@ function kit({ svelte_config }) {
458
403
  ]
459
404
  },
460
405
  server: {
461
- cors: { preflightContinue: true },
406
+ cors: resolve_cors(config.server?.cors, 'server.cors', !is_build),
462
407
  fs: {
463
408
  allow: [...allow]
464
409
  },
@@ -471,7 +416,7 @@ function kit({ svelte_config }) {
471
416
  }
472
417
  },
473
418
  preview: {
474
- cors: { preflightContinue: true }
419
+ cors: resolve_cors(config.preview?.cors, 'preview.cors', !is_build)
475
420
  },
476
421
  optimizeDeps: {
477
422
  entries: [
@@ -504,7 +449,8 @@ function kit({ svelte_config }) {
504
449
  // uses basic concatenation)
505
450
  '@sveltejs/kit/src/runtime'
506
451
  ]
507
- }
452
+ },
453
+ publicDir: kit.files.assets
508
454
  };
509
455
 
510
456
  // externalize .remote.js files to stop dependency tracing during prebundling
@@ -546,7 +492,12 @@ function kit({ svelte_config }) {
546
492
  __SVELTEKIT_SUPPORTS_ASYNC__: s(
547
493
  svelte_config.compilerOptions?.experimental?.async ?? false
548
494
  ),
549
- __SVELTEKIT_DEV__: s(!is_build)
495
+ __SVELTEKIT_DEV__: s(!is_build),
496
+ __SVELTEKIT_GLOBAL_NAME__: s(global_name),
497
+ __SVELTEKIT_CSRF_CHECK_ORIGIN__: s(!kit.csrf.trustedOrigins.includes('*')),
498
+ __SVELTEKIT_LINK_HEADER_PRELOAD__: s(kit.output.linkHeaderPreload),
499
+ __SVELTEKIT_PATHS_ORIGIN__: s(kit.paths.origin) ?? 'undefined',
500
+ __SVELTEKIT_SERVICE_WORKER__: s(kit.serviceWorker.register && !!service_worker_entry_file)
550
501
  };
551
502
 
552
503
  if (is_build) {
@@ -556,9 +507,6 @@ function kit({ svelte_config }) {
556
507
  __SVELTEKIT_APP_VERSION_FILE__: s(`${kit.appDir}/version.json`),
557
508
  __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: s(kit.version.pollInterval)
558
509
  };
559
-
560
- manifest_data = create_manifest_data(svelte_config, root);
561
- sync.all(svelte_config, root, manifest_data);
562
510
  } else {
563
511
  new_config.define = {
564
512
  ...define,
@@ -568,7 +516,7 @@ function kit({ svelte_config }) {
568
516
  __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true'
569
517
  };
570
518
 
571
- // These Kit dependencies are packaged as CommonJS, which means they must always be externalized.
519
+ // Any CommonJS dependencies of Kit (of which there are currently none) must always be externalized.
572
520
  // Without this, the tests will still pass but `pnpm dev` will fail in projects that link `@sveltejs/kit`.
573
521
  //
574
522
  // `@opentelemetry/api` must be externalized so that `instrumentation.server.js` and the
@@ -579,9 +527,18 @@ function kit({ svelte_config }) {
579
527
  // would cause those modules to be evaluated before `Server.init()` sets env vars — see
580
528
  // https://github.com/sveltejs/kit/issues/16288
581
529
  /** @type {NonNullable<UserConfig['ssr']>} */ (new_config.ssr).external = [
582
- 'cookie',
583
530
  '@opentelemetry/api'
584
531
  ];
532
+
533
+ // we avoid setting base to paths.assets in dev so that we get the
534
+ // trailing slash redirect to paths.base if it is set
535
+ new_config.base = kit.paths.base || '/';
536
+
537
+ // Vite dependency crawler needs an explicit JS entry point
538
+ // even though server otherwise works without it
539
+ new_config.build ??= {};
540
+ new_config.build.rolldownOptions ??= {};
541
+ new_config.build.rolldownOptions.input = `${runtime_directory}/client/entry.js`;
585
542
  }
586
543
 
587
544
  // Vite's `define` is a compile-time text replacement, but Vitest strips
@@ -611,16 +568,14 @@ function kit({ svelte_config }) {
611
568
  * Stores the final config.
612
569
  */
613
570
  configResolved(config) {
614
- vite_config = config;
615
-
616
571
  if (!is_build) {
617
572
  // Dependency scanning starts before configureServer creates the full manifest
618
573
  write_app_manifest(`${out_dir}/generated/dev`, undefined, false);
619
574
  }
620
575
 
621
- const unsupported_plugins = vite_config.plugins.filter((plugin) => plugin.transformIndexHtml);
576
+ const unsupported_plugins = config.plugins.filter((plugin) => plugin.transformIndexHtml);
622
577
  if (unsupported_plugins.length) {
623
- const verbose = vite_config.logLevel === 'info' || vite_config.logLevel === undefined;
578
+ const verbose = config.logLevel === 'info' || config.logLevel === undefined;
624
579
  const log = logger({ verbose });
625
580
 
626
581
  const list = unsupported_plugins
@@ -635,775 +590,6 @@ function kit({ svelte_config }) {
635
590
  `
636
591
  );
637
592
  }
638
- }
639
- };
640
-
641
- /** @type {Record<string, EnvVarConfig<any>> | null} */
642
- let explicit_env_config = null;
643
-
644
- /** @type {Map<string, Set<string>>} */
645
- const import_map = new Map();
646
-
647
- /**
648
- * Ensures that client-side code can't accidentally import server-side code,
649
- * whether in `*.server.js` files, `$app/server`, any `/server/` directory, or `$app/env/private`
650
- * @type {Plugin}
651
- */
652
- const plugin_guard = {
653
- name: 'vite-plugin-sveltekit-guard',
654
-
655
- // Run this plugin before built-in resolution, so that relative imports
656
- // are added to the module graph
657
- enforce: 'pre',
658
-
659
- applyToEnvironment(environment) {
660
- // the import map is only read for client-side violations in `load`, so skip other environments
661
- return environment.config.consumer === 'client';
662
- },
663
-
664
- resolveId: {
665
- // composable filters are not accepted type-wise but still work during build
666
- // see https://github.com/vitejs/rolldown-vite/issues/605
667
- filter: /** @type {any} */ (
668
- /** @satisfies {TopLevelFilterExpression[]} */ ([
669
- include(and(importerId(/.+/), not(importerId(/index\.html$/))))
670
- ])
671
- ),
672
- async handler(id, importer, options) {
673
- // composable filters only work during build so we still need this guard for dev
674
- // see https://github.com/vitejs/rolldown-vite/issues/605
675
- if (importer && !importer.endsWith('index.html')) {
676
- const resolved = await this.resolve(id, importer, { ...options, skipSelf: true });
677
-
678
- if (resolved) {
679
- const normalized = normalize_id(resolved.id, normalized_aliases, normalized_cwd);
680
-
681
- let importers = import_map.get(normalized);
682
-
683
- if (!importers) {
684
- importers = new Set();
685
- import_map.set(normalized, importers);
686
- }
687
-
688
- importers.add(normalize_id(importer, normalized_aliases, normalized_cwd));
689
- }
690
- }
691
- }
692
- },
693
-
694
- load: {
695
- filter: {
696
- id: [
697
- exactRegex(app_server),
698
- exactRegex(app_env_private),
699
- server_only_module_pattern,
700
- server_only_directory_pattern
701
- ]
702
- },
703
- handler(id) {
704
- const normalized = normalize_id(id, normalized_aliases, normalized_cwd);
705
-
706
- let is_server_only = normalized === '$app/env/private' || normalized === '$app/server';
707
-
708
- // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context
709
- if (id.startsWith(normalized_cwd) && !id.startsWith(normalized_node_modules)) {
710
- // e.g. `server.ts` or `foo.server.ts`
711
- is_server_only ||= server_only_module_pattern.test(id);
712
-
713
- // e.g. `server/foo.ts`, unless in `src/routes` or `static`
714
- is_server_only ||=
715
- server_only_directory_pattern.test(id) &&
716
- !id.startsWith(normalized_routes + '/') &&
717
- !id.startsWith(normalized_assets + '/');
718
- }
719
-
720
- if (!is_server_only) return;
721
-
722
- // in dev, this doesn't exist, so we need to create it
723
- manifest_data ??= create_manifest_data(svelte_config, root);
724
-
725
- /** @type {Set<string>} */
726
- const entrypoints = new Set();
727
- for (const node of manifest_data.nodes) {
728
- if (node.component) entrypoints.add(node.component);
729
- if (node.universal) entrypoints.add(node.universal);
730
- }
731
-
732
- if (manifest_data.hooks.client) entrypoints.add(manifest_data.hooks.client);
733
- if (manifest_data.hooks.universal) entrypoints.add(manifest_data.hooks.universal);
734
-
735
- if (service_worker_entry_file) {
736
- entrypoints.add(posixify(path.relative(root, service_worker_entry_file)));
737
- }
738
-
739
- // Walk up the import graph from the server-only module, looking for a chain
740
- // that leads back to a client entrypoint. We search all candidates (not just
741
- // the first) because a module can be imported by both server and client code,
742
- // and a greedy first-match could follow a server-only branch that never
743
- // reaches an entrypoint — see https://github.com/sveltejs/kit/issues/16232
744
- /** @type {Set<string>} */
745
- const visited = new Set([normalized]);
746
-
747
- /**
748
- * @param {string} current
749
- * @param {string[]} chain
750
- * @returns {string[] | null}
751
- */
752
- function find_chain(current, chain) {
753
- const importers = import_map.get(current);
754
- if (!importers) return null;
755
-
756
- for (const importer of importers) {
757
- if (visited.has(importer)) continue;
758
- visited.add(importer);
759
-
760
- const next_chain = [...chain, importer];
761
- if (entrypoints.has(importer)) {
762
- return next_chain;
763
- }
764
- const result = find_chain(importer, next_chain);
765
- if (result) return result;
766
- }
767
- return null;
768
- }
769
-
770
- const chain = find_chain(normalized, [normalized]);
771
-
772
- if (chain) {
773
- if (chain.some((id) => remote_module_pattern.test(id))) {
774
- error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
775
- }
776
-
777
- const pyramid = chain
778
- .reverse()
779
- .map((id, i) => {
780
- return `${' '.repeat(i + 1)}${id}`;
781
- })
782
- .join(' imports\n');
783
-
784
- let message = `Cannot import ${normalized} into code that runs in the browser, as this could leak sensitive information.`;
785
- message += `\n\n${pyramid}`;
786
- message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`;
787
-
788
- throw stackless(message);
789
- }
790
-
791
- // No chain from this server-only module to a client entrypoint was found —
792
- // the module is only imported from server code, which is valid.
793
- }
794
- },
795
-
796
- // avoid watch mode rebuilds using stale import map data
797
- buildEnd() {
798
- import_map.clear();
799
- }
800
- };
801
-
802
- /** @type {ViteDevServer} */
803
- let dev_server;
804
-
805
- /** @type {RemoteChunk[]} */
806
- let remotes = [];
807
-
808
- /** @type {Map<string, string>} Maps remote hash -> original module id */
809
- const remote_original_by_hash = new Map();
810
-
811
- /** @type {Set<string>} Track which remote hashes have already been emitted */
812
- const emitted_remote_hashes = new Set();
813
-
814
- /** @type {Plugin} */
815
- const plugin_remote = {
816
- name: 'vite-plugin-sveltekit-remote',
817
-
818
- applyToEnvironment(environment) {
819
- return svelte_config.experimental.remoteFunctions && environment.name !== 'serviceWorker';
820
- },
821
-
822
- // prevent other plugins from resolving our remote virtual module
823
- resolveId: {
824
- filter: {
825
- id: prefixRegex('\0sveltekit-remote:')
826
- },
827
- handler(id) {
828
- return id;
829
- }
830
- },
831
-
832
- load: {
833
- filter: {
834
- id: prefixRegex('\0sveltekit-remote:')
835
- },
836
- handler(id) {
837
- // On-the-fly generated entry point for remote file just forwards the original module
838
- // We're not using manualChunks because it can cause problems with circular dependencies
839
- // (e.g. https://github.com/sveltejs/kit/issues/14679) and module ordering in general
840
- // (e.g. https://github.com/sveltejs/kit/issues/14590).
841
- const hash_id = id.slice('\0sveltekit-remote:'.length);
842
- const original = remote_original_by_hash.get(hash_id);
843
- if (!original) throw new Error(`Expected to find metadata for remote file ${id}`);
844
- return `import * as m from ${s(original)};\nexport default m;`;
845
- }
846
- },
847
-
848
- configureServer(_dev_server) {
849
- dev_server = _dev_server;
850
- },
851
-
852
- transform: {
853
- filter: {
854
- id: remote_module_pattern
855
- },
856
- async handler(code, id) {
857
- if (!is_remote_module(id)) return;
858
-
859
- const file = posixify(path.relative(root, id));
860
- const remote = {
861
- hash: hash(file),
862
- file
863
- };
864
-
865
- if (this.environment.name === 'ssr') remotes.push(remote);
866
-
867
- if (this.environment.config.consumer !== 'client') {
868
- // we need to add an `await Promise.resolve()` because if the user imports this function
869
- // on the client AND in a load function when loading the client module we will trigger
870
- // an import during dev. During a link preload, the module can be mistakenly
871
- // loaded and transformed twice and the first time all its exports would be undefined
872
- // triggering a dev server error. By adding a microtask we ensure that the module is fully loaded
873
- const ms = new MagicString(code);
874
-
875
- // Extra newlines to prevent syntax errors around missing semicolons or comments
876
- ms.append(
877
- '\n\n' +
878
- dedent`
879
- import * as $$_self_$$ from './${path.basename(id)}';
880
- import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal/server';
881
-
882
- ${dev_server ? 'await Promise.resolve()' : ''}
883
-
884
- $$_init_$$($$_self_$$, ${s(file)}, ${s(remote.hash)});
885
-
886
- for (const [name, fn] of Object.entries($$_self_$$)) {
887
- fn.__.id = ${s(remote.hash)} + '/' + name;
888
- fn.__.name = name;
889
- }
890
- `
891
- );
892
-
893
- // Emit a dedicated entry chunk for this remote in SSR builds (prod only)
894
- if (!dev_server) {
895
- remote_original_by_hash.set(remote.hash, id);
896
- if (!emitted_remote_hashes.has(remote.hash)) {
897
- this.emitFile({
898
- type: 'chunk',
899
- id: `\0sveltekit-remote:${remote.hash}`,
900
- name: `remote-${remote.hash}`
901
- });
902
- emitted_remote_hashes.add(remote.hash);
903
- }
904
- }
905
-
906
- return {
907
- code: ms.toString(),
908
- map: ms.generateMap({ hires: 'boundary' })
909
- };
910
- }
911
-
912
- // For the client, read the exports and create a new module that only contains fetch functions with the correct metadata
913
-
914
- /** @type {Map<string, RemoteInternals['type']>} */
915
- const map = new Map();
916
-
917
- // in dev, load the server module here (which will result in this hook
918
- // being called again with `opts.ssr === true` if the module isn't
919
- // already loaded) so we can determine what it exports
920
- if (dev_server) {
921
- const module = await get_runner(vite, dev_server).import(id);
922
-
923
- for (const [name, value] of Object.entries(module)) {
924
- const type = value?.__?.type;
925
- if (type) {
926
- map.set(name, type);
927
- }
928
- }
929
- }
930
-
931
- // in prod, we already built and analysed the server code before
932
- // building the client code, so `remotes` is populated
933
- else if (build_metadata?.remotes) {
934
- const exports = build_metadata?.remotes.get(remote.hash);
935
- if (!exports) throw new Error('Expected to find metadata for remote file ' + id);
936
-
937
- for (const [name, value] of exports) {
938
- map.set(name, value.type);
939
- }
940
- }
941
-
942
- const { namespace, declarations, reexports } = create_exported_declarations(
943
- map.keys(),
944
- (name, ns) => `${ns}.${map.get(name)}('${remote.hash}/${name}')`,
945
- '__remote'
946
- );
947
-
948
- const relative = posixify(
949
- path.relative(path.dirname(id), `${runtime_directory}/client/remote-functions/index.js`)
950
- );
951
-
952
- let result = `import * as ${namespace} from '${relative}';\n\n${declarations.join('\n')}`;
953
- if (reexports.length > 0) {
954
- result += `\nexport { ${reexports.join(', ')} };`;
955
- }
956
- result += '\n';
957
-
958
- if (dev_server) {
959
- result += `\nimport.meta.hot?.accept();\n`;
960
- }
961
-
962
- return {
963
- code: result,
964
- map: null
965
- };
966
- }
967
- },
968
-
969
- buildEnd() {
970
- if (this.environment.config.consumer === 'server') {
971
- emitted_remote_hashes.clear();
972
- }
973
- }
974
- };
975
-
976
- /** @type {Plugin} */
977
- const plugin_remote_guard = {
978
- name: 'vite-plugin-sveltekit-remote-guard',
979
-
980
- applyToEnvironment() {
981
- return !svelte_config.experimental.remoteFunctions;
982
- },
983
-
984
- transform: {
985
- filter: {
986
- id: new RegExp(
987
- `.remote(${svelte_config.moduleExtensions.join('|')})$`.replaceAll('.', '\\.')
988
- )
989
- },
990
- handler() {
991
- error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
992
- }
993
- }
994
- };
995
-
996
- /** @type {Manifest} */
997
- let vite_server_manifest;
998
- /** @type {Manifest | null} */
999
- let vite_client_manifest = null;
1000
- /** @type {Prerendered} */
1001
- let prerendered;
1002
-
1003
- /** @type {Array<{ path: string }> | null} */
1004
- let immutable = null;
1005
-
1006
- /**
1007
- * Creates the service worker virtual modules
1008
- * @type {Plugin}
1009
- */
1010
- const plugin_service_worker = {
1011
- name: 'vite-plugin-sveltekit-service-worker',
1012
-
1013
- config(config) {
1014
- service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
1015
-
1016
- if (!service_worker_entry_file) return;
1017
-
1018
- service_worker_entry_file = posixify(service_worker_entry_file);
1019
-
1020
- if (kit.paths.assets) {
1021
- throw new Error('Cannot use service worker alongside config.paths.assets');
1022
- }
1023
-
1024
- const user_service_worker_output_config =
1025
- config.environments?.serviceWorker?.build?.rolldownOptions?.output;
1026
-
1027
- /** @type {import('vite').UserConfig} */
1028
- const new_config = {
1029
- environments: {
1030
- serviceWorker: {
1031
- define: {
1032
- __SVELTEKIT_PAYLOAD__: kit_global
1033
- },
1034
- build: {
1035
- modulePreload: false,
1036
- rolldownOptions: {
1037
- external: [`${kit.paths.base}/${kit.appDir}/env.js`],
1038
- input: {
1039
- 'service-worker': service_worker_entry_file
1040
- },
1041
- output: {
1042
- format: 'es',
1043
- entryFileNames: 'service-worker.js',
1044
- assetFileNames: `${kit.appDir}/immutable/assets/[name].[hash][extname]`,
1045
- codeSplitting:
1046
- (Array.isArray(user_service_worker_output_config)
1047
- ? user_service_worker_output_config[0].codeSplitting
1048
- : user_service_worker_output_config?.codeSplitting) ?? false
1049
- }
1050
- },
1051
- outDir: `${out}/client`,
1052
- minify: initial_config.build?.minify,
1053
- // avoid overwriting the client build Vite manifest
1054
- manifest: '.vite/service-worker-manifest.json'
1055
- },
1056
- consumer: 'client'
1057
- }
1058
- }
1059
- };
1060
-
1061
- warn_overridden_config(config, new_config);
1062
-
1063
- return new_config;
1064
- },
1065
-
1066
- // our serviceWorker environment only exists when building because Vite only
1067
- // supports the default client environment during development (for now)
1068
- applyToEnvironment(environment) {
1069
- return environment.name === 'serviceWorker';
1070
- },
1071
-
1072
- generateBundle(_, bundle) {
1073
- const invalid_modules = new Set();
1074
- const modules = new Map([
1075
- [`${runtime_directory}/app/forms/index.js`, '$app/forms'],
1076
- [`${runtime_directory}/app/navigation/index.js`, '$app/navigation'],
1077
- [`${runtime_directory}/app/state/index.js`, '$app/state']
1078
- ]);
1079
-
1080
- for (const output of Object.values(bundle)) {
1081
- if (output.type !== 'chunk') continue;
1082
-
1083
- for (const id of output.moduleIds) {
1084
- const module = modules.get(id);
1085
- if (module) invalid_modules.add(module);
1086
- }
1087
- }
1088
-
1089
- if (invalid_modules.size > 0) {
1090
- throw new Error(
1091
- `Cannot import ${Array.from(modules.values())
1092
- .filter((module) => invalid_modules.has(module))
1093
- .join(', ')} into service-worker code.`
1094
- );
1095
- }
1096
- }
1097
- };
1098
-
1099
- /** @type {Plugin} */
1100
- const plugin_service_worker_env = {
1101
- name: 'vite-plugin-sveltekit-service-worker-env',
1102
- configResolved() {
1103
- if (service_worker_entry_file) {
1104
- // @ts-expect-error transform is defined in this object
1105
- plugin_service_worker_env.transform.filter = {
1106
- id: exactRegex(service_worker_entry_file)
1107
- };
1108
- }
1109
- },
1110
- applyToEnvironment(environment) {
1111
- return !!service_worker_entry_file && environment.config.consumer === 'client';
1112
- },
1113
- transform: {
1114
- handler(code) {
1115
- // prepend the service worker with an import that configures
1116
- // `env`, in case `$app/env/public` is imported. In production
1117
- // this is required: dynamic public env vars aren't known at
1118
- // build time, so `env.js` is loaded at runtime. In dev, the
1119
- // imported module just inlines the current values instead.
1120
- return {
1121
- code: `import '<sveltekit:generated>/env/service-worker.js';\n${code}`
1122
- };
1123
- }
1124
- }
1125
- };
1126
-
1127
- /** @type {Map<string, Rolldown.RolldownOutput['output']>} */
1128
- const watch_build_output = new Map();
1129
- /** @type {(() => Promise<void>) | null} */
1130
- let finalise = null;
1131
-
1132
- /** @type {Plugin} */
1133
- const plugin_compile = {
1134
- name: 'vite-plugin-sveltekit-compile',
1135
-
1136
- /**
1137
- * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
1138
- * @see https://vitejs.dev/guide/api-plugin.html#config
1139
- */
1140
- config: {
1141
- // avoids overwriting the base setting that's also set by Vitest
1142
- order: 'pre',
1143
- handler(config) {
1144
- /** @type {UserConfig} */
1145
- let new_config;
1146
-
1147
- if (is_build) {
1148
- const app_immutable = `${kit.appDir}/immutable`;
1149
-
1150
- /** @type {Record<string, string>} */
1151
- const server_input = {
1152
- index: `${runtime_directory}/server/index.js`,
1153
- internal: `<sveltekit:generated>/server.js`,
1154
- env: '<sveltekit:generated>/env/config.js',
1155
- ['remote-entry']: `${runtime_directory}/app/server/remote/index.js`
1156
- };
1157
-
1158
- // add entry points for every endpoint...
1159
- manifest_data.routes.forEach((route) => {
1160
- if (route.endpoint) {
1161
- const resolved = path.resolve(root, route.endpoint.file);
1162
- const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
1163
- const name = posixify(path.join('entries/endpoints', relative.replace(/\.js$/, '')));
1164
- server_input[name] = resolved;
1165
- }
1166
- });
1167
-
1168
- // ...and every component used by pages...
1169
- manifest_data.nodes.forEach((node) => {
1170
- for (const file of [node.component, node.universal, node.server]) {
1171
- if (file) {
1172
- const resolved = path.resolve(root, file);
1173
- const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
1174
-
1175
- const name = relative.startsWith('..')
1176
- ? posixify(path.join('entries/fallbacks', path.basename(file)))
1177
- : posixify(path.join('entries/pages', relative.replace(/\.js$/, '')));
1178
- server_input[name] = resolved;
1179
- }
1180
- }
1181
- });
1182
-
1183
- // ...and the params file
1184
- if (manifest_data.params) {
1185
- server_input['entries/params'] = path.resolve(root, manifest_data.params);
1186
- }
1187
-
1188
- // ...and the hooks files
1189
- if (manifest_data.hooks.server) {
1190
- server_input['entries/hooks.server'] = path.resolve(root, manifest_data.hooks.server);
1191
- }
1192
- if (manifest_data.hooks.universal) {
1193
- server_input['entries/hooks.universal'] = path.resolve(
1194
- root,
1195
- manifest_data.hooks.universal
1196
- );
1197
- }
1198
-
1199
- // ...and the server instrumentation file
1200
- const server_instrumentation = resolve_entry(
1201
- path.join(kit.files.src, 'instrumentation.server')
1202
- );
1203
- if (server_instrumentation) {
1204
- if (kit.adapter && !kit.adapter.supports?.instrumentation?.()) {
1205
- throw new Error(`${server_instrumentation} is unsupported in ${kit.adapter.name}.`);
1206
- }
1207
- server_input['instrumentation.server'] = server_instrumentation;
1208
- }
1209
-
1210
- /** @type {Record<string, string>} */
1211
- const client_input = {};
1212
-
1213
- if (svelte_config.output.bundleStrategy !== 'split') {
1214
- client_input['bundle'] = `${runtime_directory}/client/bundle.js`;
1215
- } else {
1216
- client_input['entry/start'] = `${runtime_directory}/client/entry.js`;
1217
- client_input['entry/payload'] = `${runtime_directory}/client/payload.js`;
1218
- client_input['entry/app'] = `${out_dir}/generated/build/client-optimized/app.js`;
1219
- manifest_data.nodes.forEach((node, i) => {
1220
- if (node.component || node.universal) {
1221
- client_input[`nodes/${i}`] =
1222
- `${out_dir}/generated/build/client-optimized/nodes/${i}.js`;
1223
- }
1224
- });
1225
- }
1226
-
1227
- const inline = svelte_config.output.bundleStrategy === 'inline';
1228
-
1229
- /** @type {string} */
1230
- const base = (kit.paths.assets || kit.paths.base) + '/';
1231
- const root_to_assets = app_immutable + '/assets/';
1232
- const assets_to_root =
1233
- app_immutable
1234
- .split('/')
1235
- .map(() => '..')
1236
- .join('/') + '/../';
1237
-
1238
- const relative = kit.paths.relative !== false || !!kit.paths.assets;
1239
-
1240
- new_config = {
1241
- appType: 'custom',
1242
- // Affects how Vite loads JS assets on the client.
1243
- // If the initial HTML we render uses an absolute path for assets,
1244
- // the additional chunks Vite loads must also use an absolute path.
1245
- // Otherwise, you end up with additional chunks being loaded relative
1246
- // to the current chunk rather than the root.
1247
- base: relative ? './' : base,
1248
- build: {
1249
- cssCodeSplit: !inline,
1250
- cssMinify:
1251
- initial_config.build?.minify == null ? true : !!initial_config.build.minify,
1252
- manifest: true,
1253
- rolldownOptions: {
1254
- output: {
1255
- name: `__sveltekit_${version_hash}.app`,
1256
- assetFileNames: `${app_immutable}/assets/[name].[hash][extname]`,
1257
- hoistTransitiveImports: false,
1258
- sourcemapIgnoreList
1259
- },
1260
- preserveEntrySignatures: 'strict',
1261
- onwarn(warning, handler) {
1262
- if (
1263
- warning.code === 'IMPORT_IS_UNDEFINED' &&
1264
- warning.id === `${out_dir}/generated/build/client-optimized/app.js`
1265
- ) {
1266
- // ignore e.g. undefined `handleError` hook when
1267
- // referencing `client_hooks.handleError`
1268
- return;
1269
- }
1270
-
1271
- handler(warning);
1272
- },
1273
- watch: {
1274
- exclude: [
1275
- // Ignore all siblings of config.outDir/generated
1276
- `${out_dir}/generated/**`
1277
- ]
1278
- }
1279
- },
1280
- emptyOutDir: false,
1281
- ssrEmitAssets: true
1282
- },
1283
- builder: {
1284
- sharedConfigBuild: true,
1285
- sharedPlugins: true
1286
- },
1287
- environments: {
1288
- ssr: {
1289
- build: {
1290
- copyPublicDir: false,
1291
- outDir: `${out}/server`,
1292
- target: 'node22',
1293
- rolldownOptions: {
1294
- input: server_input,
1295
- output: {
1296
- entryFileNames: '[name].js',
1297
- chunkFileNames: 'chunks/[name].js'
1298
- }
1299
- }
1300
- },
1301
- // these are stubs that will be replaced after the initial server build
1302
- define: {
1303
- __SVELTEKIT_HAS_SERVER_LOAD__: 'true',
1304
- __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true',
1305
- __SVELTEKIT_PAYLOAD__: '{}'
1306
- }
1307
- },
1308
- client: {
1309
- build: {
1310
- outDir: `${out}/client`,
1311
- rolldownOptions: {
1312
- input: inline ? client_input['bundle'] : client_input,
1313
- output: {
1314
- format: inline ? 'iife' : 'esm',
1315
- entryFileNames: `${app_immutable}/[name].[hash].js`,
1316
- chunkFileNames: (/** @type {Rolldown.PreRenderedChunk} */ chunk_info) => {
1317
- // The manifest data chunk gets a fixed (non-hashed) filename so
1318
- // that importers' content hashes are stable regardless of the
1319
- // manifest content — this breaks the content-hash feedback loop
1320
- if (chunk_info.name === 'sveltekit-manifest') {
1321
- return `${kit.appDir}/manifest.js`;
1322
- }
1323
- return `${app_immutable}/chunks/[hash].js`;
1324
- },
1325
- codeSplitting:
1326
- svelte_config.output.bundleStrategy === 'split'
1327
- ? {
1328
- groups: [
1329
- {
1330
- name: 'sveltekit-manifest',
1331
- test: '<sveltekit:generated>/app-manifest.js'
1332
- }
1333
- ]
1334
- }
1335
- : false
1336
- },
1337
- // This silences Rolldown warnings about not supporting `import.meta`
1338
- // for the `iife` output format. We don't care because it's
1339
- // only used in development and will be treeshaken away
1340
- transform: inline
1341
- ? {
1342
- define: {
1343
- 'import.meta': '{}'
1344
- }
1345
- }
1346
- : undefined
1347
- }
1348
- },
1349
- define: {
1350
- __SVELTEKIT_PAYLOAD__:
1351
- svelte_config.output.bundleStrategy !== 'split' ? kit_global : 'undefined'
1352
- }
1353
- }
1354
- },
1355
- experimental: {
1356
- // Allows us to use relative paths in as many places as we can
1357
- renderBuiltUrl(filename, { ssr, hostType }) {
1358
- if (hostType === 'js') {
1359
- // SSR builds should use an absolute path in JS modules to
1360
- // match the default Vite behaviour
1361
- if (ssr) return base + filename;
1362
-
1363
- // We could always use a relative asset base path here, but it's better for performance not to.
1364
- // E.g. Vite generates `new URL('/asset.png', import.meta).href` for a relative path vs just '/asset.png'.
1365
- // That's larger and takes longer to run and also causes an HTML diff between SSR and client
1366
- // causing us to do a more expensive hydration check.
1367
- return { relative };
1368
- }
1369
-
1370
- if (!relative) return;
1371
-
1372
- // ensure assets loaded by CSS files are loaded relative to the
1373
- // CSS file rather than the default of relative to the root
1374
-
1375
- // _app/immutable/assets files
1376
- if (filename.startsWith(root_to_assets)) {
1377
- return `./${filename.slice(root_to_assets.length)}`;
1378
- }
1379
-
1380
- // static dir files
1381
- return assets_to_root + filename;
1382
- }
1383
- },
1384
- publicDir: kit.files.assets
1385
- };
1386
- } else {
1387
- new_config = {
1388
- appType: 'custom',
1389
- // we avoid setting base to paths.assets in dev so that we get the
1390
- // trailing slash redirect to paths.base if it is set
1391
- base: kit.paths.base || '/',
1392
- build: {
1393
- rolldownOptions: {
1394
- // Vite dependency crawler needs an explicit JS entry point
1395
- // even though server otherwise works without it
1396
- input: `${runtime_directory}/client/entry.js`
1397
- }
1398
- },
1399
- publicDir: kit.files.assets
1400
- };
1401
- }
1402
-
1403
- warn_overridden_config(config, new_config);
1404
-
1405
- return new_config;
1406
- }
1407
593
  },
1408
594
 
1409
595
  /**
@@ -1414,9 +600,8 @@ function kit({ svelte_config }) {
1414
600
  return await dev(
1415
601
  vite,
1416
602
  server,
1417
- vite_config,
1418
603
  svelte_config,
1419
- () => remotes,
604
+ () => remote_metadata.remotes,
1420
605
  root,
1421
606
  (data) => {
1422
607
  manifest_data = data;
@@ -1429,839 +614,93 @@ function kit({ svelte_config }) {
1429
614
  * @see https://vitejs.dev/guide/api-plugin.html#configurepreviewserver
1430
615
  */
1431
616
  configurePreviewServer(server) {
1432
- return preview(server, vite_config, svelte_config);
1433
- },
1434
-
1435
- applyToEnvironment(environment) {
1436
- return environment.name !== 'serviceWorker';
1437
- },
1438
-
1439
- renderChunk: {
1440
- // composable filters are not accepted type-wise but still work during build
1441
- // see https://github.com/vitejs/rolldown-vite/issues/605
1442
- filter: /** @type {any} */ (
1443
- /** @satisfies {TopLevelFilterExpression[]} */ ([include(code('__SVELTEKIT_TRACK__'))])
1444
- ),
1445
- handler(code, chunk) {
1446
- // composable filters only work during build so we still need this guard for dev
1447
- // see https://github.com/vitejs/rolldown-vite/issues/605
1448
- if (code.includes('__SVELTEKIT_TRACK__')) {
1449
- return {
1450
- // Rolldown changes our single quotes to double quotes so we need it in the regex too
1451
- code: code.replace(/__SVELTEKIT_TRACK__\(['"](.+?)['"]\)/g, (_, label) => {
1452
- (tracked_features[chunk.name + '.js'] ??= []).push(label);
1453
- // put extra whitespace at the end of the comment to preserve the source size and avoid interfering with source maps
1454
- return `/* track ${label} */`;
1455
- }),
1456
- map: null // TODO we may need to generate a sourcemap in future
1457
- };
1458
- }
1459
- }
1460
- },
1461
-
1462
- generateBundle(_options, bundle) {
1463
- // a watched build returns a watcher rather than the build output from
1464
- // `builder.build` so we need to retrieve it from the generateBundle hook
1465
- if (this.meta.watchMode) {
1466
- watch_build_output.set(
1467
- this.environment.name,
1468
- /** @type {Rolldown.RolldownOutput['output']} */ (Object.values(bundle))
1469
- );
1470
- }
1471
-
1472
- if (this.environment.config.consumer !== 'client') return;
1473
-
1474
- this.emitFile({
1475
- type: 'asset',
1476
- fileName: `${kit.appDir}/version.json`,
1477
- source: s({ version: kit.version.name })
1478
- });
1479
- },
1480
-
1481
- async buildApp(builder) {
1482
- fs.rmSync(out, { force: true, recursive: true });
1483
- fs.mkdirSync(out, { recursive: true });
1484
-
1485
- const verbose = builder.config.logLevel === 'info' || builder.config.logLevel === undefined;
1486
- const log = logger({ verbose });
1487
-
1488
- let ssr_build = await builder.build(builder.environments.ssr);
1489
-
1490
- /** @param {Rolldown.RolldownOutput['output']} server_chunks */
1491
- const process_ssr_build = async (server_chunks) => {
1492
- // Replace manifest placeholders in SSR output. `assets` and `routes`
1493
- // are known from `manifest_data`. `immutable` and `prerendered` are not
1494
- // known yet — they get sentinel strings that are replaced after
1495
- // the client build and after prerendering respectively.
1496
- replace_manifest_placeholder_variables(server_chunks, `${out}/server`, {
1497
- assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1498
- routes: get_manifest_routes(manifest_data.routes)
1499
- });
1500
-
1501
- vite_server_manifest = /** @type {Manifest} */ (
1502
- JSON.parse(read(`${out}/server/.vite/manifest.json`))
1503
- );
1504
-
1505
- const manifest_path = `${out}/server/manifest-full.js`;
1506
- const assets_path = `${kit.appDir}/immutable/assets`;
1507
-
1508
- /** @type {BuildData} */
1509
- const build_data = {
1510
- app_dir: kit.appDir,
1511
- app_path: `${kit.paths.base.slice(1)}${kit.paths.base ? '/' : ''}${kit.appDir}`,
1512
- manifest_data,
1513
- out_dir: out,
1514
- service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
1515
- client: null,
1516
- server_manifest: vite_server_manifest
1517
- };
1518
-
1519
- fs.writeFileSync(
1520
- manifest_path,
1521
- `export const manifest = ${generate_manifest({
1522
- build_data,
1523
- prerendered: [],
1524
- relative_path: '.',
1525
- routes: manifest_data.routes,
1526
- remotes,
1527
- root
1528
- })};\n`
1529
- );
1530
-
1531
- // first, build server nodes without the client manifest so we can analyse it
1532
- build_server_nodes(
1533
- out,
1534
- kit,
1535
- manifest_data,
1536
- vite_server_manifest,
1537
- null,
1538
- assets_path,
1539
- server_chunks,
1540
- root
1541
- );
1542
-
1543
- log.info('Analysing routes');
1544
-
1545
- const { metadata } = await analyse({
1546
- hash: kit.router.type === 'hash',
1547
- manifest_path,
1548
- manifest_data,
1549
- server_manifest: vite_server_manifest,
1550
- tracked_features,
1551
- env,
1552
- remotes,
1553
- vite_config_file: vite_config.configFile
1554
- });
1555
- build_metadata = metadata;
1556
-
1557
- log.info('Building app');
1558
-
1559
- const server_assets = `${out}/server/${assets_path}`;
1560
- const client_assets = `${out}/client/${assets_path}`;
1561
-
1562
- const skip_client_build = manifest_data.nodes.every(
1563
- (node) => node.page_options?.csr === false
1564
- );
1565
-
1566
- if (skip_client_build) {
1567
- copy(server_assets, client_assets);
1568
- copy(kit.files.assets, `${out}/client`);
1569
- } else {
1570
- // ...and build the client
1571
- write_client_manifest(
1572
- kit,
1573
- manifest_data,
1574
- `${out_dir}/generated/build/client-optimized`,
1575
- root,
1576
- build_metadata.nodes
1577
- );
1578
-
1579
- // Through the finished analysis we can now check if any node has server or universal load functions
1580
- const nodes = Object.values(build_metadata.nodes);
1581
- const has_server_load = nodes.some((node) => node.has_server_load);
1582
- const has_universal_load = nodes.some((node) => node.has_universal_load);
1583
-
1584
- if (builder.environments.client.config.define) {
1585
- builder.environments.client.config.define.__SVELTEKIT_HAS_SERVER_LOAD__ =
1586
- s(has_server_load);
1587
- builder.environments.client.config.define.__SVELTEKIT_HAS_UNIVERSAL_LOAD__ =
1588
- s(has_universal_load);
1589
- }
1590
-
1591
- const client_build = await builder.build(builder.environments.client);
1592
- const client_chunks = await normalise_build(
1593
- builder.environments.client.name,
1594
- client_build,
1595
- watch_build_output
1596
- );
1597
-
1598
- // We use `build.ssrEmitAssets` so that asset URLs created from
1599
- // imports in server-only modules correspond to files in the build,
1600
- // but we don't want to copy over CSS imports as these are already
1601
- // accounted for in the client bundle. In most cases it would be
1602
- // a no-op, but for SSR builds `url(...)` paths are handled
1603
- // differently (relative for client, absolute for server)
1604
- // resulting in different hashes, and thus duplication
1605
- const ssr_stylesheets = new Set(
1606
- Object.values(vite_server_manifest)
1607
- .map((chunk) => chunk.css ?? [])
1608
- .flat()
1609
- );
1610
-
1611
- if (fs.existsSync(server_assets)) {
1612
- for (const file of fs.readdirSync(server_assets)) {
1613
- const src = `${server_assets}/${file}`;
1614
- const dest = `${client_assets}/${file}`;
1615
-
1616
- if (fs.existsSync(dest) || ssr_stylesheets.has(`${assets_path}/${file}`)) {
1617
- continue;
1618
- }
1619
-
1620
- copy(src, dest);
1621
- }
1622
- }
1623
-
1624
- vite_client_manifest = /** @type {Manifest} */ (
1625
- JSON.parse(read(`${out}/client/.vite/manifest.json`))
1626
- );
1627
-
1628
- /**
1629
- * @param {string} entry
1630
- * @param {boolean} [add_dynamic_css]
1631
- */
1632
- const deps_of = (entry, add_dynamic_css = false) =>
1633
- find_deps(
1634
- /** @type {Manifest} */ (vite_client_manifest),
1635
- posixify(path.relative(root, entry)),
1636
- add_dynamic_css,
1637
- root
1638
- );
1639
-
1640
- // the inline bundle and stylesheet are deleted further down, after
1641
- // being inlined into the page, so they must not appear in `immutable`
1642
- /** @type {Set<string>} */
1643
- const inlined = new Set();
1644
- /** @type {Rolldown.OutputAsset | undefined} */
1645
- let inline_style;
1646
-
1647
- if (kit.output.bundleStrategy === 'inline') {
1648
- inline_style = /** @type {Rolldown.OutputAsset | undefined} */ (
1649
- client_chunks.find(
1650
- (chunk) =>
1651
- chunk.type === 'asset' &&
1652
- chunk.names.length === 1 &&
1653
- chunk.names[0] === 'style.css'
1654
- )
1655
- );
1656
-
1657
- inlined.add(deps_of(`${runtime_directory}/client/bundle.js`).file);
1658
- if (inline_style) inlined.add(inline_style.fileName);
1659
- }
1660
-
1661
- // Replace manifest placeholders in client output. `immutable` is
1662
- // computed from the Vite client manifest, `assets` and `routes`
1663
- // from `manifest_data`. `prerendered` is left as a placeholder
1664
- // for now — it's replaced after prerendering completes.
1665
- immutable = collect_immutable(vite_client_manifest, kit.appDir, inlined);
1666
-
1667
- replace_manifest_placeholder_variables(client_chunks, `${out}/client`, {
1668
- immutable,
1669
- assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1670
- routes: get_manifest_routes(manifest_data.routes)
1671
- });
1672
-
1673
- // Now that the client build is done, replace the `build` sentinel
1674
- // in the SSR output with the real build files
1675
- replace_manifest_placeholder_strings(`${out}/server`, { immutable });
1676
-
1677
- const has_explicit_dynamic_public_env = Object.values(explicit_env_config ?? {}).some(
1678
- (variable) => variable.public && !variable.static
1679
- );
1680
-
1681
- // the app only depends on runtime public env if it imports `$app/env/public`
1682
- // *and* at least one public env var is actually dynamic (non-static)
1683
- const uses_env_dynamic_public =
1684
- has_explicit_dynamic_public_env &&
1685
- client_chunks.some(
1686
- (chunk) =>
1687
- chunk.type === 'chunk' &&
1688
- chunk.modules[
1689
- posixify(fs.realpathSync(`${out_dir}/generated/build/env/public/client.js`))
1690
- ]
1691
- );
1692
-
1693
- if (kit.output.bundleStrategy === 'split') {
1694
- const start_entry = posixify(
1695
- path.relative(root, `${runtime_directory}/client/entry.js`)
1696
- );
1697
- const start = find_deps(vite_client_manifest, start_entry, false, root);
1698
- const runtime_entry = resolve_symlinks(vite_client_manifest, start_entry, root).chunk
1699
- .dynamicImports?.[0]; // client/entry.js dynamically imports client/client-entry.js
1700
- if (!runtime_entry) throw new Error('Could not find the client runtime chunk');
1701
- const runtime = find_deps(vite_client_manifest, runtime_entry, false, root);
1702
- const app = deps_of(`${out_dir}/generated/build/client-optimized/app.js`);
1703
-
1704
- build_data.client = {
1705
- start: start.file,
1706
- app: app.file,
1707
- imports: Array.from(
1708
- new Set([
1709
- ...start.imports,
1710
- runtime.file,
1711
- ...runtime.imports,
1712
- app.file,
1713
- ...app.imports
1714
- ])
1715
- ),
1716
- stylesheets: [...start.stylesheets, ...runtime.stylesheets, ...app.stylesheets],
1717
- fonts: [...start.fonts, ...runtime.fonts, ...app.fonts],
1718
- uses_env_dynamic_public
1719
- };
1720
-
1721
- // In case of server-side route resolution, we create a purpose-built route manifest that is
1722
- // similar to that on the client, with as much information computed upfront so that we
1723
- // don't need to include any code of the actual routes in the server bundle.
1724
- if (kit.router.resolution === 'server') {
1725
- const nodes = manifest_data.nodes.map((node, i) => {
1726
- if (node.component || node.universal) {
1727
- const entry = `${out_dir}/generated/build/client-optimized/nodes/${i}.js`;
1728
- const deps = deps_of(entry, true);
1729
- const file = resolve_symlinks(
1730
- /** @type {Manifest} */ (vite_client_manifest),
1731
- `${out_dir}/generated/build/client-optimized/nodes/${i}.js`,
1732
- root
1733
- ).chunk.file;
1734
-
1735
- return { file, css: deps.stylesheets };
1736
- }
1737
- });
1738
- build_data.client.nodes = nodes.map((node) => node?.file);
1739
- build_data.client.css = nodes.map((node) => node?.css);
1740
-
1741
- build_data.client.routes = compact(
1742
- manifest_data.routes.map((route) => {
1743
- if (!route.page) return;
1744
-
1745
- return {
1746
- id: route.id,
1747
- pattern: route.pattern,
1748
- params: route.params,
1749
- layouts: route.page.layouts.map((l) =>
1750
- l !== undefined ? [metadata.nodes[l].has_server_load, l] : undefined
1751
- ),
1752
- errors: route.page.errors,
1753
- leaf: [metadata.nodes[route.page.leaf].has_server_load, route.page.leaf]
1754
- };
1755
- })
1756
- );
1757
- }
1758
- } else {
1759
- const start = deps_of(`${runtime_directory}/client/bundle.js`);
1760
-
1761
- build_data.client = {
1762
- start: start.file,
1763
- imports: start.imports,
1764
- stylesheets: start.stylesheets,
1765
- fonts: start.fonts,
1766
- uses_env_dynamic_public
1767
- };
1768
-
1769
- if (kit.output.bundleStrategy === 'inline') {
1770
- build_data.client.inline = {
1771
- script: read(`${out}/client/${start.file}`),
1772
- style: /** @type {string | undefined} */ (inline_style?.source)
1773
- };
1774
-
1775
- // the bundle and stylesheet are inlined into the page, so the
1776
- // emitted files are never loaded
1777
- fs.unlinkSync(`${out}/client/${start.file}`);
1778
- fs.rmSync(`${out}/client/${start.file}.map`, { force: true });
1779
- if (inline_style) fs.unlinkSync(`${out}/client/${inline_style.fileName}`);
1780
- }
1781
- }
1782
-
1783
- // regenerate manifest now that we have client entry...
1784
- fs.writeFileSync(
1785
- manifest_path,
1786
- `export const manifest = ${generate_manifest({
1787
- build_data,
1788
- prerendered: [],
1789
- relative_path: '.',
1790
- routes: manifest_data.routes,
1791
- remotes,
1792
- root
1793
- })};\n`
1794
- );
1795
-
1796
- // regenerate nodes with the client manifest...
1797
- build_server_nodes(
1798
- out,
1799
- kit,
1800
- manifest_data,
1801
- vite_server_manifest,
1802
- vite_client_manifest,
1803
- assets_path,
1804
- client_chunks,
1805
- root
1806
- );
1807
- }
1808
-
1809
- // ...and prerender
1810
- let prerender_results;
1811
- try {
1812
- prerender_results = await prerender({
1813
- hash: kit.router.type === 'hash',
1814
- out,
1815
- manifest_path,
1816
- metadata,
1817
- verbose,
1818
- env,
1819
- vite_config_file: vite_config.configFile,
1820
- is_tty: process.stdout.isTTY
1821
- });
1822
-
1823
- // this silly hack is necessary to ensure that stderr from prerender is flushed before we continue
1824
- await new Promise((f) => setTimeout(f, 0));
1825
- } catch (e) {
1826
- if (e instanceof Error && e.message === '__handled__') {
1827
- // error details are already logged inside `prerender`, don't duplicate them
1828
- throw stackless('Prerendering failed');
1829
- } else {
1830
- // Unforeseen error, rethrow as-is
1831
- throw e;
1832
- }
1833
- }
1834
-
1835
- prerendered = prerender_results.prerendered;
1836
-
1837
- // Replace the `prerendered` sentinel in both SSR and client output
1838
- // with the real prerendered paths. The other sentinels (`build`)
1839
- // were already replaced after the client build.
1840
- const prerendered_paths = prerendered.paths.map((p) => {
1841
- return { path: p.replace(kit.paths.base, '').slice(1) };
1842
- });
1843
-
1844
- replace_manifest_placeholder_strings(`${out}/server`, { prerendered: prerendered_paths });
1845
- replace_manifest_placeholder_strings(`${out}/client`, { prerendered: prerendered_paths });
1846
-
1847
- // For `inline` strategy, the entry file was deleted and read into
1848
- // `build_data.client.inline.script` — replace the sentinel there too
1849
- if (build_data.client?.inline?.script) {
1850
- build_data.client.inline.script = build_data.client.inline.script.replaceAll(
1851
- '"__sveltekit_manifest_prerendered__"',
1852
- JSON.stringify(prerendered_paths)
1853
- );
1854
- }
1855
-
1856
- // generate a new manifest that doesn't include prerendered pages
1857
- fs.writeFileSync(
1858
- `${out}/server/manifest.js`,
1859
- `export const manifest = ${generate_manifest({
1860
- build_data,
1861
- prerendered: prerendered.paths,
1862
- relative_path: '.',
1863
- routes: manifest_data.routes.filter(
1864
- (route) => prerender_results.prerender_map.get(route.id) !== true
1865
- ),
1866
- remotes,
1867
- root
1868
- })};\n`
1869
- );
1870
-
1871
- await treeshake_prerendered_remotes(
1872
- vite,
1873
- out,
1874
- remotes,
1875
- remote_original_by_hash,
1876
- metadata,
1877
- process.cwd(),
1878
- server_chunks,
1879
- vite_config.build.sourcemap
1880
- );
1881
-
1882
- // defer until after other buildApp hooks have run
1883
- finalise = async () => {
1884
- // defer creating the service worker to avoid other plugins from
1885
- // overwriting it if they run a client environment build
1886
- if (service_worker_entry_file) {
1887
- log.info('Building service worker');
1888
-
1889
- // Add defines for `$app/manifest`
1890
- builder.environments.serviceWorker.config.define = {
1891
- ...builder.environments.serviceWorker.config.define,
1892
-
1893
- __SVELTEKIT_MANIFEST_ASSETS__: s(
1894
- manifest_data.assets.map((asset) => ({ path: asset.file }))
1895
- ),
1896
- __SVELTEKIT_MANIFEST_IMMUTABLE__: s(immutable),
1897
- __SVELTEKIT_MANIFEST_PRERENDERED__: s(prerendered_paths),
1898
- __SVELTEKIT_MANIFEST_ROUTES__: s(get_manifest_routes(manifest_data.routes))
1899
- };
1900
-
1901
- // we have to overwrite this because it can't be configured per environment in the config hook
1902
- builder.environments.serviceWorker.config.experimental.renderBuiltUrl = (filename) => {
1903
- return {
1904
- runtime: `new URL(${JSON.stringify(filename)}, location.href).pathname`
1905
- };
1906
- };
1907
-
1908
- const service_worker_build = await builder.build(builder.environments.serviceWorker);
1909
- await normalise_build(
1910
- builder.environments.serviceWorker.name,
1911
- service_worker_build,
1912
- watch_build_output
1913
- );
1914
- }
1915
-
1916
- console.log(
1917
- `\nRun ${styleText(['bold', 'cyan'], 'npm run preview')} to preview your production build locally.`
1918
- );
1919
-
1920
- if (kit.adapter) {
1921
- await adapt(
1922
- svelte_config,
1923
- build_data,
1924
- metadata,
1925
- prerendered,
1926
- prerender_results.prerender_map,
1927
- log,
1928
- remotes,
1929
- vite_config,
1930
- explicit_env_config
1931
- );
1932
- } else {
1933
- log.warn('\nNo adapter specified');
1934
-
1935
- const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
1936
- console.log(
1937
- `See ${link} to learn how to configure your app to run on the platform of your choosing`
1938
- );
1939
- }
1940
- };
1941
- };
1942
-
1943
- // `vite build`
1944
- ssr_build = Array.isArray(ssr_build) ? ssr_build[0] : ssr_build;
1945
- if ('output' in ssr_build) {
1946
- await load_and_validate_params({
1947
- routes: manifest_data.routes,
1948
- params_path: manifest_data.params,
1949
- root
1950
- });
1951
-
1952
- return await process_ssr_build(ssr_build.output);
1953
- }
1954
-
1955
- // `vite build --watch`
1956
- let rebuild = false;
1957
-
1958
- const before_ssr_build_rerun = async () => {
1959
- rebuild = true;
1960
-
1961
- // these are set once per plugin initialisation or when the config hook
1962
- // runs. However, those don't re-run during watch mode. So, we need to
1963
- // re-initialise them manually here
1964
- manifest_data = create_manifest_data(svelte_config, root);
1965
- sync.all(svelte_config, root, manifest_data);
1966
-
1967
- tracked_features = {};
1968
-
1969
- remotes = [];
1970
- remote_original_by_hash.clear();
1971
- emitted_remote_hashes.clear();
1972
-
1973
- immutable = null;
1974
-
1975
- finalise = null;
1976
-
1977
- fs.mkdirSync(out, { recursive: true });
1978
-
1979
- await load_and_validate_params({
1980
- routes: manifest_data.routes,
1981
- params_path: manifest_data.params,
1982
- root
1983
- });
1984
- };
1985
-
1986
- ssr_build.on('change', before_ssr_build_rerun);
1987
- ssr_build.on('restart', before_ssr_build_rerun);
1988
-
1989
- /** @type {PromiseWithResolvers<void>} */
1990
- const task = Promise.withResolvers();
617
+ return preview(server, svelte_config);
618
+ }
619
+ };
1991
620
 
1992
- ssr_build.on('event', async (event) => {
1993
- if (event.code === 'ERROR') {
1994
- return task.reject();
1995
- }
621
+ /** @type {ServerMetadata | null} build analysis results */
622
+ let build_metadata = null;
1996
623
 
1997
- if (event.code === 'BUNDLE_END') {
1998
- try {
1999
- await process_ssr_build(
2000
- /** @type {Rolldown.RolldownOutput['output']} */ (
2001
- watch_build_output.get(builder.environments.ssr.name)
2002
- )
2003
- );
2004
- // buildApp hooks don't rerun in watch mode so we need to run
2005
- // the deferred steps here on subsequent builds
2006
- if (rebuild) await finalise?.();
2007
- } catch (e) {
2008
- return task.reject(e);
2009
- } finally {
2010
- await event.result.close();
2011
- }
2012
- return task.resolve();
2013
- }
2014
- });
624
+ /** @type {Record<string, EnvVarConfig<any>> | null} */
625
+ let explicit_env_config = null;
2015
626
 
2016
- await task.promise;
2017
- }
627
+ /** @type {{ remotes: RemoteChunk[]; remote_original_by_hash: Map<string, string>}} */
628
+ let remote_metadata = {
629
+ remotes: [],
630
+ remote_original_by_hash: new Map()
2018
631
  };
2019
632
 
2020
- /** @type {Plugin} */
2021
- const plugin_adapter = {
2022
- name: 'vite-plugin-sveltekit-adapter',
2023
- apply: 'build',
2024
- buildApp: {
2025
- // this will run after any buildApp hooks provided by other Vite plugins
2026
- // see https://vite.dev/guide/api-environment-frameworks#environments-during-build
2027
- order: 'post',
2028
- async handler() {
2029
- await finalise?.();
2030
- }
2031
- }
2032
- };
633
+ /** @type {(() => Promise<void>) | null} */
634
+ let finalise = null;
635
+
636
+ if (Array.isArray(svelte_config.adapter?.vite?.plugins)) {
637
+ svelte_config.adapter.vite.plugins = {
638
+ pre: svelte_config.adapter.vite.plugins
639
+ };
640
+ }
2033
641
 
2034
642
  return /** @type {Plugin[]} */ (
2035
643
  [
2036
644
  svelte_config.adapter?.vite?.plugins?.pre,
2037
645
  plugin_resolve_root,
2038
646
  plugin_setup,
2039
- plugin_remote_guard,
2040
- plugin_remote,
647
+ plugin_remote_guard(svelte_config),
648
+ plugin_remote(
649
+ svelte_config,
650
+ () => ({ root, vite }),
651
+ () => build_metadata,
652
+ (metadata) => {
653
+ remote_metadata = metadata;
654
+ }
655
+ ),
2041
656
  plugin_env_vars(svelte_config, (vars) => {
2042
657
  explicit_env_config = vars;
2043
658
  }),
2044
- process.env.TEST !== 'true' ? plugin_guard : undefined,
2045
- plugin_service_worker,
2046
- plugin_service_worker_env,
2047
- plugin_compile,
2048
- plugin_adapter,
659
+ process.env.TEST !== 'true'
660
+ ? plugin_guard(
661
+ svelte_config,
662
+ () => ({
663
+ vite,
664
+ root,
665
+ normalized_aliases,
666
+ service_worker_entry_file
667
+ }),
668
+ // in dev, this doesn't exist yet, so we need to create it
669
+ () => (manifest_data ??= create_manifest_data(svelte_config, root))
670
+ )
671
+ : undefined,
672
+ plugin_service_worker_build(svelte_config, () => ({
673
+ service_worker_entry_file,
674
+ kit_global,
675
+ initial_config,
676
+ out
677
+ })),
678
+ plugin_service_worker_env_vars(() => service_worker_entry_file),
679
+ plugin_compile(
680
+ svelte_config,
681
+ () => ({
682
+ root,
683
+ initial_config,
684
+ global_name,
685
+ kit_global,
686
+ vite,
687
+ service_worker_entry_file,
688
+ sourcemapIgnoreList
689
+ }),
690
+ (metadata) => {
691
+ build_metadata = metadata;
692
+ },
693
+ (data) => {
694
+ manifest_data = data;
695
+ },
696
+ () => explicit_env_config,
697
+ () => remote_metadata,
698
+ (fn) => {
699
+ finalise = fn;
700
+ }
701
+ ),
702
+ plugin_adapter(async () => await finalise?.()),
2049
703
  svelte_config.adapter?.vite?.plugins?.post
2050
704
  ].filter(Boolean)
2051
705
  );
2052
706
  }
2053
-
2054
- /**
2055
- * @param {UserConfig} config
2056
- * @param {UserConfig} resolved_config
2057
- */
2058
- function warn_overridden_config(config, resolved_config) {
2059
- const overridden = find_overridden_config(config, resolved_config, enforced_config, '', []);
2060
-
2061
- if (overridden.length > 0) {
2062
- console.error(
2063
- styleText(
2064
- ['bold', 'red'],
2065
- 'The following Vite config options will be overridden by SvelteKit:'
2066
- ) + overridden.map((key) => `\n - ${key}`).join('')
2067
- );
2068
- }
2069
- }
2070
-
2071
- /**
2072
- * @param {Record<string, any>} config
2073
- * @param {Record<string, any>} resolved_config
2074
- * @param {import('./types.js').EnforcedConfig} enforced_config
2075
- * @param {string} path
2076
- * @param {string[]} out used locally to compute the return value
2077
- */
2078
- function find_overridden_config(config, resolved_config, enforced_config, path, out) {
2079
- if (config == null || resolved_config == null) {
2080
- return out;
2081
- }
2082
-
2083
- for (const key in enforced_config) {
2084
- if (typeof config === 'object' && key in config && key in resolved_config) {
2085
- const enforced = enforced_config[key];
2086
- const resolved = resolved_config[key];
2087
-
2088
- if (enforced === true) {
2089
- if (comparable(config[key]) !== comparable(resolved)) {
2090
- out.push(path + key);
2091
- }
2092
- } else {
2093
- find_overridden_config(config[key], resolved, enforced, path + key + '.', out);
2094
- }
2095
- }
2096
- }
2097
- return out;
2098
- }
2099
-
2100
- /**
2101
- * Collects the content-hashed files of a Vite manifest, in the shape of
2102
- * `$app/manifest`'s `immutable` export.
2103
- *
2104
- * @param {Manifest} manifest
2105
- * @param {string} app_dir
2106
- * @param {Set<string>} inlined files deleted from the output after being inlined into the page
2107
- * @returns {Array<{ path: string }>}
2108
- */
2109
- const collect_immutable = (manifest, app_dir, inlined) => {
2110
- const prefix = `${app_dir}/immutable`;
2111
-
2112
- /** @type {Set<string>} */
2113
- const files = new Set();
2114
-
2115
- /** @param {string} file */
2116
- const add = (file) => {
2117
- if (file.startsWith(prefix) && !inlined.has(file)) files.add(file);
2118
- };
2119
-
2120
- for (const key in manifest) {
2121
- const { file, css, assets } = manifest[key];
2122
- add(file);
2123
- if (css) for (let i = 0; i < css.length; i++) add(css[i]);
2124
- if (assets) for (let i = 0; i < assets.length; i++) add(assets[i]);
2125
- }
2126
-
2127
- return Array.from(files, (path) => ({ path }));
2128
- };
2129
-
2130
- /**
2131
- * Normalizes a config value for comparison, since Windows paths may use backslashes
2132
- * and differ in casing (e.g. the drive letter) depending on where they came from.
2133
- * @param {any} value
2134
- */
2135
- function comparable(value) {
2136
- if (typeof value !== 'string') return value;
2137
- const normalized = posixify(value);
2138
- return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
2139
- }
2140
-
2141
- /**
2142
- * Replaces manifest data placeholder identifiers in output chunks with real
2143
- * values, or strings that are valid JS (so thecode doesn't crash during prerendering)
2144
- * but findable on disk for later replacement by `replace_manifest_placeholder_strings`.
2145
- *
2146
- * @param {Rolldown.RolldownOutput['output']} chunks
2147
- * @param {string} output_dir
2148
- * @param {{
2149
- * immutable?: Array<{ path: string }>;
2150
- * assets?: Array<{ path: string }>;
2151
- * prerendered?: Array<{ path: string }>;
2152
- * routes?: Array<{ id: string; page: boolean; endpoint: boolean }>;
2153
- * }} values
2154
- */
2155
- const replace_manifest_placeholder_variables = (chunks, output_dir, values) => {
2156
- /** @type {Record<string, string>} */
2157
- const replacements = {
2158
- __SVELTEKIT_MANIFEST_IMMUTABLE__: JSON.stringify(
2159
- values.immutable ?? '__sveltekit_manifest_build__'
2160
- ),
2161
- __SVELTEKIT_MANIFEST_ASSETS__: JSON.stringify(values.assets ?? '__sveltekit_manifest_files__'),
2162
- __SVELTEKIT_MANIFEST_PRERENDERED__: JSON.stringify(
2163
- values.prerendered ?? '__sveltekit_manifest_prerendered__'
2164
- ),
2165
- __SVELTEKIT_MANIFEST_ROUTES__: JSON.stringify(values.routes ?? '__sveltekit_manifest_routes__')
2166
- };
2167
-
2168
- for (const chunk of chunks) {
2169
- if (chunk.type !== 'chunk') continue;
2170
- if (!chunk.code.includes('__SVELTEKIT_MANIFEST_')) continue;
2171
-
2172
- let code = chunk.code;
2173
-
2174
- for (const [identifier, replacement] of Object.entries(replacements)) {
2175
- code = code.replaceAll(identifier, replacement);
2176
- }
2177
-
2178
- const file_path = `${output_dir}/${chunk.fileName}`;
2179
- fs.writeFileSync(file_path, code);
2180
- }
2181
- };
2182
-
2183
- /**
2184
- * Replaces manifest sentinel strings in files on disk with real values.
2185
- * This is used for values that weren't known when the first replacement
2186
- * pass ran (e.g. `build` wasn't known until after the client build,
2187
- * `prerendered` wasn't known until after prerendering).
2188
- *
2189
- * @param {string} dir Directory to scan for .js files
2190
- * @param {{
2191
- * immutable?: Array<{ path: string }>;
2192
- * prerendered?: Array<{ path: string }>;
2193
- * }} values
2194
- */
2195
- const replace_manifest_placeholder_strings = (dir, values) => {
2196
- /** @type {Record<string, string>} */
2197
- const replacements = {};
2198
-
2199
- if (values.immutable !== undefined) {
2200
- replacements['__sveltekit_manifest_build__'] = JSON.stringify(values.immutable);
2201
- }
2202
- if (values.prerendered !== undefined) {
2203
- replacements['__sveltekit_manifest_prerendered__'] = JSON.stringify(values.prerendered);
2204
- }
2205
-
2206
- for (const file of fs.readdirSync(dir)) {
2207
- const file_path = `${dir}/${file}`;
2208
- const stat = fs.statSync(file_path);
2209
-
2210
- if (stat.isDirectory()) {
2211
- replace_manifest_placeholder_strings(file_path, values);
2212
- continue;
2213
- }
2214
-
2215
- if (!file.endsWith('.js')) continue;
2216
-
2217
- let code = read(file_path);
2218
- let changed = false;
2219
-
2220
- for (const [sentinel, replacement] of Object.entries(replacements)) {
2221
- if (code.includes(sentinel)) {
2222
- code = code.replaceAll(`"${sentinel}"`, replacement);
2223
- changed = true;
2224
- }
2225
- }
2226
-
2227
- if (changed) {
2228
- fs.writeFileSync(file_path, code);
2229
- }
2230
- }
2231
- };
2232
-
2233
- /**
2234
- * Normalises the build output to a consistent format, handling watch mode and multiple environments
2235
- * @param {string} name The name of the environment
2236
- * @param {Rolldown.RolldownOutput | Rolldown.RolldownOutput[] | Rolldown.RolldownWatcher} build The return value of builder.build
2237
- * @param {Map<string, Rolldown.RolldownOutput['output']>} build_output_map
2238
- * @returns {Promise<Rolldown.RolldownOutput['output']>}
2239
- */
2240
- async function normalise_build(name, build, build_output_map) {
2241
- if ('output' in build) {
2242
- return build.output;
2243
- }
2244
-
2245
- if (Array.isArray(build)) {
2246
- return build[0].output;
2247
- }
2248
-
2249
- /** @type {PromiseWithResolvers<void>} */
2250
- const bundling = Promise.withResolvers();
2251
-
2252
- build.on('event', async (event) => {
2253
- if (event.code === 'ERROR') {
2254
- await build.close();
2255
- return bundling.reject(event.error);
2256
- }
2257
-
2258
- if (event.code === 'BUNDLE_END') {
2259
- await build.close();
2260
- return bundling.resolve();
2261
- }
2262
- });
2263
-
2264
- await bundling.promise;
2265
-
2266
- return /** @type {Rolldown.RolldownOutput['output']} */ (build_output_map.get(name));
2267
- }