@sveltejs/kit 2.62.0 → 3.0.0-next.0

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