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

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 (144) hide show
  1. package/package.json +33 -21
  2. package/src/constants.js +2 -0
  3. package/src/core/adapt/builder.js +32 -49
  4. package/src/core/adapt/index.js +1 -4
  5. package/src/core/config/index.js +137 -71
  6. package/src/core/config/options.js +236 -244
  7. package/src/core/config/types.d.ts +1 -1
  8. package/src/core/env.js +121 -5
  9. package/src/core/generate_manifest/index.js +12 -15
  10. package/src/core/postbuild/analyse.js +7 -19
  11. package/src/core/postbuild/crawl.js +22 -6
  12. package/src/core/postbuild/fallback.js +2 -1
  13. package/src/core/postbuild/prerender.js +114 -48
  14. package/src/core/postbuild/queue.js +2 -1
  15. package/src/core/sync/create_manifest_data/index.js +53 -50
  16. package/src/core/sync/sync.js +0 -2
  17. package/src/core/sync/write_client_manifest.js +8 -15
  18. package/src/core/sync/write_env.js +2 -1
  19. package/src/core/sync/write_non_ambient.js +12 -9
  20. package/src/core/sync/write_server.js +14 -15
  21. package/src/core/sync/write_tsconfig.js +16 -8
  22. package/src/core/sync/write_tsconfig_test/package.json +7 -0
  23. package/src/core/sync/write_types/index.js +28 -24
  24. package/src/core/utils.js +30 -5
  25. package/src/exports/env/index.js +77 -0
  26. package/src/exports/hooks/index.js +3 -9
  27. package/src/exports/hooks/sequence.js +3 -2
  28. package/src/exports/index.js +35 -13
  29. package/src/exports/internal/client.js +5 -0
  30. package/src/exports/internal/env.js +8 -5
  31. package/src/exports/internal/index.js +1 -90
  32. package/src/exports/internal/{event.js → server/event.js} +1 -2
  33. package/src/exports/internal/server/index.js +33 -0
  34. package/src/exports/internal/shared.js +89 -0
  35. package/src/exports/node/index.js +62 -15
  36. package/src/exports/params.js +68 -0
  37. package/src/exports/public.d.ts +363 -189
  38. package/src/exports/url.js +86 -0
  39. package/src/exports/vite/build/build_server.js +53 -59
  40. package/src/exports/vite/build/remote.js +18 -11
  41. package/src/exports/vite/build/utils.js +0 -8
  42. package/src/exports/vite/dev/index.js +92 -50
  43. package/src/exports/vite/index.js +903 -681
  44. package/src/exports/vite/module_ids.js +0 -2
  45. package/src/exports/vite/preview/index.js +32 -24
  46. package/src/exports/vite/static_analysis/index.js +2 -4
  47. package/src/exports/vite/static_analysis/types.d.ts +14 -0
  48. package/src/exports/vite/utils.js +40 -35
  49. package/src/runtime/app/env/internal.js +4 -4
  50. package/src/runtime/app/env/types.d.ts +1 -1
  51. package/src/runtime/app/environment/index.js +3 -3
  52. package/src/runtime/app/forms.js +24 -7
  53. package/src/runtime/app/paths/client.js +4 -10
  54. package/src/runtime/app/paths/internal/client.js +4 -2
  55. package/src/runtime/app/paths/internal/server.js +2 -23
  56. package/src/runtime/app/paths/public.d.ts +0 -28
  57. package/src/runtime/app/paths/server.js +10 -6
  58. package/src/runtime/app/server/index.js +1 -1
  59. package/src/runtime/app/server/remote/command.js +0 -3
  60. package/src/runtime/app/server/remote/form.js +27 -15
  61. package/src/runtime/app/server/remote/prerender.js +29 -36
  62. package/src/runtime/app/server/remote/query.js +101 -99
  63. package/src/runtime/app/server/remote/requested.js +22 -14
  64. package/src/runtime/app/server/remote/shared.js +30 -26
  65. package/src/runtime/app/state/client.js +1 -2
  66. package/src/runtime/app/stores.js +13 -76
  67. package/src/runtime/client/bundle.js +1 -1
  68. package/src/runtime/client/client-entry.js +3 -0
  69. package/src/runtime/client/client.js +411 -318
  70. package/src/runtime/client/entry.js +24 -3
  71. package/src/runtime/client/fetcher.js +3 -2
  72. package/src/runtime/client/ndjson.js +6 -33
  73. package/src/runtime/client/payload.js +17 -0
  74. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  75. package/src/runtime/client/remote-functions/command.svelte.js +7 -32
  76. package/src/runtime/client/remote-functions/form.svelte.js +183 -112
  77. package/src/runtime/client/remote-functions/prerender.svelte.js +29 -8
  78. package/src/runtime/client/remote-functions/query/index.js +7 -14
  79. package/src/runtime/client/remote-functions/query/instance.svelte.js +63 -18
  80. package/src/runtime/client/remote-functions/query/proxy.js +3 -3
  81. package/src/runtime/client/remote-functions/query-batch.svelte.js +60 -68
  82. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +60 -18
  83. package/src/runtime/client/remote-functions/query-live/iterator.js +36 -55
  84. package/src/runtime/client/remote-functions/shared.svelte.js +88 -62
  85. package/src/runtime/client/sse.js +32 -0
  86. package/src/runtime/client/state.svelte.js +65 -49
  87. package/src/runtime/client/stream.js +39 -0
  88. package/src/runtime/client/types.d.ts +11 -7
  89. package/src/runtime/client/utils.js +0 -96
  90. package/src/runtime/components/root.svelte +66 -0
  91. package/src/runtime/env/dynamic/private.js +7 -0
  92. package/src/runtime/env/dynamic/public.js +7 -0
  93. package/src/runtime/env/static/private.js +6 -0
  94. package/src/runtime/env/static/public.js +6 -0
  95. package/src/runtime/form-utils.js +100 -22
  96. package/src/runtime/server/cookie.js +69 -52
  97. package/src/runtime/server/csrf.js +65 -0
  98. package/src/runtime/server/data/index.js +15 -19
  99. package/src/runtime/server/env_module.js +0 -5
  100. package/src/runtime/server/errors.js +135 -0
  101. package/src/runtime/server/fetch.js +1 -1
  102. package/src/runtime/server/index.js +22 -12
  103. package/src/runtime/server/internal.js +25 -0
  104. package/src/runtime/server/page/actions.js +43 -27
  105. package/src/runtime/server/page/data_serializer.js +10 -10
  106. package/src/runtime/server/page/index.js +12 -17
  107. package/src/runtime/server/page/load_data.js +2 -2
  108. package/src/runtime/server/page/render.js +114 -193
  109. package/src/runtime/server/page/respond_with_error.js +11 -11
  110. package/src/runtime/server/page/server_routing.js +27 -18
  111. package/src/runtime/server/remote-functions.js +596 -0
  112. package/src/runtime/server/respond.js +100 -68
  113. package/src/runtime/server/sourcemaps.js +183 -0
  114. package/src/runtime/server/utils.js +2 -134
  115. package/src/runtime/shared.js +83 -13
  116. package/src/runtime/telemetry/otel.js +1 -1
  117. package/src/runtime/types.d.ts +8 -0
  118. package/src/types/ambient-private.d.ts +2 -0
  119. package/src/types/ambient.d.ts +10 -5
  120. package/src/types/global-private.d.ts +13 -25
  121. package/src/types/internal.d.ts +95 -80
  122. package/src/types/private.d.ts +41 -1
  123. package/src/utils/error.js +24 -4
  124. package/src/utils/escape.js +9 -25
  125. package/src/utils/features.js +1 -1
  126. package/src/utils/fork.js +7 -2
  127. package/src/utils/import.js +6 -1
  128. package/src/utils/imports.js +83 -0
  129. package/src/utils/mime.js +9 -0
  130. package/src/utils/page_nodes.js +3 -5
  131. package/src/utils/params.js +66 -0
  132. package/src/utils/routing.js +90 -44
  133. package/src/utils/shared-iterator.js +5 -0
  134. package/src/utils/streaming.js +14 -4
  135. package/src/utils/url.js +20 -2
  136. package/src/version.js +1 -1
  137. package/types/index.d.ts +537 -557
  138. package/types/index.d.ts.map +24 -39
  139. package/src/core/sync/write_root.js +0 -148
  140. package/src/exports/internal/server.js +0 -22
  141. package/src/runtime/server/remote.js +0 -457
  142. package/src/runtime/shared-server.js +0 -7
  143. package/src/types/synthetic/$lib.md +0 -5
  144. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
@@ -1,7 +1,8 @@
1
- /** @import { EnvVarConfig } from '@sveltejs/kit' */
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 { Plugin, ResolvedConfig, UserConfig, ViteDevServer } from 'vite' */
4
+ /** @import { BuildData, ManifestData, Prerendered, ServerMetadata, RemoteInternals, ValidatedConfig, ValidatedKitConfig } from 'types' */
5
+ /** @import { Manifest, Plugin, ResolvedConfig, Rolldown, UserConfig, ViteDevServer } from 'vite' */
5
6
  import fs from 'node:fs';
6
7
  import path from 'node:path';
7
8
  import process from 'node:process';
@@ -15,30 +16,37 @@ import {
15
16
  create_sveltekit_env,
16
17
  create_sveltekit_env_public,
17
18
  resolve_explicit_env_entry,
19
+ create_sveltekit_env_service_worker,
18
20
  create_sveltekit_env_service_worker_dev,
19
- create_sveltekit_env_private
21
+ create_sveltekit_env_private,
22
+ create_exported_declarations
20
23
  } from '../../core/env.js';
21
24
  import * as sync from '../../core/sync/sync.js';
22
25
  import { create_assets } from '../../core/sync/create_manifest_data/index.js';
26
+ import { load_and_validate_params } from '../../utils/params.js';
23
27
  import { runtime_directory, logger } from '../../core/utils.js';
24
28
  import { generate_manifest } from '../../core/generate_manifest/index.js';
25
29
  import { build_server_nodes } from './build/build_server.js';
26
- import { assets_base, find_deps, resolve_symlinks } from './build/utils.js';
30
+ import { find_deps, resolve_symlinks } from './build/utils.js';
27
31
  import { dev } from './dev/index.js';
28
32
  import { preview } from './preview/index.js';
29
33
  import {
30
34
  error_for_missing_config,
31
35
  get_config_aliases,
32
36
  normalize_id,
33
- stackless,
37
+ remote_module_pattern,
38
+ server_only_directory_pattern,
39
+ server_only_module_pattern,
34
40
  strip_virtual_prefix
35
41
  } from './utils.js';
42
+ import { stackless } from '../../utils/error.js';
36
43
  import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
37
44
  import prerender from '../../core/postbuild/prerender.js';
38
45
  import analyse from '../../core/postbuild/analyse.js';
39
46
  import { s } from '../../utils/misc.js';
40
47
  import { hash } from '../../utils/hash.js';
41
48
  import { dedent } from '../../core/sync/utils.js';
49
+ import { get_import_aliases, get_hash_import_keys } from '../../utils/imports.js';
42
50
  import {
43
51
  app_env_private,
44
52
  app_server,
@@ -46,17 +54,20 @@ import {
46
54
  sveltekit_env,
47
55
  sveltekit_env_private,
48
56
  sveltekit_env_service_worker,
49
- sveltekit_server,
50
57
  sveltekit_env_public_client,
51
58
  sveltekit_env_public_server
52
59
  } from './module_ids.js';
53
60
  import { import_peer } from '../../utils/import.js';
54
61
  import { compact } from '../../utils/array.js';
55
62
  import { should_ignore, has_children } from './static_analysis/utils.js';
56
- import { process_config } from '../../core/config/index.js';
63
+ import { process_config, split_config, validate_config } from '../../core/config/index.js';
57
64
  import { treeshake_prerendered_remotes } from './build/remote.js';
58
65
 
59
- /** @type {string} */
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
+ */
60
71
  let root;
61
72
 
62
73
  /** @type {import('./types.js').EnforcedConfig} */
@@ -89,7 +100,7 @@ const enforced_config = {
89
100
  resolve: {
90
101
  alias: {
91
102
  $app: true,
92
- $lib: true,
103
+ $env: true,
93
104
  '$service-worker': true
94
105
  }
95
106
  }
@@ -146,65 +157,73 @@ let vite_plugin_svelte;
146
157
 
147
158
  /**
148
159
  * Returns the SvelteKit Vite plugins.
149
- * Since version 2.62.0 you can pass [configuration](configuration) directly, in which case `svelte.config.js` is ignored.
150
- * @param {import('@sveltejs/kit').KitConfig & Omit<SvelteConfig, 'onwarn'>} [config]
160
+ * Any options that don't belong to SvelteKit are passed through to `vite-plugin-svelte`.
161
+ *
162
+ * Since version 3.0.0 you must pass [configuration](configuration) directly.
163
+ *
164
+ * Since version 2.62.0 you can pass configuration directly, in which case `svelte.config.js` is ignored.
165
+ *
166
+ * @param {KitConfig & Omit<Options, 'onwarn'> & Pick<SvelteConfig, 'vitePlugin'>} [config]
151
167
  * @returns {Promise<Plugin[]>}
152
168
  */
153
169
  export async function sveltekit(config) {
154
170
  const cwd = process.cwd();
155
171
 
156
- const { extensions, compilerOptions, vitePlugin, preprocess, ...rest } = config ?? {};
157
- const svelte_config = process_config(
158
- { extensions, compilerOptions, vitePlugin, preprocess, kit: rest },
159
- { cwd, source: 'SvelteKit options from Vite config' }
160
- );
161
-
162
- const config_file = ['svelte.config.js', 'svelte.config.ts'].find((file) => fs.existsSync(file));
163
-
164
- if (config_file) {
165
- const message = `${config_file} is no longer used. Please pass configuration via the \`sveltekit(...)\` plugin in your Vite config.`;
166
-
167
- if (!config) {
168
- throw new Error(message);
169
- }
170
-
171
- console.warn(message);
172
+ // any options passed to the plugin that SvelteKit doesn't use itself are
173
+ // forwarded to vite-plugin-svelte, which does its own validation
174
+ const split = split_config(config ?? {});
175
+ const svelte_config = validate_config(split.svelte_config);
176
+
177
+ if (Array.isArray(svelte_config.preprocess)) {
178
+ svelte_config.preprocess.push(warning_preprocessor);
179
+ } else if (svelte_config.preprocess) {
180
+ svelte_config.preprocess = [svelte_config.preprocess, warning_preprocessor];
181
+ } else {
182
+ svelte_config.preprocess = warning_preprocessor;
172
183
  }
173
184
 
174
- /** @type {Options} */
175
- const vite_plugin_svelte_options = {
176
- // we don't want vite-plugin-svelte to load the config file itself because
177
- // it will try to validate it without knowing that kit options are valid
185
+ vite_plugin_svelte = await import_peer('@sveltejs/vite-plugin-svelte', cwd);
186
+
187
+ /** @type {Partial<Options>} */
188
+ const inline_vps_config = {
189
+ preprocess: svelte_config.preprocess,
190
+ ...(svelte_config.vitePlugin ?? {}),
191
+ // pass through any options that SvelteKit doesn't use itself, so that
192
+ // the options SvelteKit manages always take precedence
193
+ ...split.vite_plugin_svelte_config,
194
+ // we don't want vite-plugin-svelte to load the svelte.config.js file because
195
+ // we expect options to be passed through the SvelteKit Vite plugin
178
196
  configFile: false
179
197
  };
180
198
 
181
- vite_plugin_svelte = await import_peer('@sveltejs/vite-plugin-svelte', cwd);
199
+ // vite-plugin-svelte inline config options need to be added conditionally
200
+ // because passing undefined causes it to crash
201
+ if (svelte_config.extensions) {
202
+ inline_vps_config.extensions = svelte_config.extensions;
203
+ }
204
+
205
+ if (svelte_config.compilerOptions) {
206
+ inline_vps_config.compilerOptions = svelte_config.compilerOptions;
207
+ }
182
208
 
183
209
  return [
184
- plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }),
185
- ...vite_plugin_svelte.svelte(vite_plugin_svelte_options),
210
+ plugin_root(),
211
+ ...vite_plugin_svelte.svelte(inline_vps_config),
186
212
  ...kit({
187
- svelte_config,
188
- adapter: svelte_config.kit.adapter
213
+ svelte_config
189
214
  })
190
215
  ];
191
216
  }
192
217
 
193
- /** @param {import('vite').UserConfig | import('vite').ResolvedConfig} vite_config */
218
+ /** @param {UserConfig | ResolvedConfig} vite_config */
194
219
  function resolve_root(vite_config) {
195
220
  return posixify(vite_config.root ? path.resolve(vite_config.root) : process.cwd());
196
221
  }
197
222
 
198
223
  /**
199
- * Resolves the Svelte config using the `vite.config.root` setting before any
200
- * of our other plugins try to access the config objects
201
- * @param {{
202
- * vite_plugin_svelte_options: import('@sveltejs/vite-plugin-svelte').Options;
203
- * svelte_config: import('types').ValidatedConfig;
204
- * }} options
205
- * @return {import('vite').Plugin}
224
+ * @return {Plugin}
206
225
  */
207
- function plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }) {
226
+ function plugin_root() {
208
227
  return {
209
228
  name: 'vite-plugin-sveltekit-resolve-svelte-config',
210
229
  // make sure it runs first
@@ -214,21 +233,14 @@ function plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }) {
214
233
  handler(config) {
215
234
  root = resolve_root(config);
216
235
 
217
- /** @type {import('@sveltejs/vite-plugin-svelte').Options['preprocess']} */
218
- let preprocess = svelte_config.preprocess;
219
- if (Array.isArray(preprocess)) {
220
- preprocess = [...preprocess, warning_preprocessor];
221
- } else if (preprocess) {
222
- preprocess = [preprocess, warning_preprocessor];
223
- } else {
224
- preprocess = warning_preprocessor;
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
+ );
225
243
  }
226
-
227
- vite_plugin_svelte_options.extensions = svelte_config.extensions;
228
- vite_plugin_svelte_options.preprocess = preprocess;
229
- vite_plugin_svelte_options.onwarn = svelte_config.onwarn;
230
- vite_plugin_svelte_options.compilerOptions = { ...svelte_config.compilerOptions };
231
- Object.assign(vite_plugin_svelte_options, svelte_config.vitePlugin);
232
244
  }
233
245
  },
234
246
  // TODO: do we even need to set `root` based on the final Vite config?
@@ -252,15 +264,14 @@ function plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }) {
252
264
  * - https://rolldown.rs/apis/plugin-api#output-generation-hooks
253
265
  *
254
266
  * @param {object} opts
255
- * @param {import('types').ValidatedConfig} opts.svelte_config options are only resolved after the Vite `config` hook runs
256
- * @param {import('@sveltejs/kit').Adapter | undefined} opts.adapter
257
- * @return {import('vite').Plugin[]}
267
+ * @param {ValidatedConfig} opts.svelte_config
268
+ * @return {Plugin[]}
258
269
  */
259
- function kit({ svelte_config, adapter }) {
270
+ function kit({ svelte_config }) {
260
271
  /** @type {typeof import('vite')} */
261
272
  let vite;
262
273
 
263
- /** @type {import('types').ValidatedKitConfig} */
274
+ /** @type {ValidatedKitConfig} */
264
275
  let kit;
265
276
  /** @type {string} `kit.outDir` but posix-ified */
266
277
  let out_dir;
@@ -279,26 +290,30 @@ function kit({ svelte_config, adapter }) {
279
290
  /** @type {Record<string, string>} */
280
291
  let env;
281
292
 
282
- /** @type {import('types').ManifestData} */
293
+ /** @type {ManifestData} */
283
294
  let manifest_data;
284
295
 
285
- /** @type {import('types').ServerMetadata | undefined} only set at build time once analysis is finished */
296
+ /** @type {ServerMetadata | undefined} only set at build time once analysis is finished */
286
297
  let build_metadata = undefined;
287
298
 
288
299
  /** @type {UserConfig} */
289
300
  let initial_config;
290
301
 
291
302
  /** @type {string | null} */
292
- let service_worker_entry_file = resolve_entry(svelte_config.kit.files.serviceWorker);
303
+ let service_worker_entry_file;
293
304
  /** @type {import('node:path').ParsedPath} */
294
305
  let parsed_service_worker;
295
306
 
296
307
  /** @type {string} */
297
308
  let normalized_cwd;
298
- /** @type {string} */
299
- let normalized_lib;
309
+ /** @type {Array<{ alias: string, path: string }>} */
310
+ let normalized_aliases;
300
311
  /** @type {string} */
301
312
  let normalized_node_modules;
313
+ /** @type {string} */
314
+ let normalized_routes;
315
+ /** @type {string} */
316
+ let normalized_assets;
302
317
  /**
303
318
  * A map showing which features (such as `$app/server:read`) are defined
304
319
  * in which chunks, so that we can later determine which routes use which features
@@ -309,6 +324,9 @@ function kit({ svelte_config, adapter }) {
309
324
  const sourcemapIgnoreList = /** @param {string} relative_path */ (relative_path) =>
310
325
  relative_path.includes('node_modules') || relative_path.includes(kit.outDir);
311
326
 
327
+ /** @type {string} name for `globalThis.__sveltekit_xxx` */
328
+ let kit_global;
329
+
312
330
  /** @type {Plugin} */
313
331
  const plugin_setup = {
314
332
  name: 'vite-plugin-sveltekit-setup',
@@ -316,10 +334,6 @@ function kit({ svelte_config, adapter }) {
316
334
  options: svelte_config
317
335
  },
318
336
 
319
- applyToEnvironment(environment) {
320
- return environment.name !== 'serviceWorker';
321
- },
322
-
323
337
  /**
324
338
  * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
325
339
  * @see https://vitejs.dev/guide/api-plugin.html#config
@@ -330,32 +344,56 @@ function kit({ svelte_config, adapter }) {
330
344
  initial_config = config;
331
345
  is_build = config_env.command === 'build';
332
346
 
333
- ({ kit } = svelte_config);
347
+ ({ kit } = process_config(svelte_config, root));
334
348
  out_dir = posixify(kit.outDir);
335
349
  out = `${out_dir}/output`;
336
350
 
337
351
  version_hash = hash(kit.version.name);
338
352
 
339
- env = loadEnv(config_env.mode, kit.env.dir, '');
353
+ kit_global = is_build
354
+ ? `globalThis.__sveltekit_${version_hash}`
355
+ : 'globalThis.__sveltekit_dev';
340
356
 
341
- service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
342
- parsed_service_worker = path.parse(kit.files.serviceWorker);
357
+ env = loadEnv(config_env.mode, kit.env.dir, '');
343
358
 
344
359
  vite = await import_peer('vite', root);
345
360
 
346
361
  normalized_cwd = vite.normalizePath(root);
347
- normalized_lib = vite.normalizePath(kit.files.lib);
362
+ normalized_aliases = get_import_aliases(root, vite.normalizePath.bind(vite));
348
363
  normalized_node_modules = vite.normalizePath(path.resolve(root, 'node_modules'));
364
+ normalized_routes = vite.normalizePath(path.resolve(root, kit.files.routes));
365
+ normalized_assets = vite.normalizePath(path.resolve(root, kit.files.assets));
366
+
367
+ // Add `#`-prefixed import keys to the enforced config so users are warned
368
+ // if they try to set them in their Vite config's resolve.alias
369
+ const enforced_alias = /** @type {Record<string, true>} */ (
370
+ /** @type {any} */ (enforced_config.resolve).alias
371
+ );
372
+ for (const key of get_hash_import_keys(root)) {
373
+ enforced_alias[key] = true;
374
+ }
349
375
 
350
376
  const allow = new Set([
351
- kit.files.lib,
352
377
  kit.files.routes,
378
+ kit.files.src,
353
379
  kit.outDir,
354
380
  path.resolve(root, kit.files.src),
355
381
  path.resolve(root, 'node_modules'),
356
- path.resolve(process.cwd(), 'node_modules')
382
+ // ensures that the client entry is served even if it's located outside
383
+ // the local node_modules, such as the pnpm global virtual store
384
+ runtime_directory,
385
+ path.resolve('node_modules'),
386
+ // include the directory that contains the workspaces declaration
387
+ // which usually also contains hoisted packages
388
+ // see https://vite.dev/guide/api-javascript#searchforworkspaceroot
389
+ path.resolve(vite.searchForWorkspaceRoot(process.cwd()), 'node_modules')
357
390
  ]);
358
391
 
392
+ // Add directories from `#`-prefixed package.json imports to the allow list
393
+ for (const { path: alias_path } of normalized_aliases) {
394
+ allow.add(alias_path);
395
+ }
396
+
359
397
  // We can only add directories to the allow list, so we find out
360
398
  // if there's a client hooks file and pass its directory
361
399
  const client_hooks = resolve_entry(kit.files.hooks.client);
@@ -366,10 +404,23 @@ function kit({ svelte_config, adapter }) {
366
404
  // dev and preview config can be shared
367
405
  /** @type {UserConfig} */
368
406
  const new_config = {
407
+ environments: {
408
+ ssr: {
409
+ build: {
410
+ sourcemap:
411
+ config.environments?.ssr?.build?.sourcemap ?? config.build?.sourcemap ?? true
412
+ }
413
+ }
414
+ },
369
415
  resolve: {
370
416
  alias: [
371
417
  { find: '__SERVER__', replacement: `${generated}/server` },
372
418
  { find: '$app', replacement: `${runtime_directory}/app` },
419
+ { find: '$env', replacement: `${runtime_directory}/env` },
420
+ {
421
+ find: '__sveltekit/server',
422
+ replacement: `${runtime_directory}/server/internal.js`
423
+ },
373
424
  ...get_config_aliases(kit, root)
374
425
  ]
375
426
  },
@@ -381,7 +432,7 @@ function kit({ svelte_config, adapter }) {
381
432
  sourcemapIgnoreList,
382
433
  watch: {
383
434
  ignored: [
384
- // Ignore all siblings of config.kit.outDir/generated
435
+ // Ignore all siblings of config.outDir/generated
385
436
  `${out_dir}/!(generated)`
386
437
  ]
387
438
  }
@@ -421,11 +472,8 @@ function kit({ svelte_config, adapter }) {
421
472
  }
422
473
  };
423
474
 
475
+ // treat .remote.js files as empty for the purposes of prebundling
424
476
  if (kit.experimental.remoteFunctions) {
425
- // treat .remote.js files as empty for the purposes of prebundling
426
- const remote_id_filter = new RegExp(
427
- `.remote(${kit.moduleExtensions.join('|')})$`.replaceAll('.', '\\.')
428
- );
429
477
  // @ts-expect-error optimizeDeps is already set above
430
478
  new_config.optimizeDeps.rolldownOptions ??= {};
431
479
  // @ts-expect-error
@@ -434,7 +482,7 @@ function kit({ svelte_config, adapter }) {
434
482
  new_config.optimizeDeps.rolldownOptions.plugins.push({
435
483
  name: 'vite-plugin-sveltekit-setup:optimize-remote-functions',
436
484
  load: {
437
- filter: { id: remote_id_filter },
485
+ filter: { id: remote_module_pattern },
438
486
  handler() {
439
487
  return '';
440
488
  }
@@ -443,7 +491,7 @@ function kit({ svelte_config, adapter }) {
443
491
  }
444
492
 
445
493
  const define = {
446
- __SVELTEKIT_APP_DIR__: s(kit.appDir),
494
+ __SVELTEKIT_APP_DIR__: s(posixify(kit.appDir)),
447
495
  __SVELTEKIT_APP_VERSION__: s(kit.version.name),
448
496
  __SVELTEKIT_EMBEDDED__: s(kit.embedded),
449
497
  __SVELTEKIT_FORK_PRELOADS__: s(kit.experimental.forkPreloads),
@@ -452,16 +500,17 @@ function kit({ svelte_config, adapter }) {
452
500
  __SVELTEKIT_PATHS_RELATIVE__: s(kit.paths.relative),
453
501
  __SVELTEKIT_CLIENT_ROUTING__: s(kit.router.resolution === 'client'),
454
502
  __SVELTEKIT_HASH_ROUTING__: s(kit.router.type === 'hash'),
455
- __SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.experimental.tracing.server),
456
- __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__: s(kit.experimental.handleRenderingErrors),
457
- __SVELTEKIT_ROOT__: s(root),
503
+ __SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.tracing.server),
504
+ __SVELTEKIT_SUPPORTS_ASYNC__: s(
505
+ svelte_config.compilerOptions?.experimental?.async ?? false
506
+ ),
458
507
  __SVELTEKIT_DEV__: s(!is_build)
459
508
  };
460
509
 
461
510
  if (is_build) {
462
511
  new_config.define = {
463
512
  ...define,
464
- __SVELTEKIT_ADAPTER_NAME__: s(adapter?.name),
513
+ __SVELTEKIT_ADAPTER_NAME__: s(kit.adapter?.name),
465
514
  __SVELTEKIT_APP_VERSION_FILE__: s(`${kit.appDir}/version.json`),
466
515
  __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: s(kit.version.pollInterval)
467
516
  };
@@ -471,16 +520,31 @@ function kit({ svelte_config, adapter }) {
471
520
  new_config.define = {
472
521
  ...define,
473
522
  __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: '0',
474
- __SVELTEKIT_PAYLOAD__: 'globalThis.__sveltekit_dev',
523
+ __SVELTEKIT_PAYLOAD__: kit_global, // only relevant when bundleStrategy !== 'split'
475
524
  __SVELTEKIT_HAS_SERVER_LOAD__: 'true',
476
525
  __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true'
477
526
  };
478
527
 
479
528
  // These Kit dependencies are packaged as CommonJS, which means they must always be externalized.
480
529
  // Without this, the tests will still pass but `pnpm dev` will fail in projects that link `@sveltejs/kit`.
481
- /** @type {NonNullable<import('vite').UserConfig['ssr']>} */ (new_config.ssr).external = [
482
- 'cookie'
483
- ];
530
+ /** @type {NonNullable<UserConfig['ssr']>} */ (new_config.ssr).external = ['cookie'];
531
+ }
532
+
533
+ // Vite's `define` is a compile-time text replacement, but Vitest strips
534
+ // user `define` from the server config and reinstalls the values only as
535
+ // `globalThis` properties inside test workers, so anything
536
+ // that runs outside of a test will freak out over
537
+ // them not being defined
538
+ if (process.env.VITEST === 'true') {
539
+ for (const key in new_config.define) {
540
+ const value = new_config.define[key];
541
+ try {
542
+ /** @type {Record<string, any>} */ (globalThis)[key] = JSON.parse(value);
543
+ } catch {
544
+ // `kit_global` isn't JSON, so don't try to parse it. We may one day
545
+ // need to define it in Vitest somehow but for now, ignore it
546
+ }
547
+ }
484
548
  }
485
549
 
486
550
  warn_overridden_config(config, new_config);
@@ -494,6 +558,24 @@ function kit({ svelte_config, adapter }) {
494
558
  */
495
559
  configResolved(config) {
496
560
  vite_config = config;
561
+
562
+ const unsupported_plugins = vite_config.plugins.filter((plugin) => plugin.transformIndexHtml);
563
+ if (unsupported_plugins.length) {
564
+ const verbose = vite_config.logLevel === 'info';
565
+ const log = logger({ verbose });
566
+
567
+ const list = unsupported_plugins
568
+ .map((plugin) => ` - ${plugin.name || '(missing plugin name)'}`)
569
+ .join('\n');
570
+
571
+ log.warn(
572
+ dedent`
573
+ The following plugins may not work correctly because they use the \`transformIndexHtml\` hook which is not supported:
574
+
575
+ ${list}
576
+ `
577
+ );
578
+ }
497
579
  }
498
580
  };
499
581
 
@@ -507,10 +589,6 @@ function kit({ svelte_config, adapter }) {
507
589
  const plugin_virtual_modules = {
508
590
  name: 'vite-plugin-sveltekit-virtual-modules',
509
591
 
510
- applyToEnvironment(environment) {
511
- return environment.name !== 'serviceWorker';
512
- },
513
-
514
592
  async configResolved(config) {
515
593
  explicit_env_entry = resolve_explicit_env_entry(kit);
516
594
  explicit_env_config = await sync.env(kit, explicit_env_entry, config.root, config.mode);
@@ -546,52 +624,32 @@ function kit({ svelte_config, adapter }) {
546
624
  });
547
625
  },
548
626
 
549
- resolveId(id, importer) {
550
- if (id === '__sveltekit/manifest') {
551
- return `${out_dir}/generated/client-optimized/app.js`;
552
- }
553
-
554
- // If importing from a service-worker, only allow $service-worker & $app/env/public, but none of the other virtual modules.
555
- // This check won't catch transitive imports, but it will warn when the import comes from a service-worker directly.
556
- // Transitive imports will be caught during the build.
557
- // TODO move this logic to plugin_guard. add a filter to this resolveId when doing so
558
- // TODO allow $app/env/public
559
- if (importer) {
560
- const parsed_importer = path.parse(importer);
561
-
562
- const importer_is_service_worker =
563
- parsed_importer.dir === parsed_service_worker.dir &&
564
- parsed_importer.name === parsed_service_worker.name;
627
+ applyToEnvironment(environment) {
628
+ return environment.name !== 'serviceWorker';
629
+ },
565
630
 
566
- if (
567
- importer_is_service_worker &&
568
- id !== '$service-worker' &&
569
- id !== 'virtual:$app/env/public' &&
570
- id !== '__sveltekit/env/service-worker'
571
- ) {
572
- throw new Error(
573
- `Cannot import ${normalize_id(
574
- id,
575
- normalized_lib,
576
- normalized_cwd
577
- )} into service-worker code. Only the modules $service-worker and $app/env/public are available in service workers.`
578
- );
631
+ resolveId: {
632
+ filter: {
633
+ id: [exactRegex('$service-worker'), prefixRegex('__sveltekit/')]
634
+ },
635
+ handler(id) {
636
+ if (id === '__sveltekit/manifest') {
637
+ return `${out_dir}/generated/client-optimized/app.js`;
579
638
  }
580
- }
581
639
 
582
- if (id === '$service-worker') {
583
- // ids with :$ don't work with reverse proxies like nginx
584
- return `\0virtual:${id.substring(1)}`;
585
- }
640
+ if (id === '$service-worker') {
641
+ // ids with :$ don't work with reverse proxies like nginx
642
+ return `\0virtual:${id.substring(1)}`;
643
+ }
586
644
 
587
- if (id === '__sveltekit/remote') {
588
- return `${runtime_directory}/client/remote-functions/index.js`;
589
- }
645
+ if (id === '__sveltekit/remote') {
646
+ return `${runtime_directory}/client/remote-functions/index.js`;
647
+ }
590
648
 
591
- if (id.startsWith('__sveltekit/')) {
592
649
  return `\0virtual:${id}`;
593
650
  }
594
651
  },
652
+
595
653
  load: {
596
654
  filter: {
597
655
  id: [
@@ -600,27 +658,22 @@ function kit({ svelte_config, adapter }) {
600
658
  exactRegex(sveltekit_env_private),
601
659
  exactRegex(sveltekit_env_public_client),
602
660
  exactRegex(sveltekit_env_public_server),
603
- exactRegex(sveltekit_env_service_worker),
604
- exactRegex(sveltekit_server)
661
+ exactRegex(sveltekit_env_service_worker)
605
662
  ]
606
663
  },
607
664
  handler(id) {
608
- const global = is_build
609
- ? `globalThis.__sveltekit_${version_hash}`
610
- : 'globalThis.__sveltekit_dev';
611
-
612
665
  switch (id) {
613
666
  case service_worker:
614
667
  return create_service_worker_module(svelte_config);
615
668
 
616
669
  case sveltekit_env:
617
- return create_sveltekit_env(explicit_env_config, env, explicit_env_entry);
670
+ return create_sveltekit_env(explicit_env_config, env, explicit_env_entry, !is_build);
618
671
 
619
672
  case sveltekit_env_public_client:
620
673
  return create_sveltekit_env_public(
621
674
  explicit_env_config,
622
675
  env,
623
- `const env = ${global}.env;`
676
+ `import { payload } from ${s(`${runtime_directory}/client/payload.js`)};\nconst env = payload.env;`
624
677
  );
625
678
 
626
679
  case sveltekit_env_public_server:
@@ -634,23 +687,15 @@ function kit({ svelte_config, adapter }) {
634
687
  return create_sveltekit_env_private(explicit_env_config, env);
635
688
 
636
689
  case sveltekit_env_service_worker:
637
- return create_sveltekit_env_service_worker_dev(explicit_env_config, env, global);
638
-
639
- case sveltekit_server: {
640
- return dedent`
641
- export let read_implementation = null;
642
-
643
- export let manifest = null;
644
-
645
- export function set_read_implementation(fn) {
646
- read_implementation = fn;
647
- }
648
-
649
- export function set_manifest(_) {
650
- manifest = _;
651
- }
652
- `;
653
- }
690
+ return is_build
691
+ ? create_sveltekit_env_service_worker(
692
+ explicit_env_config,
693
+ env,
694
+ kit_global,
695
+ kit.paths.base,
696
+ kit.appDir
697
+ )
698
+ : create_sveltekit_env_service_worker_dev(explicit_env_config, env, kit_global);
654
699
  }
655
700
  }
656
701
  }
@@ -658,11 +703,10 @@ function kit({ svelte_config, adapter }) {
658
703
 
659
704
  /** @type {Map<string, Set<string>>} */
660
705
  const import_map = new Map();
661
- const server_only_pattern = /.*\.server\..+/;
662
706
 
663
707
  /**
664
708
  * Ensures that client-side code can't accidentally import server-side code,
665
- * whether in `*.server.js` files, `$app/server`, `$lib/server`, or `$app/env/private`
709
+ * whether in `*.server.js` files, `$app/server`, any `/server/` directory, or `$app/env/private`
666
710
  * @type {Plugin}
667
711
  */
668
712
  const plugin_guard = {
@@ -673,7 +717,8 @@ function kit({ svelte_config, adapter }) {
673
717
  enforce: 'pre',
674
718
 
675
719
  applyToEnvironment(environment) {
676
- return environment.name !== 'serviceWorker';
720
+ // 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';
677
722
  },
678
723
 
679
724
  resolveId: {
@@ -688,7 +733,7 @@ function kit({ svelte_config, adapter }) {
688
733
  const resolved = await this.resolve(id, importer, { ...options, skipSelf: true });
689
734
 
690
735
  if (resolved) {
691
- const normalized = normalize_id(resolved.id, normalized_lib, normalized_cwd);
736
+ const normalized = normalize_id(resolved.id, normalized_aliases, normalized_cwd);
692
737
 
693
738
  let importers = import_map.get(normalized);
694
739
 
@@ -697,7 +742,7 @@ function kit({ svelte_config, adapter }) {
697
742
  import_map.set(normalized, importers);
698
743
  }
699
744
 
700
- importers.add(normalize_id(importer, normalized_lib, normalized_cwd));
745
+ importers.add(normalize_id(importer, normalized_aliases, normalized_cwd));
701
746
  }
702
747
  }
703
748
  }
@@ -708,31 +753,29 @@ function kit({ svelte_config, adapter }) {
708
753
  id: [
709
754
  exactRegex(app_server),
710
755
  exactRegex(app_env_private),
711
- /\/server\//,
712
- new RegExp(`${server_only_pattern.source}$`)
756
+ server_only_module_pattern,
757
+ server_only_directory_pattern
713
758
  ]
714
759
  },
715
760
  handler(id) {
716
- if (this.environment.config.consumer !== 'client') return;
717
-
718
- // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context
719
- const is_internal =
720
- id.startsWith(normalized_cwd) && !id.startsWith(normalized_node_modules);
721
-
722
- const normalized = normalize_id(id, normalized_lib, normalized_cwd);
761
+ const normalized = normalize_id(id, normalized_aliases, normalized_cwd);
723
762
 
724
- const is_server_only =
725
- normalized === '$app/env/private' ||
726
- normalized === '$app/server' ||
727
- normalized.startsWith('$lib/server/') ||
728
- (is_internal && server_only_pattern.test(path.basename(id)));
763
+ let is_server_only = normalized === '$app/env/private' || normalized === '$app/server';
729
764
 
730
765
  // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context
731
- // TODO: address https://github.com/sveltejs/kit/issues/12529
732
- if (!is_server_only) {
733
- return;
766
+ if (id.startsWith(normalized_cwd) && !id.startsWith(normalized_node_modules)) {
767
+ // e.g. `server.ts` or `foo.server.ts`
768
+ is_server_only ||= server_only_module_pattern.test(id);
769
+
770
+ // e.g. `server/foo.ts`, unless in `src/routes` or `static`
771
+ is_server_only ||=
772
+ server_only_directory_pattern.test(id) &&
773
+ !id.startsWith(normalized_routes + '/') &&
774
+ !id.startsWith(normalized_assets + '/');
734
775
  }
735
776
 
777
+ if (!is_server_only) return;
778
+
736
779
  // in dev, this doesn't exist, so we need to create it
737
780
  manifest_data ??= sync.all(svelte_config, root).manifest_data;
738
781
 
@@ -746,49 +789,60 @@ function kit({ svelte_config, adapter }) {
746
789
  if (manifest_data.hooks.client) entrypoints.add(manifest_data.hooks.client);
747
790
  if (manifest_data.hooks.universal) entrypoints.add(manifest_data.hooks.universal);
748
791
 
749
- const chain = [normalized];
750
-
751
- let current = normalized;
752
- let includes_remote_file = false;
753
-
754
- while (true) {
792
+ // Walk up the import graph from the server-only module, looking for a chain
793
+ // that leads back to a client entrypoint. We search all candidates (not just
794
+ // the first) because a module can be imported by both server and client code,
795
+ // and a greedy first-match could follow a server-only branch that never
796
+ // reaches an entrypoint — see https://github.com/sveltejs/kit/issues/16232
797
+ /** @type {Set<string>} */
798
+ const visited = new Set([normalized]);
799
+
800
+ /**
801
+ * @param {string} current
802
+ * @param {string[]} chain
803
+ * @returns {string[] | null}
804
+ */
805
+ function find_chain(current, chain) {
755
806
  const importers = import_map.get(current);
756
- if (!importers) break;
757
-
758
- const candidates = Array.from(importers).filter((importer) => !chain.includes(importer));
759
- if (candidates.length === 0) break;
807
+ if (!importers) return null;
760
808
 
761
- chain.push((current = candidates[0]));
809
+ for (const importer of importers) {
810
+ if (visited.has(importer)) continue;
811
+ visited.add(importer);
762
812
 
763
- includes_remote_file ||= svelte_config.kit.moduleExtensions.some((ext) => {
764
- return current.endsWith(`.remote${ext}`);
765
- });
766
-
767
- if (entrypoints.has(current)) {
768
- const pyramid = chain
769
- .reverse()
770
- .map((id, i) => {
771
- return `${' '.repeat(i + 1)}${id}`;
772
- })
773
- .join(' imports\n');
774
-
775
- if (includes_remote_file) {
776
- error_for_missing_config(
777
- 'remote functions',
778
- 'kit.experimental.remoteFunctions',
779
- 'true'
780
- );
813
+ const next_chain = [...chain, importer];
814
+ if (entrypoints.has(importer)) {
815
+ return next_chain;
781
816
  }
817
+ const result = find_chain(importer, next_chain);
818
+ if (result) return result;
819
+ }
820
+ return null;
821
+ }
782
822
 
783
- let message = `Cannot import ${normalized} into code that runs in the browser, as this could leak sensitive information.`;
784
- message += `\n\n${pyramid}`;
785
- message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`;
823
+ const chain = find_chain(normalized, [normalized]);
786
824
 
787
- throw stackless(message);
825
+ if (chain) {
826
+ if (chain.some((id) => remote_module_pattern.test(id))) {
827
+ error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
788
828
  }
829
+
830
+ const pyramid = chain
831
+ .reverse()
832
+ .map((id, i) => {
833
+ return `${' '.repeat(i + 1)}${id}`;
834
+ })
835
+ .join(' imports\n');
836
+
837
+ let message = `Cannot import ${normalized} into code that runs in the browser, as this could leak sensitive information.`;
838
+ message += `\n\n${pyramid}`;
839
+ message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`;
840
+
841
+ throw stackless(message);
789
842
  }
790
843
 
791
- throw new Error('An impossible situation occurred');
844
+ // No chain from this server-only module to a client entrypoint was found —
845
+ // the module is only imported from server code, which is valid.
792
846
  }
793
847
  }
794
848
  };
@@ -810,7 +864,7 @@ function kit({ svelte_config, adapter }) {
810
864
  name: 'vite-plugin-sveltekit-remote',
811
865
 
812
866
  applyToEnvironment(environment) {
813
- return environment.name !== 'serviceWorker';
867
+ return svelte_config.kit.experimental.remoteFunctions && environment.name !== 'serviceWorker';
814
868
  },
815
869
 
816
870
  // prevent other plugins from resolving our remote virtual module
@@ -828,10 +882,6 @@ function kit({ svelte_config, adapter }) {
828
882
  id: prefixRegex('\0sveltekit-remote:')
829
883
  },
830
884
  handler(id) {
831
- if (!kit.experimental.remoteFunctions) {
832
- return null;
833
- }
834
-
835
885
  // On-the-fly generated entry point for remote file just forwards the original module
836
886
  // We're not using manualChunks because it can cause problems with circular dependencies
837
887
  // (e.g. https://github.com/sveltejs/kit/issues/14679) and module ordering in general
@@ -844,44 +894,35 @@ function kit({ svelte_config, adapter }) {
844
894
  },
845
895
 
846
896
  configureServer(_dev_server) {
847
- if (!kit.experimental.remoteFunctions) {
848
- return;
849
- }
850
-
851
897
  dev_server = _dev_server;
852
898
  },
853
899
 
854
- async transform(code, id) {
855
- if (!kit.experimental.remoteFunctions) {
856
- return;
857
- }
858
-
859
- const normalized = normalize_id(id, normalized_lib, normalized_cwd);
860
- if (!svelte_config.kit.moduleExtensions.some((ext) => normalized.endsWith(`.remote${ext}`))) {
861
- return;
862
- }
863
-
864
- const file = posixify(path.relative(root, id));
865
- const remote = {
866
- hash: hash(file),
867
- file
868
- };
900
+ transform: {
901
+ filter: {
902
+ id: remote_module_pattern
903
+ },
904
+ async handler(code, id) {
905
+ const file = posixify(path.relative(root, id));
906
+ const remote = {
907
+ hash: hash(file),
908
+ file
909
+ };
869
910
 
870
- remotes.push(remote);
911
+ remotes.push(remote);
871
912
 
872
- if (this.environment.config.consumer !== 'client') {
873
- // we need to add an `await Promise.resolve()` because if the user imports this function
874
- // on the client AND in a load function when loading the client module we will trigger
875
- // an ssrLoadModule during dev. During a link preload, the module can be mistakenly
876
- // loaded and transformed twice and the first time all its exports would be undefined
877
- // triggering a dev server error. By adding a microtask we ensure that the module is fully loaded
913
+ if (this.environment.config.consumer !== 'client') {
914
+ // we need to add an `await Promise.resolve()` because if the user imports this function
915
+ // 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
917
+ // loaded and transformed twice and the first time all its exports would be undefined
918
+ // triggering a dev server error. By adding a microtask we ensure that the module is fully loaded
878
919
 
879
- // Extra newlines to prevent syntax errors around missing semicolons or comments
880
- code +=
881
- '\n\n' +
882
- dedent`
920
+ // Extra newlines to prevent syntax errors around missing semicolons or comments
921
+ code +=
922
+ '\n\n' +
923
+ dedent`
883
924
  import * as $$_self_$$ from './${path.basename(id)}';
884
- import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal';
925
+ import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal/server';
885
926
 
886
927
  ${dev_server ? 'await Promise.resolve()' : ''}
887
928
 
@@ -893,125 +934,242 @@ function kit({ svelte_config, adapter }) {
893
934
  }
894
935
  `;
895
936
 
896
- // Emit a dedicated entry chunk for this remote in SSR builds (prod only)
897
- if (!dev_server) {
898
- remote_original_by_hash.set(remote.hash, id);
899
- if (!emitted_remote_hashes.has(remote.hash)) {
900
- this.emitFile({
901
- type: 'chunk',
902
- id: `\0sveltekit-remote:${remote.hash}`,
903
- name: `remote-${remote.hash}`
904
- });
905
- emitted_remote_hashes.add(remote.hash);
937
+ // Emit a dedicated entry chunk for this remote in SSR builds (prod only)
938
+ if (!dev_server) {
939
+ remote_original_by_hash.set(remote.hash, id);
940
+ if (!emitted_remote_hashes.has(remote.hash)) {
941
+ this.emitFile({
942
+ type: 'chunk',
943
+ id: `\0sveltekit-remote:${remote.hash}`,
944
+ name: `remote-${remote.hash}`
945
+ });
946
+ emitted_remote_hashes.add(remote.hash);
947
+ }
906
948
  }
907
- }
908
949
 
909
- return code;
910
- }
950
+ return code;
951
+ }
911
952
 
912
- // For the client, read the exports and create a new module that only contains fetch functions with the correct metadata
953
+ // For the client, read the exports and create a new module that only contains fetch functions with the correct metadata
913
954
 
914
- /** @type {Map<string, import('types').RemoteInternals['type']>} */
915
- const map = new Map();
955
+ /** @type {Map<string, RemoteInternals['type']>} */
956
+ const map = new Map();
916
957
 
917
- // in dev, load the server module here (which will result in this hook
918
- // being called again with `opts.ssr === true` if the module isn't
919
- // already loaded) so we can determine what it exports
920
- if (dev_server) {
921
- const module = await dev_server.ssrLoadModule(id);
958
+ // in dev, load the server module here (which will result in this hook
959
+ // being called again with `opts.ssr === true` if the module isn't
960
+ // already loaded) so we can determine what it exports
961
+ if (dev_server) {
962
+ const module = await dev_server.ssrLoadModule(id);
922
963
 
923
- for (const [name, value] of Object.entries(module)) {
924
- const type = value?.__?.type;
925
- if (type) {
926
- map.set(name, type);
964
+ for (const [name, value] of Object.entries(module)) {
965
+ const type = value?.__?.type;
966
+ if (type) {
967
+ map.set(name, type);
968
+ }
927
969
  }
928
970
  }
929
- }
930
971
 
931
- // in prod, we already built and analysed the server code before
932
- // building the client code, so `remotes` is populated
933
- else if (build_metadata?.remotes) {
934
- const exports = build_metadata?.remotes.get(remote.hash);
935
- if (!exports) throw new Error('Expected to find metadata for remote file ' + id);
972
+ // in prod, we already built and analysed the server code before
973
+ // building the client code, so `remotes` is populated
974
+ else if (build_metadata?.remotes) {
975
+ const exports = build_metadata?.remotes.get(remote.hash);
976
+ if (!exports) throw new Error('Expected to find metadata for remote file ' + id);
936
977
 
937
- for (const [name, value] of exports) {
938
- map.set(name, value.type);
978
+ for (const [name, value] of exports) {
979
+ map.set(name, value.type);
980
+ }
939
981
  }
940
- }
941
982
 
942
- let namespace = '__remote';
943
- let uid = 1;
944
- while (map.has(namespace)) namespace = `__remote${uid++}`;
983
+ const { namespace, declarations, reexports } = create_exported_declarations(
984
+ map.keys(),
985
+ (name, ns) => `${ns}.${map.get(name)}('${remote.hash}/${name}')`,
986
+ '__remote'
987
+ );
945
988
 
946
- const exports = Array.from(map).map(([name, type]) => {
947
- return `export const ${name} = ${namespace}.${type}('${remote.hash}/${name}');`;
948
- });
989
+ let result = `import * as ${namespace} from '__sveltekit/remote';\n\n${declarations.join('\n')}`;
990
+ if (reexports.length > 0) {
991
+ result += `\nexport { ${reexports.join(', ')} };`;
992
+ }
993
+ result += '\n';
949
994
 
950
- let result = `import * as ${namespace} from '__sveltekit/remote';\n\n${exports.join('\n')}\n`;
995
+ if (dev_server) {
996
+ result += `\nimport.meta.hot?.accept();\n`;
997
+ }
951
998
 
952
- if (dev_server) {
953
- result += `\nimport.meta.hot?.accept();\n`;
999
+ return {
1000
+ code: result
1001
+ };
954
1002
  }
1003
+ }
1004
+ };
955
1005
 
956
- return {
957
- code: result
958
- };
1006
+ /** @type {Plugin} */
1007
+ const plugin_remote_guard = {
1008
+ name: 'vite-plugin-sveltekit-remote-guard',
1009
+
1010
+ applyToEnvironment() {
1011
+ return !svelte_config.kit.experimental.remoteFunctions;
1012
+ },
1013
+
1014
+ transform: {
1015
+ filter: {
1016
+ id: new RegExp(
1017
+ `.remote(${svelte_config.kit.moduleExtensions.join('|')})$`.replaceAll('.', '\\.')
1018
+ )
1019
+ },
1020
+ handler() {
1021
+ error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
1022
+ }
959
1023
  }
960
1024
  };
961
1025
 
962
- /** @type {import('vite').Manifest} */
963
- let client_manifest;
964
- /** @type {import('types').Prerendered} */
1026
+ /** @type {Manifest} */
1027
+ let vite_server_manifest;
1028
+ /** @type {Manifest | null} */
1029
+ let vite_client_manifest = null;
1030
+ /** @type {Prerendered} */
965
1031
  let prerendered;
966
1032
 
967
1033
  /** @type {Set<string>} */
968
- let build;
1034
+ let build_files;
969
1035
  /** @type {string} */
970
1036
  let service_worker_code;
971
1037
 
972
1038
  /**
973
1039
  * Creates the service worker virtual modules
974
- * @type {import('vite').Plugin}
1040
+ * @type {Plugin}
975
1041
  */
976
1042
  const plugin_service_worker = {
977
1043
  name: 'vite-plugin-sveltekit-service-worker',
978
1044
 
1045
+ config(config) {
1046
+ service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
1047
+ parsed_service_worker = path.parse(kit.files.serviceWorker);
1048
+
1049
+ if (!service_worker_entry_file) return;
1050
+
1051
+ service_worker_entry_file = posixify(service_worker_entry_file);
1052
+
1053
+ if (kit.paths.assets) {
1054
+ throw new Error('Cannot use service worker alongside config.paths.assets');
1055
+ }
1056
+
1057
+ const user_service_worker_output_config =
1058
+ config.environments?.serviceWorker?.build?.rolldownOptions?.output;
1059
+
1060
+ /** @type {import('vite').UserConfig} */
1061
+ const new_config = {
1062
+ environments: {
1063
+ serviceWorker: {
1064
+ build: {
1065
+ modulePreload: false,
1066
+ rolldownOptions: {
1067
+ external: [`${kit.paths.base}/${kit.appDir}/env.js`],
1068
+ input: {
1069
+ 'service-worker': service_worker_entry_file
1070
+ },
1071
+ output: {
1072
+ format: 'es',
1073
+ entryFileNames: 'service-worker.js',
1074
+ assetFileNames: `${kit.appDir}/immutable/assets/[name].[hash][extname]`,
1075
+ codeSplitting:
1076
+ (Array.isArray(user_service_worker_output_config)
1077
+ ? user_service_worker_output_config[0].codeSplitting
1078
+ : user_service_worker_output_config?.codeSplitting) ?? false
1079
+ }
1080
+ },
1081
+ outDir: `${out}/client`,
1082
+ minify: initial_config.build?.minify,
1083
+ // avoid overwriting the client build Vite manifest
1084
+ manifest: '.vite/service-worker-manifest.json'
1085
+ },
1086
+ consumer: 'client'
1087
+ }
1088
+ }
1089
+ };
1090
+
1091
+ warn_overridden_config(config, new_config);
1092
+
1093
+ return new_config;
1094
+ },
1095
+
1096
+ // our serviceWorker environment only exists when building because Vite only
1097
+ // supports the default client environment during development (for now)
979
1098
  applyToEnvironment(environment) {
980
1099
  return environment.name === 'serviceWorker';
981
1100
  },
982
1101
 
983
- resolveId: {
984
- handler(id) {
985
- if (id.startsWith('$app/') || id === '$service-worker') {
986
- // ids with :$ don't work with reverse proxies like nginx
987
- return `\0virtual:${id.substring(1)}`;
988
- }
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);
989
1108
 
990
- if (id.startsWith('__sveltekit')) {
991
- return `\0virtual:${id}`;
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
+ );
992
1126
  }
993
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')) {
1135
+ return `\0virtual:${id}`;
1136
+ }
994
1137
  },
995
1138
 
996
- load(id) {
997
- if (!build) {
998
- build = new Set();
999
- for (const key in client_manifest) {
1000
- const { file, css = [], assets = [] } = client_manifest[key];
1001
- build.add(file);
1002
- css.forEach((file) => build.add(file));
1003
- assets.forEach((file) => build.add(file));
1004
- }
1139
+ load: {
1140
+ filter: {
1141
+ id: [prefixRegex('\0virtual:')]
1142
+ },
1143
+ 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
+ }
1005
1153
 
1006
- // in a service worker, `location` is the location of the service worker itself,
1007
- // which is guaranteed to be `<base>/service-worker.js`
1008
- const base = "location.pathname.split('/').slice(0, -1).join('/')";
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('/')";
1009
1167
 
1010
- service_worker_code = dedent`
1168
+ service_worker_code = dedent`
1011
1169
  export const base = /*@__PURE__*/ ${base};
1012
1170
 
1013
1171
  export const build = [
1014
- ${Array.from(build)
1172
+ ${Array.from(build_files)
1015
1173
  .map((file) => `base + ${s(`/${file}`)}`)
1016
1174
  .join(',\n')}
1017
1175
  ];
@@ -1028,65 +1186,74 @@ function kit({ svelte_config, adapter }) {
1028
1186
  ];
1029
1187
 
1030
1188
  export const version = ${s(kit.version.name)};
1031
- `;
1032
- }
1033
-
1034
- if (!id.startsWith('\0virtual:')) return;
1189
+ `;
1190
+ }
1035
1191
 
1036
- const global = is_build
1037
- ? `globalThis.__sveltekit_${version_hash}`
1038
- : 'globalThis.__sveltekit_dev';
1192
+ if (id === service_worker) {
1193
+ return service_worker_code;
1194
+ }
1039
1195
 
1040
- if (id === service_worker) {
1041
- return service_worker_code;
1042
- }
1196
+ if (id === sveltekit_env_service_worker) {
1197
+ return is_build
1198
+ ? create_sveltekit_env_service_worker(
1199
+ explicit_env_config,
1200
+ env,
1201
+ kit_global,
1202
+ kit.paths.base,
1203
+ kit.appDir
1204
+ )
1205
+ : create_sveltekit_env_service_worker_dev(explicit_env_config, env, kit_global);
1206
+ }
1043
1207
 
1044
- if (id === sveltekit_env_service_worker) {
1045
- return create_sveltekit_env_service_worker_dev(explicit_env_config, env, global);
1046
- }
1208
+ if (id === sveltekit_env_public_client) {
1209
+ return create_sveltekit_env_public(
1210
+ explicit_env_config,
1211
+ env,
1212
+ `const env = ${kit_global}.env;`
1213
+ );
1214
+ }
1047
1215
 
1048
- if (id === sveltekit_env_public_client) {
1049
- return create_sveltekit_env_public(explicit_env_config, env, `const env = ${global}.env;`);
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);
1223
+ 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.`
1225
+ );
1050
1226
  }
1051
-
1052
- const normalized_cwd = vite.normalizePath(vite_config.root);
1053
- const normalized_lib = vite.normalizePath(kit.files.lib);
1054
- const relative = normalize_id(id, normalized_lib, normalized_cwd);
1055
- const stripped = strip_virtual_prefix(relative);
1056
- throw new Error(
1057
- `Cannot import ${stripped} into service-worker code. Only the modules $service-worker and $app/env/public are available in service workers.`
1058
- );
1059
1227
  }
1060
1228
  };
1061
1229
 
1062
1230
  /** @type {Plugin} */
1063
1231
  const plugin_service_worker_env = {
1064
1232
  name: 'vite-plugin-sveltekit-service-worker-env',
1065
-
1066
- transform: {
1067
- filter: {
1068
- id: service_worker_entry_file || '<skip>'
1069
- },
1070
- handler(code) {
1071
- // in dev, we prepend the service worker with an import that
1072
- // configures `env`, in case `$app/env/public` is imported,
1073
- // in prod, where we currently use non-module service
1074
- // workers, we have to use `importScripts` instead
1075
- return {
1076
- code: `import '__sveltekit/env/service-worker';\n${code}`
1077
- };
1078
- }
1233
+ applyToEnvironment(environment) {
1234
+ return !!service_worker_entry_file && environment.config.consumer === 'client';
1235
+ },
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
+ };
1079
1247
  }
1080
1248
  };
1081
1249
 
1250
+ /** @type {() => Promise<void> | undefined} */
1251
+ let finalise;
1252
+
1082
1253
  /** @type {Plugin} */
1083
1254
  const plugin_compile = {
1084
1255
  name: 'vite-plugin-sveltekit-compile',
1085
1256
 
1086
- applyToEnvironment(environment) {
1087
- return environment.name !== 'serviceWorker';
1088
- },
1089
-
1090
1257
  /**
1091
1258
  * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
1092
1259
  * @see https://vitejs.dev/guide/api-plugin.html#config
@@ -1099,12 +1266,13 @@ function kit({ svelte_config, adapter }) {
1099
1266
  let new_config;
1100
1267
 
1101
1268
  if (is_build) {
1102
- const prefix = `${kit.appDir}/immutable`;
1269
+ const app_immutable = `${kit.appDir}/immutable`;
1103
1270
 
1104
1271
  /** @type {Record<string, string>} */
1105
1272
  const server_input = {
1106
1273
  index: `${runtime_directory}/server/index.js`,
1107
1274
  internal: `${out_dir}/generated/server/internal.js`,
1275
+ env: '__sveltekit/env',
1108
1276
  ['remote-entry']: `${runtime_directory}/app/server/remote/index.js`
1109
1277
  };
1110
1278
 
@@ -1133,11 +1301,10 @@ function kit({ svelte_config, adapter }) {
1133
1301
  }
1134
1302
  });
1135
1303
 
1136
- // ...and every matcher
1137
- Object.entries(manifest_data.matchers).forEach(([key, file]) => {
1138
- const name = posixify(path.join('entries/matchers', key));
1139
- server_input[name] = path.resolve(root, file);
1140
- });
1304
+ // ...and the params file
1305
+ if (manifest_data.params) {
1306
+ server_input['entries/params'] = path.resolve(root, manifest_data.params);
1307
+ }
1141
1308
 
1142
1309
  // ...and the hooks files
1143
1310
  if (manifest_data.hooks.server) {
@@ -1155,15 +1322,8 @@ function kit({ svelte_config, adapter }) {
1155
1322
  path.join(kit.files.src, 'instrumentation.server')
1156
1323
  );
1157
1324
  if (server_instrumentation) {
1158
- if (adapter && !adapter.supports?.instrumentation?.()) {
1159
- throw new Error(`${server_instrumentation} is unsupported in ${adapter.name}.`);
1160
- }
1161
- if (!kit.experimental.instrumentation.server) {
1162
- error_for_missing_config(
1163
- '`instrumentation.server.js`',
1164
- 'kit.experimental.instrumentation.server',
1165
- 'true'
1166
- );
1325
+ if (kit.adapter && !kit.adapter.supports?.instrumentation?.()) {
1326
+ throw new Error(`${server_instrumentation} is unsupported in ${kit.adapter.name}.`);
1167
1327
  }
1168
1328
  server_input['instrumentation.server'] = server_instrumentation;
1169
1329
  }
@@ -1175,6 +1335,7 @@ function kit({ svelte_config, adapter }) {
1175
1335
  client_input['bundle'] = `${runtime_directory}/client/bundle.js`;
1176
1336
  } else {
1177
1337
  client_input['entry/start'] = `${runtime_directory}/client/entry.js`;
1338
+ client_input['entry/payload'] = `${runtime_directory}/client/payload.js`;
1178
1339
  client_input['entry/app'] = `${out_dir}/generated/client-optimized/app.js`;
1179
1340
  manifest_data.nodes.forEach((node, i) => {
1180
1341
  if (node.component || node.universal) {
@@ -1185,20 +1346,25 @@ function kit({ svelte_config, adapter }) {
1185
1346
 
1186
1347
  const inline = svelte_config.kit.output.bundleStrategy === 'inline';
1187
1348
 
1188
- const config_base = assets_base(kit);
1189
-
1190
1349
  /** @type {string} */
1191
- const base = kit.paths.assets || kit.paths.base || '/';
1192
- const root_to_assets = prefix + '/assets/';
1350
+ const base = (kit.paths.assets || kit.paths.base) + '/';
1351
+ const root_to_assets = app_immutable + '/assets/';
1193
1352
  const assets_to_root =
1194
- prefix
1353
+ app_immutable
1195
1354
  .split('/')
1196
1355
  .map(() => '..')
1197
1356
  .join('/') + '/../';
1198
1357
 
1358
+ const relative = kit.paths.relative !== false || !!kit.paths.assets;
1359
+
1199
1360
  new_config = {
1200
1361
  appType: 'custom',
1201
- base: config_base,
1362
+ // Affects how Vite loads JS assets on the client.
1363
+ // If the initial HTML we render uses an absolute path for assets,
1364
+ // the additional chunks Vite loads must also use an absolute path.
1365
+ // Otherwise, you end up with additional chunks being loaded relative
1366
+ // to the current chunk rather than the root.
1367
+ base: relative ? './' : base,
1202
1368
  build: {
1203
1369
  cssCodeSplit: !inline,
1204
1370
  cssMinify:
@@ -1207,7 +1373,7 @@ function kit({ svelte_config, adapter }) {
1207
1373
  rolldownOptions: {
1208
1374
  output: {
1209
1375
  name: `__sveltekit_${version_hash}.app`,
1210
- assetFileNames: `${prefix}/assets/[name].[hash][extname]`,
1376
+ assetFileNames: `${app_immutable}/assets/[name].[hash][extname]`,
1211
1377
  hoistTransitiveImports: false,
1212
1378
  sourcemapIgnoreList
1213
1379
  },
@@ -1246,7 +1412,7 @@ function kit({ svelte_config, adapter }) {
1246
1412
  }
1247
1413
  }
1248
1414
  },
1249
- // during the initial server build we don't know yet
1415
+ // these are stubs that will be replaced after the initial server build
1250
1416
  define: {
1251
1417
  __SVELTEKIT_HAS_SERVER_LOAD__: 'true',
1252
1418
  __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true',
@@ -1260,9 +1426,10 @@ function kit({ svelte_config, adapter }) {
1260
1426
  input: inline ? client_input['bundle'] : client_input,
1261
1427
  output: {
1262
1428
  format: inline ? 'iife' : 'esm',
1263
- entryFileNames: `${prefix}/[name].[hash].js`,
1264
- chunkFileNames: `${prefix}/chunks/[hash].js`,
1265
- codeSplitting: svelte_config.kit.output.bundleStrategy === 'split'
1429
+ entryFileNames: `${app_immutable}/[name].[hash].js`,
1430
+ chunkFileNames: `${app_immutable}/chunks/[hash].js`,
1431
+ codeSplitting:
1432
+ svelte_config.kit.output.bundleStrategy === 'split' ? undefined : false
1266
1433
  },
1267
1434
  // This silences Rolldown warnings about not supporting `import.meta`
1268
1435
  // for the `iife` output format. We don't care because it's
@@ -1277,68 +1444,42 @@ function kit({ svelte_config, adapter }) {
1277
1444
  }
1278
1445
  },
1279
1446
  define: {
1280
- __SVELTEKIT_PAYLOAD__: `globalThis.__sveltekit_${version_hash}`
1447
+ __SVELTEKIT_PAYLOAD__:
1448
+ svelte_config.kit.output.bundleStrategy !== 'split' ? kit_global : 'undefined'
1281
1449
  }
1282
1450
  }
1283
1451
  },
1284
1452
  experimental: {
1285
- // we can't change the base path per environment so we're setting the
1286
- // base prefix for files here ourselves
1287
- renderBuiltUrl:
1288
- // if the Vite base is relative, we need to ensure paths used during SSR are absolute
1289
- config_base === './'
1290
- ? (filename, { ssr }) => {
1291
- if (ssr) return base + filename;
1292
- }
1293
- : // but if the Vite base is absolute, we just need to ensure
1294
- // client paths are relative rather than absolute
1295
- (filename, { ssr, hostType }) => {
1296
- if (ssr) return;
1297
-
1298
- if (hostType === 'js') {
1299
- // We could always use a relative asset base path here, but it's better for performance not to.
1300
- // E.g. Vite generates `new URL('/asset.png', import.meta).href` for a relative path vs just '/asset.png'.
1301
- // That's larger and takes longer to run and also causes an HTML diff between SSR and client
1302
- // causing us to do a more expensive hydration check.
1303
- return {
1304
- relative: kit.paths.relative !== false || !!kit.paths.assets
1305
- };
1306
- }
1307
-
1308
- // _app/immutable/assets files
1309
- if (filename.startsWith(root_to_assets)) {
1310
- return `./${filename.slice(root_to_assets.length)}`;
1311
- }
1312
-
1313
- // static dir files
1314
- return assets_to_root + filename;
1315
- }
1453
+ // Allows us to use relative paths in as many places as we can
1454
+ renderBuiltUrl(filename, { ssr, hostType }) {
1455
+ if (hostType === 'js') {
1456
+ // SSR builds should use an absolute path in JS modules to
1457
+ // match the default Vite behaviour
1458
+ if (ssr) return base + filename;
1459
+
1460
+ // We could always use a relative asset base path here, but it's better for performance not to.
1461
+ // E.g. Vite generates `new URL('/asset.png', import.meta).href` for a relative path vs just '/asset.png'.
1462
+ // That's larger and takes longer to run and also causes an HTML diff between SSR and client
1463
+ // causing us to do a more expensive hydration check.
1464
+ return { relative };
1465
+ }
1466
+
1467
+ if (!relative) return;
1468
+
1469
+ // ensure assets loaded by CSS files are loaded relative to the
1470
+ // CSS file rather than the default of relative to the root
1471
+
1472
+ // _app/immutable/assets files
1473
+ if (filename.startsWith(root_to_assets)) {
1474
+ return `./${filename.slice(root_to_assets.length)}`;
1475
+ }
1476
+
1477
+ // static dir files
1478
+ return assets_to_root + filename;
1479
+ }
1316
1480
  },
1317
1481
  publicDir: kit.files.assets
1318
1482
  };
1319
-
1320
- if (service_worker_entry_file) {
1321
- /** @type {Record<string, import('vite').EnvironmentOptions>} */ (
1322
- new_config.environments
1323
- ).serviceWorker = {
1324
- build: {
1325
- modulePreload: false,
1326
- rolldownOptions: {
1327
- input: {
1328
- 'service-worker': service_worker_entry_file
1329
- },
1330
- output: {
1331
- entryFileNames: 'service-worker.js',
1332
- assetFileNames: `${kit.appDir}/immutable/assets/[name].[hash][extname]`,
1333
- codeSplitting: false
1334
- }
1335
- },
1336
- outDir: `${out}/client`,
1337
- minify: initial_config.build?.minify
1338
- },
1339
- consumer: 'client'
1340
- };
1341
- }
1342
1483
  } else {
1343
1484
  new_config = {
1344
1485
  appType: 'custom',
@@ -1366,16 +1507,20 @@ function kit({ svelte_config, adapter }) {
1366
1507
  * Adds the SvelteKit middleware to do SSR in dev mode.
1367
1508
  * @see https://vitejs.dev/guide/api-plugin.html#configureserver
1368
1509
  */
1369
- async configureServer(vite) {
1370
- return await dev(vite, vite_config, svelte_config, () => remotes, root, adapter);
1510
+ async configureServer(server) {
1511
+ return await dev(server, vite_config, svelte_config, () => remotes, root);
1371
1512
  },
1372
1513
 
1373
1514
  /**
1374
1515
  * Adds the SvelteKit middleware to do SSR in preview mode.
1375
1516
  * @see https://vitejs.dev/guide/api-plugin.html#configurepreviewserver
1376
1517
  */
1377
- configurePreviewServer(vite) {
1378
- return preview(vite, vite_config, svelte_config, adapter);
1518
+ configurePreviewServer(server) {
1519
+ return preview(server, vite_config, svelte_config);
1520
+ },
1521
+
1522
+ applyToEnvironment(environment) {
1523
+ return environment.name !== 'serviceWorker';
1379
1524
  },
1380
1525
 
1381
1526
  renderChunk(code, chunk) {
@@ -1409,17 +1554,23 @@ function kit({ svelte_config, adapter }) {
1409
1554
  }
1410
1555
  mkdirp(out);
1411
1556
 
1412
- const server_bundle = /** @type {import('vite').Rolldown.RolldownOutput} */ (
1557
+ await load_and_validate_params({
1558
+ routes: manifest_data.routes,
1559
+ params_path: manifest_data.params,
1560
+ root
1561
+ });
1562
+
1563
+ const { output: server_chunks } = /** @type {Rolldown.RolldownOutput} */ (
1413
1564
  await builder.build(builder.environments.ssr)
1414
1565
  );
1415
1566
 
1416
- const verbose = vite_config.logLevel === 'info';
1567
+ const verbose = builder.config.logLevel === 'info';
1417
1568
  const log = logger({ verbose });
1418
1569
 
1419
- /** @type {import('vite').Manifest} */
1420
- const server_manifest = JSON.parse(read(`${out}/server/.vite/manifest.json`));
1570
+ /** @type {Manifest} */
1571
+ vite_server_manifest = JSON.parse(read(`${out}/server/.vite/manifest.json`));
1421
1572
 
1422
- /** @type {import('types').BuildData} */
1573
+ /** @type {BuildData} */
1423
1574
  const build_data = {
1424
1575
  app_dir: kit.appDir,
1425
1576
  app_path: `${kit.paths.base.slice(1)}${kit.paths.base ? '/' : ''}${kit.appDir}`,
@@ -1427,7 +1578,7 @@ function kit({ svelte_config, adapter }) {
1427
1578
  out_dir: out,
1428
1579
  service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
1429
1580
  client: null,
1430
- server_manifest
1581
+ server_manifest: vite_server_manifest
1431
1582
  };
1432
1583
 
1433
1584
  const manifest_path = `${out}/server/manifest-full.js`;
@@ -1443,18 +1594,30 @@ function kit({ svelte_config, adapter }) {
1443
1594
  })};\n`
1444
1595
  );
1445
1596
 
1597
+ const assets_path = `${kit.appDir}/immutable/assets`;
1598
+
1599
+ // first, build server nodes without the client manifest so we can analyse it
1600
+ build_server_nodes(
1601
+ out,
1602
+ kit,
1603
+ manifest_data,
1604
+ vite_server_manifest,
1605
+ null,
1606
+ assets_path,
1607
+ server_chunks,
1608
+ root
1609
+ );
1610
+
1446
1611
  log.info('Analysing routes');
1447
1612
 
1448
1613
  const { metadata } = await analyse({
1449
1614
  hash: kit.router.type === 'hash',
1450
1615
  manifest_path,
1451
1616
  manifest_data,
1452
- server_manifest,
1617
+ server_manifest: vite_server_manifest,
1453
1618
  tracked_features,
1454
1619
  env,
1455
- out,
1456
1620
  remotes,
1457
- root,
1458
1621
  vite_config_file: vite_config.configFile
1459
1622
  });
1460
1623
 
@@ -1470,189 +1633,231 @@ function kit({ svelte_config, adapter }) {
1470
1633
  metadata.nodes
1471
1634
  );
1472
1635
 
1473
- const nodes = Object.values(
1474
- /** @type {import('types').ServerMetadata} */ (build_metadata).nodes
1636
+ const server_assets = `${out}/server/${assets_path}`;
1637
+ const client_assets = `${out}/client/${assets_path}`;
1638
+
1639
+ const skip_client_build = manifest_data.nodes.every(
1640
+ (node) => node.page_options?.csr === false
1475
1641
  );
1476
1642
 
1477
- // Through the finished analysis we can now check if any node has server or universal load functions
1478
- const has_server_load = nodes.some((node) => node.has_server_load);
1479
- const has_universal_load = nodes.some((node) => node.has_universal_load);
1643
+ if (!skip_client_build) {
1644
+ const nodes = Object.values(build_metadata.nodes);
1480
1645
 
1481
- if (builder.environments.client.config.define) {
1482
- builder.environments.client.config.define.__SVELTEKIT_HAS_SERVER_LOAD__ =
1483
- s(has_server_load);
1484
- builder.environments.client.config.define.__SVELTEKIT_HAS_UNIVERSAL_LOAD__ =
1485
- s(has_universal_load);
1486
- }
1646
+ // Through the finished analysis we can now check if any node has server or universal load functions
1647
+ const has_server_load = nodes.some((node) => node.has_server_load);
1648
+ const has_universal_load = nodes.some((node) => node.has_universal_load);
1487
1649
 
1488
- const { output: client_chunks } = /** @type {import('vite').Rolldown.RolldownOutput} */ (
1489
- await builder.build(builder.environments.client)
1490
- );
1650
+ if (builder.environments.client.config.define) {
1651
+ builder.environments.client.config.define.__SVELTEKIT_HAS_SERVER_LOAD__ =
1652
+ s(has_server_load);
1653
+ builder.environments.client.config.define.__SVELTEKIT_HAS_UNIVERSAL_LOAD__ =
1654
+ s(has_universal_load);
1655
+ }
1491
1656
 
1492
- // We use `build.ssrEmitAssets` so that asset URLs created from
1493
- // imports in server-only modules correspond to files in the build,
1494
- // but we don't want to copy over CSS imports as these are already
1495
- // accounted for in the client bundle. In most cases it would be
1496
- // a no-op, but for SSR builds `url(...)` paths are handled
1497
- // differently (relative for client, absolute for server)
1498
- // resulting in different hashes, and thus duplication
1499
- const ssr_stylesheets = new Set(
1500
- Object.values(server_manifest)
1501
- .map((chunk) => chunk.css ?? [])
1502
- .flat()
1503
- );
1657
+ const { output: client_chunks } = /** @type {Rolldown.RolldownOutput} */ (
1658
+ await builder.build(builder.environments.client)
1659
+ );
1504
1660
 
1505
- const assets_path = `${kit.appDir}/immutable/assets`;
1506
- const server_assets = `${out}/server/${assets_path}`;
1507
- const client_assets = `${out}/client/${assets_path}`;
1661
+ // We use `build.ssrEmitAssets` so that asset URLs created from
1662
+ // imports in server-only modules correspond to files in the build,
1663
+ // but we don't want to copy over CSS imports as these are already
1664
+ // accounted for in the client bundle. In most cases it would be
1665
+ // a no-op, but for SSR builds `url(...)` paths are handled
1666
+ // differently (relative for client, absolute for server)
1667
+ // resulting in different hashes, and thus duplication
1668
+ const ssr_stylesheets = new Set(
1669
+ Object.values(vite_server_manifest)
1670
+ .map((chunk) => chunk.css ?? [])
1671
+ .flat()
1672
+ );
1508
1673
 
1509
- if (fs.existsSync(server_assets)) {
1510
- for (const file of fs.readdirSync(server_assets)) {
1511
- const src = `${server_assets}/${file}`;
1512
- const dest = `${client_assets}/${file}`;
1674
+ if (fs.existsSync(server_assets)) {
1675
+ for (const file of fs.readdirSync(server_assets)) {
1676
+ const src = `${server_assets}/${file}`;
1677
+ const dest = `${client_assets}/${file}`;
1513
1678
 
1514
- if (fs.existsSync(dest) || ssr_stylesheets.has(`${assets_path}/${file}`)) {
1515
- continue;
1679
+ if (fs.existsSync(dest) || ssr_stylesheets.has(`${assets_path}/${file}`)) {
1680
+ continue;
1681
+ }
1682
+
1683
+ copy(src, dest);
1516
1684
  }
1685
+ }
1517
1686
 
1518
- if (file.endsWith('.css')) {
1519
- // make absolute paths in CSS relative, for portability
1520
- const content = fs
1521
- .readFileSync(src, 'utf-8')
1522
- .replaceAll(`${kit.paths.base}/${assets_path}`, '.');
1687
+ const vite_manifest = (vite_client_manifest = JSON.parse(
1688
+ read(`${out}/client/.vite/manifest.json`)
1689
+ ));
1523
1690
 
1524
- fs.writeFileSync(src, content);
1525
- }
1691
+ /**
1692
+ * @param {string} entry
1693
+ * @param {boolean} [add_dynamic_css]
1694
+ */
1695
+ const deps_of = (entry, add_dynamic_css = false) =>
1696
+ find_deps(vite_manifest, posixify(path.relative(root, entry)), add_dynamic_css, root);
1526
1697
 
1527
- copy(src, dest);
1528
- }
1529
- }
1698
+ const has_explicit_dynamic_public_env = Object.values(explicit_env_config ?? {}).some(
1699
+ (variable) => variable.public && !variable.static
1700
+ );
1530
1701
 
1531
- /** @type {import('vite').Manifest} */
1532
- client_manifest = JSON.parse(read(`${out}/client/.vite/manifest.json`));
1533
-
1534
- /**
1535
- * @param {string} entry
1536
- * @param {boolean} [add_dynamic_css]
1537
- */
1538
- const deps_of = (entry, add_dynamic_css = false) =>
1539
- find_deps(client_manifest, posixify(path.relative(root, entry)), add_dynamic_css, root);
1540
-
1541
- if (svelte_config.kit.output.bundleStrategy === 'split') {
1542
- const start = deps_of(`${runtime_directory}/client/entry.js`);
1543
- const app = deps_of(`${out_dir}/generated/client-optimized/app.js`);
1544
-
1545
- build_data.client = {
1546
- start: start.file,
1547
- app: app.file,
1548
- imports: [...start.imports, ...app.imports],
1549
- stylesheets: [...start.stylesheets, ...app.stylesheets],
1550
- fonts: [...start.fonts, ...app.fonts],
1551
- uses_env_dynamic_public: client_chunks.some(
1702
+ // the app only depends on runtime public env if it imports `$app/env/public`
1703
+ // *and* at least one public env var is actually dynamic (non-static)
1704
+ const uses_env_dynamic_public =
1705
+ has_explicit_dynamic_public_env &&
1706
+ client_chunks.some(
1552
1707
  (chunk) => chunk.type === 'chunk' && chunk.modules[sveltekit_env_public_client]
1553
- )
1554
- };
1555
-
1556
- // In case of server-side route resolution, we create a purpose-built route manifest that is
1557
- // similar to that on the client, with as much information computed upfront so that we
1558
- // don't need to include any code of the actual routes in the server bundle.
1559
- if (svelte_config.kit.router.resolution === 'server') {
1560
- const nodes = manifest_data.nodes.map((node, i) => {
1561
- if (node.component || node.universal) {
1562
- const entry = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
1563
- const deps = deps_of(entry, true);
1564
- const file = resolve_symlinks(
1565
- client_manifest,
1566
- `${out_dir}/generated/client-optimized/nodes/${i}.js`,
1567
- root
1568
- ).chunk.file;
1569
-
1570
- return { file, css: deps.stylesheets };
1571
- }
1572
- });
1573
- build_data.client.nodes = nodes.map((node) => node?.file);
1574
- build_data.client.css = nodes.map((node) => node?.css);
1575
-
1576
- build_data.client.routes = compact(
1577
- manifest_data.routes.map((route) => {
1578
- if (!route.page) return;
1579
-
1580
- return {
1581
- id: route.id,
1582
- pattern: route.pattern,
1583
- params: route.params,
1584
- layouts: route.page.layouts.map((l) =>
1585
- l !== undefined ? [metadata.nodes[l].has_server_load, l] : undefined
1586
- ),
1587
- errors: route.page.errors,
1588
- leaf: [metadata.nodes[route.page.leaf].has_server_load, route.page.leaf]
1589
- };
1590
- })
1591
1708
  );
1592
- }
1593
- } else {
1594
- const start = deps_of(`${runtime_directory}/client/bundle.js`);
1595
-
1596
- build_data.client = {
1597
- start: start.file,
1598
- imports: start.imports,
1599
- stylesheets: start.stylesheets,
1600
- fonts: start.fonts,
1601
- uses_env_dynamic_public: client_chunks.some(
1602
- (chunk) => chunk.type === 'chunk' && chunk.modules[sveltekit_env_public_client]
1603
- )
1604
- };
1605
1709
 
1606
- if (svelte_config.kit.output.bundleStrategy === 'inline') {
1607
- const style = /** @type {import('vite').Rolldown.OutputAsset} */ (
1608
- client_chunks.find(
1609
- (chunk) =>
1610
- chunk.type === 'asset' && chunk.names.length === 1 && chunk.names[0] === 'style.css'
1611
- )
1612
- );
1710
+ if (svelte_config.kit.output.bundleStrategy === 'split') {
1711
+ const start_entry = posixify(path.relative(root, `${runtime_directory}/client/entry.js`));
1712
+ const start = find_deps(vite_manifest, start_entry, false, root);
1713
+ const runtime_entry = resolve_symlinks(vite_manifest, start_entry, root).chunk
1714
+ .dynamicImports?.[0]; // client/entry.js dynamically imports client/client-entry.js
1715
+ if (!runtime_entry) throw new Error('Could not find the client runtime chunk');
1716
+ const runtime = find_deps(vite_manifest, runtime_entry, false, root);
1717
+ const app = deps_of(`${out_dir}/generated/client-optimized/app.js`);
1718
+
1719
+ build_data.client = {
1720
+ start: start.file,
1721
+ app: app.file,
1722
+ imports: Array.from(
1723
+ new Set([
1724
+ ...start.imports,
1725
+ runtime.file,
1726
+ ...runtime.imports,
1727
+ app.file,
1728
+ ...app.imports
1729
+ ])
1730
+ ),
1731
+ stylesheets: [...start.stylesheets, ...runtime.stylesheets, ...app.stylesheets],
1732
+ fonts: [...start.fonts, ...runtime.fonts, ...app.fonts],
1733
+ uses_env_dynamic_public
1734
+ };
1613
1735
 
1614
- build_data.client.inline = {
1615
- script: read(`${out}/client/${start.file}`),
1616
- style: /** @type {string | undefined} */ (style?.source)
1736
+ // In case of server-side route resolution, we create a purpose-built route manifest that is
1737
+ // similar to that on the client, with as much information computed upfront so that we
1738
+ // don't need to include any code of the actual routes in the server bundle.
1739
+ if (svelte_config.kit.router.resolution === 'server') {
1740
+ const nodes = manifest_data.nodes.map((node, i) => {
1741
+ if (node.component || node.universal) {
1742
+ const entry = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
1743
+ const deps = deps_of(entry, true);
1744
+ const file = resolve_symlinks(
1745
+ vite_manifest,
1746
+ `${out_dir}/generated/client-optimized/nodes/${i}.js`,
1747
+ root
1748
+ ).chunk.file;
1749
+
1750
+ return { file, css: deps.stylesheets };
1751
+ }
1752
+ });
1753
+ build_data.client.nodes = nodes.map((node) => node?.file);
1754
+ build_data.client.css = nodes.map((node) => node?.css);
1755
+
1756
+ build_data.client.routes = compact(
1757
+ manifest_data.routes.map((route) => {
1758
+ if (!route.page) return;
1759
+
1760
+ return {
1761
+ id: route.id,
1762
+ pattern: route.pattern,
1763
+ params: route.params,
1764
+ layouts: route.page.layouts.map((l) =>
1765
+ l !== undefined ? [metadata.nodes[l].has_server_load, l] : undefined
1766
+ ),
1767
+ errors: route.page.errors,
1768
+ leaf: [metadata.nodes[route.page.leaf].has_server_load, route.page.leaf]
1769
+ };
1770
+ })
1771
+ );
1772
+ }
1773
+ } else {
1774
+ const start = deps_of(`${runtime_directory}/client/bundle.js`);
1775
+
1776
+ build_data.client = {
1777
+ start: start.file,
1778
+ imports: start.imports,
1779
+ stylesheets: start.stylesheets,
1780
+ fonts: start.fonts,
1781
+ uses_env_dynamic_public
1617
1782
  };
1783
+
1784
+ 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
+ build_data.client.inline = {
1795
+ script: read(`${out}/client/${start.file}`),
1796
+ style: /** @type {string | undefined} */ (style?.source)
1797
+ };
1798
+
1799
+ // the bundle and stylesheet are inlined into the page, so the
1800
+ // emitted files are never loaded
1801
+ fs.unlinkSync(`${out}/client/${start.file}`);
1802
+ fs.rmSync(`${out}/client/${start.file}.map`, { force: true });
1803
+ if (style) fs.unlinkSync(`${out}/client/${style.fileName}`);
1804
+ }
1618
1805
  }
1619
- }
1620
1806
 
1621
- // regenerate manifest now that we have client entry...
1622
- fs.writeFileSync(
1623
- manifest_path,
1624
- `export const manifest = ${generate_manifest({
1625
- build_data,
1626
- prerendered: [],
1627
- relative_path: '.',
1628
- routes: manifest_data.routes,
1629
- remotes,
1630
- root
1631
- })};\n`
1632
- );
1807
+ // regenerate manifest now that we have client entry...
1808
+ fs.writeFileSync(
1809
+ manifest_path,
1810
+ `export const manifest = ${generate_manifest({
1811
+ build_data,
1812
+ prerendered: [],
1813
+ relative_path: '.',
1814
+ routes: manifest_data.routes,
1815
+ remotes,
1816
+ root
1817
+ })};\n`
1818
+ );
1633
1819
 
1634
- // regenerate nodes with the client manifest...
1635
- build_server_nodes(
1636
- out,
1637
- kit,
1638
- manifest_data,
1639
- server_manifest,
1640
- client_manifest,
1641
- assets_path,
1642
- client_chunks,
1643
- root
1644
- );
1820
+ // regenerate nodes with the client manifest...
1821
+ build_server_nodes(
1822
+ out,
1823
+ kit,
1824
+ manifest_data,
1825
+ vite_server_manifest,
1826
+ vite_client_manifest,
1827
+ assets_path,
1828
+ client_chunks,
1829
+ root
1830
+ );
1831
+ } else {
1832
+ copy(server_assets, client_assets);
1833
+ copy(kit.files.assets, `${out}/client`);
1834
+ }
1645
1835
 
1646
1836
  // ...and prerender
1647
- const prerender_results = await prerender({
1648
- hash: kit.router.type === 'hash',
1649
- out,
1650
- manifest_path,
1651
- metadata,
1652
- verbose,
1653
- env,
1654
- vite_config_file: vite_config.configFile
1655
- });
1837
+ let prerender_results;
1838
+ try {
1839
+ prerender_results = await prerender({
1840
+ hash: kit.router.type === 'hash',
1841
+ out,
1842
+ manifest_path,
1843
+ metadata,
1844
+ verbose,
1845
+ env,
1846
+ vite_config_file: vite_config.configFile
1847
+ });
1848
+
1849
+ // this silly hack is necessary to ensure that stderr from prerender is flushed before we continue
1850
+ await new Promise((f) => setTimeout(f, 0));
1851
+ } catch (e) {
1852
+ if (e instanceof Error && e.message === '__handled__') {
1853
+ // error details are already logged inside `prerender`, don't duplicate them
1854
+ throw stackless('Prerendering failed');
1855
+ } else {
1856
+ // Unforeseen error, rethrow as-is
1857
+ throw e;
1858
+ }
1859
+ }
1860
+
1656
1861
  prerendered = prerender_results.prerendered;
1657
1862
 
1658
1863
  await treeshake_prerendered_remotes(
@@ -1661,7 +1866,7 @@ function kit({ svelte_config, adapter }) {
1661
1866
  remotes,
1662
1867
  metadata,
1663
1868
  process.cwd(),
1664
- server_bundle,
1869
+ server_chunks,
1665
1870
  vite_config.build.sourcemap
1666
1871
  );
1667
1872
 
@@ -1680,53 +1885,57 @@ function kit({ svelte_config, adapter }) {
1680
1885
  })};\n`
1681
1886
  );
1682
1887
 
1683
- if (service_worker_entry_file) {
1684
- if (kit.paths.assets) {
1685
- throw new Error('Cannot use service worker alongside config.kit.paths.assets');
1686
- }
1687
-
1688
- log.info('Building service worker');
1888
+ // defer the adapt step to run after any buildApp hooks the adapter might have
1889
+ finalise = async () => {
1890
+ // defer creating the service worker too because other plugins might build
1891
+ // the client environment again and overwrite our service worker which
1892
+ // outputs to the same directory
1893
+ if (service_worker_entry_file) {
1894
+ log.info('Building service worker');
1895
+
1896
+ // mirror client settings that we couldn't set per environment in the config hook
1897
+ builder.environments.serviceWorker.config.define =
1898
+ builder.environments.client.config.define;
1899
+ builder.environments.serviceWorker.config.resolve.alias = [
1900
+ ...get_config_aliases(kit, vite_config.root)
1901
+ ];
1689
1902
 
1690
- builder.environments.serviceWorker.config.define =
1691
- builder.environments.client.config.define;
1692
- builder.environments.serviceWorker.config.resolve.alias = [
1693
- ...get_config_aliases(kit, vite_config.root)
1694
- ];
1695
- builder.environments.serviceWorker.config.experimental.renderBuiltUrl = (filename) => {
1696
- return {
1697
- runtime: `new URL(${JSON.stringify(filename)}, location.href).pathname`
1903
+ // we have to overwrite this because it can't be configured per environment in the config hook
1904
+ builder.environments.serviceWorker.config.experimental.renderBuiltUrl = (filename) => {
1905
+ return {
1906
+ runtime: `new URL(${JSON.stringify(filename)}, location.href).pathname`
1907
+ };
1698
1908
  };
1699
- };
1700
1909
 
1701
- await builder.build(builder.environments.serviceWorker);
1702
- }
1703
-
1704
- console.log(
1705
- `\nRun ${styleText(['bold', 'cyan'], 'npm run preview')} to preview your production build locally.`
1706
- );
1707
-
1708
- if (adapter) {
1709
- const { adapt } = await import('../../core/adapt/index.js');
1710
- await adapt(
1711
- adapter,
1712
- svelte_config,
1713
- build_data,
1714
- metadata,
1715
- prerendered,
1716
- prerender_results.prerender_map,
1717
- log,
1718
- remotes,
1719
- vite_config,
1720
- explicit_env_config
1721
- );
1722
- } else {
1723
- console.log(styleText(['bold', 'yellow'], '\nNo adapter specified'));
1910
+ await builder.build(builder.environments.serviceWorker);
1911
+ }
1724
1912
 
1725
- const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
1726
1913
  console.log(
1727
- `See ${link} to learn how to configure your app to run on the platform of your choosing`
1914
+ `\nRun ${styleText(['bold', 'cyan'], 'npm run preview')} to preview your production build locally.`
1728
1915
  );
1729
- }
1916
+
1917
+ if (kit.adapter) {
1918
+ const { adapt } = await import('../../core/adapt/index.js');
1919
+ await adapt(
1920
+ svelte_config,
1921
+ build_data,
1922
+ metadata,
1923
+ prerendered,
1924
+ prerender_results.prerender_map,
1925
+ log,
1926
+ remotes,
1927
+ vite_config,
1928
+ explicit_env_config
1929
+ );
1930
+ } else {
1931
+ log.warn('\nNo adapter specified');
1932
+
1933
+ const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
1934
+ console.log(
1935
+ `See ${link} to learn how to configure your app to run on the platform of your choosing`
1936
+ );
1937
+ }
1938
+ };
1730
1939
  }
1731
1940
  };
1732
1941
 
@@ -1734,23 +1943,30 @@ function kit({ svelte_config, adapter }) {
1734
1943
  const plugin_adapter = {
1735
1944
  name: 'vite-plugin-sveltekit-adapter',
1736
1945
  apply: 'build',
1737
- // expose the adapter so that forked processes (e.g. prerendering)
1738
- // can retrieve it by resolving the Vite config
1739
- api: {
1740
- adapter
1946
+ buildApp: {
1947
+ // this will run after any buildApp hooks provided by other Vite plugins
1948
+ // see https://vite.dev/guide/api-environment-frameworks#environments-during-build
1949
+ order: 'post',
1950
+ async handler() {
1951
+ await finalise?.();
1952
+ }
1741
1953
  }
1742
1954
  };
1743
1955
 
1744
- return [
1745
- plugin_setup,
1746
- plugin_remote,
1747
- plugin_virtual_modules,
1748
- process.env.TEST !== 'true' ? plugin_guard : undefined,
1749
- service_worker_entry_file && plugin_service_worker_env,
1750
- plugin_service_worker,
1751
- plugin_compile,
1752
- plugin_adapter
1753
- ].filter((p) => !!p);
1956
+ return /** @type {Plugin[]} */ (
1957
+ [
1958
+ svelte_config.kit.adapter?.vite?.plugins,
1959
+ plugin_setup,
1960
+ plugin_remote_guard,
1961
+ plugin_remote,
1962
+ plugin_virtual_modules,
1963
+ process.env.TEST !== 'true' ? plugin_guard : undefined,
1964
+ plugin_service_worker,
1965
+ plugin_service_worker_env,
1966
+ plugin_compile,
1967
+ plugin_adapter
1968
+ ].filter(Boolean)
1969
+ );
1754
1970
  }
1755
1971
 
1756
1972
  /**
@@ -1785,13 +2001,18 @@ function find_overridden_config(config, resolved_config, enforced_config, path,
1785
2001
  for (const key in enforced_config) {
1786
2002
  if (typeof config === 'object' && key in config && key in resolved_config) {
1787
2003
  const enforced = enforced_config[key];
2004
+ const resolved = resolved_config[key];
1788
2005
 
1789
2006
  if (enforced === true) {
1790
- if (config[key] !== resolved_config[key]) {
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) {
1791
2012
  out.push(path + key);
1792
2013
  }
1793
2014
  } else {
1794
- find_overridden_config(config[key], resolved_config[key], enforced, path + key + '.', out);
2015
+ find_overridden_config(config[key], resolved, enforced, path + key + '.', out);
1795
2016
  }
1796
2017
  }
1797
2018
  }
@@ -1799,13 +2020,14 @@ function find_overridden_config(config, resolved_config, enforced_config, path,
1799
2020
  }
1800
2021
 
1801
2022
  /**
1802
- * @param {import('types').ValidatedConfig} config
2023
+ * @param {ValidatedConfig} config
1803
2024
  */
1804
2025
  const create_service_worker_module = (config) => dedent`
1805
2026
  if (typeof self === 'undefined' || self instanceof ServiceWorkerGlobalScope === false) {
1806
2027
  throw new Error('This module can only be imported inside a service worker');
1807
2028
  }
1808
2029
 
2030
+ export const base = location.pathname.split('/').slice(0, -1).join('/');
1809
2031
  export const build = [];
1810
2032
  export const files = [
1811
2033
  ${create_assets(config)