@sveltejs/kit 2.62.0 → 2.63.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/package.json +12 -1
  2. package/src/cli.js +32 -6
  3. package/src/core/adapt/builder.js +26 -5
  4. package/src/core/adapt/index.js +5 -2
  5. package/src/core/config/index.js +7 -5
  6. package/src/core/config/options.js +1 -0
  7. package/src/core/env.js +313 -8
  8. package/src/core/postbuild/analyse.js +2 -1
  9. package/src/core/postbuild/prerender.js +2 -1
  10. package/src/core/sync/create_manifest_data/index.js +24 -1
  11. package/src/core/sync/sync.js +16 -0
  12. package/src/core/sync/write_ambient.js +12 -6
  13. package/src/core/sync/write_env.js +33 -0
  14. package/src/core/sync/write_root.js +1 -1
  15. package/src/core/sync/write_server.js +3 -2
  16. package/src/core/sync/write_tsconfig.js +1 -0
  17. package/src/exports/hooks/index.js +13 -0
  18. package/src/exports/internal/env.js +71 -0
  19. package/src/exports/internal/types.d.ts +3 -0
  20. package/src/exports/public.d.ts +40 -0
  21. package/src/exports/vite/build/build_service_worker.js +20 -4
  22. package/src/exports/vite/dev/index.js +2 -2
  23. package/src/exports/vite/index.js +129 -36
  24. package/src/exports/vite/module_ids.js +10 -1
  25. package/src/exports/vite/static_analysis/index.js +2 -4
  26. package/src/exports/vite/static_analysis/types.d.ts +14 -0
  27. package/src/exports/vite/utils.js +7 -12
  28. package/src/runtime/app/env/index.js +2 -0
  29. package/src/runtime/app/env/internal.js +11 -0
  30. package/src/runtime/app/env/private.js +1 -0
  31. package/src/runtime/app/env/public/client.js +7 -0
  32. package/src/runtime/app/env/public/index.js +1 -0
  33. package/src/runtime/app/env/public/server.js +7 -0
  34. package/src/runtime/app/env/standard-schema.d.ts +0 -0
  35. package/src/runtime/app/env/types.d.ts +19 -0
  36. package/src/runtime/app/environment/index.js +8 -2
  37. package/src/runtime/app/server/remote/query.js +1 -1
  38. package/src/runtime/client/ndjson.js +6 -33
  39. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  40. package/src/runtime/client/remote-functions/query-live/iterator.js +36 -55
  41. package/src/runtime/client/sse.js +32 -0
  42. package/src/runtime/client/stream.js +38 -0
  43. package/src/runtime/client/utils.js +1 -1
  44. package/src/runtime/server/env_module.js +13 -3
  45. package/src/runtime/server/index.js +2 -0
  46. package/src/runtime/server/page/render.js +14 -3
  47. package/src/runtime/server/remote.js +2 -2
  48. package/src/runtime/server/respond.js +1 -1
  49. package/src/types/ambient-private.d.ts +25 -9
  50. package/src/types/global-private.d.ts +2 -0
  51. package/src/types/internal.d.ts +2 -1
  52. package/src/utils/error.js +12 -0
  53. package/src/utils/import.js +2 -2
  54. package/src/version.js +1 -1
  55. package/types/index.d.ts +81 -3
  56. package/types/index.d.ts.map +11 -3
@@ -33,6 +33,7 @@ export default function create_manifest_data({
33
33
  const matchers = create_matchers(config, cwd);
34
34
  const { nodes, routes } = create_routes_and_nodes(cwd, config, fallback);
35
35
 
36
+ // validate matcher names used in parameterised routes
36
37
  for (const route of routes) {
37
38
  for (const param of route.params) {
38
39
  if (param.matcher && !matchers[param.matcher]) {
@@ -51,6 +52,7 @@ export default function create_manifest_data({
51
52
  }
52
53
 
53
54
  /**
55
+ * Returns a list of files in the `static` directory.
54
56
  * @param {import('types').ValidatedConfig} config
55
57
  */
56
58
  export function create_assets(config) {
@@ -131,6 +133,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
131
133
  /** @type {import('types').PageNode[]} */
132
134
  const nodes = [];
133
135
 
136
+ // create route data by processing files in `src/routes`
134
137
  if (fs.existsSync(config.kit.files.routes)) {
135
138
  /**
136
139
  * @param {number} depth
@@ -390,6 +393,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
390
393
 
391
394
  prevent_conflicts(routes);
392
395
 
396
+ // fallback root layout and root error components
393
397
  const root = routes[0];
394
398
 
395
399
  if (!root.layout?.component) {
@@ -398,10 +402,11 @@ function create_routes_and_nodes(cwd, config, fallback) {
398
402
  }
399
403
 
400
404
  if (!root.error?.component) {
401
- if (!root.error) root.error = { depth: 0 };
405
+ if (!root.error) root.error = { depth: 0, parent: root.layout };
402
406
  root.error.component = posixify(path.relative(cwd, `${fallback}/error.svelte`));
403
407
  }
404
408
 
409
+ // populate the page nodes list
405
410
  // we do layouts/errors first as they are more likely to be reused,
406
411
  // and smaller indexes take fewer bytes. also, this guarantees that
407
412
  // the default error/layout are 0/1
@@ -423,6 +428,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
423
428
 
424
429
  const node_analyser = create_node_analyser();
425
430
 
431
+ // add the related layout, page, and error nodes for a route
426
432
  for (const route of routes) {
427
433
  if (!route.leaf) continue;
428
434
 
@@ -467,6 +473,22 @@ function create_routes_and_nodes(cwd, config, fallback) {
467
473
  }
468
474
  }
469
475
 
476
+ // add parents to error nodes so that we can compute which page options apply to them
477
+ for (const route of routes) {
478
+ if (!route.error) continue;
479
+
480
+ /** @type {import('types').RouteData | null} */
481
+ let current_route = route;
482
+ while (current_route) {
483
+ if (current_route.layout) {
484
+ route.error.parent = current_route.layout;
485
+ break;
486
+ }
487
+ current_route = current_route.parent;
488
+ }
489
+ }
490
+
491
+ // compute the final page options for each page node
470
492
  for (const node of nodes) {
471
493
  node.page_options = node_analyser.get_page_options(node);
472
494
  }
@@ -484,6 +506,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
484
506
  }
485
507
 
486
508
  /**
509
+ * Determine if and how the file is relevant to the routing system.
487
510
  * @param {string} project_relative
488
511
  * @param {string} file
489
512
  * @param {string[]} component_extensions
@@ -11,6 +11,8 @@ import {
11
11
  create_node_analyser,
12
12
  get_page_options
13
13
  } from '../../exports/vite/static_analysis/index.js';
14
+ import { load_explicit_env } from '../env.js';
15
+ import { write_env } from './write_env.js';
14
16
 
15
17
  /**
16
18
  * Initialize SvelteKit's generated files that only depend on the config and mode.
@@ -87,6 +89,20 @@ export function all_types(config, mode) {
87
89
  write_non_ambient(config.kit, manifest_data);
88
90
  }
89
91
 
92
+ /**
93
+ * Generate modules and types for explicit env vars
94
+ * @param {import('types').ValidatedKitConfig} kit
95
+ * @param {string | null} entry
96
+ * @param {string} mode The Vite mode
97
+ */
98
+ export async function env(kit, entry, mode) {
99
+ const env_config = await load_explicit_env(kit, entry, mode);
100
+
101
+ write_env(kit, entry, env_config);
102
+
103
+ return env_config;
104
+ }
105
+
90
106
  /**
91
107
  * Regenerate __SERVER__/internal.js in response to src/{app.html,error.html,service-worker.js} changing
92
108
  * @param {import('types').ValidatedConfig} config
@@ -53,11 +53,17 @@ ${create_dynamic_types('public', env, prefixes)}
53
53
  * @param {string} mode The Vite mode
54
54
  */
55
55
  export function write_ambient(config, mode) {
56
- const env = get_env(config.env, mode);
57
- const { publicPrefix: public_prefix, privatePrefix: private_prefix } = config.env;
56
+ /** @type {string} */
57
+ let content;
58
58
 
59
- write_if_changed(
60
- path.join(config.outDir, 'ambient.d.ts'),
61
- template(env, { public_prefix, private_prefix })
62
- );
59
+ if (config.experimental.explicitEnvironmentVariables) {
60
+ content = `${GENERATED_COMMENT}\n/// <reference types="@sveltejs/kit" />`;
61
+ } else {
62
+ const env = get_env(config.env, mode);
63
+ const { publicPrefix: public_prefix, privatePrefix: private_prefix } = config.env;
64
+
65
+ content = template(env, { public_prefix, private_prefix });
66
+ }
67
+
68
+ write_if_changed(path.join(config.outDir, 'ambient.d.ts'), content);
63
69
  }
@@ -0,0 +1,33 @@
1
+ /** @import { EnvVarConfig } from '@sveltejs/kit' */
2
+ import path from 'node:path';
3
+ import { create_explicit_env_types } from '../env.js';
4
+ import { posixify } from '../../utils/filesystem.js';
5
+ import { write_if_changed } from './utils.js';
6
+
7
+ const DOCS = '// See https://svelte.dev/docs/kit/environment-variables for more information';
8
+
9
+ /**
10
+ * Writes ambient declarations including types reference to @sveltejs/kit,
11
+ * and the existing environment variables in process.env to
12
+ * $env/static/private and $env/static/public
13
+ * @param {import('types').ValidatedKitConfig} kit
14
+ * @param {string | null} entry
15
+ * @param {Record<string, EnvVarConfig<any>> | null} env_config
16
+ */
17
+ export function write_env(kit, entry, env_config) {
18
+ const content = [];
19
+ const out = path.join(kit.outDir, 'env.d.ts');
20
+
21
+ if (entry && env_config) {
22
+ const relative = posixify(path.relative(kit.outDir, entry));
23
+ content.push(
24
+ `// This file is generated from ${relative}.\n${DOCS}`,
25
+ create_explicit_env_types(env_config, relative, 'private'),
26
+ create_explicit_env_types(env_config, relative, 'public')
27
+ );
28
+ } else {
29
+ content.push(DOCS);
30
+ }
31
+
32
+ write_if_changed(out, content.join('\n\n'));
33
+ }
@@ -100,7 +100,7 @@ export function write_root(manifest_data, config, output) {
100
100
  ${isSvelte5Plus() ? '<svelte:options runes={true} />' : ''}
101
101
  <script>
102
102
  import { setContext, ${isSvelte5Plus() ? '' : 'afterUpdate, '}onMount, tick } from 'svelte';
103
- import { browser } from '$app/environment';
103
+ import { browser } from '$app/env';
104
104
 
105
105
  // stores
106
106
  ${
@@ -30,9 +30,10 @@ const server_template = ({
30
30
  error_page
31
31
  }) => `
32
32
  import root from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}';
33
- import { set_building, set_prerendering } from '__sveltekit/environment';
33
+ import { set_building, set_prerendering } from '$app/env/internal';
34
34
  import { set_assets } from '$app/paths/internal/server';
35
35
  import { set_manifest, set_read_implementation } from '__sveltekit/server';
36
+ import { set_env } from '__sveltekit/env';
36
37
  import { set_private_env, set_public_env } from '${runtime_directory}/shared-server.js';
37
38
 
38
39
  export const options = {
@@ -92,7 +93,7 @@ export async function get_hooks() {
92
93
  };
93
94
  }
94
95
 
95
- export { set_assets, set_building, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation };
96
+ export { set_assets, set_building, set_env, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation };
96
97
  `;
97
98
 
98
99
  // TODO need to re-run this whenever src/app.html or src/error.html are
@@ -57,6 +57,7 @@ export function get_tsconfig(kit) {
57
57
 
58
58
  const include = new Set([
59
59
  'ambient.d.ts', // careful: changing this name would be a breaking change, because it's referenced in the service-workers documentation
60
+ 'env.d.ts',
60
61
  'non-ambient.d.ts',
61
62
  './types/**/$types.d.ts',
62
63
  config_relative('vite.config.js'),
@@ -1 +1,14 @@
1
+ /** @import { EnvVarConfig } from '@sveltejs/kit' */
2
+
1
3
  export { sequence } from './sequence.js';
4
+
5
+ /**
6
+ * Utility for defining [environment variables](https://svelte.dev/docs/kit/environment-variables),
7
+ * which are made available via `$app/env/public` and `$app/env/private`.
8
+ * @template {Record<string, EnvVarConfig<any>>} T
9
+ * @param {T} variables
10
+ * @returns {T}
11
+ */
12
+ export function defineEnvVars(variables) {
13
+ return variables;
14
+ }
@@ -0,0 +1,71 @@
1
+ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
+ /** @import { EnvVarConfig } from '@sveltejs/kit' */
3
+
4
+ import { stackless } from '../../utils/error.js';
5
+
6
+ const MISSING = {
7
+ message: `Value is missing. If it is optional, add a Standard Schema validator declaring it as such.`
8
+ };
9
+
10
+ const BAD_VALIDATOR = {
11
+ message: 'Variable was configured with a validator that does not implement Standard Schema'
12
+ };
13
+
14
+ const ASYNC_VALIDATOR = {
15
+ message: 'Variable uses an async validator, which is not supported'
16
+ };
17
+
18
+ /**
19
+ * @param {Record<string, EnvVarConfig<any>>} variables
20
+ * @param {string | undefined} value
21
+ * @param {string} name
22
+ * @param {Record<string, StandardSchemaV1.Issue[]>} issues
23
+ * @returns
24
+ */
25
+ export function validate(variables, value, name, issues) {
26
+ const config = variables[name] ?? {};
27
+ const validator = config.schema;
28
+
29
+ if (!validator) {
30
+ if (!value) issues[name] = [MISSING];
31
+ return value;
32
+ }
33
+
34
+ if (!validator['~standard']) {
35
+ issues[name] = [BAD_VALIDATOR];
36
+ return;
37
+ }
38
+
39
+ const result = validator['~standard'].validate(value);
40
+
41
+ if (result instanceof Promise) {
42
+ issues[name] = [ASYNC_VALIDATOR];
43
+ return;
44
+ }
45
+
46
+ if (result.issues) {
47
+ issues[name] = [...result.issues];
48
+ return;
49
+ }
50
+
51
+ return result.value;
52
+ }
53
+
54
+ /**
55
+ * @param {Record<string, StandardSchemaV1.Issue[]>} issues
56
+ */
57
+ export function handle_issues(issues) {
58
+ const entries = Object.entries(issues);
59
+
60
+ if (entries.length === 0) {
61
+ return;
62
+ }
63
+
64
+ let message = 'Invalid environment variables\n';
65
+
66
+ for (const [name, issues] of entries) {
67
+ message += `\n${name}\n${issues.map((issue) => ` - ${issue.message}`).join('\n')}\n`;
68
+ }
69
+
70
+ throw stackless(message);
71
+ }
@@ -0,0 +1,3 @@
1
+ // We re-export this so we can use it in the `$app/env/*` module declarations
2
+ // without the app needing to depend on this package
3
+ export { StandardSchemaV1 } from '@standard-schema/spec';
@@ -476,6 +476,13 @@ export interface KitConfig {
476
476
  };
477
477
  /** Experimental features. Here be dragons. These are not subject to semantic versioning, so breaking changes or removal can happen in any release. */
478
478
  experimental?: {
479
+ /**
480
+ * Whether to enable explicit environment variables using `src/env.js` or `src/env.ts`.
481
+ * @since 2.63.0
482
+ * @default false
483
+ */
484
+ explicitEnvironmentVariables?: boolean;
485
+
479
486
  /**
480
487
  * Options for enabling server-side [OpenTelemetry](https://opentelemetry.io/) tracing for SvelteKit operations including the [`handle` hook](https://svelte.dev/docs/kit/hooks#Server-hooks-handle), [`load` functions](https://svelte.dev/docs/kit/load), [form actions](https://svelte.dev/docs/kit/form-actions), and [remote functions](https://svelte.dev/docs/kit/remote-functions).
481
488
  * @default { server: false, serverFile: false }
@@ -2314,4 +2321,37 @@ export type RemoteLiveQueryFunction<Input, Output, _Validated = Input> = (
2314
2321
  arg: undefined extends Input ? Input | void : Input
2315
2322
  ) => RemoteLiveQuery<Output>;
2316
2323
 
2324
+ /**
2325
+ * [Environment variables](https://svelte.dev/docs/kit/environment-variables) can be configured by exporting
2326
+ * a `variables` object from `src/env.ts`, using [`defineEnvVars`](https://svelte.dev/docs/kit/@sveltejs-kit-hooks#defineEnvVars).
2327
+ */
2328
+ export interface EnvVarConfig<T> {
2329
+ /**
2330
+ * Whether the environment variable can be accessed by client-side code.
2331
+ * - if `true`, it can be imported from `$app/env/public`
2332
+ * - if `false`, it can be imported from `$app/env/private`, which is a [server-only module](https://svelte.dev/docs/kit/server-only-modules)
2333
+ * @default false
2334
+ */
2335
+ public?: boolean;
2336
+ /**
2337
+ * Whether the value is determined at build time or when the app runs.
2338
+ * - if `true`, the build time value is inlined into the bundle. This enables optimisations like dead-code elimination
2339
+ * - if `false`, the value is read from the environment when the app starts
2340
+ * @default false
2341
+ */
2342
+ static?: boolean;
2343
+ /**
2344
+ * A [Standard Schema](https://standardschema.dev/) validator that is applied to the value when the app starts.
2345
+ * The validator can output any value — not necessarily a string — but public, non-static values must be
2346
+ * serializable by [devalue](https://github.com/sveltejs/devalue) so that they can be sent to the browser.
2347
+ *
2348
+ * If omitted, the value must be a non-empty string.
2349
+ */
2350
+ schema?: StandardSchemaV1<string | undefined, T>;
2351
+ /**
2352
+ * A description of the variable that will be used for inline documentation on hover.
2353
+ */
2354
+ description?: string;
2355
+ }
2356
+
2317
2357
  export * from './index.js';
@@ -1,10 +1,11 @@
1
+ /** @import { EnvVarConfig } from '@sveltejs/kit' */
1
2
  import fs from 'node:fs';
2
3
  import process from 'node:process';
3
4
  import * as vite from 'vite';
4
5
  import { dedent } from '../../../core/sync/utils.js';
5
6
  import { s } from '../../../utils/misc.js';
6
7
  import { get_config_aliases, strip_virtual_prefix, get_env, normalize_id } from '../utils.js';
7
- import { create_static_module } from '../../../core/env.js';
8
+ import { create_static_module, create_sveltekit_env_public } from '../../../core/env.js';
8
9
  import { env_static_public, service_worker } from '../module_ids.js';
9
10
 
10
11
  // @ts-ignore `vite.rolldownVersion` only exists in `rolldown-vite`
@@ -18,6 +19,7 @@ const is_rolldown = !!vite.rolldownVersion;
18
19
  * @param {string} service_worker_entry_file
19
20
  * @param {import('types').Prerendered} prerendered
20
21
  * @param {import('vite').Manifest} client_manifest
22
+ * @param {Record<string, EnvVarConfig<any>> | null} env_config
21
23
  */
22
24
  export async function build_service_worker(
23
25
  out,
@@ -26,7 +28,8 @@ export async function build_service_worker(
26
28
  manifest_data,
27
29
  service_worker_entry_file,
28
30
  prerendered,
29
- client_manifest
31
+ client_manifest,
32
+ env_config
30
33
  ) {
31
34
  const build = new Set();
32
35
  for (const key in client_manifest) {
@@ -85,7 +88,20 @@ export async function build_service_worker(
85
88
  }
86
89
 
87
90
  if (id === env_static_public) {
88
- return create_static_module('$env/static/public', env.public);
91
+ return create_static_module(
92
+ '$env/static/public',
93
+ env.public,
94
+ kit.experimental.explicitEnvironmentVariables
95
+ );
96
+ }
97
+
98
+ if (id === '\0virtual:app/env/public') {
99
+ // TODO ideally we would only add the `importScripts` if there are dynamic vars that are known to be used
100
+ return create_sveltekit_env_public(
101
+ env_config,
102
+ env.all,
103
+ `importScripts('${kit.paths.base}/${kit.appDir}/env.script.js'); const env = globalThis.__sveltekit_sw.env;`
104
+ );
89
105
  }
90
106
 
91
107
  const normalized_cwd = vite.normalizePath(process.cwd());
@@ -93,7 +109,7 @@ export async function build_service_worker(
93
109
  const relative = normalize_id(id, normalized_lib, normalized_cwd);
94
110
  const stripped = strip_virtual_prefix(relative);
95
111
  throw new Error(
96
- `Cannot import ${stripped} into service-worker code. Only the modules $service-worker and $env/static/public are available in service workers.`
112
+ `Cannot import ${stripped} into service-worker code. Only the modules $service-worker, $env/static/public and $app/env/public are available in service workers.`
97
113
  );
98
114
  }
99
115
  };
@@ -322,7 +322,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
322
322
  // ssrFixStacktrace can fail on StackBlitz web containers and we don't know why
323
323
  // by ignoring it the line numbers are wrong, but at least we can show the error
324
324
  }
325
- return error.stack;
325
+ return error.stack?.replaceAll('\0', ''); // remove null bytes from e.g. virtual module IDs, or the response will fail
326
326
  }
327
327
 
328
328
  update_manifest();
@@ -587,7 +587,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
587
587
  } catch (e) {
588
588
  const error = coalesce_to_error(e);
589
589
  res.statusCode = 500;
590
- res.end(fix_stack_trace(error));
590
+ res.end(fix_stack_trace(error) || error.message); // handle `stackless` errors
591
591
  }
592
592
  });
593
593
  };