@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
@@ -1,60 +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 { code, include, prefixRegex } from '@rolldown/pluginutils';
13
- import MagicString from 'magic-string';
14
-
15
- import { copy, read, resolve_entry } from '../../utils/filesystem.js';
12
+ import { resolve_entry } from '../../utils/filesystem.js';
16
13
  import { posixify } from '../../utils/os.js';
17
14
  import { to_fs } from '../../utils/vite.js';
18
- import { create_exported_declarations } from '../../core/env.js';
19
- import * as sync from '../../core/sync/sync.js';
20
- import { load_and_validate_params } from '../../utils/params.js';
21
15
  import { runtime_directory, logger, get_global_name } from '../../core/utils.js';
22
- import { generate_manifest } from '../../core/generate_manifest/index.js';
23
- import { build_server_nodes } from './build/build_server.js';
24
- import { find_deps, resolve_symlinks } from './build/utils.js';
25
16
  import { dev } from './dev/index.js';
26
17
  import { preview } from './preview/index.js';
27
18
  import {
28
19
  enforced_config,
29
- error_for_missing_config,
30
20
  get_config_aliases,
31
21
  is_remote_module,
32
22
  remote_module_pattern,
33
23
  warn_overridden_config
34
24
  } from './utils.js';
35
25
  import { stackless } from '../../utils/error.js';
36
- import { adapt } from '../../core/adapt/index.js';
37
- import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
38
- import prerender from '../../core/postbuild/prerender.js';
39
- import analyse from '../../core/postbuild/analyse.js';
40
26
  import { s } from '../../utils/misc.js';
41
- import { hash } from '../../utils/hash.js';
42
27
  import { dedent } from '../../core/sync/utils.js';
43
28
  import create_manifest_data from '../../core/sync/create_manifest_data/index.js';
44
29
  import { get_import_aliases, get_hash_import_keys } from '../../utils/imports.js';
45
30
  import { import_peer } from '../../utils/import.js';
46
- import { compact } from '../../utils/array.js';
47
31
  import { should_ignore, has_children } from './static_analysis/utils.js';
48
32
  import { process_config, split_config, validate_config } from '../../core/config/index.js';
49
- import { treeshake_prerendered_remotes } from './build/remote.js';
50
- import { get_runner } from '../../runner.js';
51
33
  import { plugin_env_vars, plugin_service_worker_env_vars } from './plugins/env-vars.js';
52
34
  import { plugin_guard } from './plugins/guard.js';
53
- import { get_manifest_routes, write_app_manifest } from '../../core/sync/write_app_manifest.js';
35
+ import { plugin_remote, plugin_remote_guard } from './plugins/remote.js';
36
+ import { write_app_manifest } from '../../core/sync/write_app_manifest.js';
54
37
  import { plugin_service_worker_build } from './build/service-worker.js';
38
+ import { plugin_adapter, plugin_compile } from './build/index.js';
55
39
 
56
40
  const options_regex = /(export\s+const\s+(prerender|csr|ssr|trailingSlash))\s*=/s;
57
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
+
58
80
  const removed_modules = [
59
81
  {
60
82
  name: '$lib',
@@ -216,9 +238,6 @@ function resolve_root(vite_config) {
216
238
  * @return {Plugin[]}
217
239
  */
218
240
  function kit({ svelte_config }) {
219
- /** @type {typeof import('vite')} */
220
- let vite;
221
-
222
241
  /**
223
242
  * The posix-ified root of the project based on the Vite configuration.
224
243
  * @type {string}
@@ -232,21 +251,12 @@ function kit({ svelte_config }) {
232
251
  /** @type {string} The base directory for the Vite builds */
233
252
  let out;
234
253
 
235
- /** @type {ResolvedConfig} */
236
- let vite_config;
237
-
238
254
  /** @type {boolean} */
239
255
  let is_build;
240
256
 
241
- /** @type {Record<string, string>} */
242
- let env;
243
-
244
257
  /** @type {ManifestData} */
245
258
  let manifest_data;
246
259
 
247
- /** @type {ServerMetadata | null} set at build time once analysis has finished */
248
- let build_metadata = null;
249
-
250
260
  /** @type {UserConfig} */
251
261
  let initial_config;
252
262
 
@@ -254,12 +264,6 @@ function kit({ svelte_config }) {
254
264
  let service_worker_entry_file;
255
265
  /** @type {Array<{ alias: string, path: string }>} */
256
266
  let normalized_aliases;
257
- /**
258
- * A map showing which features (such as `$app/server:read`) are defined
259
- * in which chunks, so that we can later determine which routes use which features
260
- * @type {Record<string, string[]>}
261
- */
262
- let tracked_features = {};
263
267
 
264
268
  const sourcemapIgnoreList = /** @param {string} relative_path */ (relative_path) =>
265
269
  relative_path.includes('node_modules') || relative_path.includes(kit.outDir);
@@ -324,7 +328,7 @@ function kit({ svelte_config }) {
324
328
  */
325
329
  config: {
326
330
  order: 'pre',
327
- async handler(config, config_env) {
331
+ handler(config, config_env) {
328
332
  initial_config = config;
329
333
  is_build = config_env.command === 'build';
330
334
 
@@ -338,10 +342,6 @@ function kit({ svelte_config }) {
338
342
  service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
339
343
  service_worker_entry_file &&= posixify(service_worker_entry_file);
340
344
 
341
- vite = await import_peer('vite', root);
342
-
343
- env = vite.loadEnv(config_env.mode, kit.env.dir, '');
344
-
345
345
  normalized_aliases = get_import_aliases(root, vite.normalizePath.bind(vite));
346
346
 
347
347
  // Add `#`-prefixed import keys to the enforced config so users are warned
@@ -382,6 +382,7 @@ function kit({ svelte_config }) {
382
382
  // dev and preview config can be shared
383
383
  /** @type {UserConfig} */
384
384
  const new_config = {
385
+ appType: 'custom',
385
386
  environments: {
386
387
  ssr: {
387
388
  build: {
@@ -402,7 +403,7 @@ function kit({ svelte_config }) {
402
403
  ]
403
404
  },
404
405
  server: {
405
- cors: { preflightContinue: true },
406
+ cors: resolve_cors(config.server?.cors, 'server.cors', !is_build),
406
407
  fs: {
407
408
  allow: [...allow]
408
409
  },
@@ -415,7 +416,7 @@ function kit({ svelte_config }) {
415
416
  }
416
417
  },
417
418
  preview: {
418
- cors: { preflightContinue: true }
419
+ cors: resolve_cors(config.preview?.cors, 'preview.cors', !is_build)
419
420
  },
420
421
  optimizeDeps: {
421
422
  entries: [
@@ -448,7 +449,8 @@ function kit({ svelte_config }) {
448
449
  // uses basic concatenation)
449
450
  '@sveltejs/kit/src/runtime'
450
451
  ]
451
- }
452
+ },
453
+ publicDir: kit.files.assets
452
454
  };
453
455
 
454
456
  // externalize .remote.js files to stop dependency tracing during prebundling
@@ -505,9 +507,6 @@ function kit({ svelte_config }) {
505
507
  __SVELTEKIT_APP_VERSION_FILE__: s(`${kit.appDir}/version.json`),
506
508
  __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: s(kit.version.pollInterval)
507
509
  };
508
-
509
- manifest_data = create_manifest_data(svelte_config, root);
510
- sync.all(svelte_config, root, manifest_data);
511
510
  } else {
512
511
  new_config.define = {
513
512
  ...define,
@@ -517,7 +516,7 @@ function kit({ svelte_config }) {
517
516
  __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true'
518
517
  };
519
518
 
520
- // 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.
521
520
  // Without this, the tests will still pass but `pnpm dev` will fail in projects that link `@sveltejs/kit`.
522
521
  //
523
522
  // `@opentelemetry/api` must be externalized so that `instrumentation.server.js` and the
@@ -528,9 +527,18 @@ function kit({ svelte_config }) {
528
527
  // would cause those modules to be evaluated before `Server.init()` sets env vars — see
529
528
  // https://github.com/sveltejs/kit/issues/16288
530
529
  /** @type {NonNullable<UserConfig['ssr']>} */ (new_config.ssr).external = [
531
- 'cookie',
532
530
  '@opentelemetry/api'
533
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`;
534
542
  }
535
543
 
536
544
  // Vite's `define` is a compile-time text replacement, but Vitest strips
@@ -560,16 +568,14 @@ function kit({ svelte_config }) {
560
568
  * Stores the final config.
561
569
  */
562
570
  configResolved(config) {
563
- vite_config = config;
564
-
565
571
  if (!is_build) {
566
572
  // Dependency scanning starts before configureServer creates the full manifest
567
573
  write_app_manifest(`${out_dir}/generated/dev`, undefined, false);
568
574
  }
569
575
 
570
- const unsupported_plugins = vite_config.plugins.filter((plugin) => plugin.transformIndexHtml);
576
+ const unsupported_plugins = config.plugins.filter((plugin) => plugin.transformIndexHtml);
571
577
  if (unsupported_plugins.length) {
572
- const verbose = vite_config.logLevel === 'info' || vite_config.logLevel === undefined;
578
+ const verbose = config.logLevel === 'info' || config.logLevel === undefined;
573
579
  const log = logger({ verbose });
574
580
 
575
581
  const list = unsupported_plugins
@@ -584,496 +590,6 @@ function kit({ svelte_config }) {
584
590
  `
585
591
  );
586
592
  }
587
- }
588
- };
589
-
590
- /** @type {Record<string, EnvVarConfig<any>> | null} */
591
- let explicit_env_config = null;
592
-
593
- /** @type {ViteDevServer} */
594
- let dev_server;
595
-
596
- /** @type {RemoteChunk[]} */
597
- let remotes = [];
598
-
599
- /** @type {Map<string, string>} Maps remote hash -> original module id */
600
- const remote_original_by_hash = new Map();
601
-
602
- /** @type {Set<string>} Track which remote hashes have already been emitted */
603
- const emitted_remote_hashes = new Set();
604
-
605
- /** @type {Plugin} */
606
- const plugin_remote = {
607
- name: 'vite-plugin-sveltekit-remote',
608
-
609
- applyToEnvironment(environment) {
610
- return svelte_config.experimental.remoteFunctions && environment.name !== 'serviceWorker';
611
- },
612
-
613
- // prevent other plugins from resolving our remote virtual module
614
- resolveId: {
615
- filter: {
616
- id: prefixRegex('\0sveltekit-remote:')
617
- },
618
- handler(id) {
619
- return id;
620
- }
621
- },
622
-
623
- load: {
624
- filter: {
625
- id: prefixRegex('\0sveltekit-remote:')
626
- },
627
- handler(id) {
628
- // On-the-fly generated entry point for remote file just forwards the original module
629
- // We're not using manualChunks because it can cause problems with circular dependencies
630
- // (e.g. https://github.com/sveltejs/kit/issues/14679) and module ordering in general
631
- // (e.g. https://github.com/sveltejs/kit/issues/14590).
632
- const hash_id = id.slice('\0sveltekit-remote:'.length);
633
- const original = remote_original_by_hash.get(hash_id);
634
- if (!original) throw new Error(`Expected to find metadata for remote file ${id}`);
635
- return `import * as m from ${s(original)};\nexport default m;`;
636
- }
637
- },
638
-
639
- configureServer(_dev_server) {
640
- dev_server = _dev_server;
641
- },
642
-
643
- transform: {
644
- filter: {
645
- id: remote_module_pattern
646
- },
647
- async handler(code, id) {
648
- if (!is_remote_module(id)) return;
649
-
650
- const file = posixify(path.relative(root, id));
651
- const remote = {
652
- hash: hash(file),
653
- file
654
- };
655
-
656
- if (this.environment.name === 'ssr') remotes.push(remote);
657
-
658
- if (this.environment.config.consumer !== 'client') {
659
- // we need to add an `await Promise.resolve()` because if the user imports this function
660
- // on the client AND in a load function when loading the client module we will trigger
661
- // an import during dev. During a link preload, the module can be mistakenly
662
- // loaded and transformed twice and the first time all its exports would be undefined
663
- // triggering a dev server error. By adding a microtask we ensure that the module is fully loaded
664
- const ms = new MagicString(code);
665
-
666
- // Extra newlines to prevent syntax errors around missing semicolons or comments
667
- ms.append(
668
- '\n\n' +
669
- dedent`
670
- import * as $$_self_$$ from './${path.basename(id)}';
671
- import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal/server';
672
-
673
- ${dev_server ? 'await Promise.resolve()' : ''}
674
-
675
- $$_init_$$($$_self_$$, ${s(file)}, ${s(remote.hash)});
676
-
677
- for (const [name, fn] of Object.entries($$_self_$$)) {
678
- fn.__.id = ${s(remote.hash)} + '/' + name;
679
- fn.__.name = name;
680
- }
681
- `
682
- );
683
-
684
- // Emit a dedicated entry chunk for this remote in SSR builds (prod only)
685
- if (!dev_server) {
686
- remote_original_by_hash.set(remote.hash, id);
687
- if (!emitted_remote_hashes.has(remote.hash)) {
688
- this.emitFile({
689
- type: 'chunk',
690
- id: `\0sveltekit-remote:${remote.hash}`,
691
- name: `remote-${remote.hash}`
692
- });
693
- emitted_remote_hashes.add(remote.hash);
694
- }
695
- }
696
-
697
- return {
698
- code: ms.toString(),
699
- map: ms.generateMap({ hires: 'boundary' })
700
- };
701
- }
702
-
703
- // For the client, read the exports and create a new module that only contains fetch functions with the correct metadata
704
-
705
- /** @type {Map<string, RemoteInternals['type']>} */
706
- const map = new Map();
707
-
708
- // in dev, load the server module here (which will result in this hook
709
- // being called again with `opts.ssr === true` if the module isn't
710
- // already loaded) so we can determine what it exports
711
- if (dev_server) {
712
- const module = await get_runner(vite, dev_server).import(id);
713
-
714
- for (const [name, value] of Object.entries(module)) {
715
- const type = value?.__?.type;
716
- if (type) {
717
- map.set(name, type);
718
- }
719
- }
720
- }
721
-
722
- // in prod, we already built and analysed the server code before
723
- // building the client code, so `remotes` is populated
724
- else if (build_metadata?.remotes) {
725
- const exports = build_metadata?.remotes.get(remote.hash);
726
- if (!exports) throw new Error('Expected to find metadata for remote file ' + id);
727
-
728
- for (const [name, value] of exports) {
729
- map.set(name, value.type);
730
- }
731
- }
732
-
733
- const { namespace, declarations, reexports } = create_exported_declarations(
734
- map.keys(),
735
- (name, ns) => `${ns}.${map.get(name)}('${remote.hash}/${name}')`,
736
- '__remote'
737
- );
738
-
739
- const relative = posixify(
740
- path.relative(path.dirname(id), `${runtime_directory}/client/remote-functions/index.js`)
741
- );
742
-
743
- let result = `import * as ${namespace} from '${relative}';\n\n${declarations.join('\n')}`;
744
- if (reexports.length > 0) {
745
- result += `\nexport { ${reexports.join(', ')} };`;
746
- }
747
- result += '\n';
748
-
749
- if (dev_server) {
750
- result += `\nimport.meta.hot?.accept();\n`;
751
- }
752
-
753
- return {
754
- code: result,
755
- map: null
756
- };
757
- }
758
- },
759
-
760
- buildEnd() {
761
- if (this.environment.config.consumer === 'server') {
762
- emitted_remote_hashes.clear();
763
- }
764
- }
765
- };
766
-
767
- /** @type {Plugin} */
768
- const plugin_remote_guard = {
769
- name: 'vite-plugin-sveltekit-remote-guard',
770
-
771
- applyToEnvironment() {
772
- return !svelte_config.experimental.remoteFunctions;
773
- },
774
-
775
- transform: {
776
- filter: {
777
- id: new RegExp(
778
- `.remote(${svelte_config.moduleExtensions.join('|')})$`.replaceAll('.', '\\.')
779
- )
780
- },
781
- handler() {
782
- error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
783
- }
784
- }
785
- };
786
-
787
- /** @type {Manifest} */
788
- let vite_server_manifest;
789
- /** @type {Manifest | null} */
790
- let vite_client_manifest = null;
791
- /** @type {Prerendered} */
792
- let prerendered;
793
-
794
- /** @type {Array<{ path: string }> | null} */
795
- let immutable = null;
796
-
797
- /** @type {Map<string, Rolldown.RolldownOutput['output']>} */
798
- const watch_build_output = new Map();
799
- /** @type {(() => Promise<void>) | null} */
800
- let finalise = null;
801
-
802
- /** @type {Plugin} */
803
- const plugin_compile = {
804
- name: 'vite-plugin-sveltekit-compile',
805
-
806
- /**
807
- * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
808
- * @see https://vitejs.dev/guide/api-plugin.html#config
809
- */
810
- config: {
811
- // avoids overwriting the base setting that's also set by Vitest
812
- order: 'pre',
813
- handler(config) {
814
- /** @type {UserConfig} */
815
- let new_config;
816
-
817
- if (is_build) {
818
- const app_immutable = `${kit.appDir}/immutable`;
819
-
820
- /** @type {Record<string, string>} */
821
- const server_input = {
822
- index: `${runtime_directory}/server/index.js`,
823
- internal: `<sveltekit:generated>/server.js`,
824
- env: '<sveltekit:generated>/env/config.js',
825
- ['remote-entry']: `${runtime_directory}/app/server/remote/index.js`
826
- };
827
-
828
- // add entry points for every endpoint...
829
- manifest_data.routes.forEach((route) => {
830
- if (route.endpoint) {
831
- const resolved = path.resolve(root, route.endpoint.file);
832
- const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
833
- const name = posixify(path.join('entries/endpoints', relative.replace(/\.js$/, '')));
834
- server_input[name] = resolved;
835
- }
836
- });
837
-
838
- // ...and every component used by pages...
839
- manifest_data.nodes.forEach((node) => {
840
- for (const file of [node.component, node.universal, node.server]) {
841
- if (file) {
842
- const resolved = path.resolve(root, file);
843
- const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
844
-
845
- const name = relative.startsWith('..')
846
- ? posixify(path.join('entries/fallbacks', path.basename(file)))
847
- : posixify(path.join('entries/pages', relative.replace(/\.js$/, '')));
848
- server_input[name] = resolved;
849
- }
850
- }
851
- });
852
-
853
- // ...and the params file
854
- if (manifest_data.params) {
855
- server_input['entries/params'] = path.resolve(root, manifest_data.params);
856
- }
857
-
858
- // ...and the hooks files
859
- if (manifest_data.hooks.server) {
860
- server_input['entries/hooks.server'] = path.resolve(root, manifest_data.hooks.server);
861
- }
862
- if (manifest_data.hooks.universal) {
863
- server_input['entries/hooks.universal'] = path.resolve(
864
- root,
865
- manifest_data.hooks.universal
866
- );
867
- }
868
-
869
- // ...and the server instrumentation file
870
- const server_instrumentation = resolve_entry(
871
- path.join(kit.files.src, 'instrumentation.server')
872
- );
873
- if (server_instrumentation) {
874
- if (kit.adapter && !kit.adapter.supports?.instrumentation?.()) {
875
- throw new Error(`${server_instrumentation} is unsupported in ${kit.adapter.name}.`);
876
- }
877
- server_input['instrumentation.server'] = server_instrumentation;
878
- }
879
-
880
- /** @type {Record<string, string>} */
881
- const client_input = {};
882
-
883
- if (svelte_config.output.bundleStrategy !== 'split') {
884
- client_input['bundle'] = `${runtime_directory}/client/bundle.js`;
885
- } else {
886
- client_input['entry/start'] = `${runtime_directory}/client/entry.js`;
887
- client_input['entry/payload'] = `${runtime_directory}/client/payload.js`;
888
- client_input['entry/app'] = `${out_dir}/generated/build/client-optimized/app.js`;
889
- manifest_data.nodes.forEach((node, i) => {
890
- if (node.component || node.universal) {
891
- client_input[`nodes/${i}`] =
892
- `${out_dir}/generated/build/client-optimized/nodes/${i}.js`;
893
- }
894
- });
895
- }
896
-
897
- const inline = svelte_config.output.bundleStrategy === 'inline';
898
-
899
- /** @type {string} */
900
- const base = (kit.paths.assets || kit.paths.base) + '/';
901
- const root_to_assets = app_immutable + '/assets/';
902
- const assets_to_root =
903
- app_immutable
904
- .split('/')
905
- .map(() => '..')
906
- .join('/') + '/../';
907
-
908
- const relative = kit.paths.relative !== false || !!kit.paths.assets;
909
-
910
- new_config = {
911
- appType: 'custom',
912
- // Affects how Vite loads JS assets on the client.
913
- // If the initial HTML we render uses an absolute path for assets,
914
- // the additional chunks Vite loads must also use an absolute path.
915
- // Otherwise, you end up with additional chunks being loaded relative
916
- // to the current chunk rather than the root.
917
- base: relative ? './' : base,
918
- build: {
919
- cssCodeSplit: !inline,
920
- cssMinify:
921
- initial_config.build?.minify == null ? true : !!initial_config.build.minify,
922
- manifest: true,
923
- rolldownOptions: {
924
- output: {
925
- name: `${global_name}.app`,
926
- assetFileNames: `${app_immutable}/assets/[name].[hash][extname]`,
927
- hoistTransitiveImports: false,
928
- sourcemapIgnoreList
929
- },
930
- preserveEntrySignatures: 'strict',
931
- onwarn(warning, handler) {
932
- if (
933
- warning.code === 'IMPORT_IS_UNDEFINED' &&
934
- warning.id === `${out_dir}/generated/build/client-optimized/app.js`
935
- ) {
936
- // ignore e.g. undefined `handleError` hook when
937
- // referencing `client_hooks.handleError`
938
- return;
939
- }
940
-
941
- handler(warning);
942
- },
943
- watch: {
944
- exclude: [
945
- // Ignore all siblings of config.outDir/generated
946
- `${out_dir}/generated/**`
947
- ]
948
- }
949
- },
950
- emptyOutDir: false,
951
- ssrEmitAssets: true
952
- },
953
- builder: {
954
- sharedConfigBuild: true,
955
- sharedPlugins: true
956
- },
957
- environments: {
958
- ssr: {
959
- build: {
960
- copyPublicDir: false,
961
- outDir: `${out}/server`,
962
- target: 'node22',
963
- rolldownOptions: {
964
- input: server_input,
965
- output: {
966
- entryFileNames: '[name].js',
967
- chunkFileNames: 'chunks/[name].js'
968
- }
969
- }
970
- },
971
- // these are stubs that will be replaced after the initial server build
972
- define: {
973
- __SVELTEKIT_HAS_SERVER_LOAD__: 'true',
974
- __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true',
975
- __SVELTEKIT_PAYLOAD__: '{}'
976
- }
977
- },
978
- client: {
979
- build: {
980
- outDir: `${out}/client`,
981
- rolldownOptions: {
982
- input: inline ? client_input['bundle'] : client_input,
983
- output: {
984
- format: inline ? 'iife' : 'esm',
985
- entryFileNames: `${app_immutable}/[name].[hash].js`,
986
- chunkFileNames: (/** @type {Rolldown.PreRenderedChunk} */ chunk_info) => {
987
- // The manifest data chunk gets a fixed (non-hashed) filename so
988
- // that importers' content hashes are stable regardless of the
989
- // manifest content — this breaks the content-hash feedback loop
990
- if (chunk_info.name === 'sveltekit-manifest') {
991
- return `${kit.appDir}/manifest.js`;
992
- }
993
- return `${app_immutable}/chunks/[hash].js`;
994
- },
995
- codeSplitting:
996
- svelte_config.output.bundleStrategy === 'split'
997
- ? {
998
- groups: [
999
- {
1000
- name: 'sveltekit-manifest',
1001
- test: '<sveltekit:generated>/app-manifest.js'
1002
- }
1003
- ]
1004
- }
1005
- : false
1006
- },
1007
- // This silences Rolldown warnings about not supporting `import.meta`
1008
- // for the `iife` output format. We don't care because it's
1009
- // only used in development and will be treeshaken away
1010
- transform: inline
1011
- ? {
1012
- define: {
1013
- 'import.meta': '{}'
1014
- }
1015
- }
1016
- : undefined
1017
- }
1018
- },
1019
- define: {
1020
- __SVELTEKIT_PAYLOAD__:
1021
- svelte_config.output.bundleStrategy !== 'split' ? kit_global : 'undefined'
1022
- }
1023
- }
1024
- },
1025
- experimental: {
1026
- // Allows us to use relative paths in as many places as we can
1027
- renderBuiltUrl(filename, { ssr, hostType }) {
1028
- if (hostType === 'js') {
1029
- // SSR builds should use an absolute path in JS modules to
1030
- // match the default Vite behaviour
1031
- if (ssr) return base + filename;
1032
-
1033
- // We could always use a relative asset base path here, but it's better for performance not to.
1034
- // E.g. Vite generates `new URL('/asset.png', import.meta).href` for a relative path vs just '/asset.png'.
1035
- // That's larger and takes longer to run and also causes an HTML diff between SSR and client
1036
- // causing us to do a more expensive hydration check.
1037
- return { relative };
1038
- }
1039
-
1040
- if (!relative) return;
1041
-
1042
- // ensure assets loaded by CSS files are loaded relative to the
1043
- // CSS file rather than the default of relative to the root
1044
-
1045
- // _app/immutable/assets files
1046
- if (filename.startsWith(root_to_assets)) {
1047
- return `./${filename.slice(root_to_assets.length)}`;
1048
- }
1049
-
1050
- // static dir files
1051
- return assets_to_root + filename;
1052
- }
1053
- },
1054
- publicDir: kit.files.assets
1055
- };
1056
- } else {
1057
- new_config = {
1058
- appType: 'custom',
1059
- // we avoid setting base to paths.assets in dev so that we get the
1060
- // trailing slash redirect to paths.base if it is set
1061
- base: kit.paths.base || '/',
1062
- build: {
1063
- rolldownOptions: {
1064
- // Vite dependency crawler needs an explicit JS entry point
1065
- // even though server otherwise works without it
1066
- input: `${runtime_directory}/client/entry.js`
1067
- }
1068
- },
1069
- publicDir: kit.files.assets
1070
- };
1071
- }
1072
-
1073
- warn_overridden_config(config, new_config);
1074
-
1075
- return new_config;
1076
- }
1077
593
  },
1078
594
 
1079
595
  /**
@@ -1084,9 +600,8 @@ function kit({ svelte_config }) {
1084
600
  return await dev(
1085
601
  vite,
1086
602
  server,
1087
- vite_config,
1088
603
  svelte_config,
1089
- () => remotes,
604
+ () => remote_metadata.remotes,
1090
605
  root,
1091
606
  (data) => {
1092
607
  manifest_data = data;
@@ -1099,615 +614,45 @@ function kit({ svelte_config }) {
1099
614
  * @see https://vitejs.dev/guide/api-plugin.html#configurepreviewserver
1100
615
  */
1101
616
  configurePreviewServer(server) {
1102
- return preview(server, vite_config, svelte_config);
1103
- },
1104
-
1105
- applyToEnvironment(environment) {
1106
- return environment.name !== 'serviceWorker';
1107
- },
1108
-
1109
- renderChunk: {
1110
- // composable filters are not accepted type-wise but still work during build
1111
- // see https://github.com/vitejs/rolldown-vite/issues/605
1112
- filter: /** @type {any} */ (
1113
- /** @satisfies {TopLevelFilterExpression[]} */ ([include(code('__SVELTEKIT_TRACK__'))])
1114
- ),
1115
- handler(code, chunk) {
1116
- // composable filters only work during build so we still need this guard for dev
1117
- // see https://github.com/vitejs/rolldown-vite/issues/605
1118
- if (code.includes('__SVELTEKIT_TRACK__')) {
1119
- return {
1120
- // Rolldown changes our single quotes to double quotes so we need it in the regex too
1121
- code: code.replace(/__SVELTEKIT_TRACK__\(['"](.+?)['"]\)/g, (_, label) => {
1122
- (tracked_features[chunk.name + '.js'] ??= []).push(label);
1123
- // put extra whitespace at the end of the comment to preserve the source size and avoid interfering with source maps
1124
- return `/* track ${label} */`;
1125
- }),
1126
- map: null // TODO we may need to generate a sourcemap in future
1127
- };
1128
- }
1129
- }
1130
- },
1131
-
1132
- generateBundle(_options, bundle) {
1133
- // a watched build returns a watcher rather than the build output from
1134
- // `builder.build` so we need to retrieve it from the generateBundle hook
1135
- if (this.meta.watchMode) {
1136
- watch_build_output.set(
1137
- this.environment.name,
1138
- /** @type {Rolldown.RolldownOutput['output']} */ (Object.values(bundle))
1139
- );
1140
- }
1141
-
1142
- if (this.environment.config.consumer !== 'client') return;
1143
-
1144
- this.emitFile({
1145
- type: 'asset',
1146
- fileName: `${kit.appDir}/version.json`,
1147
- source: s({ version: kit.version.name })
1148
- });
1149
- },
1150
-
1151
- async buildApp(builder) {
1152
- fs.rmSync(out, { force: true, recursive: true });
1153
- fs.mkdirSync(out, { recursive: true });
1154
-
1155
- const verbose = builder.config.logLevel === 'info' || builder.config.logLevel === undefined;
1156
- const log = logger({ verbose });
1157
-
1158
- let ssr_build = await builder.build(builder.environments.ssr);
1159
-
1160
- /** @param {Rolldown.RolldownOutput['output']} server_chunks */
1161
- const process_ssr_build = async (server_chunks) => {
1162
- // Replace manifest placeholders in SSR output. `assets` and `routes`
1163
- // are known from `manifest_data`. `immutable` and `prerendered` are not
1164
- // known yet — they get sentinel strings that are replaced after
1165
- // the client build and after prerendering respectively.
1166
- replace_manifest_placeholder_variables(server_chunks, `${out}/server`, {
1167
- assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1168
- routes: get_manifest_routes(manifest_data.routes)
1169
- });
1170
-
1171
- vite_server_manifest = /** @type {Manifest} */ (
1172
- JSON.parse(read(`${out}/server/.vite/manifest.json`))
1173
- );
1174
-
1175
- const manifest_path = `${out}/server/manifest-full.js`;
1176
- const assets_path = `${kit.appDir}/immutable/assets`;
1177
-
1178
- /** @type {BuildData} */
1179
- const build_data = {
1180
- app_dir: kit.appDir,
1181
- app_path: `${kit.paths.base.slice(1)}${kit.paths.base ? '/' : ''}${kit.appDir}`,
1182
- manifest_data,
1183
- out_dir: out,
1184
- service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
1185
- client: null,
1186
- server_manifest: vite_server_manifest
1187
- };
1188
-
1189
- fs.writeFileSync(
1190
- manifest_path,
1191
- `export const manifest = ${generate_manifest({
1192
- build_data,
1193
- prerendered: [],
1194
- relative_path: '.',
1195
- routes: manifest_data.routes,
1196
- remotes,
1197
- root
1198
- })};\n`
1199
- );
1200
-
1201
- // first, build server nodes without the client manifest so we can analyse it
1202
- build_server_nodes(
1203
- out,
1204
- kit,
1205
- manifest_data,
1206
- vite_server_manifest,
1207
- null,
1208
- assets_path,
1209
- server_chunks,
1210
- root
1211
- );
1212
-
1213
- log.info('Analysing routes');
1214
-
1215
- const { metadata } = await analyse({
1216
- hash: kit.router.type === 'hash',
1217
- manifest_path,
1218
- manifest_data,
1219
- server_manifest: vite_server_manifest,
1220
- tracked_features,
1221
- env,
1222
- remotes,
1223
- vite_config_file: vite_config.configFile
1224
- });
1225
- build_metadata = metadata;
1226
-
1227
- log.info('Building app');
1228
-
1229
- const server_assets = `${out}/server/${assets_path}`;
1230
- const client_assets = `${out}/client/${assets_path}`;
1231
-
1232
- const skip_client_build = manifest_data.nodes.every(
1233
- (node) => node.page_options?.csr === false
1234
- );
1235
-
1236
- if (skip_client_build) {
1237
- copy(server_assets, client_assets);
1238
- copy(kit.files.assets, `${out}/client`);
1239
- } else {
1240
- // ...and build the client
1241
- write_client_manifest(
1242
- kit,
1243
- manifest_data,
1244
- `${out_dir}/generated/build/client-optimized`,
1245
- root,
1246
- build_metadata.nodes
1247
- );
1248
-
1249
- // Through the finished analysis we can now check if any node has server or universal load functions
1250
- const nodes = Object.values(build_metadata.nodes);
1251
- const has_server_load = nodes.some((node) => node.has_server_load);
1252
- const has_universal_load = nodes.some((node) => node.has_universal_load);
1253
-
1254
- if (builder.environments.client.config.define) {
1255
- builder.environments.client.config.define.__SVELTEKIT_HAS_SERVER_LOAD__ =
1256
- s(has_server_load);
1257
- builder.environments.client.config.define.__SVELTEKIT_HAS_UNIVERSAL_LOAD__ =
1258
- s(has_universal_load);
1259
- }
1260
-
1261
- const client_build = await builder.build(builder.environments.client);
1262
- const client_chunks = await normalise_build(
1263
- builder.environments.client.name,
1264
- client_build,
1265
- watch_build_output
1266
- );
1267
-
1268
- // We use `build.ssrEmitAssets` so that asset URLs created from
1269
- // imports in server-only modules correspond to files in the build,
1270
- // but we don't want to copy over CSS imports as these are already
1271
- // accounted for in the client bundle. In most cases it would be
1272
- // a no-op, but for SSR builds `url(...)` paths are handled
1273
- // differently (relative for client, absolute for server)
1274
- // resulting in different hashes, and thus duplication
1275
- const ssr_stylesheets = new Set(
1276
- Object.values(vite_server_manifest)
1277
- .map((chunk) => chunk.css ?? [])
1278
- .flat()
1279
- );
1280
-
1281
- if (fs.existsSync(server_assets)) {
1282
- for (const file of fs.readdirSync(server_assets)) {
1283
- const src = `${server_assets}/${file}`;
1284
- const dest = `${client_assets}/${file}`;
1285
-
1286
- if (fs.existsSync(dest) || ssr_stylesheets.has(`${assets_path}/${file}`)) {
1287
- continue;
1288
- }
1289
-
1290
- copy(src, dest);
1291
- }
1292
- }
1293
-
1294
- vite_client_manifest = /** @type {Manifest} */ (
1295
- JSON.parse(read(`${out}/client/.vite/manifest.json`))
1296
- );
1297
-
1298
- /**
1299
- * @param {string} entry
1300
- * @param {boolean} [add_dynamic_css]
1301
- */
1302
- const deps_of = (entry, add_dynamic_css = false) =>
1303
- find_deps(
1304
- /** @type {Manifest} */ (vite_client_manifest),
1305
- posixify(path.relative(root, entry)),
1306
- add_dynamic_css,
1307
- root
1308
- );
1309
-
1310
- // the inline bundle and stylesheet are deleted further down, after
1311
- // being inlined into the page, so they must not appear in `immutable`
1312
- /** @type {Set<string>} */
1313
- const inlined = new Set();
1314
- /** @type {Rolldown.OutputAsset | undefined} */
1315
- let inline_style;
1316
-
1317
- if (kit.output.bundleStrategy === 'inline') {
1318
- inline_style = /** @type {Rolldown.OutputAsset | undefined} */ (
1319
- client_chunks.find(
1320
- (chunk) =>
1321
- chunk.type === 'asset' &&
1322
- chunk.names.length === 1 &&
1323
- chunk.names[0] === 'style.css'
1324
- )
1325
- );
1326
-
1327
- inlined.add(deps_of(`${runtime_directory}/client/bundle.js`).file);
1328
- if (inline_style) inlined.add(inline_style.fileName);
1329
- }
1330
-
1331
- // Replace manifest placeholders in client output. `immutable` is
1332
- // computed from the Vite client manifest, `assets` and `routes`
1333
- // from `manifest_data`. `prerendered` is left as a placeholder
1334
- // for now — it's replaced after prerendering completes.
1335
- immutable = collect_immutable(vite_client_manifest, kit.appDir, inlined);
1336
-
1337
- replace_manifest_placeholder_variables(client_chunks, `${out}/client`, {
1338
- immutable,
1339
- assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1340
- routes: get_manifest_routes(manifest_data.routes)
1341
- });
1342
-
1343
- // Now that the client build is done, replace the `build` sentinel
1344
- // in the SSR output with the real build files
1345
- replace_manifest_placeholder_strings(`${out}/server`, { immutable });
1346
-
1347
- const has_explicit_dynamic_public_env = Object.values(explicit_env_config ?? {}).some(
1348
- (variable) => variable.public && !variable.static
1349
- );
1350
-
1351
- // the app only depends on runtime public env if it imports `$app/env/public`
1352
- // *and* at least one public env var is actually dynamic (non-static)
1353
- const uses_env_dynamic_public =
1354
- has_explicit_dynamic_public_env &&
1355
- client_chunks.some(
1356
- (chunk) =>
1357
- chunk.type === 'chunk' &&
1358
- chunk.modules[
1359
- posixify(fs.realpathSync(`${out_dir}/generated/build/env/public/client.js`))
1360
- ]
1361
- );
1362
-
1363
- if (kit.output.bundleStrategy === 'split') {
1364
- const start_entry = posixify(
1365
- path.relative(root, `${runtime_directory}/client/entry.js`)
1366
- );
1367
- const start = find_deps(vite_client_manifest, start_entry, false, root);
1368
- const runtime_entry = resolve_symlinks(vite_client_manifest, start_entry, root).chunk
1369
- .dynamicImports?.[0]; // client/entry.js dynamically imports client/client-entry.js
1370
- if (!runtime_entry) throw new Error('Could not find the client runtime chunk');
1371
- const runtime = find_deps(vite_client_manifest, runtime_entry, false, root);
1372
- const app = deps_of(`${out_dir}/generated/build/client-optimized/app.js`);
1373
-
1374
- build_data.client = {
1375
- start: start.file,
1376
- app: app.file,
1377
- imports: Array.from(
1378
- new Set([
1379
- ...start.imports,
1380
- runtime.file,
1381
- ...runtime.imports,
1382
- app.file,
1383
- ...app.imports
1384
- ])
1385
- ),
1386
- stylesheets: [...start.stylesheets, ...runtime.stylesheets, ...app.stylesheets],
1387
- fonts: [...start.fonts, ...runtime.fonts, ...app.fonts],
1388
- uses_env_dynamic_public
1389
- };
1390
-
1391
- // In case of server-side route resolution, we create a purpose-built route manifest that is
1392
- // similar to that on the client, with as much information computed upfront so that we
1393
- // don't need to include any code of the actual routes in the server bundle.
1394
- if (kit.router.resolution === 'server') {
1395
- const nodes = manifest_data.nodes.map((node, i) => {
1396
- if (node.component || node.universal) {
1397
- const entry = `${out_dir}/generated/build/client-optimized/nodes/${i}.js`;
1398
- const deps = deps_of(entry, true);
1399
- const file = resolve_symlinks(
1400
- /** @type {Manifest} */ (vite_client_manifest),
1401
- `${out_dir}/generated/build/client-optimized/nodes/${i}.js`,
1402
- root
1403
- ).chunk.file;
1404
-
1405
- return { file, css: deps.stylesheets };
1406
- }
1407
- });
1408
- build_data.client.nodes = nodes.map((node) => node?.file);
1409
- build_data.client.css = nodes.map((node) => node?.css);
1410
-
1411
- build_data.client.routes = compact(
1412
- manifest_data.routes.map((route) => {
1413
- if (!route.page) return;
1414
-
1415
- return {
1416
- id: route.id,
1417
- pattern: route.pattern,
1418
- params: route.params,
1419
- layouts: route.page.layouts.map((l) =>
1420
- l !== undefined ? [metadata.nodes[l].has_server_load, l] : undefined
1421
- ),
1422
- errors: route.page.errors,
1423
- leaf: [metadata.nodes[route.page.leaf].has_server_load, route.page.leaf]
1424
- };
1425
- })
1426
- );
1427
- }
1428
- } else {
1429
- const start = deps_of(`${runtime_directory}/client/bundle.js`);
1430
-
1431
- build_data.client = {
1432
- start: start.file,
1433
- imports: start.imports,
1434
- stylesheets: start.stylesheets,
1435
- fonts: start.fonts,
1436
- uses_env_dynamic_public
1437
- };
1438
-
1439
- if (kit.output.bundleStrategy === 'inline') {
1440
- build_data.client.inline = {
1441
- script: read(`${out}/client/${start.file}`),
1442
- style: /** @type {string | undefined} */ (inline_style?.source)
1443
- };
1444
-
1445
- // the bundle and stylesheet are inlined into the page, so the
1446
- // emitted files are never loaded
1447
- fs.unlinkSync(`${out}/client/${start.file}`);
1448
- fs.rmSync(`${out}/client/${start.file}.map`, { force: true });
1449
- if (inline_style) fs.unlinkSync(`${out}/client/${inline_style.fileName}`);
1450
- }
1451
- }
1452
-
1453
- // regenerate manifest now that we have client entry...
1454
- fs.writeFileSync(
1455
- manifest_path,
1456
- `export const manifest = ${generate_manifest({
1457
- build_data,
1458
- prerendered: [],
1459
- relative_path: '.',
1460
- routes: manifest_data.routes,
1461
- remotes,
1462
- root
1463
- })};\n`
1464
- );
1465
-
1466
- // regenerate nodes with the client manifest...
1467
- build_server_nodes(
1468
- out,
1469
- kit,
1470
- manifest_data,
1471
- vite_server_manifest,
1472
- vite_client_manifest,
1473
- assets_path,
1474
- client_chunks,
1475
- root
1476
- );
1477
- }
1478
-
1479
- // ...and prerender
1480
- let prerender_results;
1481
- try {
1482
- prerender_results = await prerender({
1483
- hash: kit.router.type === 'hash',
1484
- out,
1485
- manifest_path,
1486
- metadata,
1487
- verbose,
1488
- env,
1489
- vite_config_file: vite_config.configFile,
1490
- is_tty: process.stdout.isTTY
1491
- });
1492
-
1493
- // this silly hack is necessary to ensure that stderr from prerender is flushed before we continue
1494
- await new Promise((f) => setTimeout(f, 0));
1495
- } catch (e) {
1496
- if (e instanceof Error && e.message === '__handled__') {
1497
- // error details are already logged inside `prerender`, don't duplicate them
1498
- throw stackless('Prerendering failed');
1499
- } else {
1500
- // Unforeseen error, rethrow as-is
1501
- throw e;
1502
- }
1503
- }
1504
-
1505
- prerendered = prerender_results.prerendered;
1506
-
1507
- // Replace the `prerendered` sentinel in both SSR and client output
1508
- // with the real prerendered paths. The other sentinels (`build`)
1509
- // were already replaced after the client build.
1510
- const prerendered_paths = prerendered.paths.map((p) => {
1511
- return { path: p.replace(kit.paths.base, '').slice(1) };
1512
- });
1513
-
1514
- replace_manifest_placeholder_strings(`${out}/server`, { prerendered: prerendered_paths });
1515
- replace_manifest_placeholder_strings(`${out}/client`, { prerendered: prerendered_paths });
1516
-
1517
- // For `inline` strategy, the entry file was deleted and read into
1518
- // `build_data.client.inline.script` — replace the sentinel there too
1519
- if (build_data.client?.inline?.script) {
1520
- build_data.client.inline.script = build_data.client.inline.script.replaceAll(
1521
- '"__sveltekit_manifest_prerendered__"',
1522
- JSON.stringify(prerendered_paths)
1523
- );
1524
- }
1525
-
1526
- // generate a new manifest that doesn't include prerendered pages
1527
- fs.writeFileSync(
1528
- `${out}/server/manifest.js`,
1529
- `export const manifest = ${generate_manifest({
1530
- build_data,
1531
- prerendered: prerendered.paths,
1532
- relative_path: '.',
1533
- routes: manifest_data.routes.filter(
1534
- (route) => prerender_results.prerender_map.get(route.id) !== true
1535
- ),
1536
- remotes,
1537
- root
1538
- })};\n`
1539
- );
1540
-
1541
- await treeshake_prerendered_remotes(
1542
- vite,
1543
- out,
1544
- remotes,
1545
- remote_original_by_hash,
1546
- metadata,
1547
- process.cwd(),
1548
- server_chunks,
1549
- vite_config.build.sourcemap
1550
- );
1551
-
1552
- // defer until after other buildApp hooks have run
1553
- finalise = async () => {
1554
- // defer creating the service worker to avoid other plugins from
1555
- // overwriting it if they run a client environment build
1556
- if (service_worker_entry_file) {
1557
- log.info('Building service worker');
1558
-
1559
- // Add defines for `$app/manifest`
1560
- builder.environments.serviceWorker.config.define = {
1561
- ...builder.environments.serviceWorker.config.define,
1562
-
1563
- __SVELTEKIT_MANIFEST_ASSETS__: s(
1564
- manifest_data.assets.map((asset) => ({ path: asset.file }))
1565
- ),
1566
- __SVELTEKIT_MANIFEST_IMMUTABLE__: s(immutable),
1567
- __SVELTEKIT_MANIFEST_PRERENDERED__: s(prerendered_paths),
1568
- __SVELTEKIT_MANIFEST_ROUTES__: s(get_manifest_routes(manifest_data.routes))
1569
- };
1570
-
1571
- // we have to overwrite this because it can't be configured per environment in the config hook
1572
- builder.environments.serviceWorker.config.experimental.renderBuiltUrl = (filename) => {
1573
- return {
1574
- runtime: `new URL(${JSON.stringify(filename)}, location.href).pathname`
1575
- };
1576
- };
1577
-
1578
- const service_worker_build = await builder.build(builder.environments.serviceWorker);
1579
- await normalise_build(
1580
- builder.environments.serviceWorker.name,
1581
- service_worker_build,
1582
- watch_build_output
1583
- );
1584
- }
1585
-
1586
- console.log(
1587
- `\nRun ${styleText(['bold', 'cyan'], 'npm run preview')} to preview your production build locally.`
1588
- );
1589
-
1590
- if (kit.adapter) {
1591
- await adapt(
1592
- svelte_config,
1593
- build_data,
1594
- metadata,
1595
- prerendered,
1596
- prerender_results.prerender_map,
1597
- log,
1598
- remotes,
1599
- vite_config,
1600
- explicit_env_config
1601
- );
1602
- } else {
1603
- log.warn('\nNo adapter specified');
1604
-
1605
- const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
1606
- console.log(
1607
- `See ${link} to learn how to configure your app to run on the platform of your choosing`
1608
- );
1609
- }
1610
- };
1611
- };
1612
-
1613
- // `vite build`
1614
- ssr_build = Array.isArray(ssr_build) ? ssr_build[0] : ssr_build;
1615
- if ('output' in ssr_build) {
1616
- await load_and_validate_params({
1617
- routes: manifest_data.routes,
1618
- params_path: manifest_data.params,
1619
- root
1620
- });
1621
-
1622
- return await process_ssr_build(ssr_build.output);
1623
- }
1624
-
1625
- // `vite build --watch`
1626
- let rebuild = false;
1627
-
1628
- const before_ssr_build_rerun = async () => {
1629
- rebuild = true;
1630
-
1631
- // these are set once per plugin initialisation or when the config hook
1632
- // runs. However, those don't re-run during watch mode. So, we need to
1633
- // re-initialise them manually here
1634
- manifest_data = create_manifest_data(svelte_config, root);
1635
- sync.all(svelte_config, root, manifest_data);
1636
-
1637
- tracked_features = {};
1638
-
1639
- remotes = [];
1640
- remote_original_by_hash.clear();
1641
- emitted_remote_hashes.clear();
1642
-
1643
- immutable = null;
1644
-
1645
- finalise = null;
1646
-
1647
- fs.mkdirSync(out, { recursive: true });
1648
-
1649
- await load_and_validate_params({
1650
- routes: manifest_data.routes,
1651
- params_path: manifest_data.params,
1652
- root
1653
- });
1654
- };
1655
-
1656
- ssr_build.on('change', before_ssr_build_rerun);
1657
- ssr_build.on('restart', before_ssr_build_rerun);
1658
-
1659
- /** @type {PromiseWithResolvers<void>} */
1660
- const task = Promise.withResolvers();
617
+ return preview(server, svelte_config);
618
+ }
619
+ };
1661
620
 
1662
- ssr_build.on('event', async (event) => {
1663
- if (event.code === 'ERROR') {
1664
- return task.reject();
1665
- }
621
+ /** @type {ServerMetadata | null} build analysis results */
622
+ let build_metadata = null;
1666
623
 
1667
- if (event.code === 'BUNDLE_END') {
1668
- try {
1669
- await process_ssr_build(
1670
- /** @type {Rolldown.RolldownOutput['output']} */ (
1671
- watch_build_output.get(builder.environments.ssr.name)
1672
- )
1673
- );
1674
- // buildApp hooks don't rerun in watch mode so we need to run
1675
- // the deferred steps here on subsequent builds
1676
- if (rebuild) await finalise?.();
1677
- } catch (e) {
1678
- return task.reject(e);
1679
- } finally {
1680
- await event.result.close();
1681
- }
1682
- return task.resolve();
1683
- }
1684
- });
624
+ /** @type {Record<string, EnvVarConfig<any>> | null} */
625
+ let explicit_env_config = null;
1685
626
 
1686
- await task.promise;
1687
- }
627
+ /** @type {{ remotes: RemoteChunk[]; remote_original_by_hash: Map<string, string>}} */
628
+ let remote_metadata = {
629
+ remotes: [],
630
+ remote_original_by_hash: new Map()
1688
631
  };
1689
632
 
1690
- /** @type {Plugin} */
1691
- const plugin_adapter = {
1692
- name: 'vite-plugin-sveltekit-adapter',
1693
- apply: 'build',
1694
- buildApp: {
1695
- // this will run after any buildApp hooks provided by other Vite plugins
1696
- // see https://vite.dev/guide/api-environment-frameworks#environments-during-build
1697
- order: 'post',
1698
- async handler() {
1699
- await finalise?.();
1700
- }
1701
- }
1702
- };
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
+ }
1703
641
 
1704
642
  return /** @type {Plugin[]} */ (
1705
643
  [
1706
644
  svelte_config.adapter?.vite?.plugins?.pre,
1707
645
  plugin_resolve_root,
1708
646
  plugin_setup,
1709
- plugin_remote_guard,
1710
- 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
+ ),
1711
656
  plugin_env_vars(svelte_config, (vars) => {
1712
657
  explicit_env_config = vars;
1713
658
  }),
@@ -1731,167 +676,31 @@ function kit({ svelte_config }) {
1731
676
  out
1732
677
  })),
1733
678
  plugin_service_worker_env_vars(() => service_worker_entry_file),
1734
- plugin_compile,
1735
- plugin_adapter,
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?.()),
1736
703
  svelte_config.adapter?.vite?.plugins?.post
1737
704
  ].filter(Boolean)
1738
705
  );
1739
706
  }
1740
-
1741
- /**
1742
- * Collects the content-hashed files of a Vite manifest, in the shape of
1743
- * `$app/manifest`'s `immutable` export.
1744
- *
1745
- * @param {Manifest} manifest
1746
- * @param {string} app_dir
1747
- * @param {Set<string>} inlined files deleted from the output after being inlined into the page
1748
- * @returns {Array<{ path: string }>}
1749
- */
1750
- const collect_immutable = (manifest, app_dir, inlined) => {
1751
- const prefix = `${app_dir}/immutable`;
1752
-
1753
- /** @type {Set<string>} */
1754
- const files = new Set();
1755
-
1756
- /** @param {string} file */
1757
- const add = (file) => {
1758
- if (file.startsWith(prefix) && !inlined.has(file)) files.add(file);
1759
- };
1760
-
1761
- for (const key in manifest) {
1762
- const { file, css, assets } = manifest[key];
1763
- add(file);
1764
- if (css) for (let i = 0; i < css.length; i++) add(css[i]);
1765
- if (assets) for (let i = 0; i < assets.length; i++) add(assets[i]);
1766
- }
1767
-
1768
- return Array.from(files, (path) => ({ path }));
1769
- };
1770
-
1771
- /**
1772
- * Replaces manifest data placeholder identifiers in output chunks with real
1773
- * values, or strings that are valid JS (so thecode doesn't crash during prerendering)
1774
- * but findable on disk for later replacement by `replace_manifest_placeholder_strings`.
1775
- *
1776
- * @param {Rolldown.RolldownOutput['output']} chunks
1777
- * @param {string} output_dir
1778
- * @param {{
1779
- * immutable?: Array<{ path: string }>;
1780
- * assets?: Array<{ path: string }>;
1781
- * prerendered?: Array<{ path: string }>;
1782
- * routes?: Array<{ id: string; page: boolean; endpoint: boolean }>;
1783
- * }} values
1784
- */
1785
- const replace_manifest_placeholder_variables = (chunks, output_dir, values) => {
1786
- /** @type {Record<string, string>} */
1787
- const replacements = {
1788
- __SVELTEKIT_MANIFEST_IMMUTABLE__: JSON.stringify(
1789
- values.immutable ?? '__sveltekit_manifest_build__'
1790
- ),
1791
- __SVELTEKIT_MANIFEST_ASSETS__: JSON.stringify(values.assets ?? '__sveltekit_manifest_files__'),
1792
- __SVELTEKIT_MANIFEST_PRERENDERED__: JSON.stringify(
1793
- values.prerendered ?? '__sveltekit_manifest_prerendered__'
1794
- ),
1795
- __SVELTEKIT_MANIFEST_ROUTES__: JSON.stringify(values.routes ?? '__sveltekit_manifest_routes__')
1796
- };
1797
-
1798
- for (const chunk of chunks) {
1799
- if (chunk.type !== 'chunk') continue;
1800
- if (!chunk.code.includes('__SVELTEKIT_MANIFEST_')) continue;
1801
-
1802
- let code = chunk.code;
1803
-
1804
- for (const [identifier, replacement] of Object.entries(replacements)) {
1805
- code = code.replaceAll(identifier, replacement);
1806
- }
1807
-
1808
- const file_path = `${output_dir}/${chunk.fileName}`;
1809
- fs.writeFileSync(file_path, code);
1810
- }
1811
- };
1812
-
1813
- /**
1814
- * Replaces manifest sentinel strings in files on disk with real values.
1815
- * This is used for values that weren't known when the first replacement
1816
- * pass ran (e.g. `build` wasn't known until after the client build,
1817
- * `prerendered` wasn't known until after prerendering).
1818
- *
1819
- * @param {string} dir Directory to scan for .js files
1820
- * @param {{
1821
- * immutable?: Array<{ path: string }>;
1822
- * prerendered?: Array<{ path: string }>;
1823
- * }} values
1824
- */
1825
- const replace_manifest_placeholder_strings = (dir, values) => {
1826
- /** @type {Record<string, string>} */
1827
- const replacements = {};
1828
-
1829
- if (values.immutable !== undefined) {
1830
- replacements['__sveltekit_manifest_build__'] = JSON.stringify(values.immutable);
1831
- }
1832
- if (values.prerendered !== undefined) {
1833
- replacements['__sveltekit_manifest_prerendered__'] = JSON.stringify(values.prerendered);
1834
- }
1835
-
1836
- for (const file of fs.readdirSync(dir)) {
1837
- const file_path = `${dir}/${file}`;
1838
- const stat = fs.statSync(file_path);
1839
-
1840
- if (stat.isDirectory()) {
1841
- replace_manifest_placeholder_strings(file_path, values);
1842
- continue;
1843
- }
1844
-
1845
- if (!file.endsWith('.js')) continue;
1846
-
1847
- let code = read(file_path);
1848
- let changed = false;
1849
-
1850
- for (const [sentinel, replacement] of Object.entries(replacements)) {
1851
- if (code.includes(sentinel)) {
1852
- code = code.replaceAll(`"${sentinel}"`, replacement);
1853
- changed = true;
1854
- }
1855
- }
1856
-
1857
- if (changed) {
1858
- fs.writeFileSync(file_path, code);
1859
- }
1860
- }
1861
- };
1862
-
1863
- /**
1864
- * Normalises the build output to a consistent format, handling watch mode and multiple environments
1865
- * @param {string} name The name of the environment
1866
- * @param {Rolldown.RolldownOutput | Rolldown.RolldownOutput[] | Rolldown.RolldownWatcher} build The return value of builder.build
1867
- * @param {Map<string, Rolldown.RolldownOutput['output']>} build_output_map
1868
- * @returns {Promise<Rolldown.RolldownOutput['output']>}
1869
- */
1870
- async function normalise_build(name, build, build_output_map) {
1871
- if ('output' in build) {
1872
- return build.output;
1873
- }
1874
-
1875
- if (Array.isArray(build)) {
1876
- return build[0].output;
1877
- }
1878
-
1879
- /** @type {PromiseWithResolvers<void>} */
1880
- const bundling = Promise.withResolvers();
1881
-
1882
- build.on('event', async (event) => {
1883
- if (event.code === 'ERROR') {
1884
- await build.close();
1885
- return bundling.reject(event.error);
1886
- }
1887
-
1888
- if (event.code === 'BUNDLE_END') {
1889
- await build.close();
1890
- return bundling.resolve();
1891
- }
1892
- });
1893
-
1894
- await bundling.promise;
1895
-
1896
- return /** @type {Rolldown.RolldownOutput['output']} */ (build_output_map.get(name));
1897
- }