@sveltejs/kit 3.0.0-next.24 → 3.0.0-next.27

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 (71) hide show
  1. package/package.json +16 -10
  2. package/src/constants.js +6 -0
  3. package/src/core/adapt/builder.js +128 -33
  4. package/src/core/adapt/index.js +3 -0
  5. package/src/core/config/index.js +4 -7
  6. package/src/core/env.js +39 -14
  7. package/src/core/generate_manifest/index.js +42 -43
  8. package/src/core/postbuild/analyse.js +4 -4
  9. package/src/core/postbuild/fallback.js +1 -1
  10. package/src/core/postbuild/prerender.js +4 -4
  11. package/src/core/sync/create_manifest_data/index.js +0 -1
  12. package/src/core/sync/write_app_manifest.js +3 -2
  13. package/src/core/sync/write_server.js +3 -17
  14. package/src/core/utils.js +10 -0
  15. package/src/exports/adapter.js +22 -0
  16. package/src/exports/public.d.ts +101 -49
  17. package/src/exports/vite/build/index.js +1173 -0
  18. package/src/exports/vite/build/remote.js +4 -4
  19. package/src/exports/vite/build/service-worker.js +98 -0
  20. package/src/exports/vite/dev/generate_manifest.js +308 -0
  21. package/src/exports/vite/dev/index.js +40 -282
  22. package/src/exports/vite/index.js +156 -1717
  23. package/src/exports/vite/plugins/env-vars.js +53 -34
  24. package/src/exports/vite/plugins/guard.js +217 -0
  25. package/src/exports/vite/plugins/remote.js +237 -0
  26. package/src/exports/vite/preview/index.js +8 -8
  27. package/src/exports/vite/static_analysis/index.js +15 -15
  28. package/src/exports/vite/utils.js +98 -1
  29. package/src/runner.js +4 -3
  30. package/src/runtime/app/paths/server.js +2 -2
  31. package/src/runtime/app/server/index.js +3 -3
  32. package/src/runtime/app/server/public.d.ts +114 -57
  33. package/src/runtime/app/server/remote/query.js +2 -2
  34. package/src/runtime/app/server/remote/requested.js +31 -15
  35. package/src/runtime/app/state/client.svelte.js +3 -1
  36. package/src/runtime/client/client.js +43 -198
  37. package/src/runtime/client/fetcher.js +6 -6
  38. package/src/runtime/client/focus.js +120 -0
  39. package/src/runtime/client/remote-functions/command.svelte.js +20 -9
  40. package/src/runtime/client/remote-functions/form.svelte.js +12 -7
  41. package/src/runtime/client/remote-functions/query/instance.svelte.js +19 -5
  42. package/src/runtime/client/remote-functions/shared.svelte.js +22 -1
  43. package/src/runtime/client/scroll.js +39 -0
  44. package/src/runtime/client/utils.js +28 -0
  45. package/src/runtime/form-utils.js +254 -264
  46. package/src/runtime/server/data/index.js +14 -36
  47. package/src/runtime/server/errors.js +7 -10
  48. package/src/runtime/server/fetch.js +21 -25
  49. package/src/runtime/server/index.js +35 -31
  50. package/src/runtime/server/internal.js +34 -5
  51. package/src/runtime/server/page/actions.js +6 -8
  52. package/src/runtime/server/page/data_serializer.js +6 -10
  53. package/src/runtime/server/page/index.js +18 -28
  54. package/src/runtime/server/page/load_data.js +2 -2
  55. package/src/runtime/server/page/render.js +22 -40
  56. package/src/runtime/server/page/respond_with_error.js +10 -13
  57. package/src/runtime/server/page/server_routing.js +21 -27
  58. package/src/runtime/server/remote-functions.js +136 -136
  59. package/src/runtime/server/respond.js +66 -64
  60. package/src/runtime/server/state.js +2 -0
  61. package/src/runtime/server/utils.js +4 -11
  62. package/src/runtime/shared.js +9 -0
  63. package/src/runtime/utils.js +41 -0
  64. package/src/types/ambient-private.d.ts +1 -2
  65. package/src/types/global-private.d.ts +8 -0
  66. package/src/types/internal.d.ts +56 -17
  67. package/src/utils/filesystem.js +44 -28
  68. package/src/utils/streaming.js +5 -15
  69. package/src/version.js +1 -1
  70. package/types/index.d.ts +235 -93
  71. package/types/index.d.ts.map +9 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "3.0.0-next.24",
3
+ "version": "3.0.0-next.27",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -20,8 +20,6 @@
20
20
  "dependencies": {
21
21
  "@rolldown/pluginutils": "^1.0.1",
22
22
  "@standard-schema/spec": "^1.1.0",
23
- "@sveltejs/acorn-typescript": "^1.0.12",
24
- "acorn": "^8.18.0",
25
23
  "cookie": "^2.0.1",
26
24
  "devalue": "^5.9.0",
27
25
  "esm-env": "^1.2.2",
@@ -31,14 +29,14 @@
31
29
  },
32
30
  "devDependencies": {
33
31
  "@opentelemetry/api": "^1.9.1",
34
- "@playwright/test": "^1.62.0",
32
+ "@playwright/test": "^1.62.1",
35
33
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
36
34
  "@types/connect": "^3.4.38",
37
35
  "@typescript/native": "npm:typescript@^7.0.2",
38
36
  "@types/node": "^22.19.19",
39
37
  "dts-buddy": "^0.8.3",
40
38
  "jsdom": "^30.0.0",
41
- "svelte": "^5.56.8",
39
+ "svelte": "^5.57.0",
42
40
  "svelte-preprocess": "^6.0.5",
43
41
  "typescript": "~6.0.3",
44
42
  "valibot": "^1.4.2",
@@ -66,8 +64,8 @@
66
64
  "files": [
67
65
  "src",
68
66
  "!src/**/*.spec.js",
69
- "!src/core/**/fixtures",
70
- "!src/core/**/test",
67
+ "!src/**/fixtures",
68
+ "!src/**/test",
71
69
  "types",
72
70
  "svelte-kit.js"
73
71
  ],
@@ -148,6 +146,10 @@
148
146
  "types": "./types/index.d.ts",
149
147
  "import": "./src/exports/internal/server/index.js"
150
148
  },
149
+ "./adapter": {
150
+ "types": "./types/index.d.ts",
151
+ "import": "./src/exports/adapter.js"
152
+ },
151
153
  "./node": {
152
154
  "types": "./types/index.d.ts",
153
155
  "import": "./src/exports/node/index.js"
@@ -180,9 +182,13 @@
180
182
  "format": "oxfmt --write .",
181
183
  "test": "pnpm test:unit && pnpm test:integration",
182
184
  "test:integration": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test",
183
- "test:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:dev",
184
- "test:build": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:build",
185
- "test:cross-platform:dev": "pnpm test:unit && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:dev",
185
+ "test:dev": "pnpm -r --workspace-concurrency 2 --filter=\"./test/**\" test:dev",
186
+ "test:build": "pnpm -r --workspace-concurrency 2 --filter=\"./test/**\" test:build",
187
+ "test:dev:basics": "pnpm --filter=test-basics test:dev",
188
+ "test:dev:rest": "pnpm -r --workspace-concurrency 2 --filter=\"./test/**\" --filter=\"!test-basics\" test:dev",
189
+ "test:build:basics": "pnpm --filter=test-basics test:build",
190
+ "test:build:rest": "pnpm -r --workspace-concurrency 2 --filter=\"./test/**\" --filter=\"!test-basics\" test:build",
191
+ "test:cross-platform:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:dev",
186
192
  "test:cross-platform:build": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:build",
187
193
  "test:server-side-route-resolution:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:server-side-route-resolution:dev",
188
194
  "test:server-side-route-resolution:build": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:server-side-route-resolution:build",
package/src/constants.js CHANGED
@@ -28,3 +28,9 @@ export const SRC_ROOT = import.meta.dirname;
28
28
 
29
29
  // eslint-disable-next-line n/prefer-global/process
30
30
  export const IN_WEBCONTAINER = !!globalThis.process?.versions?.webcontainer;
31
+
32
+ /**
33
+ * If an an adapter deploys a catch-all serverless function, the rerouted URL
34
+ * is stored in this header.
35
+ */
36
+ export const REROUTED_URL_HEADER = 'x-sveltekit-rerouted-url';
@@ -1,7 +1,6 @@
1
1
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
- /** @import { Builder } from '@sveltejs/kit' */
3
2
  /** @import { ResolvedConfig } from 'vite' */
4
- /** @import { RouteDefinition } from '@sveltejs/kit' */
3
+ /** @import { Builder, RouteDefinition } from '@sveltejs/kit' */
5
4
  /** @import { EnvVarConfig } from '@sveltejs/kit/env' */
6
5
  /** @import { RouteData, ValidatedConfig, BuildData, ServerMetadata, ServerMetadataRoute, Prerendered, PrerenderMap, Logger, RemoteChunk } from 'types' */
7
6
  import { loadEnv } from 'vite';
@@ -14,19 +13,21 @@ import {
14
13
  rmSync,
15
14
  statSync
16
15
  } from 'node:fs';
17
- import { extname, resolve, join, dirname, relative } from 'node:path';
16
+ import path from 'node:path';
18
17
  import { pipeline } from 'node:stream';
19
18
  import { promisify } from 'node:util';
20
19
  import zlib from 'node:zlib';
21
- import { copy, walk } from '../../utils/filesystem.js';
20
+ import { copy, relative_path, walk } from '../../utils/filesystem.js';
22
21
  import { posixify } from '../../utils/os.js';
23
22
  import { generate_manifest } from '../generate_manifest/index.js';
24
23
  import { get_route_segments } from '../../utils/routing.js';
25
24
  import generate_fallback from '../postbuild/fallback.js';
26
- import { write } from '../sync/utils.js';
25
+ import { dedent, write } from '../sync/utils.js';
27
26
  import { find_server_assets } from '../generate_manifest/find_server_assets.js';
28
27
  import { create_exported_declarations } from '../env.js';
29
28
  import { handle_issues, validate } from '../../exports/internal/env.js';
29
+ import { get_mime_lookup } from '../utils.js';
30
+ import { lookup as mime_lookup } from '../../utils/mime.js';
30
31
 
31
32
  const pipe = promisify(pipeline);
32
33
  const extensions = [
@@ -52,6 +53,7 @@ const extensions = [
52
53
  * route_data: RouteData[];
53
54
  * prerendered: Prerendered;
54
55
  * prerender_map: PrerenderMap;
56
+ * app_manifest: typeof import('$app/manifest');
55
57
  * log: Logger;
56
58
  * vite_config: ResolvedConfig;
57
59
  * remotes: RemoteChunk[];
@@ -66,6 +68,7 @@ export function create_builder({
66
68
  route_data,
67
69
  prerendered,
68
70
  prerender_map,
71
+ app_manifest,
69
72
  log,
70
73
  vite_config,
71
74
  remotes,
@@ -104,6 +107,12 @@ export function create_builder({
104
107
  return facade;
105
108
  });
106
109
 
110
+ // $app/manifest cannot include the service worker because it's used by the service worker itself,
111
+ // but the adapter needs to know about it so we add it here.
112
+ if (build_data.service_worker) {
113
+ app_manifest.assets.push({ path: build_data.service_worker });
114
+ }
115
+
107
116
  return {
108
117
  log,
109
118
  rimraf: (dir) => rmSync(dir, { force: true, recursive: true }),
@@ -113,17 +122,38 @@ export function create_builder({
113
122
  config,
114
123
  prerendered,
115
124
  routes,
125
+ manifest: app_manifest,
126
+ get mimeTypes() {
127
+ // TODO - make the `generate_manifest` function return data instead of a string, and retrieve mime types from there
128
+ const mime_types = get_mime_lookup(build_data.manifest_data);
129
+ const server_assets = find_server_assets(
130
+ build_data,
131
+ route_data.filter((route) => prerender_map.get(route.id) !== true),
132
+ vite_config.root
133
+ );
134
+ for (const file of server_assets) {
135
+ const ext = path.extname(file);
136
+ mime_types[ext] ??= mime_lookup(ext) || '';
137
+ }
138
+
139
+ // record extensions that only exist in prerendered output, e.g. a prerendered favicon.ico
140
+ for (const pathname of prerendered.paths) {
141
+ const ext = path.extname(pathname);
142
+ if (ext) mime_types[ext] ??= mime_lookup(ext) || '';
143
+ }
144
+ return mime_types;
145
+ },
116
146
 
117
147
  async compress(directory) {
118
148
  if (!existsSync(directory)) {
119
149
  return [];
120
150
  }
121
151
 
122
- const files = [...walk(directory)].filter((file) => extensions.includes(extname(file)));
152
+ const files = [...walk(directory)].filter((file) => extensions.includes(path.extname(file)));
123
153
 
124
154
  await Promise.all(
125
155
  files.flatMap((file) => {
126
- const abs = resolve(directory, file);
156
+ const abs = path.resolve(directory, file);
127
157
  return [compress_file(abs, 'gz'), compress_file(abs, 'br')];
128
158
  })
129
159
  );
@@ -185,17 +215,34 @@ export function create_builder({
185
215
  write(`${dest}/env.js`, `export const env=${payload}`);
186
216
  },
187
217
 
188
- generateManifest({ relativePath, routes: subset }) {
189
- return generate_manifest({
190
- build_data,
191
- prerendered: prerendered.paths,
192
- relative_path: relativePath,
193
- routes: subset
194
- ? subset.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route)))
195
- : route_data.filter((route) => prerender_map.get(route.id) !== true),
196
- remotes,
197
- root: vite_config.root
198
- });
218
+ generateManifest() {
219
+ throw new Error(
220
+ 'The `generateManifest` adapter API has been removed — use `generateServerInstance` or `builder.manifest` instead. You may need to update your adapter'
221
+ );
222
+ },
223
+
224
+ generateServerInstance(dest, { routes: subset, serverDirectory } = {}) {
225
+ const relative = relative_path(
226
+ path.dirname(dest),
227
+ serverDirectory ?? this.getServerDirectory()
228
+ );
229
+ write(
230
+ dest,
231
+ dedent`
232
+ import { Server } from '${relative}/index.js';
233
+ const manifest = ${generate_manifest({
234
+ build_data,
235
+ prerendered: prerendered.paths,
236
+ relative_path: relative,
237
+ routes: subset
238
+ ? subset.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route)))
239
+ : route_data.filter((route) => prerender_map.get(route.id) !== true),
240
+ remotes,
241
+ root: vite_config.root
242
+ })};
243
+ export const server = new Server(manifest);
244
+ `
245
+ );
199
246
  },
200
247
 
201
248
  getBuildDirectory(name) {
@@ -235,6 +282,30 @@ export function create_builder({
235
282
  return copy(`${config.outDir}/output/server`, dest);
236
283
  },
237
284
 
285
+ createInstrumentationInitializer({
286
+ outputDirectory,
287
+ environment,
288
+ serverDirectory = `${config.outDir}/output/server`
289
+ }) {
290
+ const provider = path.join(outputDirectory, '__sveltekit_env.js');
291
+ write(provider, environment ?? 'export default process.env;');
292
+
293
+ const initializer = path.join(outputDirectory, '__sveltekit_env_init.js');
294
+ write(
295
+ initializer,
296
+ create_env_module({
297
+ environment: to_import_specifier(
298
+ posixify(path.relative(path.dirname(initializer), provider))
299
+ ),
300
+ set_env: to_import_specifier(
301
+ posixify(path.relative(path.dirname(initializer), `${serverDirectory}/env.js`))
302
+ )
303
+ })
304
+ );
305
+
306
+ return initializer;
307
+ },
308
+
238
309
  hasServerInstrumentationFile() {
239
310
  return existsSync(`${config.outDir}/output/server/instrumentation.server.js`);
240
311
  },
@@ -242,7 +313,8 @@ export function create_builder({
242
313
  instrument({
243
314
  entrypoint,
244
315
  instrumentation,
245
- start = join(dirname(entrypoint), 'start.js'),
316
+ start = path.join(path.dirname(entrypoint), 'start.js'),
317
+ initializer,
246
318
  module = {
247
319
  exports: ['default']
248
320
  }
@@ -257,25 +329,35 @@ export function create_builder({
257
329
  `Entrypoint file ${entrypoint} not found. This is probably a bug in your adapter.`
258
330
  );
259
331
  }
332
+ if (!existsSync(initializer)) {
333
+ throw new Error(
334
+ `Instrumentation initializer ${initializer} not found. This is probably a bug in your adapter.`
335
+ );
336
+ }
260
337
 
261
338
  copy(entrypoint, start);
262
339
  if (existsSync(`${entrypoint}.map`)) {
263
340
  copy(`${entrypoint}.map`, `${start}.map`);
264
341
  }
265
342
 
266
- const relative_instrumentation = posixify(relative(dirname(entrypoint), instrumentation));
267
- const relative_start = posixify(relative(dirname(entrypoint), start));
343
+ const relative_instrumentation = posixify(
344
+ path.relative(path.dirname(entrypoint), instrumentation)
345
+ );
346
+ const relative_start = posixify(path.relative(path.dirname(entrypoint), start));
347
+ const relative_initializer = posixify(path.relative(path.dirname(entrypoint), initializer));
268
348
 
269
349
  const facade =
270
350
  'generateText' in module
271
351
  ? module.generateText({
272
352
  instrumentation: relative_instrumentation,
273
- start: relative_start
353
+ start: relative_start,
354
+ initializer: relative_initializer
274
355
  })
275
356
  : create_instrumentation_facade({
276
357
  instrumentation: relative_instrumentation,
277
358
  start: relative_start,
278
- exports: module.exports
359
+ exports: module.exports,
360
+ initializer: relative_initializer
279
361
  });
280
362
 
281
363
  rmSync(entrypoint, { force: true, recursive: true });
@@ -308,16 +390,15 @@ async function compress_file(file, format = 'gz') {
308
390
 
309
391
  /**
310
392
  * Given a list of exports, generate a facade that:
393
+ * - Imports the environment initializer
311
394
  * - Imports the instrumentation file
312
- * - Imports `exports` from the entrypoint (dynamically, if `tla` is true)
395
+ * - Imports `exports` from the entrypoint (dynamically)
313
396
  * - Re-exports `exports` from the entrypoint
314
397
  *
315
- * @param {{ instrumentation: string; start: string; exports: string[] }} opts
398
+ * @param {{ instrumentation: string; start: string; exports: string[]; initializer: string }} opts
316
399
  * @returns {string}
317
400
  */
318
- function create_instrumentation_facade({ instrumentation, start, exports }) {
319
- const import_instrumentation = `import './${instrumentation}';`;
320
-
401
+ function create_instrumentation_facade({ instrumentation, start, exports, initializer }) {
321
402
  const { namespace, declarations, reexports } = create_exported_declarations(
322
403
  exports,
323
404
  (name, ns) => `${ns}.${name}`,
@@ -325,12 +406,26 @@ function create_instrumentation_facade({ instrumentation, start, exports }) {
325
406
  );
326
407
 
327
408
  const parts = [
328
- `const ${namespace} = await import('./${start}');`,
409
+ `import ${JSON.stringify(to_import_specifier(initializer))};`,
410
+ `import ${JSON.stringify(to_import_specifier(instrumentation))};`,
411
+ `const ${namespace} = await import(${JSON.stringify(to_import_specifier(start))});`,
329
412
  declarations.join('\n'),
330
413
  reexports.length > 0 ? `export { ${reexports.join(', ')} };` : ''
331
- ]
332
- .filter(Boolean)
333
- .join('\n');
414
+ ];
334
415
 
335
- return `${import_instrumentation}\n${parts}`;
416
+ return parts.filter(Boolean).join('\n');
417
+ }
418
+
419
+ /**
420
+ * @param {string} path
421
+ */
422
+ function to_import_specifier(path) {
423
+ return path.startsWith('.') ? path : `./${path}`;
424
+ }
425
+
426
+ /**
427
+ * @param {{ environment: string; set_env: string }} opts
428
+ */
429
+ function create_env_module({ environment, set_env }) {
430
+ return `import env from ${JSON.stringify(environment)};\nimport { set_env } from ${JSON.stringify(set_env)};\nset_env(env);\n`;
336
431
  }
@@ -7,6 +7,7 @@ import { create_builder } from './builder.js';
7
7
  * @param {import('types').ServerMetadata} server_metadata
8
8
  * @param {import('types').Prerendered} prerendered
9
9
  * @param {import('types').PrerenderMap} prerender_map
10
+ * @param {typeof import('$app/manifest')} app_manifest
10
11
  * @param {import('types').Logger} log
11
12
  * @param {import('types').RemoteChunk[]} remotes
12
13
  * @param {import('vite').ResolvedConfig} vite_config
@@ -18,6 +19,7 @@ export async function adapt(
18
19
  server_metadata,
19
20
  prerendered,
20
21
  prerender_map,
22
+ app_manifest,
21
23
  log,
22
24
  remotes,
23
25
  vite_config,
@@ -48,6 +50,7 @@ export async function adapt(
48
50
  route_data: build_data.manifest_data.routes.filter((route) => route.page || route.endpoint),
49
51
  prerendered,
50
52
  prerender_map,
53
+ app_manifest,
51
54
  log,
52
55
  remotes,
53
56
  vite_config,
@@ -200,13 +200,10 @@ export function validate_config(config) {
200
200
  }
201
201
  }
202
202
 
203
- if (
204
- validated.csp?.directives?.['require-trusted-types-for']?.includes('script') &&
205
- !validated.csp?.directives?.['trusted-types']?.includes('svelte-trusted-html')
206
- ) {
207
- throw new Error(
208
- "The `csp.directives['trusted-types']` option must include 'svelte-trusted-html'"
209
- );
203
+ if (typeof config.adapter?.vite === 'function') {
204
+ validated.adapter.vite = config.adapter.vite({
205
+ config: validated
206
+ });
210
207
  }
211
208
 
212
209
  return validated;
package/src/core/env.js CHANGED
@@ -4,13 +4,13 @@
4
4
  import path from 'node:path';
5
5
  import * as devalue from 'devalue';
6
6
  import { dedent } from './sync/utils.js';
7
- import { runtime_directory } from './utils.js';
7
+ import { get_global_name, runtime_directory } from './utils.js';
8
+ import { stackless } from '../utils/error.js';
8
9
  import { resolve_entry } from '../utils/filesystem.js';
9
10
  import { handle_issues, validate } from '../exports/internal/env.js';
10
11
  import { get_config_aliases } from '../exports/vite/utils.js';
11
12
  import { get_runner } from '../runner.js';
12
13
  import { import_peer } from '../utils/import.js';
13
- import { hash } from '../utils/hash.js';
14
14
  import { posixify } from '../utils/os.js';
15
15
 
16
16
  /**
@@ -23,7 +23,9 @@ import { posixify } from '../utils/os.js';
23
23
  * @returns {string | null}
24
24
  */
25
25
  export function resolve_env_entry(config, root) {
26
- return resolve_entry(path.resolve(root, config.files.src, 'env'));
26
+ const entry = resolve_entry(path.resolve(root, config.files.src, 'env'));
27
+ // posix, like the paths Vite hands to `hotUpdate`
28
+ return entry && posixify(entry);
27
29
  }
28
30
 
29
31
  /**
@@ -36,6 +38,8 @@ export function resolve_env_entry(config, root) {
36
38
  export async function load_explicit_env(kit, file, root, mode) {
37
39
  /** @type {Set<string>} */
38
40
  const deps = new Set();
41
+ /** @type {Map<EnvType, string>} */
42
+ const env_importers = new Map();
39
43
 
40
44
  if (!file) {
41
45
  return { variables: null, deps };
@@ -63,6 +67,16 @@ export async function load_explicit_env(kit, file, root, mode) {
63
67
  plugins: [
64
68
  {
65
69
  name: 'dependency-scanner',
70
+ enforce: 'pre',
71
+ resolveId(id, importer) {
72
+ const prefixes = ['$app/env/', `${runtime_directory}/app/env/`];
73
+ const prefix = prefixes.find((prefix) => id.startsWith(prefix));
74
+ const type = prefix && id.slice(prefix.length);
75
+
76
+ if (importer && (type === 'private' || type === 'public')) {
77
+ env_importers.set(type, importer);
78
+ }
79
+ },
66
80
  load(id) {
67
81
  deps.add(id);
68
82
  }
@@ -95,14 +109,27 @@ export async function load_explicit_env(kit, file, root, mode) {
95
109
  } catch (e) {
96
110
  const error = /** @type {any} */ (e || {});
97
111
 
98
- if (
99
- error.code === 'ERR_MODULE_NOT_FOUND' &&
100
- error.message?.includes(`Cannot find module '$app`)
101
- ) {
102
- throw new Error(
103
- `Cannot import \`$app/*\` modules other than \`$app/env\` inside \`src/env\``,
104
- { cause: e }
112
+ if (error.code === 'ERR_MODULE_NOT_FOUND') {
113
+ const match = error.message?.match(
114
+ /<sveltekit:generated>\/env\/(private|public)\/server\.js/
105
115
  );
116
+
117
+ if (match) {
118
+ const type = /** @type {EnvType} */ (match[1]);
119
+ const importer = env_importers.get(type);
120
+ const message = importer
121
+ ? `Module \`${posixify(path.relative(root, importer))}\` imports \`$app/env/${type}\`, which creates a circular dependency with \`src/env\``
122
+ : `Cannot import \`$app/env/${type}\` inside \`src/env\` or its dependencies because it creates a circular dependency`;
123
+
124
+ throw stackless(message);
125
+ }
126
+
127
+ if (error.message?.includes(`Cannot find module '$app`)) {
128
+ throw new Error(
129
+ `Cannot import \`$app/*\` modules other than \`$app/env\` inside \`src/env\``,
130
+ { cause: e }
131
+ );
132
+ }
106
133
  }
107
134
 
108
135
  throw error;
@@ -216,9 +243,7 @@ export function create_env_modules(config, variables, env, dir, entry, is_dev) {
216
243
  */
217
244
  const module = (prelude, exports) => (variables ? `${prelude}\n\n${exports.join('')}` : '');
218
245
 
219
- const global = is_dev
220
- ? 'globalThis.__sveltekit_dev'
221
- : `globalThis.__sveltekit_${hash(config.version.name)}`;
246
+ const global = `globalThis.${get_global_name(config.version.name, is_dev)}`;
222
247
 
223
248
  const version = JSON.stringify(config.version.name);
224
249
 
@@ -257,7 +282,7 @@ export function create_env_modules(config, variables, env, dir, entry, is_dev) {
257
282
  ),
258
283
  'public/client.js': module(
259
284
  is_dev
260
- ? `const { env } = globalThis.__sveltekit_dev;`
285
+ ? `const { env } = ${global};`
261
286
  : `import { payload } from ${JSON.stringify(posixify(path.relative(`${dir}/public`, `${runtime_directory}/client/payload.js`)))};\nconst env = payload.env;`,
262
287
  public_exports
263
288
  ),
@@ -1,4 +1,4 @@
1
- /** @import { RemoteChunk } from 'types' */
1
+ /** @import { BuildData, RemoteChunk, RouteData } from 'types' */
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { lookup as mime_lookup } from '../../utils/mime.js';
@@ -15,10 +15,10 @@ import { uneval } from 'devalue';
15
15
  * Generates the data used to write the server-side manifest.js file. This data is used in the Vite
16
16
  * build process, to power routing, etc.
17
17
  * @param {{
18
- * build_data: import('types').BuildData;
18
+ * build_data: BuildData;
19
19
  * prerendered: string[];
20
20
  * relative_path: string;
21
- * routes: import('types').RouteData[];
21
+ * routes: RouteData[];
22
22
  * remotes: RemoteChunk[];
23
23
  * root: string;
24
24
  * }} opts
@@ -102,49 +102,48 @@ export function generate_manifest({
102
102
 
103
103
  // prettier-ignore
104
104
  // String representation of
105
- /** @template {import('@sveltejs/kit').SSRManifest} T */
105
+ /** @template {import('types').SSRManifest} T */
106
106
  const manifest_expr = dedent`
107
107
  {
108
- appDir: ${s(build_data.app_dir)},
109
- appPath: ${s(build_data.app_path)},
108
+ app_dir: ${s(build_data.app_dir)},
109
+ app_path: ${s(build_data.app_path)},
110
110
  assets: new Set(${s(assets)}),
111
- mimeTypes: ${s(mime_types)},
112
- _: {
113
- client: ${uneval(build_data.client)},
114
- nodes: [
115
- ${(node_paths).map(loader).join(',\n')}
116
- ],
117
- remotes: {
118
- ${remotes.map((remote) => `'${remote.hash}': ${loader(join_relative(relative_path, `chunks/remote-${remote.hash}.js`))}`).join(',\n')}
119
- },
120
- routes: [
121
- ${routes.map(route => {
122
- if (!route.page && !route.endpoint) return;
123
-
124
- return dedent`
125
- {
126
- id: ${s(route.id)},
127
- pattern: ${route.pattern},
128
- params: ${s(route.params)},
129
- page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${reindexed.get(route.page.leaf)} }` : 'null'},
130
- endpoint: ${route.endpoint ? loader(join_relative(relative_path, resolve_symlinks(build_data.server_manifest, route.endpoint.file, root).chunk.file)) : 'null'}
131
- }
132
- `;
133
- }).filter(Boolean).join(',\n')}
134
- ],
135
- prerendered_routes: new Set(${s(prerendered)}),
136
- matchers: async () => {
137
- ${
138
- uses_matchers && build_data.manifest_data.params
139
- ? dedent`
140
- const { params } = await import('${join_relative(relative_path, '/entries/params.js')}');
141
- return params;
142
- `
143
- : 'return {};'
144
- }
145
- },
146
- server_assets: ${s(files)}
147
- }
111
+ mime_types: ${s(mime_types)},
112
+ client: ${uneval(build_data.client)},
113
+
114
+ nodes: [
115
+ ${(node_paths).map(loader).join(',\n')}
116
+ ],
117
+ remotes: {
118
+ ${remotes.map((remote) => `'${remote.hash}': ${loader(join_relative(relative_path, `chunks/remote-${remote.hash}.js`))}`).join(',\n')}
119
+ },
120
+ routes: [
121
+ ${routes.map(route => {
122
+ if (!route.page && !route.endpoint) return;
123
+
124
+ return dedent`
125
+ {
126
+ id: ${s(route.id)},
127
+ pattern: ${route.pattern},
128
+ params: ${s(route.params)},
129
+ page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${reindexed.get(route.page.leaf)} }` : 'null'},
130
+ endpoint: ${route.endpoint ? loader(join_relative(relative_path, resolve_symlinks(build_data.server_manifest, route.endpoint.file, root).chunk.file)) : 'null'}
131
+ }
132
+ `;
133
+ }).filter(Boolean).join(',\n')}
134
+ ],
135
+ prerendered_routes: new Set(${s(prerendered)}),
136
+ matchers: async () => {
137
+ ${
138
+ uses_matchers && build_data.manifest_data.params
139
+ ? dedent`
140
+ const { params } = await import('${join_relative(relative_path, '/entries/params.js')}');
141
+ return params;
142
+ `
143
+ : 'return {};'
144
+ }
145
+ },
146
+ server_assets: ${s(files)}
148
147
  }
149
148
  `;
150
149
 
@@ -34,7 +34,7 @@ async function analyse({
34
34
  remotes,
35
35
  vite_config_file
36
36
  }) {
37
- /** @type {import('@sveltejs/kit').SSRManifest} */
37
+ /** @type {import('types').SSRManifest} */
38
38
  const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
39
39
 
40
40
  const vite_config = await load_vite_config(vite_config_file);
@@ -65,7 +65,7 @@ async function analyse({
65
65
  remotes: new Map()
66
66
  };
67
67
 
68
- const nodes = await Promise.all(manifest._.nodes.map((loader) => loader()));
68
+ const nodes = await Promise.all(manifest.nodes.map((loader) => loader()));
69
69
 
70
70
  // analyse nodes
71
71
  for (const node of nodes) {
@@ -88,7 +88,7 @@ async function analyse({
88
88
  }
89
89
 
90
90
  // analyse routes
91
- for (const route of manifest._.routes) {
91
+ for (const route of manifest.routes) {
92
92
  const page =
93
93
  route.page &&
94
94
  analyse_page(
@@ -147,7 +147,7 @@ async function analyse({
147
147
 
148
148
  // analyse remotes
149
149
  for (const remote of remotes) {
150
- const loader = manifest._.remotes[remote.hash];
150
+ const loader = manifest.remotes[remote.hash];
151
151
  const { default: functions } = await loader();
152
152
 
153
153
  const exports = new Map();
@@ -24,7 +24,7 @@ async function generate_fallback({ manifest_path, env, out_dir, origin, assets }
24
24
  /** @type {import('types').ServerModule} */
25
25
  const { Server } = await import(pathToFileURL(`${server_root}/server/index.js`).href);
26
26
 
27
- /** @type {import('@sveltejs/kit').SSRManifest} */
27
+ /** @type {import('types').SSRManifest} */
28
28
  const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
29
29
 
30
30
  set_building();