@sveltejs/kit 3.0.0-next.4 → 3.0.0-next.7

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 (91) hide show
  1. package/package.json +23 -18
  2. package/src/core/adapt/builder.js +29 -43
  3. package/src/core/adapt/index.js +1 -4
  4. package/src/core/config/index.js +135 -71
  5. package/src/core/config/options.js +291 -244
  6. package/src/core/config/types.d.ts +1 -1
  7. package/src/core/env.js +120 -5
  8. package/src/core/generate_manifest/index.js +12 -15
  9. package/src/core/postbuild/analyse.js +8 -12
  10. package/src/core/postbuild/crawl.js +22 -6
  11. package/src/core/postbuild/prerender.js +40 -23
  12. package/src/core/sync/create_manifest_data/index.js +29 -49
  13. package/src/core/sync/write_client_manifest.js +14 -14
  14. package/src/core/sync/write_non_ambient.js +10 -7
  15. package/src/core/sync/write_root.js +9 -30
  16. package/src/core/sync/write_server.js +13 -11
  17. package/src/core/sync/write_tsconfig.js +2 -4
  18. package/src/core/sync/write_types/index.js +11 -10
  19. package/src/exports/index.js +34 -12
  20. package/src/exports/internal/client.js +5 -0
  21. package/src/exports/internal/env.js +1 -1
  22. package/src/exports/internal/index.js +1 -90
  23. package/src/exports/internal/{event.js → server/event.js} +1 -2
  24. package/src/exports/internal/server/index.js +33 -0
  25. package/src/exports/internal/shared.js +89 -0
  26. package/src/exports/node/index.js +58 -7
  27. package/src/exports/params.js +63 -0
  28. package/src/exports/public.d.ts +299 -145
  29. package/src/exports/url.js +84 -0
  30. package/src/exports/vite/build/build_server.js +6 -1
  31. package/src/exports/vite/dev/index.js +38 -37
  32. package/src/exports/vite/index.js +498 -353
  33. package/src/exports/vite/preview/index.js +16 -8
  34. package/src/exports/vite/utils.js +7 -11
  35. package/src/runtime/app/env/types.d.ts +1 -1
  36. package/src/runtime/app/forms.js +22 -5
  37. package/src/runtime/app/paths/client.js +4 -10
  38. package/src/runtime/app/paths/public.d.ts +0 -28
  39. package/src/runtime/app/paths/server.js +8 -4
  40. package/src/runtime/app/server/remote/form.js +10 -3
  41. package/src/runtime/app/server/remote/query.js +9 -18
  42. package/src/runtime/app/server/remote/requested.js +8 -4
  43. package/src/runtime/app/server/remote/shared.js +5 -7
  44. package/src/runtime/app/state/client.js +1 -2
  45. package/src/runtime/app/stores.js +13 -76
  46. package/src/runtime/client/client.js +205 -167
  47. package/src/runtime/client/fetcher.js +3 -2
  48. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  49. package/src/runtime/client/remote-functions/form.svelte.js +122 -31
  50. package/src/runtime/client/remote-functions/prerender.svelte.js +17 -4
  51. package/src/runtime/client/remote-functions/query/index.js +3 -2
  52. package/src/runtime/client/remote-functions/query/instance.svelte.js +27 -11
  53. package/src/runtime/client/remote-functions/query-batch.svelte.js +4 -3
  54. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +42 -15
  55. package/src/runtime/client/remote-functions/shared.svelte.js +18 -9
  56. package/src/runtime/client/state.svelte.js +66 -49
  57. package/src/runtime/client/types.d.ts +10 -2
  58. package/src/runtime/client/utils.js +0 -96
  59. package/src/runtime/form-utils.js +99 -18
  60. package/src/runtime/server/cookie.js +22 -33
  61. package/src/runtime/server/csrf.js +65 -0
  62. package/src/runtime/server/data/index.js +7 -7
  63. package/src/runtime/server/env_module.js +0 -5
  64. package/src/runtime/server/index.js +2 -2
  65. package/src/runtime/server/page/actions.js +41 -26
  66. package/src/runtime/server/page/index.js +2 -1
  67. package/src/runtime/server/page/load_data.js +1 -1
  68. package/src/runtime/server/page/render.js +36 -47
  69. package/src/runtime/server/page/respond_with_error.js +7 -8
  70. package/src/runtime/server/page/server_routing.js +13 -9
  71. package/src/runtime/server/remote.js +85 -39
  72. package/src/runtime/server/respond.js +92 -58
  73. package/src/runtime/server/utils.js +7 -7
  74. package/src/runtime/telemetry/otel.js +1 -1
  75. package/src/types/ambient.d.ts +10 -5
  76. package/src/types/global-private.d.ts +4 -4
  77. package/src/types/internal.d.ts +17 -20
  78. package/src/types/private.d.ts +33 -1
  79. package/src/types/synthetic/$lib.md +1 -1
  80. package/src/utils/error.js +12 -4
  81. package/src/utils/mime.js +9 -0
  82. package/src/utils/params.js +66 -0
  83. package/src/utils/routing.js +84 -38
  84. package/src/utils/shared-iterator.js +5 -0
  85. package/src/utils/streaming.js +14 -4
  86. package/src/utils/url.js +20 -2
  87. package/src/version.js +1 -1
  88. package/types/index.d.ts +434 -269
  89. package/types/index.d.ts.map +19 -15
  90. package/src/exports/internal/server.js +0 -22
  91. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
@@ -1,7 +1,7 @@
1
- /** @import { Adapter, EnvVarConfig, KitConfig } 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, Manifest, ResolvedConfig, UserConfig, ViteDevServer, Rolldown, EnvironmentOptions } from 'vite' */
4
+ /** @import { Plugin, Manifest, ResolvedConfig, UserConfig, ViteDevServer, Rolldown } from 'vite' */
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import process from 'node:process';
@@ -15,11 +15,14 @@ import {
15
15
  create_sveltekit_env,
16
16
  create_sveltekit_env_public,
17
17
  resolve_explicit_env_entry,
18
+ create_sveltekit_env_service_worker,
18
19
  create_sveltekit_env_service_worker_dev,
19
- create_sveltekit_env_private
20
+ create_sveltekit_env_private,
21
+ create_exported_declarations
20
22
  } from '../../core/env.js';
21
23
  import * as sync from '../../core/sync/sync.js';
22
24
  import { create_assets } from '../../core/sync/create_manifest_data/index.js';
25
+ import { load_and_validate_params } from '../../utils/params.js';
23
26
  import { runtime_directory, logger } from '../../core/utils.js';
24
27
  import { generate_manifest } from '../../core/generate_manifest/index.js';
25
28
  import { build_server_nodes } from './build/build_server.js';
@@ -53,10 +56,13 @@ import {
53
56
  import { import_peer } from '../../utils/import.js';
54
57
  import { compact } from '../../utils/array.js';
55
58
  import { should_ignore, has_children } from './static_analysis/utils.js';
56
- import { process_config } from '../../core/config/index.js';
59
+ import { process_config, split_config, validate_config } from '../../core/config/index.js';
57
60
  import { treeshake_prerendered_remotes } from './build/remote.js';
58
61
 
59
- /** @type {string} */
62
+ /**
63
+ * Populated after Vite plugins' `config` hooks run
64
+ * @type {string}
65
+ */
60
66
  let root;
61
67
 
62
68
  /** @type {import('./types.js').EnforcedConfig} */
@@ -146,46 +152,60 @@ let vite_plugin_svelte;
146
152
 
147
153
  /**
148
154
  * 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 {KitConfig & Omit<SvelteConfig, 'onwarn'>} [config]
155
+ * Any options that don't belong to SvelteKit are passed through to `vite-plugin-svelte`.
156
+ *
157
+ * Since version 3.0.0 you must pass [configuration](configuration) directly.
158
+ *
159
+ * Since version 2.62.0 you can pass configuration directly, in which case `svelte.config.js` is ignored.
160
+ *
161
+ * @param {KitConfig & Omit<Options, 'onwarn'> & Pick<SvelteConfig, 'vitePlugin'>} [config]
151
162
  * @returns {Promise<Plugin[]>}
152
163
  */
153
164
  export async function sveltekit(config) {
154
165
  const cwd = process.cwd();
155
166
 
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);
167
+ // any options passed to the plugin that SvelteKit doesn't use itself are
168
+ // forwarded to vite-plugin-svelte, which does its own validation
169
+ const split = split_config(config ?? {});
170
+ const svelte_config = validate_config(split.svelte_config);
171
+
172
+ if (Array.isArray(svelte_config.preprocess)) {
173
+ svelte_config.preprocess.push(warning_preprocessor);
174
+ } else if (svelte_config.preprocess) {
175
+ svelte_config.preprocess = [svelte_config.preprocess, warning_preprocessor];
176
+ } else {
177
+ svelte_config.preprocess = warning_preprocessor;
172
178
  }
173
179
 
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
180
+ vite_plugin_svelte = await import_peer('@sveltejs/vite-plugin-svelte', cwd);
181
+
182
+ /** @type {Partial<Options>} */
183
+ const inline_vps_config = {
184
+ preprocess: svelte_config.preprocess,
185
+ ...(svelte_config.vitePlugin ?? {}),
186
+ // pass through any options that SvelteKit doesn't use itself, so that
187
+ // the options SvelteKit manages always take precedence
188
+ ...split.vite_plugin_svelte_config,
189
+ // we don't want vite-plugin-svelte to load the svelte.config.js file because
190
+ // we expect options to be passed through the SvelteKit Vite plugin
178
191
  configFile: false
179
192
  };
180
193
 
181
- vite_plugin_svelte = await import_peer('@sveltejs/vite-plugin-svelte', cwd);
194
+ // vite-plugin-svelte inline config options need to be added conditionally
195
+ // because passing undefined causes it to crash
196
+ if (svelte_config.extensions) {
197
+ inline_vps_config.extensions = svelte_config.extensions;
198
+ }
199
+
200
+ if (svelte_config.compilerOptions) {
201
+ inline_vps_config.compilerOptions = svelte_config.compilerOptions;
202
+ }
182
203
 
183
204
  return [
184
- plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }),
185
- ...vite_plugin_svelte.svelte(vite_plugin_svelte_options),
205
+ plugin_root(),
206
+ ...vite_plugin_svelte.svelte(inline_vps_config),
186
207
  ...kit({
187
- svelte_config,
188
- adapter: svelte_config.kit.adapter
208
+ svelte_config
189
209
  })
190
210
  ];
191
211
  }
@@ -196,15 +216,9 @@ function resolve_root(vite_config) {
196
216
  }
197
217
 
198
218
  /**
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: Options;
203
- * svelte_config: import('types').ValidatedConfig;
204
- * }} options
205
219
  * @return {Plugin}
206
220
  */
207
- function plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }) {
221
+ function plugin_root() {
208
222
  return {
209
223
  name: 'vite-plugin-sveltekit-resolve-svelte-config',
210
224
  // make sure it runs first
@@ -214,21 +228,14 @@ function plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }) {
214
228
  handler(config) {
215
229
  root = resolve_root(config);
216
230
 
217
- /** @type {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;
231
+ const config_file = ['svelte.config.js', 'svelte.config.ts'].find((file) =>
232
+ fs.existsSync(path.join(root, file))
233
+ );
234
+ if (config_file) {
235
+ throw new Error(
236
+ `${config_file} is no longer used. Please pass configuration via the \`sveltekit(...)\` plugin in your Vite config.`
237
+ );
225
238
  }
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
239
  }
233
240
  },
234
241
  // TODO: do we even need to set `root` based on the final Vite config?
@@ -252,11 +259,10 @@ function plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }) {
252
259
  * - https://rolldown.rs/apis/plugin-api#output-generation-hooks
253
260
  *
254
261
  * @param {object} opts
255
- * @param {import('types').ValidatedConfig} opts.svelte_config options are only resolved after the Vite `config` hook runs
256
- * @param {Adapter | undefined} opts.adapter
262
+ * @param {import('types').ValidatedConfig} opts.svelte_config
257
263
  * @return {Plugin[]}
258
264
  */
259
- function kit({ svelte_config, adapter }) {
265
+ function kit({ svelte_config }) {
260
266
  /** @type {typeof import('vite')} */
261
267
  let vite;
262
268
 
@@ -289,7 +295,7 @@ function kit({ svelte_config, adapter }) {
289
295
  let initial_config;
290
296
 
291
297
  /** @type {string | null} */
292
- let service_worker_entry_file = resolve_entry(svelte_config.kit.files.serviceWorker);
298
+ let service_worker_entry_file;
293
299
  /** @type {import('node:path').ParsedPath} */
294
300
  let parsed_service_worker;
295
301
 
@@ -309,6 +315,9 @@ function kit({ svelte_config, adapter }) {
309
315
  const sourcemapIgnoreList = /** @param {string} relative_path */ (relative_path) =>
310
316
  relative_path.includes('node_modules') || relative_path.includes(kit.outDir);
311
317
 
318
+ /** @type {string} name for `globalThis.__sveltekit_xxx` */
319
+ let kit_global;
320
+
312
321
  /** @type {Plugin} */
313
322
  const plugin_setup = {
314
323
  name: 'vite-plugin-sveltekit-setup',
@@ -316,10 +325,6 @@ function kit({ svelte_config, adapter }) {
316
325
  options: svelte_config
317
326
  },
318
327
 
319
- applyToEnvironment(environment) {
320
- return environment.name !== 'serviceWorker';
321
- },
322
-
323
328
  /**
324
329
  * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
325
330
  * @see https://vitejs.dev/guide/api-plugin.html#config
@@ -330,16 +335,17 @@ function kit({ svelte_config, adapter }) {
330
335
  initial_config = config;
331
336
  is_build = config_env.command === 'build';
332
337
 
333
- ({ kit } = svelte_config);
338
+ ({ kit } = process_config(svelte_config, root));
334
339
  out_dir = posixify(kit.outDir);
335
340
  out = `${out_dir}/output`;
336
341
 
337
342
  version_hash = hash(kit.version.name);
338
343
 
339
- env = loadEnv(config_env.mode, kit.env.dir, '');
344
+ kit_global = is_build
345
+ ? `globalThis.__sveltekit_${version_hash}`
346
+ : 'globalThis.__sveltekit_dev';
340
347
 
341
- service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
342
- parsed_service_worker = path.parse(kit.files.serviceWorker);
348
+ env = loadEnv(config_env.mode, kit.env.dir, '');
343
349
 
344
350
  vite = await import_peer('vite', root);
345
351
 
@@ -350,10 +356,18 @@ function kit({ svelte_config, adapter }) {
350
356
  const allow = new Set([
351
357
  kit.files.lib,
352
358
  kit.files.routes,
359
+ kit.files.src,
353
360
  kit.outDir,
354
361
  path.resolve(root, kit.files.src),
355
362
  path.resolve(root, 'node_modules'),
356
- path.resolve(process.cwd(), 'node_modules')
363
+ // ensures that the client entry is served even if it's located outside
364
+ // the local node_modules, such as the pnpm global virtual store
365
+ runtime_directory,
366
+ path.resolve('node_modules'),
367
+ // include the directory that contains the workspaces declaration
368
+ // which usually also contains hoisted packages
369
+ // see https://vite.dev/guide/api-javascript#searchforworkspaceroot
370
+ path.resolve(vite.searchForWorkspaceRoot(process.cwd()), 'node_modules')
357
371
  ]);
358
372
 
359
373
  // We can only add directories to the allow list, so we find out
@@ -381,7 +395,7 @@ function kit({ svelte_config, adapter }) {
381
395
  sourcemapIgnoreList,
382
396
  watch: {
383
397
  ignored: [
384
- // Ignore all siblings of config.kit.outDir/generated
398
+ // Ignore all siblings of config.outDir/generated
385
399
  `${out_dir}/!(generated)`
386
400
  ]
387
401
  }
@@ -452,7 +466,7 @@ function kit({ svelte_config, adapter }) {
452
466
  __SVELTEKIT_PATHS_RELATIVE__: s(kit.paths.relative),
453
467
  __SVELTEKIT_CLIENT_ROUTING__: s(kit.router.resolution === 'client'),
454
468
  __SVELTEKIT_HASH_ROUTING__: s(kit.router.type === 'hash'),
455
- __SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.experimental.tracing.server),
469
+ __SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.tracing.server),
456
470
  __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__: s(kit.experimental.handleRenderingErrors),
457
471
  __SVELTEKIT_ROOT__: s(root),
458
472
  __SVELTEKIT_DEV__: s(!is_build)
@@ -461,7 +475,7 @@ function kit({ svelte_config, adapter }) {
461
475
  if (is_build) {
462
476
  new_config.define = {
463
477
  ...define,
464
- __SVELTEKIT_ADAPTER_NAME__: s(adapter?.name),
478
+ __SVELTEKIT_ADAPTER_NAME__: s(kit.adapter?.name),
465
479
  __SVELTEKIT_APP_VERSION_FILE__: s(`${kit.appDir}/version.json`),
466
480
  __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: s(kit.version.pollInterval)
467
481
  };
@@ -471,7 +485,7 @@ function kit({ svelte_config, adapter }) {
471
485
  new_config.define = {
472
486
  ...define,
473
487
  __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: '0',
474
- __SVELTEKIT_PAYLOAD__: 'globalThis.__sveltekit_dev',
488
+ __SVELTEKIT_PAYLOAD__: kit_global,
475
489
  __SVELTEKIT_HAS_SERVER_LOAD__: 'true',
476
490
  __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true'
477
491
  };
@@ -481,6 +495,23 @@ function kit({ svelte_config, adapter }) {
481
495
  /** @type {NonNullable<UserConfig['ssr']>} */ (new_config.ssr).external = ['cookie'];
482
496
  }
483
497
 
498
+ // Vite's `define` is a compile-time text replacement, but Vitest strips
499
+ // user `define` from the server config and reinstalls the values only as
500
+ // `globalThis` properties inside test workers, so anything
501
+ // that runs outside of a test will freak out over
502
+ // them not being defined
503
+ if (process.env.VITEST === 'true') {
504
+ for (const key in new_config.define) {
505
+ const value = new_config.define[key];
506
+ try {
507
+ /** @type {Record<string, any>} */ (globalThis)[key] = JSON.parse(value);
508
+ } catch {
509
+ // `kit_global` isn't JSON, so don't try to parse it. We may one day
510
+ // need to define it in Vitest somehow but for now, ignore it
511
+ }
512
+ }
513
+ }
514
+
484
515
  warn_overridden_config(config, new_config);
485
516
 
486
517
  return new_config;
@@ -505,10 +536,6 @@ function kit({ svelte_config, adapter }) {
505
536
  const plugin_virtual_modules = {
506
537
  name: 'vite-plugin-sveltekit-virtual-modules',
507
538
 
508
- applyToEnvironment(environment) {
509
- return environment.name !== 'serviceWorker';
510
- },
511
-
512
539
  async configResolved(config) {
513
540
  explicit_env_entry = resolve_explicit_env_entry(kit);
514
541
  explicit_env_config = await sync.env(kit, explicit_env_entry, config.root, config.mode);
@@ -544,52 +571,32 @@ function kit({ svelte_config, adapter }) {
544
571
  });
545
572
  },
546
573
 
547
- resolveId(id, importer) {
548
- if (id === '__sveltekit/manifest') {
549
- return `${out_dir}/generated/client-optimized/app.js`;
550
- }
551
-
552
- // If importing from a service-worker, only allow $service-worker & $app/env/public, but none of the other virtual modules.
553
- // This check won't catch transitive imports, but it will warn when the import comes from a service-worker directly.
554
- // Transitive imports will be caught during the build.
555
- // TODO move this logic to plugin_guard. add a filter to this resolveId when doing so
556
- // TODO allow $app/env/public
557
- if (importer) {
558
- const parsed_importer = path.parse(importer);
559
-
560
- const importer_is_service_worker =
561
- parsed_importer.dir === parsed_service_worker.dir &&
562
- parsed_importer.name === parsed_service_worker.name;
574
+ applyToEnvironment(environment) {
575
+ return environment.name !== 'serviceWorker';
576
+ },
563
577
 
564
- if (
565
- importer_is_service_worker &&
566
- id !== '$service-worker' &&
567
- id !== 'virtual:$app/env/public' &&
568
- id !== '__sveltekit/env/service-worker'
569
- ) {
570
- throw new Error(
571
- `Cannot import ${normalize_id(
572
- id,
573
- normalized_lib,
574
- normalized_cwd
575
- )} into service-worker code. Only the modules $service-worker and $app/env/public are available in service workers.`
576
- );
578
+ resolveId: {
579
+ filter: {
580
+ id: [exactRegex('$service-worker'), prefixRegex('__sveltekit/')]
581
+ },
582
+ handler(id) {
583
+ if (id === '__sveltekit/manifest') {
584
+ return `${out_dir}/generated/client-optimized/app.js`;
577
585
  }
578
- }
579
586
 
580
- if (id === '$service-worker') {
581
- // ids with :$ don't work with reverse proxies like nginx
582
- return `\0virtual:${id.substring(1)}`;
583
- }
587
+ if (id === '$service-worker') {
588
+ // ids with :$ don't work with reverse proxies like nginx
589
+ return `\0virtual:${id.substring(1)}`;
590
+ }
584
591
 
585
- if (id === '__sveltekit/remote') {
586
- return `${runtime_directory}/client/remote-functions/index.js`;
587
- }
592
+ if (id === '__sveltekit/remote') {
593
+ return `${runtime_directory}/client/remote-functions/index.js`;
594
+ }
588
595
 
589
- if (id.startsWith('__sveltekit/')) {
590
596
  return `\0virtual:${id}`;
591
597
  }
592
598
  },
599
+
593
600
  load: {
594
601
  filter: {
595
602
  id: [
@@ -603,22 +610,18 @@ function kit({ svelte_config, adapter }) {
603
610
  ]
604
611
  },
605
612
  handler(id) {
606
- const global = is_build
607
- ? `globalThis.__sveltekit_${version_hash}`
608
- : 'globalThis.__sveltekit_dev';
609
-
610
613
  switch (id) {
611
614
  case service_worker:
612
615
  return create_service_worker_module(svelte_config);
613
616
 
614
617
  case sveltekit_env:
615
- return create_sveltekit_env(explicit_env_config, env, explicit_env_entry);
618
+ return create_sveltekit_env(explicit_env_config, env, explicit_env_entry, !is_build);
616
619
 
617
620
  case sveltekit_env_public_client:
618
621
  return create_sveltekit_env_public(
619
622
  explicit_env_config,
620
623
  env,
621
- `const env = ${global}.env;`
624
+ `const env = ${kit_global}.env;`
622
625
  );
623
626
 
624
627
  case sveltekit_env_public_server:
@@ -632,7 +635,15 @@ function kit({ svelte_config, adapter }) {
632
635
  return create_sveltekit_env_private(explicit_env_config, env);
633
636
 
634
637
  case sveltekit_env_service_worker:
635
- return create_sveltekit_env_service_worker_dev(explicit_env_config, env, global);
638
+ return is_build
639
+ ? create_sveltekit_env_service_worker(
640
+ explicit_env_config,
641
+ env,
642
+ kit_global,
643
+ kit.paths.base,
644
+ kit.appDir
645
+ )
646
+ : create_sveltekit_env_service_worker_dev(explicit_env_config, env, kit_global);
636
647
 
637
648
  case sveltekit_server: {
638
649
  return dedent`
@@ -656,7 +667,10 @@ function kit({ svelte_config, adapter }) {
656
667
 
657
668
  /** @type {Map<string, Set<string>>} */
658
669
  const import_map = new Map();
659
- const server_only_pattern = /.*\.server\..+/;
670
+ // Matches any ID that has .server. in its filename
671
+ const server_only_module_pattern = /\.server\.[^/]+$/;
672
+ // Matches any ID that has /server/ in its path
673
+ const server_only_directory_pattern = /\/server\//;
660
674
 
661
675
  /**
662
676
  * Ensures that client-side code can't accidentally import server-side code,
@@ -706,8 +720,8 @@ function kit({ svelte_config, adapter }) {
706
720
  id: [
707
721
  exactRegex(app_server),
708
722
  exactRegex(app_env_private),
709
- /\/server\//,
710
- new RegExp(`${server_only_pattern.source}$`)
723
+ server_only_module_pattern,
724
+ server_only_directory_pattern
711
725
  ]
712
726
  },
713
727
  handler(id) {
@@ -722,8 +736,8 @@ function kit({ svelte_config, adapter }) {
722
736
  const is_server_only =
723
737
  normalized === '$app/env/private' ||
724
738
  normalized === '$app/server' ||
725
- normalized.startsWith('$lib/server/') ||
726
- (is_internal && server_only_pattern.test(path.basename(id)));
739
+ (normalized.startsWith('$lib/') && server_only_directory_pattern.test(id)) ||
740
+ (is_internal && server_only_module_pattern.test(id));
727
741
 
728
742
  // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context
729
743
  // TODO: address https://github.com/sveltejs/kit/issues/12529
@@ -744,49 +758,56 @@ function kit({ svelte_config, adapter }) {
744
758
  if (manifest_data.hooks.client) entrypoints.add(manifest_data.hooks.client);
745
759
  if (manifest_data.hooks.universal) entrypoints.add(manifest_data.hooks.universal);
746
760
 
747
- const chain = [normalized];
748
-
749
- let current = normalized;
750
- let includes_remote_file = false;
761
+ // Walk up the import graph from the server-only module, looking for a chain
762
+ // that leads back to a client entrypoint. We search all candidates (not just
763
+ // the first) because a module can be imported by both server and client code,
764
+ // and a greedy first-match could follow a server-only branch that never
765
+ // reaches an entrypoint — see https://github.com/sveltejs/kit/issues/16232
766
+ /** @type {Set<string>} */
767
+ const visited = new Set([normalized]);
751
768
 
752
- while (true) {
769
+ /**
770
+ * @param {string} current
771
+ * @param {string[]} chain
772
+ * @returns {string[] | null}
773
+ */
774
+ function find_chain(current, chain) {
753
775
  const importers = import_map.get(current);
754
- if (!importers) break;
776
+ if (!importers) return null;
755
777
 
756
- const candidates = Array.from(importers).filter((importer) => !chain.includes(importer));
757
- if (candidates.length === 0) break;
778
+ for (const importer of importers) {
779
+ if (visited.has(importer)) continue;
780
+ visited.add(importer);
758
781
 
759
- chain.push((current = candidates[0]));
782
+ const next_chain = [...chain, importer];
783
+ if (entrypoints.has(importer)) {
784
+ return next_chain;
785
+ }
786
+ const result = find_chain(importer, next_chain);
787
+ if (result) return result;
788
+ }
789
+ return null;
790
+ }
760
791
 
761
- includes_remote_file ||= svelte_config.kit.moduleExtensions.some((ext) => {
762
- return current.endsWith(`.remote${ext}`);
763
- });
792
+ const chain = find_chain(normalized, [normalized]);
764
793
 
765
- if (entrypoints.has(current)) {
766
- const pyramid = chain
767
- .reverse()
768
- .map((id, i) => {
769
- return `${' '.repeat(i + 1)}${id}`;
770
- })
771
- .join(' imports\n');
772
-
773
- if (includes_remote_file) {
774
- error_for_missing_config(
775
- 'remote functions',
776
- 'kit.experimental.remoteFunctions',
777
- 'true'
778
- );
779
- }
794
+ if (chain) {
795
+ const pyramid = chain
796
+ .reverse()
797
+ .map((id, i) => {
798
+ return `${' '.repeat(i + 1)}${id}`;
799
+ })
800
+ .join(' imports\n');
780
801
 
781
- let message = `Cannot import ${normalized} into code that runs in the browser, as this could leak sensitive information.`;
782
- message += `\n\n${pyramid}`;
783
- message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`;
802
+ let message = `Cannot import ${normalized} into code that runs in the browser, as this could leak sensitive information.`;
803
+ message += `\n\n${pyramid}`;
804
+ message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`;
784
805
 
785
- throw stackless(message);
786
- }
806
+ throw stackless(message);
787
807
  }
788
808
 
789
- throw new Error('An impossible situation occurred');
809
+ // No chain from this server-only module to a client entrypoint was found —
810
+ // the module is only imported from server code, which is valid.
790
811
  }
791
812
  }
792
813
  };
@@ -808,7 +829,7 @@ function kit({ svelte_config, adapter }) {
808
829
  name: 'vite-plugin-sveltekit-remote',
809
830
 
810
831
  applyToEnvironment(environment) {
811
- return environment.name !== 'serviceWorker';
832
+ return svelte_config.kit.experimental.remoteFunctions && environment.name !== 'serviceWorker';
812
833
  },
813
834
 
814
835
  // prevent other plugins from resolving our remote virtual module
@@ -826,10 +847,6 @@ function kit({ svelte_config, adapter }) {
826
847
  id: prefixRegex('\0sveltekit-remote:')
827
848
  },
828
849
  handler(id) {
829
- if (!kit.experimental.remoteFunctions) {
830
- return null;
831
- }
832
-
833
850
  // On-the-fly generated entry point for remote file just forwards the original module
834
851
  // We're not using manualChunks because it can cause problems with circular dependencies
835
852
  // (e.g. https://github.com/sveltejs/kit/issues/14679) and module ordering in general
@@ -842,18 +859,10 @@ function kit({ svelte_config, adapter }) {
842
859
  },
843
860
 
844
861
  configureServer(_dev_server) {
845
- if (!kit.experimental.remoteFunctions) {
846
- return;
847
- }
848
-
849
862
  dev_server = _dev_server;
850
863
  },
851
864
 
852
865
  async transform(code, id) {
853
- if (!kit.experimental.remoteFunctions) {
854
- return;
855
- }
856
-
857
866
  const normalized = normalize_id(id, normalized_lib, normalized_cwd);
858
867
  if (!svelte_config.kit.moduleExtensions.some((ext) => normalized.endsWith(`.remote${ext}`))) {
859
868
  return;
@@ -879,7 +888,7 @@ function kit({ svelte_config, adapter }) {
879
888
  '\n\n' +
880
889
  dedent`
881
890
  import * as $$_self_$$ from './${path.basename(id)}';
882
- import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal';
891
+ import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal/server';
883
892
 
884
893
  ${dev_server ? 'await Promise.resolve()' : ''}
885
894
 
@@ -937,15 +946,17 @@ function kit({ svelte_config, adapter }) {
937
946
  }
938
947
  }
939
948
 
940
- let namespace = '__remote';
941
- let uid = 1;
942
- while (map.has(namespace)) namespace = `__remote${uid++}`;
943
-
944
- const exports = Array.from(map).map(([name, type]) => {
945
- return `export const ${name} = ${namespace}.${type}('${remote.hash}/${name}');`;
946
- });
949
+ const { namespace, declarations, reexports } = create_exported_declarations(
950
+ map.keys(),
951
+ (name, ns) => `${ns}.${map.get(name)}('${remote.hash}/${name}')`,
952
+ '__remote'
953
+ );
947
954
 
948
- let result = `import * as ${namespace} from '__sveltekit/remote';\n\n${exports.join('\n')}\n`;
955
+ let result = `import * as ${namespace} from '__sveltekit/remote';\n\n${declarations.join('\n')}`;
956
+ if (reexports.length > 0) {
957
+ result += `\nexport { ${reexports.join(', ')} };`;
958
+ }
959
+ result += '\n';
949
960
 
950
961
  if (dev_server) {
951
962
  result += `\nimport.meta.hot?.accept();\n`;
@@ -957,6 +968,26 @@ function kit({ svelte_config, adapter }) {
957
968
  }
958
969
  };
959
970
 
971
+ /** @type {Plugin} */
972
+ const plugin_remote_guard = {
973
+ name: 'vite-plugin-sveltekit-remote-guard',
974
+
975
+ applyToEnvironment() {
976
+ return !svelte_config.kit.experimental.remoteFunctions;
977
+ },
978
+
979
+ transform: {
980
+ filter: {
981
+ id: new RegExp(
982
+ `.remote(${svelte_config.kit.moduleExtensions.join('|')})$`.replaceAll('.', '\\.')
983
+ )
984
+ },
985
+ handler() {
986
+ error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
987
+ }
988
+ }
989
+ };
990
+
960
991
  /** @type {Manifest} */
961
992
  let vite_server_manifest;
962
993
  /** @type {Manifest | null} */
@@ -965,7 +996,7 @@ function kit({ svelte_config, adapter }) {
965
996
  let prerendered;
966
997
 
967
998
  /** @type {Set<string>} */
968
- let build;
999
+ let build_files;
969
1000
  /** @type {string} */
970
1001
  let service_worker_code;
971
1002
 
@@ -976,43 +1007,134 @@ function kit({ svelte_config, adapter }) {
976
1007
  const plugin_service_worker = {
977
1008
  name: 'vite-plugin-sveltekit-service-worker',
978
1009
 
1010
+ config(config) {
1011
+ service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
1012
+ parsed_service_worker = path.parse(kit.files.serviceWorker);
1013
+
1014
+ if (!service_worker_entry_file) return;
1015
+
1016
+ service_worker_entry_file = posixify(service_worker_entry_file);
1017
+
1018
+ if (kit.paths.assets) {
1019
+ throw new Error('Cannot use service worker alongside config.paths.assets');
1020
+ }
1021
+
1022
+ const user_service_worker_output_config =
1023
+ config.environments?.serviceWorker?.build?.rolldownOptions?.output;
1024
+
1025
+ /** @type {import('vite').UserConfig} */
1026
+ const new_config = {
1027
+ environments: {
1028
+ serviceWorker: {
1029
+ build: {
1030
+ modulePreload: false,
1031
+ rolldownOptions: {
1032
+ external: [`${kit.paths.base}/${kit.appDir}/env.js`],
1033
+ input: {
1034
+ 'service-worker': service_worker_entry_file
1035
+ },
1036
+ output: {
1037
+ format: 'es',
1038
+ entryFileNames: 'service-worker.js',
1039
+ assetFileNames: `${kit.appDir}/immutable/assets/[name].[hash][extname]`,
1040
+ codeSplitting:
1041
+ (Array.isArray(user_service_worker_output_config)
1042
+ ? user_service_worker_output_config[0].codeSplitting
1043
+ : user_service_worker_output_config?.codeSplitting) ?? false
1044
+ }
1045
+ },
1046
+ outDir: `${out}/client`,
1047
+ minify: initial_config.build?.minify,
1048
+ // avoid overwriting the client build Vite manifest
1049
+ manifest: '.vite/service-worker-manifest.json'
1050
+ },
1051
+ consumer: 'client'
1052
+ }
1053
+ }
1054
+ };
1055
+
1056
+ warn_overridden_config(config, new_config);
1057
+
1058
+ return new_config;
1059
+ },
1060
+
1061
+ // our serviceWorker environment only exists when building because Vite only
1062
+ // supports the default client environment during development (for now)
979
1063
  applyToEnvironment(environment) {
980
1064
  return environment.name === 'serviceWorker';
981
1065
  },
982
1066
 
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
- }
1067
+ resolveId(id, importer) {
1068
+ // If importing from a service-worker, only allow $service-worker & $app/env/public, but none of the other virtual modules.
1069
+ // This check won't catch transitive imports, but it will warn when the import comes from a service-worker directly.
1070
+ // Transitive imports will be caught during the build.
1071
+ if (importer) {
1072
+ const parsed_importer = path.parse(importer);
1073
+
1074
+ const importer_is_service_worker =
1075
+ parsed_importer.dir === parsed_service_worker.dir &&
1076
+ parsed_importer.name === parsed_service_worker.name;
989
1077
 
990
- if (id.startsWith('__sveltekit')) {
991
- return `\0virtual:${id}`;
1078
+ if (
1079
+ importer_is_service_worker &&
1080
+ id !== '$service-worker' &&
1081
+ id !== 'virtual:$app/env/public' &&
1082
+ id !== '__sveltekit/env/service-worker'
1083
+ ) {
1084
+ throw new Error(
1085
+ `Cannot import ${normalize_id(
1086
+ id,
1087
+ normalized_lib,
1088
+ normalized_cwd
1089
+ )} into service-worker code. Only the modules $service-worker and $app/env/public are available in service workers.`
1090
+ );
992
1091
  }
993
1092
  }
1093
+
1094
+ if (id.startsWith('$app/') || id === '$service-worker') {
1095
+ // ids with :$ don't work with reverse proxies like nginx
1096
+ return `\0virtual:${id.substring(1)}`;
1097
+ }
1098
+
1099
+ if (id.startsWith('__sveltekit')) {
1100
+ return `\0virtual:${id}`;
1101
+ }
994
1102
  },
995
1103
 
996
- load(id) {
997
- if (!build) {
998
- build = new Set();
999
- const manifest = vite_client_manifest ?? vite_server_manifest;
1000
- for (const key in manifest) {
1001
- const { file, css = [], assets = [] } = manifest[key];
1002
- build.add(file);
1003
- css.forEach((file) => build.add(file));
1004
- assets.forEach((file) => build.add(file));
1005
- }
1104
+ load: {
1105
+ filter: {
1106
+ id: [prefixRegex('\0virtual:')]
1107
+ },
1108
+ handler(id) {
1109
+ if (!build_files) {
1110
+ build_files = new Set();
1111
+ const manifest = vite_client_manifest ?? vite_server_manifest;
1112
+ for (const key in manifest) {
1113
+ const { file, css = [], assets = [] } = manifest[key];
1114
+ build_files.add(file);
1115
+ css.forEach((file) => build_files.add(file));
1116
+ assets.forEach((file) => build_files.add(file));
1117
+ }
1006
1118
 
1007
- // in a service worker, `location` is the location of the service worker itself,
1008
- // which is guaranteed to be `<base>/service-worker.js`
1009
- const base = "location.pathname.split('/').slice(0, -1).join('/')";
1119
+ if (kit.output.bundleStrategy === 'inline') {
1120
+ // the bundle and stylesheet are inlined into the page and their files
1121
+ // deleted, so they must not appear in the list of cacheable assets
1122
+ for (const file of build_files) {
1123
+ if (!fs.existsSync(`${out}/client/${file}`)) {
1124
+ build_files.delete(file);
1125
+ }
1126
+ }
1127
+ }
1010
1128
 
1011
- service_worker_code = dedent`
1129
+ // in a service worker, `location` is the location of the service worker itself,
1130
+ // which is guaranteed to be `<base>/service-worker.js`
1131
+ const base = "location.pathname.split('/').slice(0, -1).join('/')";
1132
+
1133
+ service_worker_code = dedent`
1012
1134
  export const base = /*@__PURE__*/ ${base};
1013
1135
 
1014
1136
  export const build = [
1015
- ${Array.from(build)
1137
+ ${Array.from(build_files)
1016
1138
  .map((file) => `base + ${s(`/${file}`)}`)
1017
1139
  .join(',\n')}
1018
1140
  ];
@@ -1029,65 +1151,71 @@ function kit({ svelte_config, adapter }) {
1029
1151
  ];
1030
1152
 
1031
1153
  export const version = ${s(kit.version.name)};
1032
- `;
1033
- }
1034
-
1035
- if (!id.startsWith('\0virtual:')) return;
1154
+ `;
1155
+ }
1036
1156
 
1037
- const global = is_build
1038
- ? `globalThis.__sveltekit_${version_hash}`
1039
- : 'globalThis.__sveltekit_dev';
1157
+ if (id === service_worker) {
1158
+ return service_worker_code;
1159
+ }
1040
1160
 
1041
- if (id === service_worker) {
1042
- return service_worker_code;
1043
- }
1161
+ if (id === sveltekit_env_service_worker) {
1162
+ return is_build
1163
+ ? create_sveltekit_env_service_worker(
1164
+ explicit_env_config,
1165
+ env,
1166
+ kit_global,
1167
+ kit.paths.base,
1168
+ kit.appDir
1169
+ )
1170
+ : create_sveltekit_env_service_worker_dev(explicit_env_config, env, kit_global);
1171
+ }
1044
1172
 
1045
- if (id === sveltekit_env_service_worker) {
1046
- return create_sveltekit_env_service_worker_dev(explicit_env_config, env, global);
1047
- }
1173
+ if (id === sveltekit_env_public_client) {
1174
+ return create_sveltekit_env_public(
1175
+ explicit_env_config,
1176
+ env,
1177
+ `const env = ${kit_global}.env;`
1178
+ );
1179
+ }
1048
1180
 
1049
- if (id === sveltekit_env_public_client) {
1050
- return create_sveltekit_env_public(explicit_env_config, env, `const env = ${global}.env;`);
1181
+ const normalized_cwd = vite.normalizePath(vite_config.root);
1182
+ const normalized_lib = vite.normalizePath(kit.files.lib);
1183
+ const relative = normalize_id(id, normalized_lib, normalized_cwd);
1184
+ const stripped = strip_virtual_prefix(relative);
1185
+ throw new Error(
1186
+ `Cannot import ${stripped} into service-worker code. Only the modules $service-worker and $app/env/public are available in service workers.`
1187
+ );
1051
1188
  }
1052
-
1053
- const normalized_cwd = vite.normalizePath(vite_config.root);
1054
- const normalized_lib = vite.normalizePath(kit.files.lib);
1055
- const relative = normalize_id(id, normalized_lib, normalized_cwd);
1056
- const stripped = strip_virtual_prefix(relative);
1057
- throw new Error(
1058
- `Cannot import ${stripped} into service-worker code. Only the modules $service-worker and $app/env/public are available in service workers.`
1059
- );
1060
1189
  }
1061
1190
  };
1062
1191
 
1063
1192
  /** @type {Plugin} */
1064
1193
  const plugin_service_worker_env = {
1065
1194
  name: 'vite-plugin-sveltekit-service-worker-env',
1066
-
1067
- transform: {
1068
- filter: {
1069
- id: service_worker_entry_file || '<skip>'
1070
- },
1071
- handler(code) {
1072
- // in dev, we prepend the service worker with an import that
1073
- // configures `env`, in case `$app/env/public` is imported,
1074
- // in prod, where we currently use non-module service
1075
- // workers, we have to use `importScripts` instead
1076
- return {
1077
- code: `import '__sveltekit/env/service-worker';\n${code}`
1078
- };
1079
- }
1195
+ applyToEnvironment(environment) {
1196
+ return !!service_worker_entry_file && environment.config.consumer === 'client';
1197
+ },
1198
+ transform(code, id) {
1199
+ if (id !== service_worker_entry_file) return;
1200
+
1201
+ // prepend the service worker with an import that configures
1202
+ // `env`, in case `$app/env/public` is imported. In production
1203
+ // this is required: dynamic public env vars aren't known at
1204
+ // build time, so `env.js` is loaded at runtime. In dev, the
1205
+ // imported module just inlines the current values instead.
1206
+ return {
1207
+ code: `import '__sveltekit/env/service-worker';\n${code}`
1208
+ };
1080
1209
  }
1081
1210
  };
1082
1211
 
1212
+ /** @type {() => Promise<void> | undefined} */
1213
+ let finalise;
1214
+
1083
1215
  /** @type {Plugin} */
1084
1216
  const plugin_compile = {
1085
1217
  name: 'vite-plugin-sveltekit-compile',
1086
1218
 
1087
- applyToEnvironment(environment) {
1088
- return environment.name !== 'serviceWorker';
1089
- },
1090
-
1091
1219
  /**
1092
1220
  * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
1093
1221
  * @see https://vitejs.dev/guide/api-plugin.html#config
@@ -1106,6 +1234,7 @@ function kit({ svelte_config, adapter }) {
1106
1234
  const server_input = {
1107
1235
  index: `${runtime_directory}/server/index.js`,
1108
1236
  internal: `${out_dir}/generated/server/internal.js`,
1237
+ env: '__sveltekit/env',
1109
1238
  ['remote-entry']: `${runtime_directory}/app/server/remote/index.js`
1110
1239
  };
1111
1240
 
@@ -1134,11 +1263,10 @@ function kit({ svelte_config, adapter }) {
1134
1263
  }
1135
1264
  });
1136
1265
 
1137
- // ...and every matcher
1138
- Object.entries(manifest_data.matchers).forEach(([key, file]) => {
1139
- const name = posixify(path.join('entries/matchers', key));
1140
- server_input[name] = path.resolve(root, file);
1141
- });
1266
+ // ...and the params file
1267
+ if (manifest_data.params) {
1268
+ server_input['entries/params'] = path.resolve(root, manifest_data.params);
1269
+ }
1142
1270
 
1143
1271
  // ...and the hooks files
1144
1272
  if (manifest_data.hooks.server) {
@@ -1156,15 +1284,8 @@ function kit({ svelte_config, adapter }) {
1156
1284
  path.join(kit.files.src, 'instrumentation.server')
1157
1285
  );
1158
1286
  if (server_instrumentation) {
1159
- if (adapter && !adapter.supports?.instrumentation?.()) {
1160
- throw new Error(`${server_instrumentation} is unsupported in ${adapter.name}.`);
1161
- }
1162
- if (!kit.experimental.instrumentation.server) {
1163
- error_for_missing_config(
1164
- '`instrumentation.server.js`',
1165
- 'kit.experimental.instrumentation.server',
1166
- 'true'
1167
- );
1287
+ if (kit.adapter && !kit.adapter.supports?.instrumentation?.()) {
1288
+ throw new Error(`${server_instrumentation} is unsupported in ${kit.adapter.name}.`);
1168
1289
  }
1169
1290
  server_input['instrumentation.server'] = server_instrumentation;
1170
1291
  }
@@ -1195,9 +1316,16 @@ function kit({ svelte_config, adapter }) {
1195
1316
  .map(() => '..')
1196
1317
  .join('/') + '/../';
1197
1318
 
1319
+ const relative = kit.paths.relative !== false || !!kit.paths.assets;
1320
+
1198
1321
  new_config = {
1199
1322
  appType: 'custom',
1200
- base: './',
1323
+ // Affects how Vite loads JS assets on the client.
1324
+ // If the initial HTML we render uses an absolute path for assets,
1325
+ // the additional chunks Vite loads must also use an absolute path.
1326
+ // Otherwise, you end up with additional chunks being loaded relative
1327
+ // to the current chunk rather than the root.
1328
+ base: relative ? './' : base,
1201
1329
  build: {
1202
1330
  cssCodeSplit: !inline,
1203
1331
  cssMinify:
@@ -1261,7 +1389,8 @@ function kit({ svelte_config, adapter }) {
1261
1389
  format: inline ? 'iife' : 'esm',
1262
1390
  entryFileNames: `${prefix}/[name].[hash].js`,
1263
1391
  chunkFileNames: `${prefix}/chunks/[hash].js`,
1264
- codeSplitting: svelte_config.kit.output.bundleStrategy === 'split'
1392
+ codeSplitting:
1393
+ svelte_config.kit.output.bundleStrategy === 'split' ? undefined : false
1265
1394
  },
1266
1395
  // This silences Rolldown warnings about not supporting `import.meta`
1267
1396
  // for the `iife` output format. We don't care because it's
@@ -1276,7 +1405,7 @@ function kit({ svelte_config, adapter }) {
1276
1405
  }
1277
1406
  },
1278
1407
  define: {
1279
- __SVELTEKIT_PAYLOAD__: `globalThis.__sveltekit_${version_hash}`
1408
+ __SVELTEKIT_PAYLOAD__: kit_global
1280
1409
  }
1281
1410
  }
1282
1411
  },
@@ -1292,9 +1421,7 @@ function kit({ svelte_config, adapter }) {
1292
1421
  // E.g. Vite generates `new URL('/asset.png', import.meta).href` for a relative path vs just '/asset.png'.
1293
1422
  // That's larger and takes longer to run and also causes an HTML diff between SSR and client
1294
1423
  // causing us to do a more expensive hydration check.
1295
- return {
1296
- relative: kit.paths.relative !== false || !!kit.paths.assets
1297
- };
1424
+ return { relative };
1298
1425
  }
1299
1426
 
1300
1427
  // _app/immutable/assets files
@@ -1308,29 +1435,6 @@ function kit({ svelte_config, adapter }) {
1308
1435
  },
1309
1436
  publicDir: kit.files.assets
1310
1437
  };
1311
-
1312
- if (service_worker_entry_file) {
1313
- /** @type {Record<string, EnvironmentOptions>} */ (
1314
- new_config.environments
1315
- ).serviceWorker = {
1316
- build: {
1317
- modulePreload: false,
1318
- rolldownOptions: {
1319
- input: {
1320
- 'service-worker': service_worker_entry_file
1321
- },
1322
- output: {
1323
- entryFileNames: 'service-worker.js',
1324
- assetFileNames: `${kit.appDir}/immutable/assets/[name].[hash][extname]`,
1325
- codeSplitting: false
1326
- }
1327
- },
1328
- outDir: `${out}/client`,
1329
- minify: initial_config.build?.minify
1330
- },
1331
- consumer: 'client'
1332
- };
1333
- }
1334
1438
  } else {
1335
1439
  new_config = {
1336
1440
  appType: 'custom',
@@ -1359,7 +1463,7 @@ function kit({ svelte_config, adapter }) {
1359
1463
  * @see https://vitejs.dev/guide/api-plugin.html#configureserver
1360
1464
  */
1361
1465
  async configureServer(vite) {
1362
- return await dev(vite, vite_config, svelte_config, () => remotes, root, adapter);
1466
+ return await dev(vite, vite_config, svelte_config, () => remotes, root);
1363
1467
  },
1364
1468
 
1365
1469
  /**
@@ -1367,7 +1471,11 @@ function kit({ svelte_config, adapter }) {
1367
1471
  * @see https://vitejs.dev/guide/api-plugin.html#configurepreviewserver
1368
1472
  */
1369
1473
  configurePreviewServer(vite) {
1370
- return preview(vite, vite_config, svelte_config, adapter);
1474
+ return preview(vite, vite_config, svelte_config);
1475
+ },
1476
+
1477
+ applyToEnvironment(environment) {
1478
+ return environment.name !== 'serviceWorker';
1371
1479
  },
1372
1480
 
1373
1481
  renderChunk(code, chunk) {
@@ -1401,6 +1509,12 @@ function kit({ svelte_config, adapter }) {
1401
1509
  }
1402
1510
  mkdirp(out);
1403
1511
 
1512
+ await load_and_validate_params({
1513
+ routes: manifest_data.routes,
1514
+ params_path: manifest_data.params,
1515
+ root
1516
+ });
1517
+
1404
1518
  const { output: server_chunks } = /** @type {Rolldown.RolldownOutput} */ (
1405
1519
  await builder.build(builder.environments.ssr)
1406
1520
  );
@@ -1536,6 +1650,18 @@ function kit({ svelte_config, adapter }) {
1536
1650
  const deps_of = (entry, add_dynamic_css = false) =>
1537
1651
  find_deps(vite_manifest, posixify(path.relative(root, entry)), add_dynamic_css, root);
1538
1652
 
1653
+ const has_explicit_dynamic_public_env = Object.values(explicit_env_config ?? {}).some(
1654
+ (variable) => variable.public && !variable.static
1655
+ );
1656
+
1657
+ // the app only depends on runtime public env if it imports `$app/env/public`
1658
+ // *and* at least one public env var is actually dynamic (non-static)
1659
+ const uses_env_dynamic_public =
1660
+ has_explicit_dynamic_public_env &&
1661
+ client_chunks.some(
1662
+ (chunk) => chunk.type === 'chunk' && chunk.modules[sveltekit_env_public_client]
1663
+ );
1664
+
1539
1665
  if (svelte_config.kit.output.bundleStrategy === 'split') {
1540
1666
  const start = deps_of(`${runtime_directory}/client/entry.js`);
1541
1667
  const app = deps_of(`${out_dir}/generated/client-optimized/app.js`);
@@ -1546,9 +1672,7 @@ function kit({ svelte_config, adapter }) {
1546
1672
  imports: [...start.imports, ...app.imports],
1547
1673
  stylesheets: [...start.stylesheets, ...app.stylesheets],
1548
1674
  fonts: [...start.fonts, ...app.fonts],
1549
- uses_env_dynamic_public: client_chunks.some(
1550
- (chunk) => chunk.type === 'chunk' && chunk.modules[sveltekit_env_public_client]
1551
- )
1675
+ uses_env_dynamic_public
1552
1676
  };
1553
1677
 
1554
1678
  // In case of server-side route resolution, we create a purpose-built route manifest that is
@@ -1596,9 +1720,7 @@ function kit({ svelte_config, adapter }) {
1596
1720
  imports: start.imports,
1597
1721
  stylesheets: start.stylesheets,
1598
1722
  fonts: start.fonts,
1599
- uses_env_dynamic_public: client_chunks.some(
1600
- (chunk) => chunk.type === 'chunk' && chunk.modules[sveltekit_env_public_client]
1601
- )
1723
+ uses_env_dynamic_public
1602
1724
  };
1603
1725
 
1604
1726
  if (svelte_config.kit.output.bundleStrategy === 'inline') {
@@ -1615,6 +1737,12 @@ function kit({ svelte_config, adapter }) {
1615
1737
  script: read(`${out}/client/${start.file}`),
1616
1738
  style: /** @type {string | undefined} */ (style?.source)
1617
1739
  };
1740
+
1741
+ // the bundle and stylesheet are inlined into the page, so the
1742
+ // emitted files are never loaded
1743
+ fs.unlinkSync(`${out}/client/${start.file}`);
1744
+ fs.rmSync(`${out}/client/${start.file}.map`, { force: true });
1745
+ if (style) fs.unlinkSync(`${out}/client/${style.fileName}`);
1618
1746
  }
1619
1747
  }
1620
1748
 
@@ -1637,7 +1765,7 @@ function kit({ svelte_config, adapter }) {
1637
1765
  kit,
1638
1766
  manifest_data,
1639
1767
  vite_server_manifest,
1640
- vite_manifest,
1768
+ vite_client_manifest,
1641
1769
  assets_path,
1642
1770
  client_chunks,
1643
1771
  root
@@ -1684,53 +1812,57 @@ function kit({ svelte_config, adapter }) {
1684
1812
  })};\n`
1685
1813
  );
1686
1814
 
1687
- if (service_worker_entry_file) {
1688
- if (kit.paths.assets) {
1689
- throw new Error('Cannot use service worker alongside config.kit.paths.assets');
1690
- }
1691
-
1692
- log.info('Building service worker');
1815
+ // defer the adapt step to run after any buildApp hooks the adapter might have
1816
+ finalise = async () => {
1817
+ // defer creating the service worker too because other plugins might build
1818
+ // the client environment again and overwrite our service worker which
1819
+ // outputs to the same directory
1820
+ if (service_worker_entry_file) {
1821
+ log.info('Building service worker');
1822
+
1823
+ // mirror client settings that we couldn't set per environment in the config hook
1824
+ builder.environments.serviceWorker.config.define =
1825
+ builder.environments.client.config.define;
1826
+ builder.environments.serviceWorker.config.resolve.alias = [
1827
+ ...get_config_aliases(kit, vite_config.root)
1828
+ ];
1693
1829
 
1694
- builder.environments.serviceWorker.config.define =
1695
- builder.environments.client.config.define;
1696
- builder.environments.serviceWorker.config.resolve.alias = [
1697
- ...get_config_aliases(kit, vite_config.root)
1698
- ];
1699
- builder.environments.serviceWorker.config.experimental.renderBuiltUrl = (filename) => {
1700
- return {
1701
- runtime: `new URL(${JSON.stringify(filename)}, location.href).pathname`
1830
+ // we have to overwrite this because it can't be configured per environment in the config hook
1831
+ builder.environments.serviceWorker.config.experimental.renderBuiltUrl = (filename) => {
1832
+ return {
1833
+ runtime: `new URL(${JSON.stringify(filename)}, location.href).pathname`
1834
+ };
1702
1835
  };
1703
- };
1704
-
1705
- await builder.build(builder.environments.serviceWorker);
1706
- }
1707
-
1708
- console.log(
1709
- `\nRun ${styleText(['bold', 'cyan'], 'npm run preview')} to preview your production build locally.`
1710
- );
1711
1836
 
1712
- if (adapter) {
1713
- const { adapt } = await import('../../core/adapt/index.js');
1714
- await adapt(
1715
- adapter,
1716
- svelte_config,
1717
- build_data,
1718
- metadata,
1719
- prerendered,
1720
- prerender_results.prerender_map,
1721
- log,
1722
- remotes,
1723
- vite_config,
1724
- explicit_env_config
1725
- );
1726
- } else {
1727
- console.log(styleText(['bold', 'yellow'], '\nNo adapter specified'));
1837
+ await builder.build(builder.environments.serviceWorker);
1838
+ }
1728
1839
 
1729
- const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
1730
1840
  console.log(
1731
- `See ${link} to learn how to configure your app to run on the platform of your choosing`
1841
+ `\nRun ${styleText(['bold', 'cyan'], 'npm run preview')} to preview your production build locally.`
1732
1842
  );
1733
- }
1843
+
1844
+ if (kit.adapter) {
1845
+ const { adapt } = await import('../../core/adapt/index.js');
1846
+ await adapt(
1847
+ svelte_config,
1848
+ build_data,
1849
+ metadata,
1850
+ prerendered,
1851
+ prerender_results.prerender_map,
1852
+ log,
1853
+ remotes,
1854
+ vite_config,
1855
+ explicit_env_config
1856
+ );
1857
+ } else {
1858
+ console.log(styleText(['bold', 'yellow'], '\nNo adapter specified'));
1859
+
1860
+ const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
1861
+ console.log(
1862
+ `See ${link} to learn how to configure your app to run on the platform of your choosing`
1863
+ );
1864
+ }
1865
+ };
1734
1866
  }
1735
1867
  };
1736
1868
 
@@ -1738,23 +1870,30 @@ function kit({ svelte_config, adapter }) {
1738
1870
  const plugin_adapter = {
1739
1871
  name: 'vite-plugin-sveltekit-adapter',
1740
1872
  apply: 'build',
1741
- // expose the adapter so that forked processes (e.g. prerendering)
1742
- // can retrieve it by resolving the Vite config
1743
- api: {
1744
- adapter
1873
+ buildApp: {
1874
+ // this will run after any buildApp hooks provided by other Vite plugins
1875
+ // see https://vite.dev/guide/api-environment-frameworks#environments-during-build
1876
+ order: 'post',
1877
+ async handler() {
1878
+ await finalise?.();
1879
+ }
1745
1880
  }
1746
1881
  };
1747
1882
 
1748
- return [
1749
- plugin_setup,
1750
- plugin_remote,
1751
- plugin_virtual_modules,
1752
- process.env.TEST !== 'true' ? plugin_guard : undefined,
1753
- service_worker_entry_file && plugin_service_worker_env,
1754
- plugin_service_worker,
1755
- plugin_compile,
1756
- plugin_adapter
1757
- ].filter((p) => !!p);
1883
+ return /** @type {Plugin[]} */ (
1884
+ [
1885
+ svelte_config.kit.adapter?.vite?.plugins,
1886
+ plugin_setup,
1887
+ plugin_remote_guard,
1888
+ plugin_remote,
1889
+ plugin_virtual_modules,
1890
+ process.env.TEST !== 'true' ? plugin_guard : undefined,
1891
+ plugin_service_worker,
1892
+ plugin_service_worker_env,
1893
+ plugin_compile,
1894
+ plugin_adapter
1895
+ ].filter(Boolean)
1896
+ );
1758
1897
  }
1759
1898
 
1760
1899
  /**
@@ -1789,13 +1928,18 @@ function find_overridden_config(config, resolved_config, enforced_config, path,
1789
1928
  for (const key in enforced_config) {
1790
1929
  if (typeof config === 'object' && key in config && key in resolved_config) {
1791
1930
  const enforced = enforced_config[key];
1931
+ const resolved = resolved_config[key];
1792
1932
 
1793
1933
  if (enforced === true) {
1794
- if (config[key] !== resolved_config[key]) {
1934
+ // Normalize path separators before comparing to avoid false positives on Windows,
1935
+ // where config values like `root` may use backslashes while SvelteKit uses forward slashes.
1936
+ const a = typeof config[key] === 'string' ? posixify(config[key]) : config[key];
1937
+ const b = typeof resolved === 'string' ? posixify(resolved) : resolved;
1938
+ if (a !== b) {
1795
1939
  out.push(path + key);
1796
1940
  }
1797
1941
  } else {
1798
- find_overridden_config(config[key], resolved_config[key], enforced, path + key + '.', out);
1942
+ find_overridden_config(config[key], resolved, enforced, path + key + '.', out);
1799
1943
  }
1800
1944
  }
1801
1945
  }
@@ -1810,6 +1954,7 @@ const create_service_worker_module = (config) => dedent`
1810
1954
  throw new Error('This module can only be imported inside a service worker');
1811
1955
  }
1812
1956
 
1957
+ export const base = location.pathname.split('/').slice(0, -1).join('/');
1813
1958
  export const build = [];
1814
1959
  export const files = [
1815
1960
  ${create_assets(config)