@sveltejs/kit 3.0.0-next.11 → 3.0.0-next.12

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 (61) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +8 -8
  3. package/src/constants.js +1 -1
  4. package/src/core/config/options.js +43 -35
  5. package/src/core/env.js +25 -11
  6. package/src/core/sync/sync.js +11 -14
  7. package/src/core/sync/utils.js +21 -1
  8. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +23 -21
  9. package/src/core/sync/write_client_manifest.js +4 -12
  10. package/src/core/sync/write_env.js +6 -4
  11. package/src/core/sync/write_server.js +10 -15
  12. package/src/core/sync/write_tsconfig/index.js +245 -0
  13. package/src/core/sync/write_tsconfig/utils.js +162 -0
  14. package/src/core/sync/write_types/index.js +80 -88
  15. package/src/exports/internal/server/index.js +3 -1
  16. package/src/exports/public.d.ts +28 -51
  17. package/src/exports/vite/dev/index.js +88 -76
  18. package/src/exports/vite/index.js +364 -144
  19. package/src/exports/vite/module_ids.js +3 -1
  20. package/src/exports/vite/utils.js +1 -10
  21. package/src/runner.js +13 -0
  22. package/src/runtime/app/forms.js +4 -0
  23. package/src/runtime/app/manifest/index.js +1 -0
  24. package/src/runtime/app/paths/client.js +30 -30
  25. package/src/runtime/app/paths/internal/client.js +26 -0
  26. package/src/runtime/app/paths/server.js +22 -9
  27. package/src/runtime/app/paths/types.d.ts +11 -19
  28. package/src/runtime/app/server/remote/command.js +12 -7
  29. package/src/runtime/app/server/remote/form.js +29 -26
  30. package/src/runtime/app/server/remote/prerender.js +9 -2
  31. package/src/runtime/app/server/remote/query.js +5 -4
  32. package/src/runtime/app/server/remote/shared.js +48 -30
  33. package/src/runtime/app/service-worker/index.js +24 -0
  34. package/src/runtime/app/state/index.js +1 -1
  35. package/src/runtime/client/client.js +46 -9
  36. package/src/runtime/client/remote-functions/form.svelte.js +43 -51
  37. package/src/runtime/client/remote-functions/query/index.js +2 -2
  38. package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -3
  39. package/src/runtime/client/remote-functions/query-live/iterator.js +5 -2
  40. package/src/runtime/client/remote-functions/shared.svelte.js +4 -1
  41. package/src/runtime/client/state.svelte.js +54 -21
  42. package/src/runtime/form-utils.js +89 -54
  43. package/src/runtime/server/cookie.js +1 -1
  44. package/src/runtime/server/data/index.js +31 -28
  45. package/src/runtime/server/errors.js +1 -1
  46. package/src/runtime/server/page/actions.js +3 -3
  47. package/src/runtime/server/page/render.js +19 -28
  48. package/src/runtime/server/remote-functions.js +66 -35
  49. package/src/runtime/server/respond.js +56 -14
  50. package/src/runtime/server/utils.js +10 -0
  51. package/src/types/ambient-private.d.ts +8 -0
  52. package/src/types/ambient.d.ts +22 -27
  53. package/src/types/global-private.d.ts +12 -0
  54. package/src/types/internal.d.ts +13 -3
  55. package/src/utils/url.js +12 -0
  56. package/src/version.js +1 -1
  57. package/types/index.d.ts +80 -98
  58. package/types/index.d.ts.map +4 -1
  59. package/src/core/sync/write_ambient.js +0 -18
  60. package/src/core/sync/write_tsconfig.js +0 -258
  61. /package/src/core/sync/{write_tsconfig_test → write_tsconfig/test-app}/package.json +0 -0
@@ -288,7 +288,7 @@ export interface Cookies {
288
288
  /**
289
289
  * Sets a cookie. This will add a `set-cookie` header to the response, but also make the cookie available via `cookies.get` or `cookies.getAll` during the current request.
290
290
  *
291
- * The `httpOnly` and `secure` options are `true` by default (except on http://localhost, where `secure` is `false`), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
291
+ * The `httpOnly` is `true` by default, as is `secure`, except during development, when it defaults to `false`. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
292
292
  *
293
293
  * The `path` option is `'/'` by default. You can use relative paths, or set `path: ''` to make the cookie only available on the current path and its children.
294
294
  * @param name the name of the cookie
@@ -300,7 +300,7 @@ export interface Cookies {
300
300
  /**
301
301
  * Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
302
302
  *
303
- * The `httpOnly` and `secure` options are `true` by default (except on http://localhost, where `secure` is `false`), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
303
+ * The `httpOnly` is `true` by default, as is `secure`, except during development, when it defaults to `false`. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
304
304
  *
305
305
  * The `path` option is `'/'` by default. You can use relative paths, or set `path: ''` to make the cookie only available on the current path and its children.
306
306
  * @param name the name of the cookie
@@ -335,7 +335,7 @@ export interface Cookies {
335
335
  /**
336
336
  * Serialize a cookie name-value pair into a `Set-Cookie` header string, but don't apply it to the response.
337
337
  *
338
- * The `httpOnly` and `secure` options are `true` by default (except on http://localhost, where `secure` is `false`), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
338
+ * The `httpOnly` is `true` by default, as is `secure`, except during development, when it defaults to `false`. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
339
339
  *
340
340
  * The `path` option is `'/'` by default. You can use relative paths, or set `path: ''` to make the cookie only available on the current path and its children.
341
341
  * @param name the name of the cookie
@@ -365,32 +365,10 @@ export interface KitConfig {
365
365
  /**
366
366
  * An object containing zero or more aliases used to replace values in `import` statements. These aliases are automatically passed to Vite and TypeScript.
367
367
  *
368
- * ```js
369
- * /// file: vite.config.js
370
- * import { defineConfig } from 'vite';
371
- * import { sveltekit } from '@sveltejs/kit/vite';
372
- *
373
- * export default defineConfig({
374
- * plugins: [
375
- * sveltekit({
376
- * alias: {
377
- * // this will match a file
378
- * 'my-file': 'path/to/my-file.js',
379
- *
380
- * // this will match a directory and its contents
381
- * // (`my-directory/x` resolves to `path/to/my-directory/x`)
382
- * 'my-directory': 'path/to/my-directory',
383
- *
384
- * // an alias ending /* will only match
385
- * // the contents of a directory, not the directory itself
386
- * 'my-directory/*': 'path/to/my-directory/*'
387
- * }
388
- * })
389
- * ]
390
- * });
391
- * ```
368
+ * This option is deprecated. Use [subpath imports](https://svelte.dev/docs/kit/$lib) instead.
392
369
  *
393
370
  * > [!NOTE] You will need to run `npm run dev` to have SvelteKit automatically generate the required alias configuration in `jsconfig.json` or `tsconfig.json`.
371
+ * @deprecated
394
372
  * @default {}
395
373
  */
396
374
  alias?: Record<string, string>;
@@ -846,13 +824,7 @@ export interface KitConfig {
846
824
  */
847
825
  resolution?: 'client' | 'server';
848
826
  };
849
- serviceWorker?: {
850
- /**
851
- * Determine which files in your `static` directory will be available in `$service-worker.files`.
852
- * @default (filename) => !/\.DS_Store/.test(filename)
853
- */
854
- files?: (file: string) => boolean;
855
- } & (
827
+ serviceWorker?:
856
828
  | {
857
829
  /**
858
830
  * Whether to automatically register the service worker, if it exists.
@@ -870,8 +842,7 @@ export interface KitConfig {
870
842
  * @default true
871
843
  */
872
844
  register?: false;
873
- }
874
- );
845
+ };
875
846
  /**
876
847
  * Options for enabling [OpenTelemetry](https://opentelemetry.io/) tracing for SvelteKit operations.
877
848
  * @default { server: false }
@@ -883,6 +854,9 @@ export interface KitConfig {
883
854
  */
884
855
  server?: boolean;
885
856
  };
857
+ /**
858
+ * @deprecated Add configuration to `tsconfig.json` directly
859
+ */
886
860
  typescript?: {
887
861
  /**
888
862
  * A function that allows you to edit the generated `tsconfig.json`. You can mutate the config (recommended) or return a new one.
@@ -897,9 +871,9 @@ export interface KitConfig {
897
871
  };
898
872
  /**
899
873
  * Client-side navigation can be buggy if you deploy a new version of your app while people are using it. If the code for the new page is already loaded, it may have stale content; if it isn't, the app's route manifest may point to a JavaScript file that no longer exists.
900
- * SvelteKit helps you solve this problem through version management.
874
+ * SvelteKit helps you solve this problem through version management. The current version is included in data, remote, and form action responses via the `x-sveltekit-version` header, so SvelteKit can detect new deployments without polling — for example when a navigation triggers a server `load` function, or when a remote function is called. SvelteKit also checks for new versions when the tab regains focus or becomes visible.
901
875
  * If SvelteKit encounters an error while loading the page and detects that a new version has been deployed (using the `name` specified here, which defaults to a timestamp of the build) it will fall back to traditional full-page navigation.
902
- * Not all navigations will result in an error though, for example if the JavaScript for the next page is already loaded. If you still want to force a full-page navigation in these cases, use techniques such as setting the `pollInterval` and then using `beforeNavigate`:
876
+ * Not all navigations will result in an error though, for example if the JavaScript for the next page is already loaded. If you still want to force a full-page navigation in these cases, use `beforeNavigate`:
903
877
  * ```html
904
878
  * /// file: +layout.svelte
905
879
  * <script>
@@ -914,7 +888,7 @@ export interface KitConfig {
914
888
  * </script>
915
889
  * ```
916
890
  *
917
- * If you set `pollInterval` to a non-zero value, SvelteKit will poll for new versions in the background and set the value of [`updated.current`](https://svelte.dev/docs/kit/$app-state#updated) `true` when it detects one.
891
+ * In addition to these checks, SvelteKit polls for new versions on an interval and sets [`updated.current`](https://svelte.dev/docs/kit/$app-state#updated) to `true` when it detects one. Set `pollInterval` to `0` to disable polling (the header- and event-based checks will still run).
918
892
  */
919
893
  version?: {
920
894
  /**
@@ -941,8 +915,8 @@ export interface KitConfig {
941
915
  */
942
916
  name?: string;
943
917
  /**
944
- * The interval in milliseconds to poll for version changes. If this is `0`, no polling occurs.
945
- * @default 0
918
+ * The interval in milliseconds to poll for version changes. If this is `0`, no polling occurs. SvelteKit also checks for new versions on server responses (via the `x-sveltekit-version` header) and when the tab regains focus or becomes visible, so polling is only needed for long-lived sessions on a single page.
919
+ * @default 3600000
946
920
  */
947
921
  pollInterval?: number;
948
922
  };
@@ -1615,9 +1589,10 @@ export interface RequestEvent<
1615
1589
  /**
1616
1590
  * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
1617
1591
  *
1618
- * In the context of a remote function request initiated by the client, this relates to the page the remote function
1619
- * was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use this to determine
1620
- * whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.
1592
+ * Inside `query` functions (including `query.batch` and `query.live`), accessing this property throws an error.
1593
+ * Pass values from the page as arguments to the query instead. Inside `form` and `command` functions it relates to the page
1594
+ * the remote function was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use it
1595
+ * to determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.
1621
1596
  */
1622
1597
  params: Params;
1623
1598
  /**
@@ -1635,9 +1610,10 @@ export interface RequestEvent<
1635
1610
  /**
1636
1611
  * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`. It is `null` when no route is matched.
1637
1612
  *
1638
- * In the context of a remote function request initiated by the client, this relates to the page the remote function
1639
- * was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use this to determine
1640
- * whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.
1613
+ * Inside `query` functions (including `query.batch` and `query.live`), accessing this property throws an error.
1614
+ * Pass values from the page as arguments to the query instead. Inside `form` and `command` functions it relates to the page
1615
+ * the remote function was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use it
1616
+ * to determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.
1641
1617
  */
1642
1618
  id: RouteId;
1643
1619
  };
@@ -1667,9 +1643,10 @@ export interface RequestEvent<
1667
1643
  /**
1668
1644
  * The requested URL.
1669
1645
  *
1670
- * In the context of a remote function request initiated by the client, this relates to the page the remote function
1671
- * was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use this to determine
1672
- * whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.
1646
+ * Inside `query` functions (including `query.batch` and `query.live`), accessing this property throws an error.
1647
+ * Pass values from the page as arguments to the query instead. Inside `form` and `command` functions it relates to the page
1648
+ * the remote function was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use it
1649
+ * to determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.
1673
1650
  */
1674
1651
  url: URL;
1675
1652
  /**
@@ -2107,7 +2084,7 @@ type AsArgs<Type extends keyof InputTypeMap, Value> = Type extends 'checkbox'
2107
2084
  ? [type: Type, value: Value | (string & {})]
2108
2085
  : Type extends 'file' | 'file multiple'
2109
2086
  ? [type: Type]
2110
- : [type: Type] | [type: Type, value: Value | (string & {})];
2087
+ : [type: Type] | [type: Type, value: Value | undefined];
2111
2088
 
2112
2089
  /**
2113
2090
  * Form field accessor type that provides name(), value(), and issues() methods
@@ -29,6 +29,7 @@ import {
29
29
  import { SCHEME } from '../../../utils/url.js';
30
30
  import { check_feature } from '../../../utils/features.js';
31
31
  import { escape_html } from '../../../utils/escape.js';
32
+ import { get_runner } from '../../../runner.js';
32
33
 
33
34
  // vite-specifc queries that we should skip handling for css urls
34
35
  const vite_css_query_regex = /(?:\?|&)(?:raw|url|inline)(?:&|$)/;
@@ -39,9 +40,10 @@ const vite_css_query_regex = /(?:\?|&)(?:raw|url|inline)(?:&|$)/;
39
40
  * @param {import('types').ValidatedConfig} svelte_config
40
41
  * @param {() => Array<{ hash: string, file: string }>} get_remotes
41
42
  * @param {string} root The project root directory
43
+ * @param {(manifest_data: import('types').ManifestData) => void} set_manifest_data
42
44
  * @return {Promise<Promise<() => void>>}
43
45
  */
44
- export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
46
+ export async function dev(vite, vite_config, svelte_config, get_remotes, root, set_manifest_data) {
45
47
  /** @type {AsyncLocalStorage<{ event: RequestEvent, config: any, prerender: PrerenderOption }>} */
46
48
  const async_local_storage = new AsyncLocalStorage();
47
49
 
@@ -78,10 +80,15 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
78
80
  /** @type {Error | null} */
79
81
  let manifest_error = null;
80
82
 
81
- /** @param {string} url */
83
+ const runner = get_runner(vite);
84
+
85
+ /**
86
+ * @param {string} url
87
+ * @returns {Promise<Record<string, any>>}
88
+ */
82
89
  async function loud_ssr_load_module(url) {
83
90
  try {
84
- return await vite.ssrLoadModule(url, { fixStacktrace: true });
91
+ return await runner.import(url);
85
92
  } catch (/** @type {any} */ err) {
86
93
  const msg = buildErrorMessage(err, [
87
94
  styleText('red', `Internal server error: ${err.message}`)
@@ -92,10 +99,10 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
92
99
  }
93
100
 
94
101
  // TODO this is inadequate — it doesn't reliably show the overlay on every page load,
95
- // and when it does appear it may immediately vanish. `vite.ws.send` broadcasts
102
+ // and when it does appear it may immediately vanish. `vite.hot.send` broadcasts
96
103
  // to all connected clients, even ones that are unaffected by the error.
97
104
  // we need a more considered approach
98
- vite.ws.send({
105
+ vite.hot.send({
99
106
  type: 'error',
100
107
  err: /** @type {import('vite').ErrorPayload['err']} */ ({
101
108
  ...err,
@@ -116,7 +123,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
116
123
 
117
124
  const module = await loud_ssr_load_module(url);
118
125
 
119
- const module_node = await vite.moduleGraph.getModuleByUrl(url);
126
+ const module_node = await vite.environments.ssr.moduleGraph.getModuleByUrl(url);
120
127
  if (!module_node) throw new Error(`Could not find node for ${url}`);
121
128
 
122
129
  return { module, module_node, url };
@@ -125,6 +132,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
125
132
  async function update_manifest() {
126
133
  try {
127
134
  ({ manifest_data } = sync.create(svelte_config, root));
135
+ set_manifest_data(manifest_data);
128
136
 
129
137
  await load_and_validate_params({
130
138
  routes: manifest_data.routes,
@@ -135,13 +143,13 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
135
143
 
136
144
  if (manifest_error) {
137
145
  manifest_error = null;
138
- vite.ws.send({ type: 'full-reload' });
146
+ vite.hot.send({ type: 'full-reload' });
139
147
  }
140
148
  } catch (error) {
141
149
  manifest_error = /** @type {Error} */ (error);
142
150
 
143
151
  console.error(styleText(['bold', 'red'], manifest_error.message));
144
- vite.ws.send({
152
+ vite.hot.send({
145
153
  type: 'error',
146
154
  err: {
147
155
  message: manifest_error.message ?? 'Invalid routes',
@@ -214,7 +222,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
214
222
  result.stylesheets = [];
215
223
  result.fonts = [];
216
224
 
217
- /** @type {import('vite').ModuleNode[]} */
225
+ /** @type {import('vite').EnvironmentModuleNode[]} */
218
226
  const module_nodes = [];
219
227
 
220
228
  if (node.component) {
@@ -248,7 +256,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
248
256
  // in dev we inline all styles to avoid FOUC. this gets populated lazily so that
249
257
  // components/stylesheets loaded via import() during `load` are included
250
258
  result.inline_styles = async () => {
251
- /** @type {Set<import('vite').ModuleNode | import('vite').EnvironmentModuleNode>} */
259
+ /** @type {Set<import('vite').EnvironmentModuleNode>} */
252
260
  const deps = new Set();
253
261
 
254
262
  for (const module_node of module_nodes) {
@@ -264,7 +272,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
264
272
  ? dep.url.replace('?', '?inline&')
265
273
  : dep.url + '?inline';
266
274
  try {
267
- const mod = await vite.ssrLoadModule(inlineCssUrl);
275
+ const mod = await runner.import(inlineCssUrl);
268
276
  styles[dep.url] = mod.default;
269
277
  } catch {
270
278
  // this can happen with dynamically imported modules, I think
@@ -285,7 +293,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
285
293
  return Object.fromEntries(
286
294
  get_remotes().map((remote) => [
287
295
  remote.hash,
288
- () => vite.ssrLoadModule(remote.file).then((module) => ({ default: module }))
296
+ () => runner.import(remote.file).then((module) => ({ default: module }))
289
297
  ])
290
298
  );
291
299
  },
@@ -314,7 +322,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
314
322
  if (!manifest_data.params) return {};
315
323
 
316
324
  const url = path.resolve(root, manifest_data.params);
317
- const module = await vite.ssrLoadModule(url, { fixStacktrace: true });
325
+ const module = await runner.import(url);
318
326
 
319
327
  if (!module.params) {
320
328
  throw new Error(
@@ -334,48 +342,37 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
334
342
  return;
335
343
  }
336
344
 
337
- try {
338
- vite.ssrFixStacktrace(error);
339
- } catch {
340
- // ssrFixStacktrace can fail on StackBlitz web containers and we don't know why
341
- // by ignoring it the line numbers are wrong, but at least we can show the error
342
- }
345
+ let end = 0;
346
+
347
+ error.stack = error.stack
348
+ .replaceAll('\0', '') // remove null bytes from e.g. virtual module IDs, or the response will fail
349
+ .split('\n')
350
+ .map((line, i) => {
351
+ const match = /^ {4}at (?:[^ ]+ \((.+)\)|(.+))$/.exec(line);
352
+ if (!match) {
353
+ end = i + 1;
354
+ return line;
355
+ }
343
356
 
344
- if (error.stack) {
345
- let end = 0;
357
+ const loc = match[1] ?? match[2];
358
+ const file = loc.replace(/:\d+:\d+$/, '');
346
359
 
347
- error.stack = error.stack
348
- .replaceAll('\0', '') // remove null bytes from e.g. virtual module IDs, or the response will fail
349
- .split('\n')
350
- .map((line, i) => {
351
- const match = /^ {4}at (?:[^ ]+ \((.+)\)|(.+))$/.exec(line);
352
- if (!match) {
360
+ if (fs.existsSync(file)) {
361
+ if (!file.includes('node_modules') && !file.includes(SRC_ROOT)) {
353
362
  end = i + 1;
354
- return line;
355
363
  }
356
364
 
357
- const loc = match[1] ?? match[2];
358
- const file = loc.replace(/:\d+:\d+$/, '');
359
-
360
- if (fs.existsSync(file)) {
361
- if (!file.includes('node_modules') && !file.includes(SRC_ROOT)) {
362
- end = i + 1;
363
- }
364
-
365
- return line.replace(file, path.posix.relative(process.cwd(), file));
366
- }
365
+ return line.replace(file, path.relative(process.cwd(), file));
366
+ }
367
367
 
368
- return line;
369
- })
370
- .slice(0, end)
371
- .join('\n');
368
+ return line;
369
+ })
370
+ .slice(0, end)
371
+ .join('\n');
372
372
 
373
- return error.stack;
374
- }
373
+ return error.stack;
375
374
  }
376
375
 
377
- await update_manifest();
378
-
379
376
  const params_file = resolve_entry(svelte_config.kit.files.params);
380
377
 
381
378
  /**
@@ -386,6 +383,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
386
383
  vite.watcher.on(event, (file) => {
387
384
  if (
388
385
  file.startsWith(svelte_config.kit.files.routes + path.sep) ||
386
+ file.startsWith(svelte_config.kit.files.assets + path.sep) ||
389
387
  (params_file && file === params_file) ||
390
388
  remote_module_pattern.test(file) ||
391
389
  // in contrast to server hooks, client hooks are written to the client manifest
@@ -413,6 +411,11 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
413
411
  watch('add', () => debounce(update_manifest));
414
412
  watch('unlink', () => debounce(update_manifest));
415
413
  watch('change', (file) => {
414
+ // `manifest_data` is populated lazily on the first request (see `update_manifest`
415
+ // call in the middleware below), so it may still be undefined if a file changes
416
+ // before the dev server has served a request. In that case there's nothing to
417
+ // update — the manifest will be created from scratch on the first request.
418
+ if (!manifest_data) return;
416
419
  // Don't run for a single file if the whole manifest is about to get updated
417
420
  // Unless it's a file where the trailing slash page option might have changed
418
421
  if (timeout || !/\+(page|layout|server).*$/.test(file)) return;
@@ -427,7 +430,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
427
430
  if (appTemplate !== 'index.html') {
428
431
  vite.watcher.on('change', (file) => {
429
432
  if (file === appTemplate) {
430
- vite.ws.send({ type: 'full-reload' });
433
+ vite.hot.send({ type: 'full-reload' });
431
434
  }
432
435
  });
433
436
  }
@@ -477,6 +480,9 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
477
480
  const env = loadEnv(vite_config.mode, svelte_config.kit.env.dir, '');
478
481
  const emulator = await svelte_config.kit.adapter?.emulate?.();
479
482
 
483
+ /** @type {Promise<void> | undefined} */
484
+ let init_manifest;
485
+
480
486
  return () => {
481
487
  const serve_static_middleware = vite.middlewares.stack.find(
482
488
  (middleware) =>
@@ -488,6 +494,11 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
488
494
  remove_static_middlewares(vite.middlewares);
489
495
 
490
496
  vite.middlewares.use(async (req, res) => {
497
+ // Vite throws a Cannot read properties of undefined (reading 'wrapDynamicImport')
498
+ // if you try to run ssr.runner.import before the server has started so
499
+ // we do it inside here to avoid that
500
+ await (init_manifest ??= update_manifest());
501
+
491
502
  // Vite's base middleware strips out the base path. Restore it
492
503
  const original_url = req.url;
493
504
  req.url = req.originalUrl;
@@ -541,22 +552,20 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
541
552
  );
542
553
 
543
554
  if (resolved_instrumentation) {
544
- await vite.ssrLoadModule(resolved_instrumentation);
555
+ await runner.import(resolved_instrumentation);
545
556
  }
546
557
 
547
558
  // we have to import `Server` before calling `set_assets`
548
559
  const { Server } = /** @type {import('types').ServerModule} */ (
549
- await vite.ssrLoadModule(`${get_runtime_base(root)}/server/index.js`, {
550
- fixStacktrace: true
551
- })
560
+ await runner.import(`${get_runtime_base(root)}/server/index.js`)
552
561
  );
553
562
 
554
- const { set_fix_stack_trace } = await vite.ssrLoadModule(
563
+ const { set_fix_stack_trace } = await runner.import(
555
564
  `${get_runtime_base(root)}/server/internal.js`
556
565
  );
557
566
  set_fix_stack_trace(fix_stack_trace);
558
567
 
559
- const { set_assets } = await vite.ssrLoadModule('$app/paths/internal/server');
568
+ const { set_assets } = await runner.import('$app/paths/internal/server');
560
569
  set_assets(assets);
561
570
 
562
571
  const server = new Server(manifest);
@@ -649,16 +658,16 @@ function remove_static_middlewares(server) {
649
658
 
650
659
  /**
651
660
  * @param {import('vite').ViteDevServer} vite
652
- * @param {import('vite').ModuleNode | import('vite').EnvironmentModuleNode} node
653
- * @param {Set<import('vite').ModuleNode | import('vite').EnvironmentModuleNode>} deps
661
+ * @param {import('vite').EnvironmentModuleNode} node
662
+ * @param {Set<import('vite').EnvironmentModuleNode>} deps
654
663
  */
655
664
  async function find_deps(vite, node, deps) {
656
- // since `ssrTransformResult.deps` contains URLs instead of `ModuleNode`s, this process is asynchronous.
665
+ // since `transformResult.deps` contains URLs instead of `ModuleNode`s, this process is asynchronous.
657
666
  // instead of using `await`, we resolve all branches in parallel.
658
667
  /** @type {Promise<void>[]} */
659
668
  const branches = [];
660
669
 
661
- /** @param {import('vite').ModuleNode | import('vite').EnvironmentModuleNode} node */
670
+ /** @param {import('vite').EnvironmentModuleNode} node */
662
671
  async function add(node) {
663
672
  if (!deps.has(node)) {
664
673
  deps.add(node);
@@ -668,23 +677,20 @@ async function find_deps(vite, node, deps) {
668
677
 
669
678
  /** @param {string} url */
670
679
  async function add_by_url(url) {
671
- const node = await get_server_module_by_url(vite, url);
680
+ const node = await vite.environments.ssr.moduleGraph.getModuleByUrl(url);
672
681
 
673
682
  if (node) {
674
683
  await add(node);
675
684
  }
676
685
  }
677
686
 
678
- const transform_result =
679
- /** @type {import('vite').ModuleNode} */ (node).ssrTransformResult || node.transformResult;
680
-
681
- if (transform_result) {
682
- if (transform_result.deps) {
683
- transform_result.deps.forEach((url) => branches.push(add_by_url(url)));
687
+ if (node.transformResult) {
688
+ if (node.transformResult.deps) {
689
+ node.transformResult.deps.forEach((url) => branches.push(add_by_url(url)));
684
690
  }
685
691
 
686
- if (transform_result.dynamicDeps) {
687
- transform_result.dynamicDeps.forEach((url) => branches.push(add_by_url(url)));
692
+ if (node.transformResult.dynamicDeps) {
693
+ node.transformResult.dynamicDeps.forEach((url) => branches.push(add_by_url(url)));
688
694
  }
689
695
  } else {
690
696
  node.importedModules.forEach((node) => branches.push(add(node)));
@@ -693,16 +699,6 @@ async function find_deps(vite, node, deps) {
693
699
  await Promise.all(branches);
694
700
  }
695
701
 
696
- /**
697
- * @param {import('vite').ViteDevServer} vite
698
- * @param {string} url
699
- */
700
- function get_server_module_by_url(vite, url) {
701
- return vite.environments
702
- ? vite.environments.ssr.moduleGraph.getModuleByUrl(url)
703
- : vite.moduleGraph.getModuleByUrl(url, true);
704
- }
705
-
706
702
  /**
707
703
  * Determine if a file is being requested with the correct case,
708
704
  * to ensure consistent behaviour between dev and prod and across
@@ -723,3 +719,19 @@ function has_correct_case(file, assets) {
723
719
 
724
720
  return false;
725
721
  }
722
+
723
+ /**
724
+ * Invalidates a module in all environments.
725
+ * @param {import('vite').ViteDevServer} server
726
+ * @param {string} id
727
+ * @returns {void}
728
+ */
729
+ export function invalidate_module(server, id) {
730
+ for (const environment in server.environments) {
731
+ const module = server.environments[environment].moduleGraph.getModuleById(id);
732
+ if (module) {
733
+ server.environments[environment].moduleGraph.invalidateModule(module);
734
+ void server.environments[environment].reloadModule(module);
735
+ }
736
+ }
737
+ }