@sveltejs/kit 3.0.0-next.11 → 3.0.0-next.13

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 (84) hide show
  1. package/package.json +10 -10
  2. package/src/cli.js +8 -8
  3. package/src/constants.js +1 -1
  4. package/src/core/config/options.js +43 -35
  5. package/src/core/env.js +25 -11
  6. package/src/core/postbuild/prerender.js +3 -3
  7. package/src/core/sync/sync.js +11 -14
  8. package/src/core/sync/utils.js +21 -1
  9. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +24 -23
  10. package/src/core/sync/write_client_manifest.js +4 -12
  11. package/src/core/sync/write_env.js +6 -4
  12. package/src/core/sync/write_server.js +10 -15
  13. package/src/core/sync/write_tsconfig/index.js +257 -0
  14. package/src/core/sync/write_tsconfig/utils.js +162 -0
  15. package/src/core/sync/write_types/index.js +80 -88
  16. package/src/exports/index.js +23 -12
  17. package/src/exports/internal/server/index.js +3 -1
  18. package/src/exports/internal/shared.js +4 -12
  19. package/src/exports/public.d.ts +87 -62
  20. package/src/exports/vite/build/remote.js +10 -12
  21. package/src/exports/vite/dev/index.js +113 -88
  22. package/src/exports/vite/index.js +452 -237
  23. package/src/exports/vite/module_ids.js +3 -1
  24. package/src/exports/vite/utils.js +1 -10
  25. package/src/runner.js +13 -0
  26. package/src/runtime/app/forms.js +4 -0
  27. package/src/runtime/app/manifest/index.js +1 -0
  28. package/src/runtime/app/paths/client.js +30 -30
  29. package/src/runtime/app/paths/internal/client.js +26 -0
  30. package/src/runtime/app/paths/server.js +22 -9
  31. package/src/runtime/app/paths/types.d.ts +11 -19
  32. package/src/runtime/app/server/remote/command.js +12 -7
  33. package/src/runtime/app/server/remote/form.js +37 -26
  34. package/src/runtime/app/server/remote/prerender.js +22 -11
  35. package/src/runtime/app/server/remote/query.js +5 -4
  36. package/src/runtime/app/server/remote/requested.js +4 -4
  37. package/src/runtime/app/server/remote/shared.js +48 -30
  38. package/src/runtime/app/service-worker/index.js +24 -0
  39. package/src/runtime/app/state/client.js +3 -0
  40. package/src/runtime/app/state/index.js +3 -3
  41. package/src/runtime/app/state/server.js +3 -0
  42. package/src/runtime/client/client.js +627 -311
  43. package/src/runtime/client/constants.js +2 -6
  44. package/src/runtime/client/fetcher.js +27 -17
  45. package/src/runtime/client/remote-functions/form.svelte.js +82 -58
  46. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  47. package/src/runtime/client/remote-functions/query/index.js +2 -2
  48. package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
  49. package/src/runtime/client/remote-functions/query-batch.svelte.js +3 -4
  50. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
  51. package/src/runtime/client/remote-functions/query-live/iterator.js +15 -10
  52. package/src/runtime/client/remote-functions/shared.svelte.js +15 -11
  53. package/src/runtime/client/state.svelte.js +54 -33
  54. package/src/runtime/client/types.d.ts +1 -2
  55. package/src/runtime/client/utils.js +21 -14
  56. package/src/runtime/components/root.svelte +4 -14
  57. package/src/runtime/form-utils.js +89 -54
  58. package/src/runtime/props.svelte.js +71 -0
  59. package/src/runtime/server/cookie.js +1 -1
  60. package/src/runtime/server/data/index.js +31 -28
  61. package/src/runtime/server/errors.js +1 -1
  62. package/src/runtime/server/fetch.js +4 -6
  63. package/src/runtime/server/page/actions.js +3 -3
  64. package/src/runtime/server/page/csp.js +86 -103
  65. package/src/runtime/server/page/index.js +1 -2
  66. package/src/runtime/server/page/load_data.js +15 -5
  67. package/src/runtime/server/page/render.js +46 -52
  68. package/src/runtime/server/page/respond_with_error.js +1 -3
  69. package/src/runtime/server/remote-functions.js +66 -35
  70. package/src/runtime/server/respond.js +56 -16
  71. package/src/runtime/server/utils.js +10 -0
  72. package/src/types/ambient-private.d.ts +9 -1
  73. package/src/types/ambient.d.ts +23 -28
  74. package/src/types/global-private.d.ts +12 -0
  75. package/src/types/internal.d.ts +15 -10
  76. package/src/types/private.d.ts +3 -12
  77. package/src/utils/url.js +12 -0
  78. package/src/version.js +1 -1
  79. package/types/index.d.ts +172 -155
  80. package/types/index.d.ts.map +5 -1
  81. package/src/core/sync/write_ambient.js +0 -18
  82. package/src/core/sync/write_tsconfig.js +0 -258
  83. package/src/runtime/types.d.ts +0 -8
  84. /package/src/core/sync/{write_tsconfig_test → write_tsconfig/test-app}/package.json +0 -0
@@ -1,17 +1,19 @@
1
1
  /** @import { EnvVarConfig, KitConfig } from '@sveltejs/kit' */
2
2
  /** @import { Options, SvelteConfig } from '@sveltejs/vite-plugin-svelte' */
3
3
  /** @import { PreprocessorGroup } from 'svelte/compiler' */
4
- /** @import { BuildData, ManifestData, Prerendered, ServerMetadata, RemoteInternals, ValidatedConfig, ValidatedKitConfig } from 'types' */
4
+ /** @import { BuildData, ManifestData, Prerendered, RemoteChunk, RemoteInternals, ServerMetadata, ValidatedConfig, ValidatedKitConfig } from 'types' */
5
5
  /** @import { Manifest, Plugin, ResolvedConfig, Rolldown, UserConfig, ViteDevServer } from 'vite' */
6
6
  import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import process from 'node:process';
9
9
  import { styleText } from 'node:util';
10
+ import MagicString from 'magic-string';
10
11
  import { loadEnv } from 'vite';
11
12
  import { exactRegex, prefixRegex } from 'rolldown/filter';
12
13
 
13
14
  import { copy, mkdirp, read, resolve_entry, rimraf } from '../../utils/filesystem.js';
14
15
  import { posixify } from '../../utils/os.js';
16
+ import { to_fs } from '../../utils/vite.js';
15
17
  import {
16
18
  create_sveltekit_env,
17
19
  create_sveltekit_env_public,
@@ -22,13 +24,12 @@ import {
22
24
  create_exported_declarations
23
25
  } from '../../core/env.js';
24
26
  import * as sync from '../../core/sync/sync.js';
25
- import { create_assets } from '../../core/sync/create_manifest_data/index.js';
26
27
  import { load_and_validate_params } from '../../utils/params.js';
27
28
  import { runtime_directory, logger } from '../../core/utils.js';
28
29
  import { generate_manifest } from '../../core/generate_manifest/index.js';
29
30
  import { build_server_nodes } from './build/build_server.js';
30
31
  import { find_deps, resolve_symlinks } from './build/utils.js';
31
- import { dev } from './dev/index.js';
32
+ import { dev, invalidate_module } from './dev/index.js';
32
33
  import { preview } from './preview/index.js';
33
34
  import {
34
35
  error_for_missing_config,
@@ -36,8 +37,7 @@ import {
36
37
  normalize_id,
37
38
  remote_module_pattern,
38
39
  server_only_directory_pattern,
39
- server_only_module_pattern,
40
- strip_virtual_prefix
40
+ server_only_module_pattern
41
41
  } from './utils.js';
42
42
  import { stackless } from '../../utils/error.js';
43
43
  import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
@@ -50,10 +50,10 @@ import { get_import_aliases, get_hash_import_keys } from '../../utils/imports.js
50
50
  import {
51
51
  app_env_private,
52
52
  app_server,
53
- service_worker,
54
53
  sveltekit_env,
55
54
  sveltekit_env_private,
56
55
  sveltekit_env_service_worker,
56
+ sveltekit_manifest_data,
57
57
  sveltekit_env_public_client,
58
58
  sveltekit_env_public_server
59
59
  } from './module_ids.js';
@@ -62,13 +62,7 @@ import { compact } from '../../utils/array.js';
62
62
  import { should_ignore, has_children } from './static_analysis/utils.js';
63
63
  import { process_config, split_config, validate_config } from '../../core/config/index.js';
64
64
  import { treeshake_prerendered_remotes } from './build/remote.js';
65
-
66
- /**
67
- * The posix-ified root of the project based on the Vite configuration.
68
- * Populated after Vite plugins' `config` hooks run
69
- * @type {string}
70
- */
71
- let root;
65
+ import { get_runner } from '../../runner.js';
72
66
 
73
67
  /** @type {import('./types.js').EnforcedConfig} */
74
68
  const enforced_config = {
@@ -100,8 +94,7 @@ const enforced_config = {
100
94
  resolve: {
101
95
  alias: {
102
96
  $app: true,
103
- $env: true,
104
- '$service-worker': true
97
+ $env: true
105
98
  }
106
99
  }
107
100
  };
@@ -123,7 +116,7 @@ const warning_preprocessor = {
123
116
  const fixed = basename.replace('.svelte', '(.server).js/ts');
124
117
 
125
118
  const message =
126
- `\n${styleText(['bold', 'red'], path.relative(root, filename))}\n` +
119
+ `\n${styleText(['bold', 'red'], path.relative(process.cwd(), filename))}\n` +
127
120
  `\`${match[1]}\` will be ignored — move it to ${fixed} instead. See https://svelte.dev/docs/kit/page-options for more information.`;
128
121
 
129
122
  if (!warned.has(message)) {
@@ -140,7 +133,7 @@ const warning_preprocessor = {
140
133
 
141
134
  if (basename.startsWith('+layout.') && !has_children(content, true)) {
142
135
  const message =
143
- `\n${styleText(['bold', 'red'], path.relative(root, filename))}\n` +
136
+ `\n${styleText(['bold', 'red'], path.relative(process.cwd(), filename))}\n` +
144
137
  '`<slot />` or `{@render ...}` tag' +
145
138
  ' missing — inner content will not be rendered';
146
139
 
@@ -206,13 +199,7 @@ export async function sveltekit(config) {
206
199
  inline_vps_config.compilerOptions = svelte_config.compilerOptions;
207
200
  }
208
201
 
209
- return [
210
- plugin_root(),
211
- ...vite_plugin_svelte.svelte(inline_vps_config),
212
- ...kit({
213
- svelte_config
214
- })
215
- ];
202
+ return [...vite_plugin_svelte.svelte(inline_vps_config), ...kit({ svelte_config })];
216
203
  }
217
204
 
218
205
  /** @param {UserConfig | ResolvedConfig} vite_config */
@@ -220,39 +207,6 @@ function resolve_root(vite_config) {
220
207
  return posixify(vite_config.root ? path.resolve(vite_config.root) : process.cwd());
221
208
  }
222
209
 
223
- /**
224
- * @return {Plugin}
225
- */
226
- function plugin_root() {
227
- return {
228
- name: 'vite-plugin-sveltekit-resolve-svelte-config',
229
- // make sure it runs first
230
- enforce: 'pre',
231
- config: {
232
- order: 'pre',
233
- handler(config) {
234
- root = resolve_root(config);
235
-
236
- const config_file = ['svelte.config.js', 'svelte.config.ts'].find((file) =>
237
- fs.existsSync(path.join(root, file))
238
- );
239
- if (config_file) {
240
- throw new Error(
241
- `${config_file} is no longer used. Please pass configuration via the \`sveltekit(...)\` plugin in your Vite config.`
242
- );
243
- }
244
- }
245
- },
246
- // TODO: do we even need to set `root` based on the final Vite config?
247
- configResolved: {
248
- order: 'pre',
249
- handler(config) {
250
- root = resolve_root(config);
251
- }
252
- }
253
- };
254
- }
255
-
256
210
  /**
257
211
  * Returns the SvelteKit Vite plugin. Vite executes Rolldown hooks as well as some of its own.
258
212
  * Background reading is available at:
@@ -271,6 +225,12 @@ function kit({ svelte_config }) {
271
225
  /** @type {typeof import('vite')} */
272
226
  let vite;
273
227
 
228
+ /**
229
+ * The posix-ified root of the project based on the Vite configuration.
230
+ * @type {string}
231
+ */
232
+ let root;
233
+
274
234
  /** @type {ValidatedKitConfig} */
275
235
  let kit;
276
236
  /** @type {string} `kit.outDir` but posix-ified */
@@ -301,9 +261,6 @@ function kit({ svelte_config }) {
301
261
 
302
262
  /** @type {string | null} */
303
263
  let service_worker_entry_file;
304
- /** @type {import('node:path').ParsedPath} */
305
- let parsed_service_worker;
306
-
307
264
  /** @type {string} */
308
265
  let normalized_cwd;
309
266
  /** @type {Array<{ alias: string, path: string }>} */
@@ -327,6 +284,27 @@ function kit({ svelte_config }) {
327
284
  /** @type {string} name for `globalThis.__sveltekit_xxx` */
328
285
  let kit_global;
329
286
 
287
+ /** @type {Plugin} */
288
+ const plugin_resolve_root = {
289
+ name: 'vite-plugin-sveltekit-resolve-root',
290
+ // make sure it runs first
291
+ enforce: 'pre',
292
+ config: {
293
+ order: 'pre',
294
+ handler(config) {
295
+ root = resolve_root(config);
296
+
297
+ for (const file of ['svelte.config.js', 'svelte.config.ts']) {
298
+ if (fs.existsSync(path.join(root, file))) {
299
+ throw new Error(
300
+ `${file} is no longer used. Please pass configuration via the \`sveltekit(...)\` plugin in your Vite config.`
301
+ );
302
+ }
303
+ }
304
+ }
305
+ }
306
+ };
307
+
330
308
  /** @type {Plugin} */
331
309
  const plugin_setup = {
332
310
  name: 'vite-plugin-sveltekit-setup',
@@ -453,7 +431,9 @@ function kit({ svelte_config }) {
453
431
  // this does not affect app code, just handling of imported libraries that use $app or $env
454
432
  '$app',
455
433
  '$env'
456
- ]
434
+ ],
435
+ // avoid Vite dev server reloading the first time a page is requested
436
+ include: ['@sveltejs/kit > devalue', '@sveltejs/kit > esm-env']
457
437
  },
458
438
  ssr: {
459
439
  noExternal: [
@@ -472,27 +452,33 @@ function kit({ svelte_config }) {
472
452
  }
473
453
  };
474
454
 
475
- // treat .remote.js files as empty for the purposes of prebundling
455
+ // externalize .remote.js files to stop dependency tracing during prebundling
476
456
  if (kit.experimental.remoteFunctions) {
477
457
  // @ts-expect-error optimizeDeps is already set above
478
458
  new_config.optimizeDeps.rolldownOptions ??= {};
479
459
  // @ts-expect-error
480
460
  new_config.optimizeDeps.rolldownOptions.plugins ??= [];
481
461
  // @ts-expect-error
482
- new_config.optimizeDeps.rolldownOptions.plugins.push({
483
- name: 'vite-plugin-sveltekit-setup:optimize-remote-functions',
484
- load: {
485
- filter: { id: remote_module_pattern },
486
- handler() {
487
- return '';
462
+ new_config.optimizeDeps.rolldownOptions.plugins.push(
463
+ /** @type {Rolldown.Plugin} */ ({
464
+ name: 'vite-plugin-sveltekit-setup:optimize-remote-functions',
465
+ resolveId: {
466
+ filter: { id: remote_module_pattern },
467
+ async handler(id, importer) {
468
+ const resolved = await this.resolve(id, importer, { skipSelf: true });
469
+ if (!resolved) return { id, external: true };
470
+ // a servable /@fs url; 'absolute' stops rolldown relativizing it in the deps bundle
471
+ return { id: to_fs(resolved.id), external: 'absolute' };
472
+ }
488
473
  }
489
- }
490
- });
474
+ })
475
+ );
491
476
  }
492
477
 
493
478
  const define = {
494
479
  __SVELTEKIT_APP_DIR__: s(posixify(kit.appDir)),
495
480
  __SVELTEKIT_APP_VERSION__: s(kit.version.name),
481
+ __SVELTEKIT_APP_VERSION_CHECKS_ENABLED__: s(kit.output.bundleStrategy !== 'inline'),
496
482
  __SVELTEKIT_EMBEDDED__: s(kit.embedded),
497
483
  __SVELTEKIT_FORK_PRELOADS__: s(kit.experimental.forkPreloads),
498
484
  __SVELTEKIT_PATHS_ASSETS__: s(kit.paths.assets),
@@ -612,14 +598,10 @@ function kit({ svelte_config }) {
612
598
  );
613
599
 
614
600
  for (const id of [sveltekit_env, sveltekit_env_public_client]) {
615
- const module = server.moduleGraph.getModuleById(id);
616
-
617
- if (module) {
618
- server.moduleGraph.invalidateModule(module);
619
- }
601
+ invalidate_module(server, id);
620
602
  }
621
603
 
622
- server.ws.send({ type: 'full-reload' });
604
+ server.hot.send({ type: 'full-reload' });
623
605
  }
624
606
  });
625
607
  },
@@ -630,18 +612,13 @@ function kit({ svelte_config }) {
630
612
 
631
613
  resolveId: {
632
614
  filter: {
633
- id: [exactRegex('$service-worker'), prefixRegex('__sveltekit/')]
615
+ id: [prefixRegex('__sveltekit/')]
634
616
  },
635
617
  handler(id) {
636
618
  if (id === '__sveltekit/manifest') {
637
619
  return `${out_dir}/generated/client-optimized/app.js`;
638
620
  }
639
621
 
640
- if (id === '$service-worker') {
641
- // ids with :$ don't work with reverse proxies like nginx
642
- return `\0virtual:${id.substring(1)}`;
643
- }
644
-
645
622
  if (id === '__sveltekit/remote') {
646
623
  return `${runtime_directory}/client/remote-functions/index.js`;
647
624
  }
@@ -653,18 +630,18 @@ function kit({ svelte_config }) {
653
630
  load: {
654
631
  filter: {
655
632
  id: [
656
- exactRegex(service_worker),
657
633
  exactRegex(sveltekit_env),
658
634
  exactRegex(sveltekit_env_private),
659
635
  exactRegex(sveltekit_env_public_client),
660
636
  exactRegex(sveltekit_env_public_server),
661
- exactRegex(sveltekit_env_service_worker)
637
+ exactRegex(sveltekit_env_service_worker),
638
+ exactRegex(sveltekit_manifest_data)
662
639
  ]
663
640
  },
664
641
  handler(id) {
665
642
  switch (id) {
666
- case service_worker:
667
- return create_service_worker_module(svelte_config);
643
+ case sveltekit_manifest_data:
644
+ return create_manifest_data_module(is_build, manifest_data);
668
645
 
669
646
  case sveltekit_env:
670
647
  return create_sveltekit_env(explicit_env_config, env, explicit_env_entry, !is_build);
@@ -691,11 +668,17 @@ function kit({ svelte_config }) {
691
668
  ? create_sveltekit_env_service_worker(
692
669
  explicit_env_config,
693
670
  env,
671
+ kit.version.name,
694
672
  kit_global,
695
673
  kit.paths.base,
696
674
  kit.appDir
697
675
  )
698
- : create_sveltekit_env_service_worker_dev(explicit_env_config, env, kit_global);
676
+ : create_sveltekit_env_service_worker_dev(
677
+ explicit_env_config,
678
+ env,
679
+ kit.version.name,
680
+ kit_global
681
+ );
699
682
  }
700
683
  }
701
684
  }
@@ -718,7 +701,7 @@ function kit({ svelte_config }) {
718
701
 
719
702
  applyToEnvironment(environment) {
720
703
  // the import map is only read for client-side violations in `load`, so skip other environments
721
- return environment.config.consumer === 'client' && environment.name !== 'serviceWorker';
704
+ return environment.config.consumer === 'client';
722
705
  },
723
706
 
724
707
  resolveId: {
@@ -789,6 +772,10 @@ function kit({ svelte_config }) {
789
772
  if (manifest_data.hooks.client) entrypoints.add(manifest_data.hooks.client);
790
773
  if (manifest_data.hooks.universal) entrypoints.add(manifest_data.hooks.universal);
791
774
 
775
+ if (service_worker_entry_file) {
776
+ entrypoints.add(posixify(path.relative(root, service_worker_entry_file)));
777
+ }
778
+
792
779
  // Walk up the import graph from the server-only module, looking for a chain
793
780
  // that leads back to a client entrypoint. We search all candidates (not just
794
781
  // the first) because a module can be imported by both server and client code,
@@ -850,7 +837,7 @@ function kit({ svelte_config }) {
850
837
  /** @type {ViteDevServer} */
851
838
  let dev_server;
852
839
 
853
- /** @type {Array<{ hash: string, file: string }>} */
840
+ /** @type {RemoteChunk[]} */
854
841
  const remotes = [];
855
842
 
856
843
  /** @type {Map<string, string>} Maps remote hash -> original module id */
@@ -908,31 +895,33 @@ function kit({ svelte_config }) {
908
895
  file
909
896
  };
910
897
 
911
- remotes.push(remote);
898
+ if (this.environment.name === 'ssr') remotes.push(remote);
912
899
 
913
900
  if (this.environment.config.consumer !== 'client') {
914
901
  // we need to add an `await Promise.resolve()` because if the user imports this function
915
902
  // on the client AND in a load function when loading the client module we will trigger
916
- // an ssrLoadModule during dev. During a link preload, the module can be mistakenly
903
+ // an import during dev. During a link preload, the module can be mistakenly
917
904
  // loaded and transformed twice and the first time all its exports would be undefined
918
905
  // triggering a dev server error. By adding a microtask we ensure that the module is fully loaded
906
+ const ms = new MagicString(code);
919
907
 
920
908
  // Extra newlines to prevent syntax errors around missing semicolons or comments
921
- code +=
909
+ ms.append(
922
910
  '\n\n' +
923
- dedent`
924
- import * as $$_self_$$ from './${path.basename(id)}';
925
- import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal/server';
911
+ dedent`
912
+ import * as $$_self_$$ from './${path.basename(id)}';
913
+ import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal/server';
926
914
 
927
- ${dev_server ? 'await Promise.resolve()' : ''}
915
+ ${dev_server ? 'await Promise.resolve()' : ''}
928
916
 
929
- $$_init_$$($$_self_$$, ${s(file)}, ${s(remote.hash)});
917
+ $$_init_$$($$_self_$$, ${s(file)}, ${s(remote.hash)});
930
918
 
931
- for (const [name, fn] of Object.entries($$_self_$$)) {
932
- fn.__.id = ${s(remote.hash)} + '/' + name;
933
- fn.__.name = name;
934
- }
935
- `;
919
+ for (const [name, fn] of Object.entries($$_self_$$)) {
920
+ fn.__.id = ${s(remote.hash)} + '/' + name;
921
+ fn.__.name = name;
922
+ }
923
+ `
924
+ );
936
925
 
937
926
  // Emit a dedicated entry chunk for this remote in SSR builds (prod only)
938
927
  if (!dev_server) {
@@ -947,7 +936,10 @@ function kit({ svelte_config }) {
947
936
  }
948
937
  }
949
938
 
950
- return code;
939
+ return {
940
+ code: ms.toString(),
941
+ map: ms.generateMap({ hires: 'boundary' })
942
+ };
951
943
  }
952
944
 
953
945
  // For the client, read the exports and create a new module that only contains fetch functions with the correct metadata
@@ -959,7 +951,7 @@ function kit({ svelte_config }) {
959
951
  // being called again with `opts.ssr === true` if the module isn't
960
952
  // already loaded) so we can determine what it exports
961
953
  if (dev_server) {
962
- const module = await dev_server.ssrLoadModule(id);
954
+ const module = await get_runner(dev_server).import(id);
963
955
 
964
956
  for (const [name, value] of Object.entries(module)) {
965
957
  const type = value?.__?.type;
@@ -997,7 +989,8 @@ function kit({ svelte_config }) {
997
989
  }
998
990
 
999
991
  return {
1000
- code: result
992
+ code: result,
993
+ map: null
1001
994
  };
1002
995
  }
1003
996
  }
@@ -1030,10 +1023,10 @@ function kit({ svelte_config }) {
1030
1023
  /** @type {Prerendered} */
1031
1024
  let prerendered;
1032
1025
 
1033
- /** @type {Set<string>} */
1034
- let build_files;
1026
+ /** @type {Array<{ path: string }>} */
1027
+ let immutable;
1035
1028
  /** @type {string} */
1036
- let service_worker_code;
1029
+ let manifest_data_code;
1037
1030
 
1038
1031
  /**
1039
1032
  * Creates the service worker virtual modules
@@ -1044,7 +1037,6 @@ function kit({ svelte_config }) {
1044
1037
 
1045
1038
  config(config) {
1046
1039
  service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
1047
- parsed_service_worker = path.parse(kit.files.serviceWorker);
1048
1040
 
1049
1041
  if (!service_worker_entry_file) return;
1050
1042
 
@@ -1099,98 +1091,55 @@ function kit({ svelte_config }) {
1099
1091
  return environment.name === 'serviceWorker';
1100
1092
  },
1101
1093
 
1102
- resolveId(id, importer) {
1103
- // If importing from a service-worker, only allow $service-worker & $app/env/public, but none of the other virtual modules.
1104
- // This check won't catch transitive imports, but it will warn when the import comes from a service-worker directly.
1105
- // Transitive imports will be caught during the build.
1106
- if (importer) {
1107
- const parsed_importer = path.parse(importer);
1108
-
1109
- const importer_is_service_worker =
1110
- parsed_importer.dir === parsed_service_worker.dir &&
1111
- parsed_importer.name === parsed_service_worker.name;
1112
-
1113
- if (
1114
- importer_is_service_worker &&
1115
- id !== '$service-worker' &&
1116
- id !== 'virtual:$app/env/public' &&
1117
- id !== '__sveltekit/env/service-worker'
1118
- ) {
1119
- throw new Error(
1120
- `Cannot import ${normalize_id(
1121
- id,
1122
- normalized_aliases,
1123
- normalized_cwd
1124
- )} into service-worker code. Only the modules $service-worker and $app/env/public are available in service workers.`
1125
- );
1126
- }
1127
- }
1128
-
1129
- if (id.startsWith('$app/') || id === '$service-worker') {
1130
- // ids with :$ don't work with reverse proxies like nginx
1131
- return `\0virtual:${id.substring(1)}`;
1132
- }
1133
-
1134
- if (id.startsWith('__sveltekit')) {
1094
+ resolveId: {
1095
+ filter: {
1096
+ id: [prefixRegex('__sveltekit/')]
1097
+ },
1098
+ handler(id) {
1135
1099
  return `\0virtual:${id}`;
1136
1100
  }
1137
1101
  },
1138
1102
 
1139
1103
  load: {
1140
1104
  filter: {
1141
- id: [prefixRegex('\0virtual:')]
1105
+ id: [
1106
+ exactRegex('\0virtual:app/manifest'),
1107
+ exactRegex(sveltekit_manifest_data),
1108
+ exactRegex(sveltekit_env_service_worker),
1109
+ exactRegex(sveltekit_env_public_client)
1110
+ ]
1142
1111
  },
1143
1112
  handler(id) {
1144
- if (!build_files) {
1145
- build_files = new Set();
1146
- const manifest = vite_client_manifest ?? vite_server_manifest;
1147
- for (const key in manifest) {
1148
- const { file, css = [], assets = [] } = manifest[key];
1149
- build_files.add(file);
1150
- css.forEach((file) => build_files.add(file));
1151
- assets.forEach((file) => build_files.add(file));
1152
- }
1153
-
1154
- if (kit.output.bundleStrategy === 'inline') {
1155
- // the bundle and stylesheet are inlined into the page and their files
1156
- // deleted, so they must not appear in the list of cacheable assets
1157
- for (const file of build_files) {
1158
- if (!fs.existsSync(`${out}/client/${file}`)) {
1159
- build_files.delete(file);
1160
- }
1161
- }
1162
- }
1163
-
1164
- // in a service worker, `location` is the location of the service worker itself,
1165
- // which is guaranteed to be `<base>/service-worker.js`
1166
- const base = "location.pathname.split('/').slice(0, -1).join('/')";
1167
-
1168
- service_worker_code = dedent`
1169
- export const base = /*@__PURE__*/ ${base};
1170
-
1171
- export const build = [
1172
- ${Array.from(build_files)
1173
- .map((file) => `base + ${s(`/${file}`)}`)
1174
- .join(',\n')}
1113
+ if (!manifest_data_code) {
1114
+ // the client build computes `immutable`, unless it was skipped
1115
+ // because every route has `csr: false`
1116
+ immutable ??= collect_immutable(vite_server_manifest, kit.appDir, new Set());
1117
+
1118
+ manifest_data_code = dedent`
1119
+ export const immutable = [
1120
+ ${immutable.map((entry) => s(entry)).join(',\n')}
1175
1121
  ];
1176
1122
 
1177
- export const files = [
1178
- ${manifest_data.assets
1179
- .filter((asset) => kit.serviceWorker.files(asset.file))
1180
- .map((asset) => `base + ${s(`/${asset.file}`)}`)
1181
- .join(',\n')}
1123
+ export const assets = [
1124
+ ${manifest_data.assets.map((asset) => s({ path: asset.file })).join(',\n')}
1182
1125
  ];
1183
1126
 
1184
1127
  export const prerendered = [
1185
- ${prerendered.paths.map((path) => `base + ${s(path.replace(kit.paths.base, ''))}`).join(',\n')}
1128
+ ${prerendered.paths.map((path) => s({ path: path.replace(kit.paths.base, '').slice(1) })).join(',\n')}
1186
1129
  ];
1187
1130
 
1188
- export const version = ${s(kit.version.name)};
1131
+ export const routes = [
1132
+ ${manifest_data.routes.map((route) => s({ id: route.id })).join(',\n')}
1133
+ ];
1189
1134
  `;
1190
1135
  }
1191
1136
 
1192
- if (id === service_worker) {
1193
- return service_worker_code;
1137
+ if (id === '\0virtual:app/manifest') {
1138
+ return `export { immutable, assets, prerendered, routes } from '__sveltekit/manifest-data';`;
1139
+ }
1140
+
1141
+ if (id === sveltekit_manifest_data) {
1142
+ return manifest_data_code;
1194
1143
  }
1195
1144
 
1196
1145
  if (id === sveltekit_env_service_worker) {
@@ -1198,11 +1147,17 @@ function kit({ svelte_config }) {
1198
1147
  ? create_sveltekit_env_service_worker(
1199
1148
  explicit_env_config,
1200
1149
  env,
1150
+ kit.version.name,
1201
1151
  kit_global,
1202
1152
  kit.paths.base,
1203
1153
  kit.appDir
1204
1154
  )
1205
- : create_sveltekit_env_service_worker_dev(explicit_env_config, env, kit_global);
1155
+ : create_sveltekit_env_service_worker_dev(
1156
+ explicit_env_config,
1157
+ env,
1158
+ kit.version.name,
1159
+ kit_global
1160
+ );
1206
1161
  }
1207
1162
 
1208
1163
  if (id === sveltekit_env_public_client) {
@@ -1212,16 +1167,31 @@ function kit({ svelte_config }) {
1212
1167
  `const env = ${kit_global}.env;`
1213
1168
  );
1214
1169
  }
1170
+ }
1171
+ },
1215
1172
 
1216
- const sw_normalized_cwd = vite.normalizePath(vite_config.root);
1217
- const sw_normalized_aliases = get_import_aliases(
1218
- vite_config.root,
1219
- vite.normalizePath.bind(vite)
1220
- );
1221
- const relative = normalize_id(id, sw_normalized_aliases, sw_normalized_cwd);
1222
- const stripped = strip_virtual_prefix(relative);
1173
+ generateBundle(_, bundle) {
1174
+ const invalid_modules = new Set();
1175
+ const modules = new Map([
1176
+ [`${runtime_directory}/app/forms.js`, '$app/forms'],
1177
+ [`${runtime_directory}/app/navigation.js`, '$app/navigation'],
1178
+ [`${runtime_directory}/app/state/index.js`, '$app/state']
1179
+ ]);
1180
+
1181
+ for (const output of Object.values(bundle)) {
1182
+ if (output.type !== 'chunk') continue;
1183
+
1184
+ for (const id of output.moduleIds) {
1185
+ const module = modules.get(id);
1186
+ if (module) invalid_modules.add(module);
1187
+ }
1188
+ }
1189
+
1190
+ if (invalid_modules.size > 0) {
1223
1191
  throw new Error(
1224
- `Cannot import ${stripped} into service-worker code. Only the modules $service-worker and $app/env/public are available in service workers.`
1192
+ `Cannot import ${Array.from(modules.values())
1193
+ .filter((module) => invalid_modules.has(module))
1194
+ .join(', ')} into service-worker code.`
1225
1195
  );
1226
1196
  }
1227
1197
  }
@@ -1233,17 +1203,19 @@ function kit({ svelte_config }) {
1233
1203
  applyToEnvironment(environment) {
1234
1204
  return !!service_worker_entry_file && environment.config.consumer === 'client';
1235
1205
  },
1236
- transform(code, id) {
1237
- if (id !== service_worker_entry_file) return;
1238
-
1239
- // prepend the service worker with an import that configures
1240
- // `env`, in case `$app/env/public` is imported. In production
1241
- // this is required: dynamic public env vars aren't known at
1242
- // build time, so `env.js` is loaded at runtime. In dev, the
1243
- // imported module just inlines the current values instead.
1244
- return {
1245
- code: `import '__sveltekit/env/service-worker';\n${code}`
1246
- };
1206
+ transform: {
1207
+ handler(code, id) {
1208
+ if (id !== service_worker_entry_file) return;
1209
+
1210
+ // prepend the service worker with an import that configures
1211
+ // `env`, in case `$app/env/public` is imported. In production
1212
+ // this is required: dynamic public env vars aren't known at
1213
+ // build time, so `env.js` is loaded at runtime. In dev, the
1214
+ // imported module just inlines the current values instead.
1215
+ return {
1216
+ code: `import '__sveltekit/env/service-worker';\n${code}`
1217
+ };
1218
+ }
1247
1219
  }
1248
1220
  };
1249
1221
 
@@ -1427,9 +1399,26 @@ function kit({ svelte_config }) {
1427
1399
  output: {
1428
1400
  format: inline ? 'iife' : 'esm',
1429
1401
  entryFileNames: `${app_immutable}/[name].[hash].js`,
1430
- chunkFileNames: `${app_immutable}/chunks/[hash].js`,
1402
+ chunkFileNames: (/** @type {Rolldown.PreRenderedChunk} */ chunk_info) => {
1403
+ // The manifest data chunk gets a fixed (non-hashed) filename so
1404
+ // that importers' content hashes are stable regardless of the
1405
+ // manifest content — this breaks the content-hash feedback loop
1406
+ if (chunk_info.name === 'sveltekit-manifest') {
1407
+ return `${kit.appDir}/manifest.js`;
1408
+ }
1409
+ return `${app_immutable}/chunks/[hash].js`;
1410
+ },
1431
1411
  codeSplitting:
1432
- svelte_config.kit.output.bundleStrategy === 'split' ? undefined : false
1412
+ svelte_config.kit.output.bundleStrategy === 'split'
1413
+ ? {
1414
+ groups: [
1415
+ {
1416
+ name: 'sveltekit-manifest',
1417
+ test: sveltekit_manifest_data
1418
+ }
1419
+ ]
1420
+ }
1421
+ : false
1433
1422
  },
1434
1423
  // This silences Rolldown warnings about not supporting `import.meta`
1435
1424
  // for the `iife` output format. We don't care because it's
@@ -1508,7 +1497,18 @@ function kit({ svelte_config }) {
1508
1497
  * @see https://vitejs.dev/guide/api-plugin.html#configureserver
1509
1498
  */
1510
1499
  async configureServer(server) {
1511
- return await dev(server, vite_config, svelte_config, () => remotes, root);
1500
+ return await dev(
1501
+ server,
1502
+ vite_config,
1503
+ svelte_config,
1504
+ () => remotes,
1505
+ root,
1506
+ (data) => {
1507
+ manifest_data = data;
1508
+ // Invalidate the manifest data module so it reloads with new routes/files
1509
+ invalidate_module(server, sveltekit_manifest_data);
1510
+ }
1511
+ );
1512
1512
  },
1513
1513
 
1514
1514
  /**
@@ -1564,6 +1564,15 @@ function kit({ svelte_config }) {
1564
1564
  await builder.build(builder.environments.ssr)
1565
1565
  );
1566
1566
 
1567
+ // Replace manifest placeholders in SSR output. `assets` and `routes`
1568
+ // are known from `manifest_data`. `immutable` and `prerendered` are not
1569
+ // known yet — they get sentinel strings that are replaced after
1570
+ // the client build and after prerendering respectively.
1571
+ replace_manifest_placeholder_variables(server_chunks, `${out}/server`, {
1572
+ assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1573
+ routes: manifest_data.routes.map((route) => ({ id: route.id }))
1574
+ });
1575
+
1567
1576
  const verbose = builder.config.logLevel === 'info';
1568
1577
  const log = logger({ verbose });
1569
1578
 
@@ -1695,6 +1704,41 @@ function kit({ svelte_config }) {
1695
1704
  const deps_of = (entry, add_dynamic_css = false) =>
1696
1705
  find_deps(vite_manifest, posixify(path.relative(root, entry)), add_dynamic_css, root);
1697
1706
 
1707
+ // the inline bundle and stylesheet are deleted further down, after
1708
+ // being inlined into the page, so they must not appear in `immutable`
1709
+ /** @type {Set<string>} */
1710
+ const inlined = new Set();
1711
+ /** @type {Rolldown.OutputAsset | undefined} */
1712
+ let inline_style;
1713
+
1714
+ if (kit.output.bundleStrategy === 'inline') {
1715
+ inline_style = /** @type {Rolldown.OutputAsset | undefined} */ (
1716
+ client_chunks.find(
1717
+ (chunk) =>
1718
+ chunk.type === 'asset' && chunk.names.length === 1 && chunk.names[0] === 'style.css'
1719
+ )
1720
+ );
1721
+
1722
+ inlined.add(deps_of(`${runtime_directory}/client/bundle.js`).file);
1723
+ if (inline_style) inlined.add(inline_style.fileName);
1724
+ }
1725
+
1726
+ // Replace manifest placeholders in client output. `immutable` is
1727
+ // computed from the Vite client manifest, `assets` and `routes`
1728
+ // from `manifest_data`. `prerendered` is left as a placeholder
1729
+ // for now — it's replaced after prerendering completes.
1730
+ immutable = collect_immutable(vite_manifest, kit.appDir, inlined);
1731
+
1732
+ replace_manifest_placeholder_variables(client_chunks, `${out}/client`, {
1733
+ immutable,
1734
+ assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1735
+ routes: manifest_data.routes.map((route) => ({ id: route.id }))
1736
+ });
1737
+
1738
+ // Now that the client build is done, replace the `build` sentinel
1739
+ // in the SSR output with the real build files
1740
+ replace_manifest_placeholder_strings(`${out}/server`, { immutable });
1741
+
1698
1742
  const has_explicit_dynamic_public_env = Object.values(explicit_env_config ?? {}).some(
1699
1743
  (variable) => variable.public && !variable.static
1700
1744
  );
@@ -1782,25 +1826,16 @@ function kit({ svelte_config }) {
1782
1826
  };
1783
1827
 
1784
1828
  if (svelte_config.kit.output.bundleStrategy === 'inline') {
1785
- const style = /** @type {Rolldown.OutputAsset} */ (
1786
- client_chunks.find(
1787
- (chunk) =>
1788
- chunk.type === 'asset' &&
1789
- chunk.names.length === 1 &&
1790
- chunk.names[0] === 'style.css'
1791
- )
1792
- );
1793
-
1794
1829
  build_data.client.inline = {
1795
1830
  script: read(`${out}/client/${start.file}`),
1796
- style: /** @type {string | undefined} */ (style?.source)
1831
+ style: /** @type {string | undefined} */ (inline_style?.source)
1797
1832
  };
1798
1833
 
1799
1834
  // the bundle and stylesheet are inlined into the page, so the
1800
1835
  // emitted files are never loaded
1801
1836
  fs.unlinkSync(`${out}/client/${start.file}`);
1802
1837
  fs.rmSync(`${out}/client/${start.file}.map`, { force: true });
1803
- if (style) fs.unlinkSync(`${out}/client/${style.fileName}`);
1838
+ if (inline_style) fs.unlinkSync(`${out}/client/${inline_style.fileName}`);
1804
1839
  }
1805
1840
  }
1806
1841
 
@@ -1860,10 +1895,30 @@ function kit({ svelte_config }) {
1860
1895
 
1861
1896
  prerendered = prerender_results.prerendered;
1862
1897
 
1898
+ // Replace the `prerendered` sentinel in both SSR and client output
1899
+ // with the real prerendered paths. The other sentinels (`build`)
1900
+ // were already replaced after the client build.
1901
+ const prerendered_paths = prerendered.paths.map((p) => {
1902
+ return { path: p.replace(kit.paths.base, '').slice(1) };
1903
+ });
1904
+
1905
+ replace_manifest_placeholder_strings(`${out}/server`, { prerendered: prerendered_paths });
1906
+ replace_manifest_placeholder_strings(`${out}/client`, { prerendered: prerendered_paths });
1907
+
1908
+ // For `inline` strategy, the entry file was deleted and read into
1909
+ // `build_data.client.inline.script` — replace the sentinel there too
1910
+ if (build_data.client?.inline?.script) {
1911
+ build_data.client.inline.script = build_data.client.inline.script.replaceAll(
1912
+ '"__sveltekit_manifest_prerendered__"',
1913
+ JSON.stringify(prerendered_paths)
1914
+ );
1915
+ }
1916
+
1863
1917
  await treeshake_prerendered_remotes(
1864
1918
  vite,
1865
1919
  out,
1866
1920
  remotes,
1921
+ remote_original_by_hash,
1867
1922
  metadata,
1868
1923
  process.cwd(),
1869
1924
  server_chunks,
@@ -1956,6 +2011,7 @@ function kit({ svelte_config }) {
1956
2011
  return /** @type {Plugin[]} */ (
1957
2012
  [
1958
2013
  svelte_config.kit.adapter?.vite?.plugins,
2014
+ plugin_resolve_root,
1959
2015
  plugin_setup,
1960
2016
  plugin_remote_guard,
1961
2017
  plugin_remote,
@@ -2004,11 +2060,7 @@ function find_overridden_config(config, resolved_config, enforced_config, path,
2004
2060
  const resolved = resolved_config[key];
2005
2061
 
2006
2062
  if (enforced === true) {
2007
- // Normalize path separators before comparing to avoid false positives on Windows,
2008
- // where config values like `root` may use backslashes while SvelteKit uses forward slashes.
2009
- const a = typeof config[key] === 'string' ? posixify(config[key]) : config[key];
2010
- const b = typeof resolved === 'string' ? posixify(resolved) : resolved;
2011
- if (a !== b) {
2063
+ if (comparable(config[key]) !== comparable(resolved)) {
2012
2064
  out.push(path + key);
2013
2065
  }
2014
2066
  } else {
@@ -2020,21 +2072,184 @@ function find_overridden_config(config, resolved_config, enforced_config, path,
2020
2072
  }
2021
2073
 
2022
2074
  /**
2023
- * @param {ValidatedConfig} config
2075
+ * Collects the content-hashed files of a Vite manifest, in the shape of
2076
+ * `$app/manifest`'s `immutable` export.
2077
+ *
2078
+ * @param {Manifest} manifest
2079
+ * @param {string} app_dir
2080
+ * @param {Set<string>} inlined files deleted from the output after being inlined into the page
2081
+ * @returns {Array<{ path: string }>}
2024
2082
  */
2025
- const create_service_worker_module = (config) => dedent`
2026
- if (typeof self === 'undefined' || self instanceof ServiceWorkerGlobalScope === false) {
2027
- throw new Error('This module can only be imported inside a service worker');
2083
+ const collect_immutable = (manifest, app_dir, inlined) => {
2084
+ const prefix = `${app_dir}/immutable`;
2085
+
2086
+ /** @type {Set<string>} */
2087
+ const files = new Set();
2088
+
2089
+ /** @param {string} file */
2090
+ const add = (file) => {
2091
+ if (file.startsWith(prefix) && !inlined.has(file)) files.add(file);
2092
+ };
2093
+
2094
+ for (const key in manifest) {
2095
+ const { file, css, assets } = manifest[key];
2096
+ add(file);
2097
+ if (css) for (let i = 0; i < css.length; i++) add(css[i]);
2098
+ if (assets) for (let i = 0; i < assets.length; i++) add(assets[i]);
2028
2099
  }
2029
2100
 
2030
- export const base = location.pathname.split('/').slice(0, -1).join('/');
2031
- export const build = [];
2032
- export const files = [
2033
- ${create_assets(config)
2034
- .filter((asset) => config.kit.serviceWorker.files(asset.file))
2035
- .map((asset) => `${s(`${config.kit.paths.base}/${asset.file}`)}`)
2036
- .join(',\n')}
2037
- ];
2038
- export const prerendered = [];
2039
- export const version = ${s(config.kit.version.name)};
2040
- `;
2101
+ return Array.from(files, (path) => ({ path }));
2102
+ };
2103
+
2104
+ /**
2105
+ * Normalizes a config value for comparison, since Windows paths may use backslashes
2106
+ * and differ in casing (e.g. the drive letter) depending on where they came from.
2107
+ * @param {any} value
2108
+ */
2109
+ function comparable(value) {
2110
+ if (typeof value !== 'string') return value;
2111
+ const normalized = posixify(value);
2112
+ return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
2113
+ }
2114
+
2115
+ /**
2116
+ * Creates the `$app/manifest` data module. During development, real values
2117
+ * are emitted for `assets` and `routes` (the only data known at that point).
2118
+ *
2119
+ * During build, bare identifier placeholders (fake globals) are emitted.
2120
+ * The bundler leaves these as unresolved global references in the output,
2121
+ * which are then replaced with real values by scanning the output chunks
2122
+ * after each build completes. This avoids the content-hash feedback loop:
2123
+ * the manifest data lives in its own chunk with a fixed filename, so
2124
+ * importers' hashes are stable regardless of the manifest content.
2125
+ *
2126
+ * @param {boolean} is_build
2127
+ * @param {ManifestData | undefined} manifest_data
2128
+ * @returns {string}
2129
+ */
2130
+ const create_manifest_data_module = (is_build, manifest_data) => {
2131
+ if (is_build) {
2132
+ // Bare identifiers (fake globals) — the bundler leaves these as
2133
+ // unresolved global references in the output. They are replaced
2134
+ // with real values by `replace_manifest_placeholder_variables`
2135
+ // after each build completes.
2136
+ return dedent`
2137
+ export const immutable = __SVELTEKIT_MANIFEST_IMMUTABLE__;
2138
+ export const assets = __SVELTEKIT_MANIFEST_ASSETS__;
2139
+ export const prerendered = __SVELTEKIT_MANIFEST_PRERENDERED__;
2140
+ export const routes = __SVELTEKIT_MANIFEST_ROUTES__;
2141
+ `;
2142
+ }
2143
+
2144
+ // In dev, `manifest_data` may not be set yet on the very first load,
2145
+ // but `configureServer` (which calls `sync.create`) runs before any
2146
+ // module is served, so it will be set by the time this is called.
2147
+ const routes = manifest_data?.routes.map((route) => s({ id: route.id })).join(',\n') ?? '';
2148
+ const assets = manifest_data?.assets.map((asset) => s({ path: asset.file })).join(',\n') ?? '';
2149
+
2150
+ return dedent`
2151
+ // empty during dev
2152
+ export const immutable = [];
2153
+ export const prerendered = [];
2154
+
2155
+ export const assets = [
2156
+ ${assets}
2157
+ ];
2158
+
2159
+ export const routes = [
2160
+ ${routes}
2161
+ ];
2162
+ `;
2163
+ };
2164
+
2165
+ /**
2166
+ * Replaces manifest data placeholder identifiers in output chunks with real
2167
+ * values, or strings that are valid JS (so thecode doesn't crash during prerendering)
2168
+ * but findable on disk for later replacement by `replace_manifest_placeholder_strings`.
2169
+ *
2170
+ * @param {Rolldown.RolldownOutput['output']} chunks
2171
+ * @param {string} output_dir
2172
+ * @param {{
2173
+ * immutable?: Array<{ path: string }>;
2174
+ * assets?: Array<{ path: string }>;
2175
+ * prerendered?: Array<{ path: string }>;
2176
+ * routes?: Array<{ id: string }>;
2177
+ * }} values
2178
+ */
2179
+ const replace_manifest_placeholder_variables = (chunks, output_dir, values) => {
2180
+ /** @type {Record<string, string>} */
2181
+ const replacements = {
2182
+ __SVELTEKIT_MANIFEST_IMMUTABLE__: JSON.stringify(
2183
+ values.immutable ?? '__sveltekit_manifest_build__'
2184
+ ),
2185
+ __SVELTEKIT_MANIFEST_ASSETS__: JSON.stringify(values.assets ?? '__sveltekit_manifest_files__'),
2186
+ __SVELTEKIT_MANIFEST_PRERENDERED__: JSON.stringify(
2187
+ values.prerendered ?? '__sveltekit_manifest_prerendered__'
2188
+ ),
2189
+ __SVELTEKIT_MANIFEST_ROUTES__: JSON.stringify(values.routes ?? '__sveltekit_manifest_routes__')
2190
+ };
2191
+
2192
+ for (const chunk of chunks) {
2193
+ if (chunk.type !== 'chunk') continue;
2194
+ if (!chunk.code.includes('__SVELTEKIT_MANIFEST_')) continue;
2195
+
2196
+ let code = chunk.code;
2197
+
2198
+ for (const [identifier, replacement] of Object.entries(replacements)) {
2199
+ code = code.replaceAll(identifier, replacement);
2200
+ }
2201
+
2202
+ const file_path = `${output_dir}/${chunk.fileName}`;
2203
+ fs.writeFileSync(file_path, code);
2204
+ }
2205
+ };
2206
+
2207
+ /**
2208
+ * Replaces manifest sentinel strings in files on disk with real values.
2209
+ * This is used for values that weren't known when the first replacement
2210
+ * pass ran (e.g. `build` wasn't known until after the client build,
2211
+ * `prerendered` wasn't known until after prerendering).
2212
+ *
2213
+ * @param {string} dir Directory to scan for .js files
2214
+ * @param {{
2215
+ * immutable?: Array<{ path: string }>;
2216
+ * prerendered?: Array<{ path: string }>;
2217
+ * }} values
2218
+ */
2219
+ const replace_manifest_placeholder_strings = (dir, values) => {
2220
+ /** @type {Record<string, string>} */
2221
+ const replacements = {};
2222
+
2223
+ if (values.immutable !== undefined) {
2224
+ replacements['__sveltekit_manifest_build__'] = JSON.stringify(values.immutable);
2225
+ }
2226
+ if (values.prerendered !== undefined) {
2227
+ replacements['__sveltekit_manifest_prerendered__'] = JSON.stringify(values.prerendered);
2228
+ }
2229
+
2230
+ for (const file of fs.readdirSync(dir)) {
2231
+ const file_path = `${dir}/${file}`;
2232
+ const stat = fs.statSync(file_path);
2233
+
2234
+ if (stat.isDirectory()) {
2235
+ replace_manifest_placeholder_strings(file_path, values);
2236
+ continue;
2237
+ }
2238
+
2239
+ if (!file.endsWith('.js')) continue;
2240
+
2241
+ let code = read(file_path);
2242
+ let changed = false;
2243
+
2244
+ for (const [sentinel, replacement] of Object.entries(replacements)) {
2245
+ if (code.includes(sentinel)) {
2246
+ code = code.replaceAll(`"${sentinel}"`, replacement);
2247
+ changed = true;
2248
+ }
2249
+ }
2250
+
2251
+ if (changed) {
2252
+ fs.writeFileSync(file_path, code);
2253
+ }
2254
+ }
2255
+ };