@sveltejs/kit 2.63.0 → 3.0.0-next.1

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 (95) hide show
  1. package/package.json +17 -22
  2. package/src/cli.js +19 -16
  3. package/src/core/adapt/builder.js +25 -77
  4. package/src/core/adapt/index.js +6 -4
  5. package/src/core/config/index.js +12 -84
  6. package/src/core/config/options.js +36 -46
  7. package/src/core/env.js +5 -130
  8. package/src/core/generate_manifest/find_server_assets.js +3 -2
  9. package/src/core/generate_manifest/index.js +11 -3
  10. package/src/core/postbuild/analyse.js +19 -16
  11. package/src/core/postbuild/fallback.js +0 -3
  12. package/src/core/postbuild/prerender.js +18 -17
  13. package/src/core/postbuild/queue.js +3 -4
  14. package/src/core/sync/create_manifest_data/index.js +16 -18
  15. package/src/core/sync/sync.js +30 -25
  16. package/src/core/sync/utils.js +0 -15
  17. package/src/core/sync/write_ambient.js +3 -54
  18. package/src/core/sync/write_client_manifest.js +8 -9
  19. package/src/core/sync/write_env.js +5 -1
  20. package/src/core/sync/write_non_ambient.js +7 -7
  21. package/src/core/sync/write_root.js +40 -88
  22. package/src/core/sync/write_server.js +16 -23
  23. package/src/core/sync/write_tsconfig.js +28 -24
  24. package/src/core/sync/write_types/index.js +31 -25
  25. package/src/core/utils.js +14 -11
  26. package/src/exports/index.js +8 -21
  27. package/src/exports/node/index.js +8 -13
  28. package/src/exports/public.d.ts +35 -76
  29. package/src/exports/vite/build/build_server.js +14 -11
  30. package/src/exports/vite/build/remote.js +6 -7
  31. package/src/exports/vite/build/utils.js +7 -5
  32. package/src/exports/vite/dev/index.js +30 -31
  33. package/src/exports/vite/index.js +938 -732
  34. package/src/exports/vite/module_ids.js +1 -6
  35. package/src/exports/vite/options.js +17 -0
  36. package/src/exports/vite/preview/index.js +3 -5
  37. package/src/exports/vite/static_analysis/index.js +11 -5
  38. package/src/exports/vite/utils.js +9 -45
  39. package/src/runtime/app/env/internal.js +3 -0
  40. package/src/runtime/app/env/public/client.js +0 -6
  41. package/src/runtime/app/env/public/server.js +0 -6
  42. package/src/runtime/app/environment/index.js +3 -7
  43. package/src/runtime/app/server/index.js +4 -2
  44. package/src/runtime/app/server/remote/form.js +0 -51
  45. package/src/runtime/app/server/remote/query.js +0 -7
  46. package/src/runtime/app/server/remote/shared.js +4 -3
  47. package/src/runtime/app/state/client.js +1 -12
  48. package/src/runtime/client/client.js +1 -1
  49. package/src/runtime/client/constants.js +0 -1
  50. package/src/runtime/client/remote-functions/form.svelte.js +0 -31
  51. package/src/runtime/client/remote-functions/query/index.js +2 -2
  52. package/src/runtime/client/remote-functions/query/instance.svelte.js +1 -2
  53. package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -2
  54. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +5 -8
  55. package/src/runtime/client/utils.js +8 -10
  56. package/src/runtime/components/{svelte-5/layout.svelte → layout.svelte} +1 -1
  57. package/src/runtime/form-utils.js +4 -41
  58. package/src/runtime/server/cookie.js +21 -37
  59. package/src/runtime/server/env_module.js +1 -3
  60. package/src/runtime/server/fetch.js +6 -10
  61. package/src/runtime/server/index.js +3 -9
  62. package/src/runtime/server/page/render.js +32 -44
  63. package/src/runtime/server/page/types.d.ts +4 -2
  64. package/src/runtime/server/respond.js +2 -2
  65. package/src/runtime/server/utils.js +2 -4
  66. package/src/runtime/shared-server.js +0 -22
  67. package/src/runtime/shared.js +0 -15
  68. package/src/types/global-private.d.ts +5 -1
  69. package/src/types/internal.d.ts +3 -8
  70. package/src/utils/css.js +3 -19
  71. package/src/utils/filesystem.js +1 -30
  72. package/src/utils/http.js +0 -21
  73. package/src/utils/import.js +7 -6
  74. package/src/utils/os.js +7 -0
  75. package/src/utils/path.js +23 -0
  76. package/src/utils/shared-iterator.js +3 -0
  77. package/src/utils/streaming.js +2 -4
  78. package/src/utils/url.js +1 -1
  79. package/src/utils/vite.js +28 -0
  80. package/src/version.js +1 -1
  81. package/types/index.d.ts +45 -138
  82. package/types/index.d.ts.map +1 -8
  83. package/src/exports/vite/build/build_service_worker.js +0 -165
  84. package/src/runtime/app/environment/types.d.ts +0 -19
  85. package/src/runtime/components/svelte-4/error.svelte +0 -6
  86. package/src/runtime/components/svelte-4/layout.svelte +0 -1
  87. package/src/runtime/env/dynamic/private.js +0 -1
  88. package/src/runtime/env/dynamic/public.js +0 -1
  89. package/src/types/synthetic/$env+dynamic+private.md +0 -43
  90. package/src/types/synthetic/$env+dynamic+public.md +0 -46
  91. package/src/types/synthetic/$env+static+private.md +0 -31
  92. package/src/types/synthetic/$env+static+public.md +0 -31
  93. package/src/utils/env.js +0 -13
  94. package/src/utils/promise.js +0 -29
  95. /package/src/runtime/components/{svelte-5/error.svelte → error.svelte} +0 -0
@@ -1,20 +1,19 @@
1
1
  /** @import { EnvVarConfig } from '@sveltejs/kit' */
2
2
  /** @import { Options, SvelteConfig } from '@sveltejs/vite-plugin-svelte' */
3
3
  /** @import { PreprocessorGroup } from 'svelte/compiler' */
4
- /** @import { KitConfig } from '@sveltejs/kit' */
5
- /** @import { ConfigEnv, Manifest, Plugin, ResolvedConfig, UserConfig, ViteDevServer } from 'vite' */
6
- /** @import { ValidatedConfig } from 'types' */
4
+ /** @import { Plugin, ResolvedConfig, UserConfig, ViteDevServer } from 'vite' */
7
5
  import fs from 'node:fs';
8
6
  import path from 'node:path';
9
7
  import process from 'node:process';
10
- import colors from 'kleur';
8
+ import { styleText } from 'node:util';
9
+ import { loadEnv } from 'vite';
10
+ import { exactRegex, prefixRegex } from 'rolldown/filter';
11
11
 
12
- import { copy, mkdirp, posixify, read, resolve_entry, rimraf } from '../../utils/filesystem.js';
12
+ import { copy, mkdirp, read, resolve_entry, rimraf } from '../../utils/filesystem.js';
13
+ import { posixify } from '../../utils/os.js';
13
14
  import {
14
- create_dynamic_module,
15
15
  create_sveltekit_env,
16
16
  create_sveltekit_env_public,
17
- create_static_module,
18
17
  resolve_explicit_env_entry,
19
18
  create_sveltekit_env_service_worker_dev,
20
19
  create_sveltekit_env_private
@@ -22,31 +21,27 @@ import {
22
21
  import * as sync from '../../core/sync/sync.js';
23
22
  import { create_assets } from '../../core/sync/create_manifest_data/index.js';
24
23
  import { runtime_directory, logger } from '../../core/utils.js';
25
- import { load_svelte_config, process_config } from '../../core/config/index.js';
26
24
  import { generate_manifest } from '../../core/generate_manifest/index.js';
27
25
  import { build_server_nodes } from './build/build_server.js';
28
- import { build_service_worker } from './build/build_service_worker.js';
29
26
  import { assets_base, find_deps, resolve_symlinks } from './build/utils.js';
30
27
  import { dev } from './dev/index.js';
31
28
  import { preview } from './preview/index.js';
32
29
  import {
33
30
  error_for_missing_config,
34
31
  get_config_aliases,
35
- get_env,
36
32
  normalize_id,
37
- stackless
33
+ stackless,
34
+ strip_virtual_prefix
38
35
  } from './utils.js';
39
36
  import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
40
37
  import prerender from '../../core/postbuild/prerender.js';
41
38
  import analyse from '../../core/postbuild/analyse.js';
42
39
  import { s } from '../../utils/misc.js';
43
40
  import { hash } from '../../utils/hash.js';
44
- import { dedent, isSvelte5Plus } from '../../core/sync/utils.js';
41
+ import { dedent } from '../../core/sync/utils.js';
45
42
  import {
46
- env_dynamic_private,
47
- env_dynamic_public,
48
- env_static_private,
49
- env_static_public,
43
+ app_env_private,
44
+ app_server,
50
45
  service_worker,
51
46
  sveltekit_env,
52
47
  sveltekit_env_private,
@@ -58,9 +53,11 @@ import {
58
53
  import { import_peer } from '../../utils/import.js';
59
54
  import { compact } from '../../utils/array.js';
60
55
  import { should_ignore, has_children } from './static_analysis/utils.js';
56
+ import { process_config } from '../../core/config/index.js';
61
57
  import { treeshake_prerendered_remotes } from './build/remote.js';
62
58
 
63
- const cwd = posixify(process.cwd());
59
+ /** @type {string} */
60
+ let root;
64
61
 
65
62
  /** @type {import('./types.js').EnforcedConfig} */
66
63
  const enforced_config = {
@@ -76,7 +73,7 @@ const enforced_config = {
76
73
  },
77
74
  manifest: true,
78
75
  outDir: true,
79
- rollupOptions: {
76
+ rolldownOptions: {
80
77
  input: true,
81
78
  output: {
82
79
  format: true,
@@ -95,8 +92,7 @@ const enforced_config = {
95
92
  $lib: true,
96
93
  '$service-worker': true
97
94
  }
98
- },
99
- root: true
95
+ }
100
96
  };
101
97
 
102
98
  const options_regex = /(export\s+const\s+(prerender|csr|ssr|trailingSlash))\s*=/s;
@@ -116,7 +112,7 @@ const warning_preprocessor = {
116
112
  const fixed = basename.replace('.svelte', '(.server).js/ts');
117
113
 
118
114
  const message =
119
- `\n${colors.bold().red(path.relative('.', filename))}\n` +
115
+ `\n${styleText(['bold', 'red'], path.relative(root, filename))}\n` +
120
116
  `\`${match[1]}\` will be ignored — move it to ${fixed} instead. See https://svelte.dev/docs/kit/page-options for more information.`;
121
117
 
122
118
  if (!warned.has(message)) {
@@ -131,10 +127,10 @@ const warning_preprocessor = {
131
127
 
132
128
  const basename = path.basename(filename);
133
129
 
134
- if (basename.startsWith('+layout.') && !has_children(content, isSvelte5Plus())) {
130
+ if (basename.startsWith('+layout.') && !has_children(content, true)) {
135
131
  const message =
136
- `\n${colors.bold().red(path.relative('.', filename))}\n` +
137
- `\`<slot />\`${isSvelte5Plus() ? ' or `{@render ...}` tag' : ''}` +
132
+ `\n${styleText(['bold', 'red'], path.relative(root, filename))}\n` +
133
+ '`<slot />` or `{@render ...}` tag' +
138
134
  ' missing — inner content will not be rendered';
139
135
 
140
136
  if (!warned.has(message)) {
@@ -145,126 +141,164 @@ const warning_preprocessor = {
145
141
  }
146
142
  };
147
143
 
144
+ /** @type {typeof import('@sveltejs/vite-plugin-svelte')} */
145
+ let vite_plugin_svelte;
146
+
148
147
  /**
149
148
  * Returns the SvelteKit Vite plugins.
150
149
  * Since version 2.62.0 you can pass [configuration](configuration) directly, in which case `svelte.config.js` is ignored.
151
- * @param {KitConfig & Omit<SvelteConfig, 'onwarn'>} [config]
150
+ * @param {import('@sveltejs/kit').KitConfig & Omit<SvelteConfig, 'onwarn'>} [config]
152
151
  * @returns {Promise<Plugin[]>}
153
152
  */
154
153
  export async function sveltekit(config) {
155
- /** @type {ValidatedConfig} */
156
- let svelte_config;
157
-
158
- if (config !== undefined) {
159
- const { extensions, compilerOptions, vitePlugin, preprocess, ...kit } = config;
160
- svelte_config = process_config(
161
- { extensions, compilerOptions, vitePlugin, preprocess, kit },
162
- { cwd, source: 'SvelteKit options from Vite config' }
163
- );
154
+ const cwd = process.cwd();
164
155
 
165
- const config_file = ['svelte.config.js', 'svelte.config.ts'].find((file) =>
166
- fs.existsSync(file)
167
- );
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
+ );
168
161
 
169
- if (config_file) {
170
- console.warn(`${config_file} is ignored when options are passed via your Vite config`);
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);
171
169
  }
172
- } else {
173
- svelte_config = await load_svelte_config();
174
- }
175
170
 
176
- /** @type {Options['preprocess']} */
177
- let preprocess = svelte_config.preprocess;
178
- if (Array.isArray(preprocess)) {
179
- preprocess = [...preprocess, warning_preprocessor];
180
- } else if (preprocess) {
181
- preprocess = [preprocess, warning_preprocessor];
182
- } else {
183
- preprocess = warning_preprocessor;
171
+ console.warn(message);
184
172
  }
185
173
 
186
174
  /** @type {Options} */
187
175
  const vite_plugin_svelte_options = {
188
- configFile: false,
189
- extensions: svelte_config.extensions,
190
- preprocess,
191
- onwarn: svelte_config.onwarn,
192
- compilerOptions: {
193
- // @ts-ignore - ignore this property when running `pnpm check` against Svelte 5 in the ecosystem CI
194
- hydratable: isSvelte5Plus() ? undefined : true,
195
- ...svelte_config.compilerOptions
196
- },
197
- ...svelte_config.vitePlugin
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
178
+ configFile: false
198
179
  };
199
180
 
200
- const { svelte } = await import_peer('@sveltejs/vite-plugin-svelte');
181
+ vite_plugin_svelte = await import_peer('@sveltejs/vite-plugin-svelte', cwd);
201
182
 
202
- return [...svelte(vite_plugin_svelte_options), ...(await kit({ svelte_config }))];
183
+ return [
184
+ plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }),
185
+ ...vite_plugin_svelte.svelte(vite_plugin_svelte_options),
186
+ ...kit({
187
+ svelte_config,
188
+ adapter: svelte_config.kit.adapter
189
+ })
190
+ ];
203
191
  }
204
192
 
205
- // These variables live outside the `kit()` function because it is re-invoked by each Vite build
206
-
207
- let secondary_build_started = false;
193
+ /** @param {import('vite').UserConfig | import('vite').ResolvedConfig} vite_config */
194
+ function resolve_root(vite_config) {
195
+ return posixify(vite_config.root ? path.resolve(vite_config.root) : process.cwd());
196
+ }
208
197
 
209
- /** @type {import('types').ManifestData} */
210
- let manifest_data;
198
+ /**
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}
206
+ */
207
+ function plugin_svelte_config({ vite_plugin_svelte_options, svelte_config }) {
208
+ return {
209
+ name: 'vite-plugin-sveltekit-resolve-svelte-config',
210
+ // make sure it runs first
211
+ enforce: 'pre',
212
+ config: {
213
+ order: 'pre',
214
+ handler(config) {
215
+ root = resolve_root(config);
216
+
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;
225
+ }
211
226
 
212
- /** @type {import('types').ServerMetadata | undefined} only set at build time once analysis is finished */
213
- let build_metadata = undefined;
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
+ }
233
+ },
234
+ // TODO: do we even need to set `root` based on the final Vite config?
235
+ configResolved: {
236
+ order: 'pre',
237
+ handler(config) {
238
+ root = resolve_root(config);
239
+ }
240
+ }
241
+ };
242
+ }
214
243
 
215
244
  /**
216
- * Returns the SvelteKit Vite plugin. Vite executes Rollup hooks as well as some of its own.
245
+ * Returns the SvelteKit Vite plugin. Vite executes Rolldown hooks as well as some of its own.
217
246
  * Background reading is available at:
218
- * - https://vitejs.dev/guide/api-plugin.html
219
- * - https://rollupjs.org/guide/en/#plugin-development
247
+ * - https://vite.dev/guide/api-plugin.html
248
+ * - https://rolldown.rs/apis/plugin-api
220
249
  *
221
250
  * You can get an idea of the lifecycle by looking at the flow charts here:
222
- * - https://rollupjs.org/guide/en/#build-hooks
223
- * - https://rollupjs.org/guide/en/#output-generation-hooks
251
+ * - https://rolldown.rs/apis/plugin-api#build-hooks
252
+ * - https://rolldown.rs/apis/plugin-api#output-generation-hooks
224
253
  *
225
- * @param {{ svelte_config: import('types').ValidatedConfig }} options
226
- * @return {Promise<Plugin[]>}
254
+ * @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[]}
227
258
  */
228
- async function kit({ svelte_config }) {
259
+ function kit({ svelte_config, adapter }) {
229
260
  /** @type {typeof import('vite')} */
230
- const vite = await import_peer('vite');
261
+ let vite;
231
262
 
232
- // @ts-ignore `vite.rolldownVersion` only exists in `vite 8`
233
- const is_rolldown = !!vite.rolldownVersion;
263
+ /** @type {import('types').ValidatedKitConfig} */
264
+ let kit;
265
+ /** @type {string} `kit.outDir` but posix-ified */
266
+ let out_dir;
267
+ /** @type {string} The base directory for the Vite builds */
268
+ let out;
234
269
 
235
- const { kit } = svelte_config;
236
- /** `kit.outDir` but posix-ified */
237
- const out_dir = posixify(kit.outDir);
238
- /** The base directory for the Vite builds */
239
- const out = `${out_dir}/output`;
240
-
241
- const version_hash = hash(kit.version.name);
270
+ /** @type {string} */
271
+ let version_hash;
242
272
 
243
273
  /** @type {ResolvedConfig} */
244
274
  let vite_config;
245
275
 
246
- /** @type {ConfigEnv} */
247
- let vite_config_env;
248
-
249
276
  /** @type {boolean} */
250
277
  let is_build;
251
278
 
252
- /** @type {{ all: Record<string, string>; public: Record<string, string>; private: Record<string, string> }} */
279
+ /** @type {Record<string, string>} */
253
280
  let env;
254
281
 
255
- /** @type {() => Promise<void>} */
256
- let finalise;
282
+ /** @type {import('types').ManifestData} */
283
+ let manifest_data;
284
+
285
+ /** @type {import('types').ServerMetadata | undefined} only set at build time once analysis is finished */
286
+ let build_metadata = undefined;
257
287
 
258
288
  /** @type {UserConfig} */
259
289
  let initial_config;
260
290
 
261
- const service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
262
- const parsed_service_worker = path.parse(kit.files.serviceWorker);
263
-
264
- const normalized_cwd = vite.normalizePath(cwd);
265
- const normalized_lib = vite.normalizePath(kit.files.lib);
266
- const normalized_node_modules = vite.normalizePath(path.resolve('node_modules'));
267
-
291
+ /** @type {string | null} */
292
+ let service_worker_entry_file = resolve_entry(svelte_config.kit.files.serviceWorker);
293
+ /** @type {import('node:path').ParsedPath} */
294
+ let parsed_service_worker;
295
+
296
+ /** @type {string} */
297
+ let normalized_cwd;
298
+ /** @type {string} */
299
+ let normalized_lib;
300
+ /** @type {string} */
301
+ let normalized_node_modules;
268
302
  /**
269
303
  * A map showing which features (such as `$app/server:read`) are defined
270
304
  * in which chunks, so that we can later determine which routes use which features
@@ -282,26 +316,44 @@ async function kit({ svelte_config }) {
282
316
  options: svelte_config
283
317
  },
284
318
 
319
+ applyToEnvironment(environment) {
320
+ return environment.name !== 'serviceWorker';
321
+ },
322
+
285
323
  /**
286
324
  * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
287
325
  * @see https://vitejs.dev/guide/api-plugin.html#config
288
326
  */
289
327
  config: {
290
328
  order: 'pre',
291
- handler(config, config_env) {
329
+ async handler(config, config_env) {
292
330
  initial_config = config;
293
- vite_config_env = config_env;
294
331
  is_build = config_env.command === 'build';
295
332
 
296
- env = get_env(kit.env, vite_config_env.mode);
333
+ ({ kit } = svelte_config);
334
+ out_dir = posixify(kit.outDir);
335
+ out = `${out_dir}/output`;
336
+
337
+ version_hash = hash(kit.version.name);
338
+
339
+ env = loadEnv(config_env.mode, kit.env.dir, '');
340
+
341
+ service_worker_entry_file = resolve_entry(kit.files.serviceWorker);
342
+ parsed_service_worker = path.parse(kit.files.serviceWorker);
343
+
344
+ vite = await import_peer('vite', root);
345
+
346
+ normalized_cwd = vite.normalizePath(root);
347
+ normalized_lib = vite.normalizePath(kit.files.lib);
348
+ normalized_node_modules = vite.normalizePath(path.resolve(root, 'node_modules'));
297
349
 
298
350
  const allow = new Set([
299
351
  kit.files.lib,
300
352
  kit.files.routes,
301
353
  kit.outDir,
302
- path.resolve('src'), // TODO this isn't correct if user changed all his files to sth else than src (like in test/options)
303
- path.resolve('node_modules'),
304
- path.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules')
354
+ path.resolve(root, kit.files.src),
355
+ path.resolve(root, 'node_modules'),
356
+ path.resolve(process.cwd(), 'node_modules')
305
357
  ]);
306
358
 
307
359
  // We can only add directories to the allow list, so we find out
@@ -318,10 +370,9 @@ async function kit({ svelte_config }) {
318
370
  alias: [
319
371
  { find: '__SERVER__', replacement: `${generated}/server` },
320
372
  { find: '$app', replacement: `${runtime_directory}/app` },
321
- ...get_config_aliases(kit)
373
+ ...get_config_aliases(kit, root)
322
374
  ]
323
375
  },
324
- root: cwd,
325
376
  server: {
326
377
  cors: { preflightContinue: true },
327
378
  fs: {
@@ -359,7 +410,7 @@ async function kit({ svelte_config }) {
359
410
  // export conditions resolved correctly through Vite. This prevents adapters
360
411
  // that bundle later on from resolving the export conditions incorrectly
361
412
  // and for example include browser-only code in the server output
362
- // because they for example use esbuild.build with `platform: 'browser'`
413
+ // because they for example use rolldown.build with `platform: 'browser'`
363
414
  'esm-env',
364
415
  // This forces `$app/*` modules to be bundled, since they depend on
365
416
  // virtual modules like `__sveltekit/env` (this isn't a valid bare
@@ -372,36 +423,23 @@ async function kit({ svelte_config }) {
372
423
 
373
424
  if (kit.experimental.remoteFunctions) {
374
425
  // treat .remote.js files as empty for the purposes of prebundling
375
- // detects rolldown to avoid a warning message in vite 8 beta
376
426
  const remote_id_filter = new RegExp(
377
427
  `.remote(${kit.moduleExtensions.join('|')})$`.replaceAll('.', '\\.')
378
428
  );
379
- new_config.optimizeDeps ??= {}; // for some reason ts says this could be undefined even though it was set above
380
- if (is_rolldown) {
381
- // @ts-ignore
382
- new_config.optimizeDeps.rolldownOptions ??= {};
383
- // @ts-ignore
384
- new_config.optimizeDeps.rolldownOptions.plugins ??= [];
385
- // @ts-ignore
386
- new_config.optimizeDeps.rolldownOptions.plugins.push({
387
- name: 'vite-plugin-sveltekit-setup:optimize-remote-functions',
388
- load: {
389
- filter: { id: remote_id_filter },
390
- handler() {
391
- return '';
392
- }
429
+ // @ts-expect-error optimizeDeps is already set above
430
+ new_config.optimizeDeps.rolldownOptions ??= {};
431
+ // @ts-expect-error
432
+ new_config.optimizeDeps.rolldownOptions.plugins ??= [];
433
+ // @ts-expect-error
434
+ new_config.optimizeDeps.rolldownOptions.plugins.push({
435
+ name: 'vite-plugin-sveltekit-setup:optimize-remote-functions',
436
+ load: {
437
+ filter: { id: remote_id_filter },
438
+ handler() {
439
+ return '';
393
440
  }
394
- });
395
- } else {
396
- new_config.optimizeDeps.esbuildOptions ??= {};
397
- new_config.optimizeDeps.esbuildOptions.plugins ??= [];
398
- new_config.optimizeDeps.esbuildOptions.plugins.push({
399
- name: 'vite-plugin-sveltekit-setup:optimize-remote-functions',
400
- setup(build) {
401
- build.onLoad({ filter: remote_id_filter }, () => ({ contents: '' }));
402
- }
403
- });
404
- }
441
+ }
442
+ });
405
443
  }
406
444
 
407
445
  const define = {
@@ -416,43 +454,19 @@ async function kit({ svelte_config }) {
416
454
  __SVELTEKIT_HASH_ROUTING__: s(kit.router.type === 'hash'),
417
455
  __SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.experimental.tracing.server),
418
456
  __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__: s(kit.experimental.handleRenderingErrors),
419
- __SVELTEKIT_EXPERIMENTAL_EXPLICIT_ENVIRONMENT_VARIABLES__: s(
420
- kit.experimental.explicitEnvironmentVariables
421
- ),
457
+ __SVELTEKIT_ROOT__: s(root),
422
458
  __SVELTEKIT_DEV__: s(!is_build)
423
459
  };
424
460
 
425
461
  if (is_build) {
426
- if (!new_config.build) new_config.build = {};
427
- new_config.build.ssr = !secondary_build_started;
428
-
429
462
  new_config.define = {
430
463
  ...define,
431
- __SVELTEKIT_ADAPTER_NAME__: s(kit.adapter?.name),
464
+ __SVELTEKIT_ADAPTER_NAME__: s(adapter?.name),
432
465
  __SVELTEKIT_APP_VERSION_FILE__: s(`${kit.appDir}/version.json`),
433
- __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: s(kit.version.pollInterval),
434
- __SVELTEKIT_PAYLOAD__: new_config.build.ssr
435
- ? '{}'
436
- : `globalThis.__sveltekit_${version_hash}`
466
+ __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: s(kit.version.pollInterval)
437
467
  };
438
468
 
439
- if (!secondary_build_started) {
440
- manifest_data = sync.all(svelte_config, config_env.mode).manifest_data;
441
- // During the initial server build we don't know yet
442
- new_config.define.__SVELTEKIT_HAS_SERVER_LOAD__ = 'true';
443
- new_config.define.__SVELTEKIT_HAS_UNIVERSAL_LOAD__ = 'true';
444
- } else {
445
- const nodes = Object.values(
446
- /** @type {import('types').ServerMetadata} */ (build_metadata).nodes
447
- );
448
-
449
- // Through the finished analysis we can now check if any node has server or universal load functions
450
- const has_server_load = nodes.some((node) => node.has_server_load);
451
- const has_universal_load = nodes.some((node) => node.has_universal_load);
452
-
453
- new_config.define.__SVELTEKIT_HAS_SERVER_LOAD__ = s(has_server_load);
454
- new_config.define.__SVELTEKIT_HAS_UNIVERSAL_LOAD__ = s(has_universal_load);
455
- }
469
+ manifest_data = sync.all(svelte_config, root).manifest_data;
456
470
  } else {
457
471
  new_config.define = {
458
472
  ...define,
@@ -462,14 +476,10 @@ async function kit({ svelte_config }) {
462
476
  __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true'
463
477
  };
464
478
 
465
- // @ts-ignore this prevents a reference error if `client.js` is imported on the server
466
- globalThis.__sveltekit_dev = {};
467
-
468
479
  // These Kit dependencies are packaged as CommonJS, which means they must always be externalized.
469
480
  // Without this, the tests will still pass but `pnpm dev` will fail in projects that link `@sveltejs/kit`.
470
481
  /** @type {NonNullable<import('vite').UserConfig['ssr']>} */ (new_config.ssr).external = [
471
- 'cookie',
472
- 'set-cookie-parser'
482
+ 'cookie'
473
483
  ];
474
484
  }
475
485
 
@@ -497,16 +507,16 @@ async function kit({ svelte_config }) {
497
507
  const plugin_virtual_modules = {
498
508
  name: 'vite-plugin-sveltekit-virtual-modules',
499
509
 
510
+ applyToEnvironment(environment) {
511
+ return environment.name !== 'serviceWorker';
512
+ },
513
+
500
514
  async configResolved(config) {
501
515
  explicit_env_entry = resolve_explicit_env_entry(kit);
502
- explicit_env_config = await sync.env(kit, explicit_env_entry, config.mode);
516
+ explicit_env_config = await sync.env(kit, explicit_env_entry, config.root, config.mode);
503
517
  },
504
518
 
505
519
  configureServer(server) {
506
- if (!kit.experimental.explicitEnvironmentVariables) {
507
- return;
508
- }
509
-
510
520
  server.watcher.on('all', async (_, file) => {
511
521
  if (!file.includes('env')) {
512
522
  return;
@@ -516,7 +526,12 @@ async function kit({ svelte_config }) {
516
526
 
517
527
  if (file === explicit_env_entry || file === resolved) {
518
528
  explicit_env_entry = resolved;
519
- explicit_env_config = await sync.env(kit, explicit_env_entry, vite_config_env.mode);
529
+ explicit_env_config = await sync.env(
530
+ kit,
531
+ explicit_env_entry,
532
+ vite_config.root,
533
+ vite_config.mode
534
+ );
520
535
 
521
536
  for (const id of [sveltekit_env, sveltekit_env_public_client]) {
522
537
  const module = server.moduleGraph.getModuleById(id);
@@ -536,10 +551,10 @@ async function kit({ svelte_config }) {
536
551
  return `${out_dir}/generated/client-optimized/app.js`;
537
552
  }
538
553
 
539
- // If importing from a service-worker, only allow $service-worker & $env/static/public, but none of the other virtual modules.
554
+ // If importing from a service-worker, only allow $service-worker & $app/env/public, but none of the other virtual modules.
540
555
  // This check won't catch transitive imports, but it will warn when the import comes from a service-worker directly.
541
556
  // Transitive imports will be caught during the build.
542
- // TODO move this logic to plugin_guard
557
+ // TODO move this logic to plugin_guard. add a filter to this resolveId when doing so
543
558
  // TODO allow $app/env/public
544
559
  if (importer) {
545
560
  const parsed_importer = path.parse(importer);
@@ -551,7 +566,6 @@ async function kit({ svelte_config }) {
551
566
  if (
552
567
  importer_is_service_worker &&
553
568
  id !== '$service-worker' &&
554
- id !== '$env/static/public' &&
555
569
  id !== 'virtual:$app/env/public' &&
556
570
  id !== '__sveltekit/env/service-worker'
557
571
  ) {
@@ -560,13 +574,12 @@ async function kit({ svelte_config }) {
560
574
  id,
561
575
  normalized_lib,
562
576
  normalized_cwd
563
- )} into service-worker code. Only the modules $service-worker, $env/static/public and $app/env/public are available in service workers.`
577
+ )} into service-worker code. Only the modules $service-worker and $app/env/public are available in service workers.`
564
578
  );
565
579
  }
566
580
  }
567
581
 
568
- // treat $env/static/[public|private] as virtual
569
- if (id.startsWith('$env/') || id === '$service-worker') {
582
+ if (id === '$service-worker') {
570
583
  // ids with :$ don't work with reverse proxies like nginx
571
584
  return `\0virtual:${id.substring(1)}`;
572
585
  }
@@ -579,70 +592,52 @@ async function kit({ svelte_config }) {
579
592
  return `\0virtual:${id}`;
580
593
  }
581
594
  },
595
+ load: {
596
+ filter: {
597
+ id: [
598
+ exactRegex(service_worker),
599
+ exactRegex(sveltekit_env),
600
+ exactRegex(sveltekit_env_private),
601
+ exactRegex(sveltekit_env_public_client),
602
+ exactRegex(sveltekit_env_public_server),
603
+ exactRegex(sveltekit_env_service_worker),
604
+ exactRegex(sveltekit_server)
605
+ ]
606
+ },
607
+ handler(id) {
608
+ const global = is_build
609
+ ? `globalThis.__sveltekit_${version_hash}`
610
+ : 'globalThis.__sveltekit_dev';
611
+
612
+ switch (id) {
613
+ case service_worker:
614
+ return create_service_worker_module(svelte_config);
615
+
616
+ case sveltekit_env:
617
+ return create_sveltekit_env(explicit_env_config, env, explicit_env_entry);
618
+
619
+ case sveltekit_env_public_client:
620
+ return create_sveltekit_env_public(
621
+ explicit_env_config,
622
+ env,
623
+ `const env = ${global}.env;`
624
+ );
582
625
 
583
- load(id, options) {
584
- const browser = !options?.ssr;
585
-
586
- const global = is_build
587
- ? `globalThis.__sveltekit_${version_hash}`
588
- : 'globalThis.__sveltekit_dev';
589
-
590
- const explicit_env_flag = kit.experimental.explicitEnvironmentVariables;
591
-
592
- switch (id) {
593
- case env_static_private:
594
- return create_static_module('$env/static/private', env.private, explicit_env_flag);
595
-
596
- case env_static_public:
597
- return create_static_module('$env/static/public', env.public, explicit_env_flag);
598
-
599
- case env_dynamic_private:
600
- return create_dynamic_module(
601
- 'private',
602
- vite_config_env.command === 'serve' ? env.private : undefined,
603
- explicit_env_flag
604
- );
605
-
606
- case env_dynamic_public:
607
- // populate `$env/dynamic/public` from `window`
608
- if (browser) {
609
- return `export const env = ${global}.env;`;
610
- }
611
-
612
- return create_dynamic_module(
613
- 'public',
614
- vite_config_env.command === 'serve' ? env.public : undefined,
615
- explicit_env_flag
616
- );
617
-
618
- case service_worker:
619
- return create_service_worker_module(svelte_config);
620
-
621
- case sveltekit_env:
622
- return create_sveltekit_env(explicit_env_config, env.all, explicit_env_entry);
623
-
624
- case sveltekit_env_public_client:
625
- return create_sveltekit_env_public(
626
- explicit_env_config,
627
- env.all,
628
- `const env = ${global}.env;`
629
- );
630
-
631
- case sveltekit_env_public_server:
632
- return create_sveltekit_env_public(
633
- explicit_env_config,
634
- env.all,
635
- `import { rendered_env as env } from '__sveltekit/env';`
636
- );
626
+ case sveltekit_env_public_server:
627
+ return create_sveltekit_env_public(
628
+ explicit_env_config,
629
+ env,
630
+ `import { rendered_env as env } from '__sveltekit/env';`
631
+ );
637
632
 
638
- case sveltekit_env_private:
639
- return create_sveltekit_env_private(explicit_env_config, env.all);
633
+ case sveltekit_env_private:
634
+ return create_sveltekit_env_private(explicit_env_config, env);
640
635
 
641
- case sveltekit_env_service_worker:
642
- return create_sveltekit_env_service_worker_dev(explicit_env_config, env.all, global);
636
+ case sveltekit_env_service_worker:
637
+ return create_sveltekit_env_service_worker_dev(explicit_env_config, env, global);
643
638
 
644
- case sveltekit_server: {
645
- return dedent`
639
+ case sveltekit_server: {
640
+ return dedent`
646
641
  export let read_implementation = null;
647
642
 
648
643
  export let manifest = null;
@@ -655,6 +650,7 @@ async function kit({ svelte_config }) {
655
650
  manifest = _;
656
651
  }
657
652
  `;
653
+ }
658
654
  }
659
655
  }
660
656
  }
@@ -666,7 +662,7 @@ async function kit({ svelte_config }) {
666
662
 
667
663
  /**
668
664
  * Ensures that client-side code can't accidentally import server-side code,
669
- * whether in `*.server.js` files, `$app/server`, `$lib/server`, `$app/env/private`, or `$env/[static|dynamic]/private`
665
+ * whether in `*.server.js` files, `$app/server`, `$lib/server`, or `$app/env/private`
670
666
  * @type {Plugin}
671
667
  */
672
668
  const plugin_guard = {
@@ -676,46 +672,69 @@ async function kit({ svelte_config }) {
676
672
  // are added to the module graph
677
673
  enforce: 'pre',
678
674
 
679
- async resolveId(id, importer, options) {
680
- if (importer && !importer.endsWith('index.html')) {
681
- const resolved = await this.resolve(id, importer, { ...options, skipSelf: true });
682
-
683
- if (resolved) {
684
- const normalized = normalize_id(resolved.id, normalized_lib, normalized_cwd);
675
+ applyToEnvironment(environment) {
676
+ return environment.name !== 'serviceWorker';
677
+ },
685
678
 
686
- let importers = import_map.get(normalized);
679
+ resolveId: {
680
+ // TODO: use composable filter API here when supported:
681
+ // https://github.com/vitejs/rolldown-vite/issues/605
682
+ // filter: ([
683
+ // exclude(importerId(/index\.html$/)),
684
+ // include(importerId(/.+/))
685
+ // ]),
686
+ async handler(id, importer, options) {
687
+ if (importer && !importer.endsWith('index.html')) {
688
+ const resolved = await this.resolve(id, importer, { ...options, skipSelf: true });
689
+
690
+ if (resolved) {
691
+ const normalized = normalize_id(resolved.id, normalized_lib, normalized_cwd);
692
+
693
+ let importers = import_map.get(normalized);
694
+
695
+ if (!importers) {
696
+ importers = new Set();
697
+ import_map.set(normalized, importers);
698
+ }
687
699
 
688
- if (!importers) {
689
- importers = new Set();
690
- import_map.set(normalized, importers);
700
+ importers.add(normalize_id(importer, normalized_lib, normalized_cwd));
691
701
  }
692
-
693
- importers.add(normalize_id(importer, normalized_lib, normalized_cwd));
694
702
  }
695
703
  }
696
704
  },
697
705
 
698
- load(id, options) {
699
- if (options?.ssr === true || process.env.TEST === 'true') {
700
- return;
701
- }
706
+ load: {
707
+ filter: {
708
+ id: [
709
+ exactRegex(app_server),
710
+ exactRegex(app_env_private),
711
+ /\/server\//,
712
+ new RegExp(`${server_only_pattern.source}$`)
713
+ ]
714
+ },
715
+ handler(id) {
716
+ if (this.environment.config.consumer !== 'client') return;
702
717
 
703
- // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context
704
- const is_internal = id.startsWith(normalized_cwd) && !id.startsWith(normalized_node_modules);
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);
705
721
 
706
- const normalized = normalize_id(id, normalized_lib, normalized_cwd);
722
+ const normalized = normalize_id(id, normalized_lib, normalized_cwd);
723
+
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)));
707
729
 
708
- const is_server_only =
709
- normalized === '$env/static/private' ||
710
- normalized === '$env/dynamic/private' ||
711
- normalized === '$app/env/private' ||
712
- normalized === '$app/server' ||
713
- normalized.startsWith('$lib/server/') ||
714
- (is_internal && server_only_pattern.test(path.basename(id)));
730
+ // 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;
734
+ }
715
735
 
716
- if (is_server_only) {
717
736
  // in dev, this doesn't exist, so we need to create it
718
- manifest_data ??= sync.all(svelte_config, vite_config_env.mode).manifest_data;
737
+ manifest_data ??= sync.all(svelte_config, root).manifest_data;
719
738
 
720
739
  /** @type {Set<string>} */
721
740
  const entrypoints = new Set();
@@ -727,7 +746,6 @@ async function kit({ svelte_config }) {
727
746
  if (manifest_data.hooks.client) entrypoints.add(manifest_data.hooks.client);
728
747
  if (manifest_data.hooks.universal) entrypoints.add(manifest_data.hooks.universal);
729
748
 
730
- const normalized = normalize_id(id, normalized_lib, normalized_cwd);
731
749
  const chain = [normalized];
732
750
 
733
751
  let current = normalized;
@@ -791,16 +809,33 @@ async function kit({ svelte_config }) {
791
809
  const plugin_remote = {
792
810
  name: 'vite-plugin-sveltekit-remote',
793
811
 
794
- resolveId(id) {
795
- if (id.startsWith('\0sveltekit-remote:')) return id;
812
+ applyToEnvironment(environment) {
813
+ return environment.name !== 'serviceWorker';
796
814
  },
797
815
 
798
- load(id) {
799
- // On-the-fly generated entry point for remote file just forwards the original module
800
- // We're not using manualChunks because it can cause problems with circular dependencies
801
- // (e.g. https://github.com/sveltejs/kit/issues/14679) and module ordering in general
802
- // (e.g. https://github.com/sveltejs/kit/issues/14590).
803
- if (id.startsWith('\0sveltekit-remote:')) {
816
+ // prevent other plugins from resolving our remote virtual module
817
+ resolveId: {
818
+ filter: {
819
+ id: prefixRegex('\0sveltekit-remote:')
820
+ },
821
+ handler(id) {
822
+ return id;
823
+ }
824
+ },
825
+
826
+ load: {
827
+ filter: {
828
+ id: prefixRegex('\0sveltekit-remote:')
829
+ },
830
+ handler(id) {
831
+ if (!kit.experimental.remoteFunctions) {
832
+ return null;
833
+ }
834
+
835
+ // On-the-fly generated entry point for remote file just forwards the original module
836
+ // We're not using manualChunks because it can cause problems with circular dependencies
837
+ // (e.g. https://github.com/sveltejs/kit/issues/14679) and module ordering in general
838
+ // (e.g. https://github.com/sveltejs/kit/issues/14590).
804
839
  const hash_id = id.slice('\0sveltekit-remote:'.length);
805
840
  const original = remote_original_by_hash.get(hash_id);
806
841
  if (!original) throw new Error(`Expected to find metadata for remote file ${id}`);
@@ -809,16 +844,24 @@ async function kit({ svelte_config }) {
809
844
  },
810
845
 
811
846
  configureServer(_dev_server) {
847
+ if (!kit.experimental.remoteFunctions) {
848
+ return;
849
+ }
850
+
812
851
  dev_server = _dev_server;
813
852
  },
814
853
 
815
- async transform(code, id, opts) {
854
+ async transform(code, id) {
855
+ if (!kit.experimental.remoteFunctions) {
856
+ return;
857
+ }
858
+
816
859
  const normalized = normalize_id(id, normalized_lib, normalized_cwd);
817
860
  if (!svelte_config.kit.moduleExtensions.some((ext) => normalized.endsWith(`.remote${ext}`))) {
818
861
  return;
819
862
  }
820
863
 
821
- const file = posixify(path.relative(cwd, id));
864
+ const file = posixify(path.relative(root, id));
822
865
  const remote = {
823
866
  hash: hash(file),
824
867
  file
@@ -826,7 +869,7 @@ async function kit({ svelte_config }) {
826
869
 
827
870
  remotes.push(remote);
828
871
 
829
- if (opts?.ssr) {
872
+ if (this.environment.config.consumer !== 'client') {
830
873
  // we need to add an `await Promise.resolve()` because if the user imports this function
831
874
  // on the client AND in a load function when loading the client module we will trigger
832
875
  // an ssrLoadModule during dev. During a link preload, the module can be mistakenly
@@ -886,7 +929,7 @@ async function kit({ svelte_config }) {
886
929
  }
887
930
 
888
931
  // in prod, we already built and analysed the server code before
889
- // building the client code, so `remote_exports` is populated
932
+ // building the client code, so `remotes` is populated
890
933
  else if (build_metadata?.remotes) {
891
934
  const exports = build_metadata?.remotes.get(remote.hash);
892
935
  if (!exports) throw new Error('Expected to find metadata for remote file ' + id);
@@ -916,6 +959,106 @@ async function kit({ svelte_config }) {
916
959
  }
917
960
  };
918
961
 
962
+ /** @type {import('vite').Manifest} */
963
+ let client_manifest;
964
+ /** @type {import('types').Prerendered} */
965
+ let prerendered;
966
+
967
+ /** @type {Set<string>} */
968
+ let build;
969
+ /** @type {string} */
970
+ let service_worker_code;
971
+
972
+ /**
973
+ * Creates the service worker virtual modules
974
+ * @type {import('vite').Plugin}
975
+ */
976
+ const plugin_service_worker = {
977
+ name: 'vite-plugin-sveltekit-service-worker',
978
+
979
+ applyToEnvironment(environment) {
980
+ return environment.name === 'serviceWorker';
981
+ },
982
+
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
+ }
989
+
990
+ if (id.startsWith('__sveltekit')) {
991
+ return `\0virtual:${id}`;
992
+ }
993
+ }
994
+ },
995
+
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
+ }
1005
+
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('/')";
1009
+
1010
+ service_worker_code = dedent`
1011
+ export const base = /*@__PURE__*/ ${base};
1012
+
1013
+ export const build = [
1014
+ ${Array.from(build)
1015
+ .map((file) => `base + ${s(`/${file}`)}`)
1016
+ .join(',\n')}
1017
+ ];
1018
+
1019
+ export const files = [
1020
+ ${manifest_data.assets
1021
+ .filter((asset) => kit.serviceWorker.files(asset.file))
1022
+ .map((asset) => `base + ${s(`/${asset.file}`)}`)
1023
+ .join(',\n')}
1024
+ ];
1025
+
1026
+ export const prerendered = [
1027
+ ${prerendered.paths.map((path) => `base + ${s(path.replace(kit.paths.base, ''))}`).join(',\n')}
1028
+ ];
1029
+
1030
+ export const version = ${s(kit.version.name)};
1031
+ `;
1032
+ }
1033
+
1034
+ if (!id.startsWith('\0virtual:')) return;
1035
+
1036
+ const global = is_build
1037
+ ? `globalThis.__sveltekit_${version_hash}`
1038
+ : 'globalThis.__sveltekit_dev';
1039
+
1040
+ if (id === service_worker) {
1041
+ return service_worker_code;
1042
+ }
1043
+
1044
+ if (id === sveltekit_env_service_worker) {
1045
+ return create_sveltekit_env_service_worker_dev(explicit_env_config, env, global);
1046
+ }
1047
+
1048
+ if (id === sveltekit_env_public_client) {
1049
+ return create_sveltekit_env_public(explicit_env_config, env, `const env = ${global}.env;`);
1050
+ }
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
+ }
1060
+ };
1061
+
919
1062
  /** @type {Plugin} */
920
1063
  const plugin_service_worker_env = {
921
1064
  name: 'vite-plugin-sveltekit-service-worker-env',
@@ -940,6 +1083,10 @@ async function kit({ svelte_config }) {
940
1083
  const plugin_compile = {
941
1084
  name: 'vite-plugin-sveltekit-compile',
942
1085
 
1086
+ applyToEnvironment(environment) {
1087
+ return environment.name !== 'serviceWorker';
1088
+ },
1089
+
943
1090
  /**
944
1091
  * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
945
1092
  * @see https://vitejs.dev/guide/api-plugin.html#config
@@ -951,131 +1098,123 @@ async function kit({ svelte_config }) {
951
1098
  /** @type {UserConfig} */
952
1099
  let new_config;
953
1100
 
954
- const kit_paths_base = kit.paths.base || '/';
955
-
956
1101
  if (is_build) {
957
- const ssr = /** @type {boolean} */ (config.build?.ssr);
958
1102
  const prefix = `${kit.appDir}/immutable`;
959
1103
 
960
1104
  /** @type {Record<string, string>} */
961
- const input = {};
1105
+ const server_input = {
1106
+ index: `${runtime_directory}/server/index.js`,
1107
+ internal: `${out_dir}/generated/server/internal.js`,
1108
+ ['remote-entry']: `${runtime_directory}/app/server/remote/index.js`
1109
+ };
962
1110
 
963
- if (ssr) {
964
- input.index = `${runtime_directory}/server/index.js`;
965
- input.internal = `${out_dir}/generated/server/internal.js`;
966
- input['remote-entry'] = `${runtime_directory}/app/server/remote/index.js`;
1111
+ // add entry points for every endpoint...
1112
+ manifest_data.routes.forEach((route) => {
1113
+ if (route.endpoint) {
1114
+ const resolved = path.resolve(root, route.endpoint.file);
1115
+ const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
1116
+ const name = posixify(path.join('entries/endpoints', relative.replace(/\.js$/, '')));
1117
+ server_input[name] = resolved;
1118
+ }
1119
+ });
967
1120
 
968
- // add entry points for every endpoint...
969
- manifest_data.routes.forEach((route) => {
970
- if (route.endpoint) {
971
- const resolved = path.resolve(route.endpoint.file);
1121
+ // ...and every component used by pages...
1122
+ manifest_data.nodes.forEach((node) => {
1123
+ for (const file of [node.component, node.universal, node.server]) {
1124
+ if (file) {
1125
+ const resolved = path.resolve(root, file);
972
1126
  const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
973
- const name = posixify(
974
- path.join('entries/endpoints', relative.replace(/\.js$/, ''))
975
- );
976
- input[name] = resolved;
977
- }
978
- });
979
1127
 
980
- // ...and every component used by pages...
981
- manifest_data.nodes.forEach((node) => {
982
- for (const file of [node.component, node.universal, node.server]) {
983
- if (file) {
984
- const resolved = path.resolve(file);
985
- const relative = decodeURIComponent(path.relative(kit.files.routes, resolved));
986
-
987
- const name = relative.startsWith('..')
988
- ? posixify(path.join('entries/fallbacks', path.basename(file)))
989
- : posixify(path.join('entries/pages', relative.replace(/\.js$/, '')));
990
- input[name] = resolved;
991
- }
1128
+ const name = relative.startsWith('..')
1129
+ ? posixify(path.join('entries/fallbacks', path.basename(file)))
1130
+ : posixify(path.join('entries/pages', relative.replace(/\.js$/, '')));
1131
+ server_input[name] = resolved;
992
1132
  }
993
- });
1133
+ }
1134
+ });
994
1135
 
995
- // ...and every matcher
996
- Object.entries(manifest_data.matchers).forEach(([key, file]) => {
997
- const name = posixify(path.join('entries/matchers', key));
998
- input[name] = path.resolve(file);
999
- });
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
+ });
1141
+
1142
+ // ...and the hooks files
1143
+ if (manifest_data.hooks.server) {
1144
+ server_input['entries/hooks.server'] = path.resolve(root, manifest_data.hooks.server);
1145
+ }
1146
+ if (manifest_data.hooks.universal) {
1147
+ server_input['entries/hooks.universal'] = path.resolve(
1148
+ root,
1149
+ manifest_data.hooks.universal
1150
+ );
1151
+ }
1000
1152
 
1001
- // ...and the hooks files
1002
- if (manifest_data.hooks.server) {
1003
- input['entries/hooks.server'] = path.resolve(manifest_data.hooks.server);
1153
+ // ...and the server instrumentation file
1154
+ const server_instrumentation = resolve_entry(
1155
+ path.join(kit.files.src, 'instrumentation.server')
1156
+ );
1157
+ if (server_instrumentation) {
1158
+ if (adapter && !adapter.supports?.instrumentation?.()) {
1159
+ throw new Error(`${server_instrumentation} is unsupported in ${adapter.name}.`);
1004
1160
  }
1005
- if (manifest_data.hooks.universal) {
1006
- input['entries/hooks.universal'] = path.resolve(manifest_data.hooks.universal);
1161
+ if (!kit.experimental.instrumentation.server) {
1162
+ error_for_missing_config(
1163
+ '`instrumentation.server.js`',
1164
+ 'kit.experimental.instrumentation.server',
1165
+ 'true'
1166
+ );
1007
1167
  }
1168
+ server_input['instrumentation.server'] = server_instrumentation;
1169
+ }
1008
1170
 
1009
- // ...and the server instrumentation file
1010
- const server_instrumentation = resolve_entry(
1011
- path.join(kit.files.src, 'instrumentation.server')
1012
- );
1013
- if (server_instrumentation) {
1014
- const { adapter } = kit;
1015
- if (adapter && !adapter.supports?.instrumentation?.()) {
1016
- throw new Error(`${server_instrumentation} is unsupported in ${adapter.name}.`);
1017
- }
1018
- if (!kit.experimental.instrumentation.server) {
1019
- error_for_missing_config(
1020
- '`instrumentation.server.js`',
1021
- 'kit.experimental.instrumentation.server',
1022
- 'true'
1023
- );
1024
- }
1025
- input['instrumentation.server'] = server_instrumentation;
1026
- }
1027
- } else if (svelte_config.kit.output.bundleStrategy !== 'split') {
1028
- input['bundle'] = `${runtime_directory}/client/bundle.js`;
1171
+ /** @type {Record<string, string>} */
1172
+ const client_input = {};
1173
+
1174
+ if (svelte_config.kit.output.bundleStrategy !== 'split') {
1175
+ client_input['bundle'] = `${runtime_directory}/client/bundle.js`;
1029
1176
  } else {
1030
- input['entry/start'] = `${runtime_directory}/client/entry.js`;
1031
- input['entry/app'] = `${out_dir}/generated/client-optimized/app.js`;
1177
+ client_input['entry/start'] = `${runtime_directory}/client/entry.js`;
1178
+ client_input['entry/app'] = `${out_dir}/generated/client-optimized/app.js`;
1032
1179
  manifest_data.nodes.forEach((node, i) => {
1033
1180
  if (node.component || node.universal) {
1034
- input[`nodes/${i}`] = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
1181
+ client_input[`nodes/${i}`] = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
1035
1182
  }
1036
1183
  });
1037
1184
  }
1038
1185
 
1039
- // see the kit.output.preloadStrategy option for details on why we have multiple options here
1040
- const ext = kit.output.preloadStrategy === 'preload-mjs' ? 'mjs' : 'js';
1186
+ const inline = svelte_config.kit.output.bundleStrategy === 'inline';
1041
1187
 
1042
- // We could always use a relative asset base path here, but it's better for performance not to.
1043
- // E.g. Vite generates `new URL('/asset.png', import.meta).href` for a relative path vs just '/asset.png'.
1044
- // That's larger and takes longer to run and also causes an HTML diff between SSR and client
1045
- // causing us to do a more expensive hydration check.
1046
- const client_base =
1047
- kit.paths.relative !== false || kit.paths.assets ? './' : kit_paths_base;
1188
+ const config_base = assets_base(kit);
1048
1189
 
1049
- const inline = !ssr && svelte_config.kit.output.bundleStrategy === 'inline';
1050
- const split = ssr || svelte_config.kit.output.bundleStrategy === 'split';
1190
+ /** @type {string} */
1191
+ const base = kit.paths.assets || kit.paths.base || '/';
1192
+ const root_to_assets = prefix + '/assets/';
1193
+ const assets_to_root =
1194
+ prefix
1195
+ .split('/')
1196
+ .map(() => '..')
1197
+ .join('/') + '/../';
1051
1198
 
1052
1199
  new_config = {
1053
- base: ssr ? assets_base(kit) : client_base,
1200
+ appType: 'custom',
1201
+ base: config_base,
1054
1202
  build: {
1055
- copyPublicDir: !ssr,
1056
- cssCodeSplit: svelte_config.kit.output.bundleStrategy !== 'inline',
1203
+ cssCodeSplit: !inline,
1057
1204
  cssMinify:
1058
1205
  initial_config.build?.minify == null ? true : !!initial_config.build.minify,
1059
1206
  manifest: true,
1060
- outDir: `${out}/${ssr ? 'server' : 'client'}`,
1061
- rollupOptions: {
1062
- input: inline ? input['bundle'] : input,
1207
+ rolldownOptions: {
1063
1208
  output: {
1064
- format: inline ? 'iife' : 'esm',
1065
1209
  name: `__sveltekit_${version_hash}.app`,
1066
- entryFileNames: ssr ? '[name].js' : `${prefix}/[name].[hash].${ext}`,
1067
- chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[hash].${ext}`,
1068
1210
  assetFileNames: `${prefix}/assets/[name].[hash][extname]`,
1069
1211
  hoistTransitiveImports: false,
1070
- sourcemapIgnoreList,
1071
- inlineDynamicImports: is_rolldown ? undefined : !split
1212
+ sourcemapIgnoreList
1072
1213
  },
1073
1214
  preserveEntrySignatures: 'strict',
1074
1215
  onwarn(warning, handler) {
1075
1216
  if (
1076
- (is_rolldown
1077
- ? warning.code === 'IMPORT_IS_UNDEFINED'
1078
- : warning.code === 'MISSING_EXPORT') &&
1217
+ warning.code === 'IMPORT_IS_UNDEFINED' &&
1079
1218
  warning.id === `${out_dir}/generated/client-optimized/app.js`
1080
1219
  ) {
1081
1220
  // ignore e.g. undefined `handleError` hook when
@@ -1086,34 +1225,128 @@ async function kit({ svelte_config }) {
1086
1225
  handler(warning);
1087
1226
  }
1088
1227
  },
1089
- ssrEmitAssets: true,
1090
- target: ssr ? 'node18.13' : undefined
1228
+ emptyOutDir: false,
1229
+ ssrEmitAssets: true
1091
1230
  },
1092
- publicDir: kit.files.assets,
1093
- worker: {
1094
- rollupOptions: {
1095
- output: {
1096
- entryFileNames: `${prefix}/workers/[name]-[hash].js`,
1097
- chunkFileNames: `${prefix}/workers/chunks/[hash].js`,
1098
- assetFileNames: `${prefix}/workers/assets/[name]-[hash][extname]`,
1099
- hoistTransitiveImports: false
1231
+ builder: {
1232
+ sharedConfigBuild: true,
1233
+ sharedPlugins: true
1234
+ },
1235
+ environments: {
1236
+ ssr: {
1237
+ build: {
1238
+ copyPublicDir: false,
1239
+ outDir: `${out}/server`,
1240
+ target: 'node22',
1241
+ rolldownOptions: {
1242
+ input: server_input,
1243
+ output: {
1244
+ entryFileNames: '[name].js',
1245
+ chunkFileNames: 'chunks/[name].js'
1246
+ }
1247
+ }
1248
+ },
1249
+ // during the initial server build we don't know yet
1250
+ define: {
1251
+ __SVELTEKIT_HAS_SERVER_LOAD__: 'true',
1252
+ __SVELTEKIT_HAS_UNIVERSAL_LOAD__: 'true',
1253
+ __SVELTEKIT_PAYLOAD__: '{}'
1254
+ }
1255
+ },
1256
+ client: {
1257
+ build: {
1258
+ outDir: `${out}/client`,
1259
+ rolldownOptions: {
1260
+ input: inline ? client_input['bundle'] : client_input,
1261
+ output: {
1262
+ format: inline ? 'iife' : 'esm',
1263
+ entryFileNames: `${prefix}/[name].[hash].js`,
1264
+ chunkFileNames: `${prefix}/chunks/[hash].js`,
1265
+ codeSplitting: svelte_config.kit.output.bundleStrategy === 'split'
1266
+ },
1267
+ // This silences Rolldown warnings about not supporting `import.meta`
1268
+ // for the `iife` output format. We don't care because it's
1269
+ // only used in development and will be treeshaken away
1270
+ transform: inline
1271
+ ? {
1272
+ define: {
1273
+ 'import.meta': '{}'
1274
+ }
1275
+ }
1276
+ : undefined
1277
+ }
1278
+ },
1279
+ define: {
1280
+ __SVELTEKIT_PAYLOAD__: `globalThis.__sveltekit_${version_hash}`
1100
1281
  }
1101
1282
  }
1102
- }
1283
+ },
1284
+ 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
+ }
1316
+ },
1317
+ publicDir: kit.files.assets
1103
1318
  };
1104
1319
 
1105
- // we must reference Vite 8 options conditionally. Otherwise, older Vite
1106
- // versions throw an error about unknown config options
1107
- if (is_rolldown && new_config?.build?.rollupOptions?.output) {
1108
- // @ts-ignore only available in Vite 8
1109
- new_config.build.rollupOptions.output.codeSplitting = split;
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
+ };
1110
1341
  }
1111
1342
  } else {
1112
1343
  new_config = {
1113
1344
  appType: 'custom',
1114
- base: kit_paths_base,
1345
+ // we avoid setting base to paths.assets in dev so that we get the
1346
+ // trailing slash redirect to paths.base if it is set
1347
+ base: kit.paths.base || '/',
1115
1348
  build: {
1116
- rollupOptions: {
1349
+ rolldownOptions: {
1117
1350
  // Vite dependency crawler needs an explicit JS entry point
1118
1351
  // even though server otherwise works without it
1119
1352
  input: `${runtime_directory}/client/entry.js`
@@ -1134,7 +1367,7 @@ async function kit({ svelte_config }) {
1134
1367
  * @see https://vitejs.dev/guide/api-plugin.html#configureserver
1135
1368
  */
1136
1369
  async configureServer(vite) {
1137
- return await dev(vite, vite_config, svelte_config, () => remotes);
1370
+ return await dev(vite, vite_config, svelte_config, () => remotes, root, adapter);
1138
1371
  },
1139
1372
 
1140
1373
  /**
@@ -1142,21 +1375,7 @@ async function kit({ svelte_config }) {
1142
1375
  * @see https://vitejs.dev/guide/api-plugin.html#configurepreviewserver
1143
1376
  */
1144
1377
  configurePreviewServer(vite) {
1145
- return preview(vite, vite_config, svelte_config);
1146
- },
1147
-
1148
- /**
1149
- * Clears the output directories.
1150
- */
1151
- buildStart() {
1152
- if (secondary_build_started) return;
1153
-
1154
- if (is_build) {
1155
- if (!vite_config.build.watch) {
1156
- rimraf(out);
1157
- }
1158
- mkdirp(out);
1159
- }
1378
+ return preview(vite, vite_config, svelte_config, adapter);
1160
1379
  },
1161
1380
 
1162
1381
  renderChunk(code, chunk) {
@@ -1174,7 +1393,7 @@ async function kit({ svelte_config }) {
1174
1393
  },
1175
1394
 
1176
1395
  generateBundle() {
1177
- if (vite_config.build.ssr) return;
1396
+ if (this.environment.config.consumer !== 'client') return;
1178
1397
 
1179
1398
  this.emitFile({
1180
1399
  type: 'asset',
@@ -1183,369 +1402,354 @@ async function kit({ svelte_config }) {
1183
1402
  });
1184
1403
  },
1185
1404
 
1186
- /**
1187
- * Vite builds a single bundle. We need three bundles: client, server, and service worker.
1188
- * The user's package.json scripts will invoke the Vite CLI to execute the server build. We
1189
- * then use this hook to kick off builds for the client and service worker.
1190
- */
1191
- writeBundle: {
1192
- sequential: true,
1193
- async handler(_options, server_bundle) {
1194
- if (secondary_build_started) return; // only run this once
1195
-
1196
- const verbose = vite_config.logLevel === 'info';
1197
- const log = logger({ verbose });
1198
-
1199
- /** @type {Manifest} */
1200
- const server_manifest = JSON.parse(read(`${out}/server/.vite/manifest.json`));
1201
-
1202
- /** @type {import('types').BuildData} */
1203
- const build_data = {
1204
- app_dir: kit.appDir,
1205
- app_path: `${kit.paths.base.slice(1)}${kit.paths.base ? '/' : ''}${kit.appDir}`,
1206
- manifest_data,
1207
- out_dir: out,
1208
- service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
1209
- client: null,
1210
- server_manifest
1211
- };
1405
+ async buildApp(builder) {
1406
+ // clears the output directories
1407
+ if (!builder.config.build.watch) {
1408
+ rimraf(out);
1409
+ }
1410
+ mkdirp(out);
1411
+
1412
+ const server_bundle = /** @type {import('vite').Rolldown.RolldownOutput} */ (
1413
+ await builder.build(builder.environments.ssr)
1414
+ );
1415
+
1416
+ const verbose = vite_config.logLevel === 'info';
1417
+ const log = logger({ verbose });
1418
+
1419
+ /** @type {import('vite').Manifest} */
1420
+ const server_manifest = JSON.parse(read(`${out}/server/.vite/manifest.json`));
1421
+
1422
+ /** @type {import('types').BuildData} */
1423
+ const build_data = {
1424
+ app_dir: kit.appDir,
1425
+ app_path: `${kit.paths.base.slice(1)}${kit.paths.base ? '/' : ''}${kit.appDir}`,
1426
+ manifest_data,
1427
+ out_dir: out,
1428
+ service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
1429
+ client: null,
1430
+ server_manifest
1431
+ };
1212
1432
 
1213
- const manifest_path = `${out}/server/manifest-full.js`;
1214
- fs.writeFileSync(
1215
- manifest_path,
1216
- `export const manifest = ${generate_manifest({
1217
- build_data,
1218
- prerendered: [],
1219
- relative_path: '.',
1220
- routes: manifest_data.routes,
1221
- remotes
1222
- })};\n`
1223
- );
1433
+ const manifest_path = `${out}/server/manifest-full.js`;
1434
+ fs.writeFileSync(
1435
+ manifest_path,
1436
+ `export const manifest = ${generate_manifest({
1437
+ build_data,
1438
+ prerendered: [],
1439
+ relative_path: '.',
1440
+ routes: manifest_data.routes,
1441
+ remotes,
1442
+ root
1443
+ })};\n`
1444
+ );
1445
+
1446
+ log.info('Analysing routes');
1447
+
1448
+ const { metadata } = await analyse({
1449
+ hash: kit.router.type === 'hash',
1450
+ manifest_path,
1451
+ manifest_data,
1452
+ server_manifest,
1453
+ tracked_features,
1454
+ env,
1455
+ out,
1456
+ remotes,
1457
+ root,
1458
+ vite_config_file: vite_config.configFile
1459
+ });
1224
1460
 
1225
- log.info('Analysing routes');
1226
-
1227
- const { metadata } = await analyse({
1228
- hash: kit.router.type === 'hash',
1229
- manifest_path,
1230
- manifest_data,
1231
- server_manifest,
1232
- tracked_features,
1233
- env: { ...env.private, ...env.public },
1234
- out,
1235
- remotes
1236
- });
1237
-
1238
- build_metadata = metadata;
1239
-
1240
- log.info('Building app');
1241
-
1242
- // create client build
1243
- write_client_manifest(
1244
- kit,
1245
- manifest_data,
1246
- `${out_dir}/generated/client-optimized`,
1247
- metadata.nodes
1248
- );
1461
+ build_metadata = metadata;
1249
1462
 
1250
- secondary_build_started = true;
1463
+ log.info('Building app');
1251
1464
 
1252
- let client_chunks;
1465
+ // create client build
1466
+ write_client_manifest(
1467
+ kit,
1468
+ manifest_data,
1469
+ `${out_dir}/generated/client-optimized`,
1470
+ metadata.nodes
1471
+ );
1253
1472
 
1254
- try {
1255
- const bundle = /** @type {import('vite').Rollup.RollupOutput} */ (
1256
- await vite.build({
1257
- configFile: vite_config.configFile,
1258
- // CLI args
1259
- mode: vite_config_env.mode,
1260
- logLevel: vite_config.logLevel,
1261
- clearScreen: vite_config.clearScreen,
1262
- build: {
1263
- minify: initial_config.build?.minify,
1264
- assetsInlineLimit: vite_config.build.assetsInlineLimit,
1265
- sourcemap: vite_config.build.sourcemap
1266
- },
1267
- optimizeDeps: {
1268
- force: vite_config.optimizeDeps.force
1269
- }
1270
- })
1271
- );
1473
+ const nodes = Object.values(
1474
+ /** @type {import('types').ServerMetadata} */ (build_metadata).nodes
1475
+ );
1272
1476
 
1273
- client_chunks = bundle.output;
1274
- } catch (e) {
1275
- const error =
1276
- e instanceof Error ? e : new Error(/** @type {any} */ (e).message ?? e ?? '<unknown>');
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);
1277
1480
 
1278
- // without this, errors that occur during the secondary build
1279
- // will be logged twice
1280
- throw stackless(error.stack ?? error.message);
1281
- }
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
+ }
1282
1487
 
1283
- // We use `build.ssrEmitAssets` so that asset URLs created from
1284
- // imports in server-only modules correspond to files in the build,
1285
- // but we don't want to copy over CSS imports as these are already
1286
- // accounted for in the client bundle. In most cases it would be
1287
- // a no-op, but for SSR builds `url(...)` paths are handled
1288
- // differently (relative for client, absolute for server)
1289
- // resulting in different hashes, and thus duplication
1290
- const ssr_stylesheets = new Set(
1291
- Object.values(server_manifest)
1292
- .map((chunk) => chunk.css ?? [])
1293
- .flat()
1294
- );
1488
+ const { output: client_chunks } = /** @type {import('vite').Rolldown.RolldownOutput} */ (
1489
+ await builder.build(builder.environments.client)
1490
+ );
1491
+
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
+ );
1504
+
1505
+ const assets_path = `${kit.appDir}/immutable/assets`;
1506
+ const server_assets = `${out}/server/${assets_path}`;
1507
+ const client_assets = `${out}/client/${assets_path}`;
1508
+
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}`;
1513
+
1514
+ if (fs.existsSync(dest) || ssr_stylesheets.has(`${assets_path}/${file}`)) {
1515
+ continue;
1516
+ }
1295
1517
 
1296
- const assets_path = `${kit.appDir}/immutable/assets`;
1297
- const server_assets = `${out}/server/${assets_path}`;
1298
- const client_assets = `${out}/client/${assets_path}`;
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}`, '.');
1299
1523
 
1300
- if (fs.existsSync(server_assets)) {
1301
- for (const file of fs.readdirSync(server_assets)) {
1302
- const src = `${server_assets}/${file}`;
1303
- const dest = `${client_assets}/${file}`;
1524
+ fs.writeFileSync(src, content);
1525
+ }
1304
1526
 
1305
- if (fs.existsSync(dest) || ssr_stylesheets.has(`${assets_path}/${file}`)) {
1306
- continue;
1307
- }
1527
+ copy(src, dest);
1528
+ }
1529
+ }
1308
1530
 
1309
- if (file.endsWith('.css')) {
1310
- // make absolute paths in CSS relative, for portability
1311
- const content = fs
1312
- .readFileSync(src, 'utf-8')
1313
- .replaceAll(`${kit.paths.base}/${assets_path}`, '.');
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(
1552
+ (chunk) => chunk.type === 'chunk' && chunk.modules[sveltekit_env_public_client]
1553
+ )
1554
+ };
1314
1555
 
1315
- fs.writeFileSync(src, content);
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 };
1316
1571
  }
1317
-
1318
- copy(src, dest);
1319
- }
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
+ );
1320
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
+ };
1321
1605
 
1322
- /** @type {Manifest} */
1323
- const client_manifest = JSON.parse(read(`${out}/client/.vite/manifest.json`));
1324
-
1325
- /**
1326
- * @param {string} entry
1327
- * @param {boolean} [add_dynamic_css]
1328
- */
1329
- const deps_of = (entry, add_dynamic_css = false) =>
1330
- find_deps(client_manifest, posixify(path.relative('.', entry)), add_dynamic_css);
1331
-
1332
- if (svelte_config.kit.output.bundleStrategy === 'split') {
1333
- const start = deps_of(`${runtime_directory}/client/entry.js`);
1334
- const app = deps_of(`${out_dir}/generated/client-optimized/app.js`);
1335
-
1336
- build_data.client = {
1337
- start: start.file,
1338
- app: app.file,
1339
- imports: [...start.imports, ...app.imports],
1340
- stylesheets: [...start.stylesheets, ...app.stylesheets],
1341
- fonts: [...start.fonts, ...app.fonts],
1342
- uses_env_dynamic_public: client_chunks.some(
1343
- (chunk) => chunk.type === 'chunk' && chunk.modules[env_dynamic_public]
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'
1344
1611
  )
1345
- };
1612
+ );
1346
1613
 
1347
- // In case of server-side route resolution, we create a purpose-built route manifest that is
1348
- // similar to that on the client, with as much information computed upfront so that we
1349
- // don't need to include any code of the actual routes in the server bundle.
1350
- if (svelte_config.kit.router.resolution === 'server') {
1351
- const nodes = manifest_data.nodes.map((node, i) => {
1352
- if (node.component || node.universal) {
1353
- const entry = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
1354
- const deps = deps_of(entry, true);
1355
- const file = resolve_symlinks(
1356
- client_manifest,
1357
- `${out_dir}/generated/client-optimized/nodes/${i}.js`
1358
- ).chunk.file;
1359
-
1360
- return { file, css: deps.stylesheets };
1361
- }
1362
- });
1363
- build_data.client.nodes = nodes.map((node) => node?.file);
1364
- build_data.client.css = nodes.map((node) => node?.css);
1365
-
1366
- build_data.client.routes = compact(
1367
- manifest_data.routes.map((route) => {
1368
- if (!route.page) return;
1369
-
1370
- return {
1371
- id: route.id,
1372
- pattern: route.pattern,
1373
- params: route.params,
1374
- layouts: route.page.layouts.map((l) =>
1375
- l !== undefined ? [metadata.nodes[l].has_server_load, l] : undefined
1376
- ),
1377
- errors: route.page.errors,
1378
- leaf: [metadata.nodes[route.page.leaf].has_server_load, route.page.leaf]
1379
- };
1380
- })
1381
- );
1382
- }
1383
- } else {
1384
- const start = deps_of(`${runtime_directory}/client/bundle.js`);
1385
-
1386
- build_data.client = {
1387
- start: start.file,
1388
- imports: start.imports,
1389
- stylesheets: start.stylesheets,
1390
- fonts: start.fonts,
1391
- uses_env_dynamic_public: client_chunks.some(
1392
- (chunk) => chunk.type === 'chunk' && chunk.modules[env_dynamic_public]
1393
- )
1614
+ build_data.client.inline = {
1615
+ script: read(`${out}/client/${start.file}`),
1616
+ style: /** @type {string | undefined} */ (style?.source)
1394
1617
  };
1618
+ }
1619
+ }
1395
1620
 
1396
- if (svelte_config.kit.output.bundleStrategy === 'inline') {
1397
- const style = /** @type {import('vite').Rollup.OutputAsset} */ (
1398
- client_chunks.find(
1399
- (chunk) =>
1400
- chunk.type === 'asset' &&
1401
- chunk.names.length === 1 &&
1402
- chunk.names[0] === 'style.css'
1403
- )
1404
- );
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
+ );
1633
+
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
+ );
1645
+
1646
+ // ...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
+ });
1656
+ prerendered = prerender_results.prerendered;
1657
+
1658
+ await treeshake_prerendered_remotes(
1659
+ vite,
1660
+ out,
1661
+ remotes,
1662
+ metadata,
1663
+ process.cwd(),
1664
+ server_bundle,
1665
+ vite_config.build.sourcemap
1666
+ );
1667
+
1668
+ // generate a new manifest that doesn't include prerendered pages
1669
+ fs.writeFileSync(
1670
+ `${out}/server/manifest.js`,
1671
+ `export const manifest = ${generate_manifest({
1672
+ build_data,
1673
+ prerendered: prerendered.paths,
1674
+ relative_path: '.',
1675
+ routes: manifest_data.routes.filter(
1676
+ (route) => prerender_results.prerender_map.get(route.id) !== true
1677
+ ),
1678
+ remotes,
1679
+ root
1680
+ })};\n`
1681
+ );
1405
1682
 
1406
- build_data.client.inline = {
1407
- script: read(`${out}/client/${start.file}`),
1408
- style: /** @type {string | undefined} */ (style?.source)
1409
- };
1410
- }
1683
+ if (service_worker_entry_file) {
1684
+ if (kit.paths.assets) {
1685
+ throw new Error('Cannot use service worker alongside config.kit.paths.assets');
1411
1686
  }
1412
1687
 
1413
- // regenerate manifest now that we have client entry...
1414
- fs.writeFileSync(
1415
- manifest_path,
1416
- `export const manifest = ${generate_manifest({
1417
- build_data,
1418
- prerendered: [],
1419
- relative_path: '.',
1420
- routes: manifest_data.routes,
1421
- remotes
1422
- })};\n`
1423
- );
1688
+ log.info('Building service worker');
1424
1689
 
1425
- // regenerate nodes with the client manifest...
1426
- build_server_nodes(
1427
- out,
1428
- kit,
1429
- manifest_data,
1430
- server_manifest,
1431
- client_manifest,
1432
- assets_path,
1433
- client_chunks
1434
- );
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`
1698
+ };
1699
+ };
1435
1700
 
1436
- // ...and prerender
1437
- const { prerendered, prerender_map } = await prerender({
1438
- hash: kit.router.type === 'hash',
1439
- out,
1440
- manifest_path,
1441
- metadata,
1442
- verbose,
1443
- env: { ...env.private, ...env.public }
1444
- });
1701
+ await builder.build(builder.environments.serviceWorker);
1702
+ }
1445
1703
 
1446
- await treeshake_prerendered_remotes(
1447
- out,
1448
- remotes,
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,
1449
1714
  metadata,
1450
- cwd,
1451
- server_bundle,
1452
- vite_config.build.sourcemap
1715
+ prerendered,
1716
+ prerender_results.prerender_map,
1717
+ log,
1718
+ remotes,
1719
+ vite_config,
1720
+ explicit_env_config
1453
1721
  );
1722
+ } else {
1723
+ console.log(styleText(['bold', 'yellow'], '\nNo adapter specified'));
1454
1724
 
1455
- // generate a new manifest that doesn't include prerendered pages
1456
- fs.writeFileSync(
1457
- `${out}/server/manifest.js`,
1458
- `export const manifest = ${generate_manifest({
1459
- build_data,
1460
- prerendered: prerendered.paths,
1461
- relative_path: '.',
1462
- routes: manifest_data.routes.filter((route) => prerender_map.get(route.id) !== true),
1463
- remotes
1464
- })};\n`
1725
+ const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
1726
+ console.log(
1727
+ `See ${link} to learn how to configure your app to run on the platform of your choosing`
1465
1728
  );
1466
-
1467
- if (service_worker_entry_file) {
1468
- if (kit.paths.assets) {
1469
- throw new Error('Cannot use service worker alongside config.kit.paths.assets');
1470
- }
1471
-
1472
- log.info('Building service worker');
1473
-
1474
- await build_service_worker(
1475
- out,
1476
- kit,
1477
- {
1478
- ...vite_config,
1479
- build: {
1480
- ...vite_config.build,
1481
- minify: initial_config.build?.minify ?? true
1482
- }
1483
- },
1484
- manifest_data,
1485
- service_worker_entry_file,
1486
- prerendered,
1487
- client_manifest,
1488
- explicit_env_config
1489
- );
1490
- }
1491
-
1492
- // we need to defer this to closeBundle, so that adapters copy files
1493
- // created by other Vite plugins
1494
- finalise = async () => {
1495
- console.log(
1496
- `\nRun ${colors
1497
- .bold()
1498
- .cyan('npm run preview')} to preview your production build locally.`
1499
- );
1500
-
1501
- if (kit.adapter) {
1502
- const { adapt } = await import('../../core/adapt/index.js');
1503
- await adapt(
1504
- svelte_config,
1505
- build_data,
1506
- metadata,
1507
- prerendered,
1508
- prerender_map,
1509
- log,
1510
- remotes,
1511
- vite_config,
1512
- explicit_env_config
1513
- );
1514
- } else {
1515
- console.log(colors.bold().yellow('\nNo adapter specified'));
1516
-
1517
- const link = colors.bold().cyan('https://svelte.dev/docs/kit/adapters');
1518
- console.log(
1519
- `See ${link} to learn how to configure your app to run on the platform of your choosing`
1520
- );
1521
- }
1522
-
1523
- secondary_build_started = false;
1524
- };
1525
1729
  }
1526
- },
1730
+ }
1731
+ };
1527
1732
 
1528
- /**
1529
- * Runs the adapter.
1530
- */
1531
- closeBundle: {
1532
- sequential: true,
1533
- async handler() {
1534
- if (!vite_config.build.ssr) return;
1535
- await finalise?.();
1536
- }
1733
+ /** @type {Plugin} */
1734
+ const plugin_adapter = {
1735
+ name: 'vite-plugin-sveltekit-adapter',
1736
+ 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
1537
1741
  }
1538
1742
  };
1539
1743
 
1540
1744
  return [
1541
1745
  plugin_setup,
1542
- kit.experimental.remoteFunctions && plugin_remote,
1746
+ plugin_remote,
1543
1747
  plugin_virtual_modules,
1544
- plugin_guard,
1545
- kit.experimental.explicitEnvironmentVariables &&
1546
- service_worker_entry_file &&
1547
- plugin_service_worker_env,
1548
- plugin_compile
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
1549
1753
  ].filter((p) => !!p);
1550
1754
  }
1551
1755
 
@@ -1558,8 +1762,10 @@ function warn_overridden_config(config, resolved_config) {
1558
1762
 
1559
1763
  if (overridden.length > 0) {
1560
1764
  console.error(
1561
- colors.bold().red('The following Vite config options will be overridden by SvelteKit:') +
1562
- overridden.map((key) => `\n - ${key}`).join('')
1765
+ styleText(
1766
+ ['bold', 'red'],
1767
+ 'The following Vite config options will be overridden by SvelteKit:'
1768
+ ) + overridden.map((key) => `\n - ${key}`).join('')
1563
1769
  );
1564
1770
  }
1565
1771
  }