@sveltejs/kit 3.0.0-next.3 → 3.0.0-next.6

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 (68) hide show
  1. package/package.json +17 -17
  2. package/src/core/adapt/builder.js +18 -4
  3. package/src/core/adapt/index.js +1 -4
  4. package/src/core/config/index.js +64 -5
  5. package/src/core/config/options.js +73 -21
  6. package/src/core/env.js +35 -4
  7. package/src/core/postbuild/analyse.js +8 -12
  8. package/src/core/postbuild/crawl.js +22 -6
  9. package/src/core/postbuild/prerender.js +40 -23
  10. package/src/core/sync/create_manifest_data/index.js +23 -5
  11. package/src/core/sync/write_client_manifest.js +7 -0
  12. package/src/core/sync/write_server.js +13 -10
  13. package/src/core/sync/write_tsconfig.js +2 -4
  14. package/src/exports/index.js +32 -10
  15. package/src/exports/internal/env.js +1 -1
  16. package/src/exports/internal/index.js +6 -5
  17. package/src/exports/node/index.js +57 -6
  18. package/src/exports/public.d.ts +218 -114
  19. package/src/exports/vite/build/build_server.js +6 -1
  20. package/src/exports/vite/dev/index.js +10 -20
  21. package/src/exports/vite/index.js +306 -222
  22. package/src/exports/vite/preview/index.js +15 -7
  23. package/src/exports/vite/utils.js +8 -10
  24. package/src/runtime/app/env/types.d.ts +1 -1
  25. package/src/runtime/app/forms.js +22 -5
  26. package/src/runtime/app/paths/client.js +1 -3
  27. package/src/runtime/app/paths/public.d.ts +0 -28
  28. package/src/runtime/app/paths/server.js +7 -3
  29. package/src/runtime/app/server/remote/form.js +10 -3
  30. package/src/runtime/app/server/remote/query.js +3 -6
  31. package/src/runtime/app/server/remote/requested.js +8 -4
  32. package/src/runtime/app/server/remote/shared.js +5 -7
  33. package/src/runtime/client/client.js +190 -95
  34. package/src/runtime/client/fetcher.js +3 -2
  35. package/src/runtime/client/remote-functions/form.svelte.js +134 -24
  36. package/src/runtime/client/remote-functions/prerender.svelte.js +8 -2
  37. package/src/runtime/client/remote-functions/query/index.js +3 -2
  38. package/src/runtime/client/remote-functions/query/instance.svelte.js +26 -10
  39. package/src/runtime/client/remote-functions/query-batch.svelte.js +4 -3
  40. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +26 -9
  41. package/src/runtime/client/remote-functions/shared.svelte.js +17 -7
  42. package/src/runtime/client/types.d.ts +9 -1
  43. package/src/runtime/client/utils.js +1 -1
  44. package/src/runtime/form-utils.js +84 -16
  45. package/src/runtime/server/cookie.js +22 -33
  46. package/src/runtime/server/csrf.js +65 -0
  47. package/src/runtime/server/data/index.js +7 -7
  48. package/src/runtime/server/env_module.js +0 -5
  49. package/src/runtime/server/index.js +1 -1
  50. package/src/runtime/server/page/actions.js +41 -26
  51. package/src/runtime/server/page/index.js +2 -1
  52. package/src/runtime/server/page/render.js +21 -23
  53. package/src/runtime/server/page/respond_with_error.js +7 -8
  54. package/src/runtime/server/remote.js +64 -27
  55. package/src/runtime/server/respond.js +81 -50
  56. package/src/runtime/server/utils.js +7 -7
  57. package/src/runtime/telemetry/otel.js +1 -1
  58. package/src/types/ambient.d.ts +5 -4
  59. package/src/types/global-private.d.ts +4 -4
  60. package/src/types/internal.d.ts +8 -10
  61. package/src/types/private.d.ts +33 -1
  62. package/src/types/synthetic/$lib.md +1 -1
  63. package/src/utils/error.js +11 -3
  64. package/src/utils/shared-iterator.js +5 -0
  65. package/src/utils/url.js +99 -2
  66. package/src/version.js +1 -1
  67. package/types/index.d.ts +340 -186
  68. package/types/index.d.ts.map +10 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "3.0.0-next.3",
3
+ "version": "3.0.0-next.6",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -18,37 +18,37 @@
18
18
  "homepage": "https://svelte.dev",
19
19
  "type": "module",
20
20
  "dependencies": {
21
- "@standard-schema/spec": "^1.0.0",
22
- "@sveltejs/acorn-typescript": "^1.0.9",
23
- "acorn": "^8.16.0",
24
- "cookie": "^1.1.1",
21
+ "@standard-schema/spec": "^1.1.0",
22
+ "@sveltejs/acorn-typescript": "^1.0.10",
23
+ "acorn": "^8.17.0",
24
+ "cookie": "^2.0.0",
25
25
  "devalue": "^5.8.1",
26
26
  "esm-env": "^1.2.2",
27
- "magic-string": "^0.30.5",
27
+ "magic-string": "^0.30.21",
28
28
  "mrmime": "^2.0.0",
29
- "sirv": "^3.0.0"
29
+ "sirv": "^3.0.2"
30
30
  },
31
31
  "devDependencies": {
32
- "@opentelemetry/api": "^1.0.0",
32
+ "@opentelemetry/api": "^1.9.0",
33
33
  "@playwright/test": "^1.60.0",
34
34
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
35
35
  "@types/connect": "^3.4.38",
36
36
  "@types/node": "^22.19.19",
37
- "dts-buddy": "^0.8.0",
37
+ "dts-buddy": "^0.8.1",
38
38
  "jsdom": "^29.1.1",
39
- "rolldown": "^1.0.0-rc.6",
39
+ "rolldown": "^1.1.2",
40
40
  "svelte": "^5.56.3",
41
41
  "svelte-preprocess": "^6.0.5",
42
- "typescript": "~6.0.0",
43
- "vite": "^8.0.10",
44
- "vitest": "^4.1.7"
42
+ "typescript": "~6.0.3",
43
+ "vite": "^8.0.16",
44
+ "vitest": "^4.1.9"
45
45
  },
46
46
  "peerDependencies": {
47
- "@sveltejs/vite-plugin-svelte": "^7.0.0",
48
47
  "@opentelemetry/api": "^1.0.0",
48
+ "@sveltejs/vite-plugin-svelte": "^7.0.0",
49
49
  "svelte": "^5.48.0",
50
50
  "typescript": "^6.0.0",
51
- "vite": "^8.0.0"
51
+ "vite": "^8.0.12"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@opentelemetry/api": {
@@ -94,7 +94,7 @@
94
94
  "import": "./src/exports/internal/env.js"
95
95
  },
96
96
  "./internal/types": {
97
- "import": "./src/exports/internal/types.js"
97
+ "types": "./src/exports/internal/types.d.ts"
98
98
  },
99
99
  "./internal/server": {
100
100
  "types": "./types/index.d.ts",
@@ -119,7 +119,7 @@
119
119
  },
120
120
  "scripts": {
121
121
  "lint": "prettier --config ../../.prettierrc --check .",
122
- "check": "tsc && cd ./test/types && tsc",
122
+ "check": "tsc && cd ./test/types && tsc && cd ./app-error-enhanced && tsc",
123
123
  "check:all": "tsc && pnpm -r --filter=\"./**\" check",
124
124
  "format": "prettier --config ../../.prettierrc --write .",
125
125
  "test": "pnpm test:unit && pnpm test:integration",
@@ -22,7 +22,19 @@ import { reserved } from '../env.js';
22
22
  import { handle_issues, validate } from '../../exports/internal/env.js';
23
23
 
24
24
  const pipe = promisify(pipeline);
25
- const extensions = ['.html', '.js', '.mjs', '.json', '.css', '.svg', '.xml', '.wasm', '.txt'];
25
+ const extensions = [
26
+ '.html',
27
+ '.js',
28
+ '.mjs',
29
+ '.json',
30
+ '.css',
31
+ '.svg',
32
+ '.xml',
33
+ '.wasm',
34
+ '.txt',
35
+ '.md',
36
+ '.mdx'
37
+ ];
26
38
 
27
39
  /**
28
40
  * Creates the Builder which is passed to adapters for building the application.
@@ -125,7 +137,7 @@ export function create_builder({
125
137
  manifest_path,
126
138
  env,
127
139
  out_dir: config.kit.outDir,
128
- origin: config.kit.prerender.origin,
140
+ origin: config.kit.paths.origin || 'http://sveltekit-prerender',
129
141
  assets: config.kit.files.assets
130
142
  });
131
143
 
@@ -144,7 +156,7 @@ export function create_builder({
144
156
  generateEnvModule() {
145
157
  if (!build_data.client?.uses_env_dynamic_public) return;
146
158
 
147
- const dest = `${config.kit.outDir}/output/prerendered/dependencies/${config.kit.appDir}/env.js`;
159
+ const dest = `${config.kit.outDir}/output/prerendered/dependencies/${config.kit.appDir}`;
148
160
  const env = loadEnv(vite_config.mode, config.kit.env.dir, '');
149
161
 
150
162
  /** @type {Record<string, any>} */
@@ -161,7 +173,9 @@ export function create_builder({
161
173
 
162
174
  handle_issues(issues);
163
175
 
164
- write(dest, `export const env=${devalue.uneval(values)}`);
176
+ const payload = devalue.uneval(values);
177
+
178
+ write(`${dest}/env.js`, `export const env=${payload}`);
165
179
  },
166
180
 
167
181
  generateManifest({ relativePath, routes: subset }) {
@@ -1,9 +1,7 @@
1
- /** @import { Adapter } from '@sveltejs/kit' */
2
1
  import { styleText } from 'node:util';
3
2
  import { create_builder } from './builder.js';
4
3
 
5
4
  /**
6
- * @param {Adapter} adapter
7
5
  * @param {import('types').ValidatedConfig} config
8
6
  * @param {import('types').BuildData} build_data
9
7
  * @param {import('types').ServerMetadata} server_metadata
@@ -15,7 +13,6 @@ import { create_builder } from './builder.js';
15
13
  * @param {Record<string, import('@sveltejs/kit').EnvVarConfig<any>> | null} explicit_env_config
16
14
  */
17
15
  export async function adapt(
18
- adapter,
19
16
  config,
20
17
  build_data,
21
18
  server_metadata,
@@ -26,7 +23,7 @@ export async function adapt(
26
23
  vite_config,
27
24
  explicit_env_config
28
25
  ) {
29
- const { name, adapt } = adapter;
26
+ const { name, adapt } = config.kit.adapter;
30
27
 
31
28
  console.log(styleText(['bold', 'cyan'], `\n> Using ${name}`));
32
29
 
@@ -1,14 +1,72 @@
1
- /** @import { Config } from '@sveltejs/kit' */
1
+ /** @import { Config, KitConfig } from '@sveltejs/kit' */
2
+ /** @import { Options, SvelteConfig } from '@sveltejs/vite-plugin-svelte' */
2
3
  /** @import { ValidatedConfig } from 'types' */
3
4
  /** @import { ResolvedConfig } from 'vite' */
4
5
  import fs from 'node:fs';
5
6
  import path from 'node:path';
6
7
  import process from 'node:process';
7
8
  import * as url from 'node:url';
8
- import options from './options.js';
9
+ import { options, kit_options, kit_experimental_options } from './options.js';
9
10
  import { resolve_entry } from '../../utils/filesystem.js';
10
11
  import { import_peer } from '../../utils/import.js';
11
12
 
13
+ /**
14
+ * Splits the config passed to the `sveltekit` Vite plugin into the options that
15
+ * SvelteKit processes itself and the options that are forwarded to
16
+ * `vite-plugin-svelte`. SvelteKit makes no assumptions about which options
17
+ * `vite-plugin-svelte` accepts — it plucks out its own options and passes
18
+ * everything else along (`vite-plugin-svelte` does its own validation).
19
+ * @param {KitConfig & Omit<Options, 'onwarn'> & Pick<SvelteConfig, 'vitePlugin'>} config
20
+ * @returns {{ svelte_config: Config, vite_plugin_svelte_config: Record<string, any> }}
21
+ */
22
+ export function split_config(config) {
23
+ const { extensions, compilerOptions, vitePlugin, preprocess, ...rest } = config;
24
+
25
+ /** @type {KitConfig} */
26
+ const kit = {};
27
+
28
+ /** @type {Record<string, any>} */
29
+ const vite_plugin_svelte_config = {};
30
+
31
+ for (const key in rest) {
32
+ if (key === 'experimental') {
33
+ // `experimental` is a namespace that both SvelteKit and vite-plugin-svelte
34
+ // use, so pluck out the flags SvelteKit recognises and pass the rest along
35
+ const experimental = /** @type {Record<string, any>} */ (rest[key]) ?? {};
36
+
37
+ /** @type {Record<string, any>} */
38
+ const kit_experimental = {};
39
+ /** @type {Record<string, any>} */
40
+ const vps_experimental = {};
41
+
42
+ for (const flag in experimental) {
43
+ if (kit_experimental_options.includes(flag)) {
44
+ kit_experimental[flag] = experimental[flag];
45
+ } else {
46
+ vps_experimental[flag] = experimental[flag];
47
+ }
48
+ }
49
+
50
+ if (Object.keys(kit_experimental).length > 0) {
51
+ kit.experimental = kit_experimental;
52
+ }
53
+ if (Object.keys(vps_experimental).length > 0) {
54
+ vite_plugin_svelte_config.experimental = vps_experimental;
55
+ }
56
+ } else if (kit_options.includes(key)) {
57
+ // @ts-expect-error - we've verified this is one of SvelteKit's own options
58
+ kit[key] = rest[key];
59
+ } else {
60
+ vite_plugin_svelte_config[key] = /** @type {Record<string, any>} */ (rest)[key];
61
+ }
62
+ }
63
+
64
+ return {
65
+ svelte_config: { extensions, compilerOptions, vitePlugin, preprocess, kit },
66
+ vite_plugin_svelte_config
67
+ };
68
+ }
69
+
12
70
  /**
13
71
  * Loads the template (src/app.html by default) and validates that it has the
14
72
  * required content.
@@ -77,11 +135,12 @@ export function extract_svelte_config(vite_config) {
77
135
  * @param {Config} config
78
136
  * @returns {ValidatedConfig}
79
137
  */
80
- export function process_config(config, { cwd = process.cwd(), source = 'svelte.config.js' } = {}) {
138
+ export function process_config(config, { cwd = process.cwd() } = {}) {
81
139
  try {
82
140
  const validated = validate_config(config, cwd);
83
141
 
84
142
  validated.kit.outDir = path.resolve(cwd, validated.kit.outDir);
143
+ validated.kit.env.dir = path.resolve(cwd, validated.kit.env.dir);
85
144
 
86
145
  for (const key in validated.kit.files) {
87
146
  if (key === 'hooks') {
@@ -102,7 +161,7 @@ export function process_config(config, { cwd = process.cwd(), source = 'svelte.c
102
161
  const error = /** @type {Error} */ (e);
103
162
 
104
163
  // redact the stack trace — it's not helpful to users
105
- error.stack = `Error loading ${source}: ${error.message}\n`;
164
+ error.stack = `Error loading SvelteKit options from Vite config: ${error.message}\n`;
106
165
  throw error;
107
166
  }
108
167
  }
@@ -115,7 +174,7 @@ export function process_config(config, { cwd = process.cwd(), source = 'svelte.c
115
174
  export function validate_config(config, cwd = process.cwd()) {
116
175
  if (typeof config !== 'object') {
117
176
  throw new Error(
118
- 'The Svelte config file must have a configuration object as its default export. See https://svelte.dev/docs/kit/configuration'
177
+ 'The SvelteKit options from the Vite config must be an object. See https://svelte.dev/docs/kit/configuration'
119
178
  );
120
179
  }
121
180
 
@@ -1,7 +1,5 @@
1
1
  /** @import { Validator } from './types.js' */
2
2
 
3
- import process from 'node:process';
4
-
5
3
  const directives = object({
6
4
  'child-src': string_array(),
7
5
  'default-src': string_array(),
@@ -37,7 +35,7 @@ const directives = object({
37
35
  });
38
36
 
39
37
  /** @type {Validator} */
40
- const options = object(
38
+ export const options = object(
41
39
  {
42
40
  extensions: validate(['.svelte'], (input, keypath) => {
43
41
  if (!Array.isArray(input) || !input.every((page) => typeof page === 'string')) {
@@ -85,7 +83,7 @@ const options = object(
85
83
  if (input) {
86
84
  if (input.startsWith('/') || input.endsWith('/')) {
87
85
  throw new Error(
88
- "config.kit.appDir cannot start or end with '/'. See https://svelte.dev/docs/kit/configuration"
86
+ `${keypath} cannot start or end with '/'. See https://svelte.dev/docs/kit/configuration`
89
87
  );
90
88
  }
91
89
  } else {
@@ -111,7 +109,7 @@ const options = object(
111
109
  embedded: boolean(false),
112
110
 
113
111
  env: object({
114
- dir: string(process.cwd())
112
+ dir: string('')
115
113
  }),
116
114
 
117
115
  experimental: object({
@@ -187,6 +185,35 @@ const options = object(
187
185
 
188
186
  return input;
189
187
  }),
188
+ origin: validate(undefined, (input, keypath) => {
189
+ assert_string(input, keypath);
190
+
191
+ let url;
192
+
193
+ try {
194
+ url = new URL(input);
195
+ } catch {
196
+ throw new Error(
197
+ `${keypath} must be a valid origin (e.g. 'https://my-site.com'). '${input}' could not be parsed as a URL`
198
+ );
199
+ }
200
+
201
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
202
+ throw new Error(
203
+ `${keypath} must be a valid origin — only 'http' and 'https' protocols are supported, received '${url.protocol}'`
204
+ );
205
+ }
206
+
207
+ const origin = url.origin;
208
+
209
+ if (input !== origin) {
210
+ throw new Error(
211
+ `${keypath} must be a valid origin — received '${input}' which contains a path, query, or hash. Use the bare origin '${origin}' instead`
212
+ );
213
+ }
214
+
215
+ return origin;
216
+ }),
190
217
  relative: boolean(true)
191
218
  }),
192
219
 
@@ -265,23 +292,23 @@ const options = object(
265
292
  }
266
293
  ),
267
294
 
268
- origin: validate('http://sveltekit-prerender', (input, keypath) => {
269
- assert_string(input, keypath);
270
-
271
- let origin;
272
-
273
- try {
274
- origin = new URL(input).origin;
275
- } catch {
276
- throw new Error(`${keypath} must be a valid origin`);
277
- }
278
-
279
- if (input !== origin) {
280
- throw new Error(`${keypath} must be a valid origin (${origin} rather than ${input})`);
295
+ handleInvalidUrl: validate(
296
+ (/** @type {any} */ { message }) => {
297
+ throw new Error(
298
+ message +
299
+ '\nTo suppress or handle this error, implement `handleInvalidUrl` in https://svelte.dev/docs/kit/configuration#prerender'
300
+ );
301
+ },
302
+ (input, keypath) => {
303
+ if (typeof input === 'function') return input;
304
+ if (['fail', 'warn', 'ignore'].includes(input)) return input;
305
+ throw new Error(`${keypath} should be "fail", "warn", "ignore" or a custom function`);
281
306
  }
307
+ ),
282
308
 
283
- return origin;
284
- })
309
+ origin: removed(
310
+ (keypath) => `\`${keypath}\` has been removed in favour of \`config.paths.origin\``
311
+ )
285
312
  }),
286
313
 
287
314
  router: object({
@@ -321,6 +348,8 @@ const options = object(
321
348
  // `The \`${keypath}\` option is deprecated, and will be removed in a future version`
322
349
  // ) {
323
350
  // return (input, keypath) => {
351
+ // keypath = remove_kit_prefix(keypath);
352
+ //
324
353
  // if (input !== undefined) {
325
354
  // console.warn(styleText(['bold', 'yellow'], get_message(keypath)));
326
355
  // }
@@ -329,6 +358,17 @@ const options = object(
329
358
  // };
330
359
  // }
331
360
 
361
+ // Derive the names of SvelteKit's own config options from the schema, so they
362
+ // stay in sync automatically. These are used to separate Kit's options from
363
+ // `vite-plugin-svelte`'s options when config is passed via the Vite plugin.
364
+ const defaults = /** @type {Record<string, any>} */ (options({}, 'config'));
365
+
366
+ /** The names of the options that live under the `kit` namespace */
367
+ export const kit_options = Object.keys(defaults.kit);
368
+
369
+ /** The names of the options that live under the `kit.experimental` namespace */
370
+ export const kit_experimental_options = Object.keys(defaults.kit.experimental);
371
+
332
372
  /**
333
373
  * @param {(keypath: string) => string} get_message
334
374
  * @returns {Validator}
@@ -338,6 +378,8 @@ function removed(
338
378
  `The \`${keypath}\` option has been removed. Please see the list of breaking changes for your major release`
339
379
  ) {
340
380
  return (input, keypath) => {
381
+ keypath = remove_kit_prefix(keypath);
382
+
341
383
  if (typeof input !== 'undefined') {
342
384
  throw new Error(get_message(keypath));
343
385
  }
@@ -351,6 +393,8 @@ function removed(
351
393
  */
352
394
  export function object(children, allow_unknown = false) {
353
395
  return (input, keypath) => {
396
+ keypath = remove_kit_prefix(keypath);
397
+
354
398
  /** @type {Record<string, any>} */
355
399
  const output = {};
356
400
 
@@ -391,6 +435,7 @@ export function object(children, allow_unknown = false) {
391
435
  */
392
436
  export function validate(fallback, fn) {
393
437
  return (input, keypath) => {
438
+ keypath = remove_kit_prefix(keypath);
394
439
  return input === undefined ? fallback : fn(input, keypath);
395
440
  };
396
441
  }
@@ -488,9 +533,16 @@ function fun(fallback) {
488
533
  * @param {string} keypath
489
534
  */
490
535
  function assert_string(input, keypath) {
536
+ keypath = remove_kit_prefix(keypath);
491
537
  if (typeof input !== 'string') {
492
538
  throw new Error(`${keypath} should be a string, if specified`);
493
539
  }
494
540
  }
495
541
 
496
- export default options;
542
+ /**
543
+ * @param {string} keypath
544
+ * @deprecated TODO get rid of the nesting so this is unnecessary
545
+ */
546
+ function remove_kit_prefix(keypath) {
547
+ return keypath.replace('.kit.', '.');
548
+ }
package/src/core/env.js CHANGED
@@ -51,6 +51,10 @@ export async function load_explicit_env(kit, file, root, mode) {
51
51
  /** @type {Record<string, EnvVarConfig<any>>} */
52
52
  let variables;
53
53
 
54
+ /** @type {import('../runtime/app/env/internal.js')} */ (
55
+ await server.ssrLoadModule(`${runtime_directory}/app/env/internal.js`)
56
+ ).set_building();
57
+
54
58
  try {
55
59
  ({ variables } = await server.ssrLoadModule(file));
56
60
 
@@ -87,7 +91,7 @@ export async function load_explicit_env(kit, file, root, mode) {
87
91
 
88
92
  /**
89
93
  * Creates the `__sveltekit/env` module
90
- * @param {Record<string, EnvVarConfig<any>> | null} variables
94
+ * @param {Record<string, EnvVarConfig<any> | undefined> | null} variables
91
95
  * @param {Record<string, string>} env
92
96
  * @param {string | null} entry
93
97
  */
@@ -106,7 +110,7 @@ export function create_sveltekit_env(variables, env, entry) {
106
110
  const issues = {};
107
111
 
108
112
  for (const [name, config] of Object.entries(variables ?? {})) {
109
- if (config.static) {
113
+ if (config?.static) {
110
114
  if (config.public) {
111
115
  const value = validate(variables ?? {}, env[name], name, issues);
112
116
  declarations.push(`explicit_public_env.${name} = ${devalue.uneval(value)};`);
@@ -116,7 +120,7 @@ export function create_sveltekit_env(variables, env, entry) {
116
120
  `const ${name} = validate(variables, env.${name}, ${JSON.stringify(name)}, issues);`
117
121
  );
118
122
 
119
- if (config.public) {
123
+ if (config?.public) {
120
124
  setters.push(`explicit_public_env.${name} = ${name};`);
121
125
  setters.push(`rendered_env.${name} = ${name};`);
122
126
  } else {
@@ -213,9 +217,36 @@ export function create_sveltekit_env_public(variables, env, prelude) {
213
217
  return `${prelude}\n\n${exports.join('')}`;
214
218
  }
215
219
 
220
+ /**
221
+ * Creates the `__sveltekit/env/service-worker` module used in production. When an app uses
222
+ * dynamic public env vars, they're loaded at runtime via an import of the prerendered
223
+ * `env.js`. If there are none, values are inlined.
224
+ * @param {Record<string, EnvVarConfig<any>> | null} variables
225
+ * @param {Record<string, string>} env
226
+ * @param {string} global
227
+ * @param {string} base
228
+ * @param {string} app_dir
229
+ */
230
+ export function create_sveltekit_env_service_worker(variables, env, global, base, app_dir) {
231
+ const has_dynamic_public_env = Object.values(variables ?? {}).some(
232
+ (config) => config.public && !config.static
233
+ );
234
+
235
+ if (!has_dynamic_public_env) {
236
+ return create_sveltekit_env_service_worker_dev(variables, env, global);
237
+ }
238
+
239
+ return dedent`
240
+ import { env } from '${base}/${app_dir}/env.js';
241
+
242
+ globalThis.__SVELTEKIT_EXPERIMENTAL_EXPLICIT_ENVIRONMENT_VARIABLES__ = true;
243
+
244
+ ${global} = { env };
245
+ `;
246
+ }
247
+
216
248
  /**
217
249
  * Creates the `__sveltekit/env/service-worker` module used in development
218
- * (but not in prod, which goes through build_service_worker instead)
219
250
  * @param {Record<string, EnvVarConfig<any>> | null} variables
220
251
  * @param {Record<string, string>} env
221
252
  * @param {string} global
@@ -1,4 +1,3 @@
1
- /** @import { Adapter } from '@sveltejs/kit' */
2
1
  /** @import { RemoteChunk } from 'types' */
3
2
  import { join } from 'node:path';
4
3
  import { pathToFileURL } from 'node:url';
@@ -39,15 +38,7 @@ async function analyse({
39
38
  const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
40
39
 
41
40
  const vite_config = await load_vite_config(vite_config_file);
42
-
43
41
  const config = extract_svelte_config(vite_config).kit;
44
-
45
- // TODO i think this can just be config.adapter?
46
- /** @type {Adapter | undefined} */
47
- const adapter = vite_config.plugins.find(
48
- (plugin) => plugin.name === 'vite-plugin-sveltekit-adapter'
49
- )?.api?.adapter;
50
-
51
42
  const server_root = join(config.outDir, 'output');
52
43
 
53
44
  /** @type {import('types').ServerInternalModule} */
@@ -57,11 +48,16 @@ async function analyse({
57
48
  // essential we do this before analysing the code
58
49
  internal.set_building();
59
50
 
60
- // set env, `read`, and `manifest`, in case they're used in initialisation
61
- internal.set_env(env);
51
+ // set `read` and `manifest`, in case they're used in initialisation
62
52
  internal.set_manifest(manifest);
63
53
  internal.set_read_implementation((file) => createReadableStream(`${server_root}/server/${file}`));
64
54
 
55
+ // `set_env` lives in a separate module that imports the user's `src/env` config. We import it
56
+ // *after* `set_building()` so that `building`-dependent expressions resolve correctly
57
+ /** @type {import('__sveltekit/env')} */
58
+ const { set_env } = await import(pathToFileURL(`${server_root}/server/env.js`).href);
59
+ set_env(env);
60
+
65
61
  /** @type {import('types').ServerMetadata} */
66
62
  const metadata = {
67
63
  nodes: [],
@@ -126,7 +122,7 @@ async function analyse({
126
122
  server_manifest,
127
123
  tracked_features
128
124
  )) {
129
- check_feature(route.id, route_config, feature, adapter);
125
+ check_feature(route.id, route_config, feature, config.adapter);
130
126
  }
131
127
  }
132
128
 
@@ -38,6 +38,18 @@ export function crawl(html, base) {
38
38
  /** @type {string[]} */
39
39
  const hrefs = [];
40
40
 
41
+ /** @type {string[]} */
42
+ const invalid = [];
43
+
44
+ /** @param {string} url */
45
+ const push_href = (url) => {
46
+ try {
47
+ hrefs.push(resolve(base, url));
48
+ } catch {
49
+ invalid.push(url);
50
+ }
51
+ };
52
+
41
53
  let i = 0;
42
54
  main: while (i < html.length) {
43
55
  const char = html[i];
@@ -186,9 +198,13 @@ export function crawl(html, base) {
186
198
 
187
199
  if (href) {
188
200
  if (tag === 'BASE') {
189
- base = resolve(base, href);
201
+ try {
202
+ base = resolve(base, href);
203
+ } catch {
204
+ invalid.push(href);
205
+ }
190
206
  } else if (!rel || !/\bexternal\b/i.test(rel)) {
191
- hrefs.push(resolve(base, href));
207
+ push_href(href);
192
208
  }
193
209
  }
194
210
 
@@ -201,7 +217,7 @@ export function crawl(html, base) {
201
217
  }
202
218
 
203
219
  if (src) {
204
- hrefs.push(resolve(base, src));
220
+ push_href(src);
205
221
  }
206
222
 
207
223
  if (srcset) {
@@ -222,7 +238,7 @@ export function crawl(html, base) {
222
238
  candidates.push(value);
223
239
  for (const candidate of candidates) {
224
240
  const src = candidate.split(WHITESPACE)[0];
225
- if (src) hrefs.push(resolve(base, src));
241
+ if (src) push_href(src);
226
242
  }
227
243
  }
228
244
 
@@ -230,7 +246,7 @@ export function crawl(html, base) {
230
246
  const attr = name ?? property;
231
247
 
232
248
  if (attr && CRAWLABLE_META_NAME_ATTRS.has(attr)) {
233
- hrefs.push(resolve(base, content));
249
+ push_href(content);
234
250
  }
235
251
  }
236
252
  }
@@ -239,5 +255,5 @@ export function crawl(html, base) {
239
255
  i += 1;
240
256
  }
241
257
 
242
- return { ids, hrefs };
258
+ return { ids, hrefs, invalid };
243
259
  }