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

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 (84) hide show
  1. package/package.json +10 -10
  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/postbuild/prerender.js +3 -3
  7. package/src/core/sync/sync.js +11 -14
  8. package/src/core/sync/utils.js +21 -1
  9. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +24 -23
  10. package/src/core/sync/write_client_manifest.js +4 -12
  11. package/src/core/sync/write_env.js +6 -4
  12. package/src/core/sync/write_server.js +10 -15
  13. package/src/core/sync/write_tsconfig/index.js +257 -0
  14. package/src/core/sync/write_tsconfig/utils.js +162 -0
  15. package/src/core/sync/write_types/index.js +80 -88
  16. package/src/exports/index.js +23 -12
  17. package/src/exports/internal/server/index.js +3 -1
  18. package/src/exports/internal/shared.js +4 -12
  19. package/src/exports/public.d.ts +87 -62
  20. package/src/exports/vite/build/remote.js +10 -12
  21. package/src/exports/vite/dev/index.js +113 -88
  22. package/src/exports/vite/index.js +452 -237
  23. package/src/exports/vite/module_ids.js +3 -1
  24. package/src/exports/vite/utils.js +1 -10
  25. package/src/runner.js +13 -0
  26. package/src/runtime/app/forms.js +4 -0
  27. package/src/runtime/app/manifest/index.js +1 -0
  28. package/src/runtime/app/paths/client.js +30 -30
  29. package/src/runtime/app/paths/internal/client.js +26 -0
  30. package/src/runtime/app/paths/server.js +22 -9
  31. package/src/runtime/app/paths/types.d.ts +11 -19
  32. package/src/runtime/app/server/remote/command.js +12 -7
  33. package/src/runtime/app/server/remote/form.js +37 -26
  34. package/src/runtime/app/server/remote/prerender.js +22 -11
  35. package/src/runtime/app/server/remote/query.js +5 -4
  36. package/src/runtime/app/server/remote/requested.js +4 -4
  37. package/src/runtime/app/server/remote/shared.js +48 -30
  38. package/src/runtime/app/service-worker/index.js +24 -0
  39. package/src/runtime/app/state/client.js +3 -0
  40. package/src/runtime/app/state/index.js +3 -3
  41. package/src/runtime/app/state/server.js +3 -0
  42. package/src/runtime/client/client.js +627 -311
  43. package/src/runtime/client/constants.js +2 -6
  44. package/src/runtime/client/fetcher.js +27 -17
  45. package/src/runtime/client/remote-functions/form.svelte.js +82 -58
  46. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  47. package/src/runtime/client/remote-functions/query/index.js +2 -2
  48. package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
  49. package/src/runtime/client/remote-functions/query-batch.svelte.js +3 -4
  50. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
  51. package/src/runtime/client/remote-functions/query-live/iterator.js +15 -10
  52. package/src/runtime/client/remote-functions/shared.svelte.js +15 -11
  53. package/src/runtime/client/state.svelte.js +54 -33
  54. package/src/runtime/client/types.d.ts +1 -2
  55. package/src/runtime/client/utils.js +21 -14
  56. package/src/runtime/components/root.svelte +4 -14
  57. package/src/runtime/form-utils.js +89 -54
  58. package/src/runtime/props.svelte.js +71 -0
  59. package/src/runtime/server/cookie.js +1 -1
  60. package/src/runtime/server/data/index.js +31 -28
  61. package/src/runtime/server/errors.js +1 -1
  62. package/src/runtime/server/fetch.js +4 -6
  63. package/src/runtime/server/page/actions.js +3 -3
  64. package/src/runtime/server/page/csp.js +86 -103
  65. package/src/runtime/server/page/index.js +1 -2
  66. package/src/runtime/server/page/load_data.js +15 -5
  67. package/src/runtime/server/page/render.js +46 -52
  68. package/src/runtime/server/page/respond_with_error.js +1 -3
  69. package/src/runtime/server/remote-functions.js +66 -35
  70. package/src/runtime/server/respond.js +56 -16
  71. package/src/runtime/server/utils.js +10 -0
  72. package/src/types/ambient-private.d.ts +9 -1
  73. package/src/types/ambient.d.ts +23 -28
  74. package/src/types/global-private.d.ts +12 -0
  75. package/src/types/internal.d.ts +15 -10
  76. package/src/types/private.d.ts +3 -12
  77. package/src/utils/url.js +12 -0
  78. package/src/version.js +1 -1
  79. package/types/index.d.ts +172 -155
  80. package/types/index.d.ts.map +5 -1
  81. package/src/core/sync/write_ambient.js +0 -18
  82. package/src/core/sync/write_tsconfig.js +0 -258
  83. package/src/runtime/types.d.ts +0 -8
  84. /package/src/core/sync/{write_tsconfig_test → write_tsconfig/test-app}/package.json +0 -0
@@ -1,5 +1,6 @@
1
- /** @import { RequestEvent } from '@sveltejs/kit' */
2
- /** @import { PrerenderOption, UniversalNode } from 'types' */
1
+ /** @import { RequestEvent, SSRManifest } from '@sveltejs/kit' */
2
+ /** @import { EnvironmentModuleNode, ErrorPayload, ResolvedConfig, ViteDevServer } from 'vite' */
3
+ /** @import { ManifestData, PrerenderOption, RemoteChunk, ServerModule, SSRNode, UniversalNode, ValidatedConfig } from 'types' */
3
4
  import process from 'node:process';
4
5
  import fs from 'node:fs';
5
6
  import path from 'node:path';
@@ -29,19 +30,21 @@ import {
29
30
  import { SCHEME } from '../../../utils/url.js';
30
31
  import { check_feature } from '../../../utils/features.js';
31
32
  import { escape_html } from '../../../utils/escape.js';
33
+ import { get_runner } from '../../../runner.js';
32
34
 
33
35
  // vite-specifc queries that we should skip handling for css urls
34
36
  const vite_css_query_regex = /(?:\?|&)(?:raw|url|inline)(?:&|$)/;
35
37
 
36
38
  /**
37
- * @param {import('vite').ViteDevServer} vite
38
- * @param {import('vite').ResolvedConfig} vite_config
39
- * @param {import('types').ValidatedConfig} svelte_config
40
- * @param {() => Array<{ hash: string, file: string }>} get_remotes
39
+ * @param {ViteDevServer} vite
40
+ * @param {ResolvedConfig} vite_config
41
+ * @param {ValidatedConfig} svelte_config
42
+ * @param {() => RemoteChunk[]} get_remotes
41
43
  * @param {string} root The project root directory
44
+ * @param {(manifest_data: ManifestData) => void} set_manifest_data
42
45
  * @return {Promise<Promise<() => void>>}
43
46
  */
44
- export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
47
+ export async function dev(vite, vite_config, svelte_config, get_remotes, root, set_manifest_data) {
45
48
  /** @type {AsyncLocalStorage<{ event: RequestEvent, config: any, prerender: PrerenderOption }>} */
46
49
  const async_local_storage = new AsyncLocalStorage();
47
50
 
@@ -70,18 +73,23 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
70
73
 
71
74
  sync.init(svelte_config, root);
72
75
 
73
- /** @type {import('types').ManifestData} */
76
+ /** @type {ManifestData} */
74
77
  let manifest_data;
75
- /** @type {import('@sveltejs/kit').SSRManifest} */
78
+ /** @type {SSRManifest} */
76
79
  let manifest;
77
80
 
78
81
  /** @type {Error | null} */
79
82
  let manifest_error = null;
80
83
 
81
- /** @param {string} url */
84
+ const runner = get_runner(vite);
85
+
86
+ /**
87
+ * @param {string} url
88
+ * @returns {Promise<Record<string, any>>}
89
+ */
82
90
  async function loud_ssr_load_module(url) {
83
91
  try {
84
- return await vite.ssrLoadModule(url, { fixStacktrace: true });
92
+ return await runner.import(url);
85
93
  } catch (/** @type {any} */ err) {
86
94
  const msg = buildErrorMessage(err, [
87
95
  styleText('red', `Internal server error: ${err.message}`)
@@ -92,12 +100,12 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
92
100
  }
93
101
 
94
102
  // 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
103
+ // and when it does appear it may immediately vanish. `vite.hot.send` broadcasts
96
104
  // to all connected clients, even ones that are unaffected by the error.
97
105
  // we need a more considered approach
98
- vite.ws.send({
106
+ vite.hot.send({
99
107
  type: 'error',
100
- err: /** @type {import('vite').ErrorPayload['err']} */ ({
108
+ err: /** @type {ErrorPayload['err']} */ ({
101
109
  ...err,
102
110
  // these properties are non-enumerable and will
103
111
  // not be serialized unless we explicitly include them
@@ -116,7 +124,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
116
124
 
117
125
  const module = await loud_ssr_load_module(url);
118
126
 
119
- const module_node = await vite.moduleGraph.getModuleByUrl(url);
127
+ const module_node = await vite.environments.ssr.moduleGraph.getModuleByUrl(url);
120
128
  if (!module_node) throw new Error(`Could not find node for ${url}`);
121
129
 
122
130
  return { module, module_node, url };
@@ -125,6 +133,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
125
133
  async function update_manifest() {
126
134
  try {
127
135
  ({ manifest_data } = sync.create(svelte_config, root));
136
+ set_manifest_data(manifest_data);
128
137
 
129
138
  await load_and_validate_params({
130
139
  routes: manifest_data.routes,
@@ -135,13 +144,13 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
135
144
 
136
145
  if (manifest_error) {
137
146
  manifest_error = null;
138
- vite.ws.send({ type: 'full-reload' });
147
+ vite.hot.send({ type: 'full-reload' });
139
148
  }
140
149
  } catch (error) {
141
150
  manifest_error = /** @type {Error} */ (error);
142
151
 
143
152
  console.error(styleText(['bold', 'red'], manifest_error.message));
144
- vite.ws.send({
153
+ vite.hot.send({
145
154
  type: 'error',
146
155
  err: {
147
156
  message: manifest_error.message ?? 'Invalid routes',
@@ -203,7 +212,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
203
212
  ),
204
213
  nodes: manifest_data.nodes.map((node, index) => {
205
214
  return async () => {
206
- /** @type {import('types').SSRNode} */
215
+ /** @type {SSRNode} */
207
216
  const result = {};
208
217
  result.index = index;
209
218
  result.universal_id = node.universal;
@@ -214,7 +223,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
214
223
  result.stylesheets = [];
215
224
  result.fonts = [];
216
225
 
217
- /** @type {import('vite').ModuleNode[]} */
226
+ /** @type {EnvironmentModuleNode[]} */
218
227
  const module_nodes = [];
219
228
 
220
229
  if (node.component) {
@@ -248,7 +257,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
248
257
  // in dev we inline all styles to avoid FOUC. this gets populated lazily so that
249
258
  // components/stylesheets loaded via import() during `load` are included
250
259
  result.inline_styles = async () => {
251
- /** @type {Set<import('vite').ModuleNode | import('vite').EnvironmentModuleNode>} */
260
+ /** @type {Set<EnvironmentModuleNode>} */
252
261
  const deps = new Set();
253
262
 
254
263
  for (const module_node of module_nodes) {
@@ -264,7 +273,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
264
273
  ? dep.url.replace('?', '?inline&')
265
274
  : dep.url + '?inline';
266
275
  try {
267
- const mod = await vite.ssrLoadModule(inlineCssUrl);
276
+ const mod = await runner.import(inlineCssUrl);
268
277
  styles[dep.url] = mod.default;
269
278
  } catch {
270
279
  // this can happen with dynamically imported modules, I think
@@ -285,7 +294,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
285
294
  return Object.fromEntries(
286
295
  get_remotes().map((remote) => [
287
296
  remote.hash,
288
- () => vite.ssrLoadModule(remote.file).then((module) => ({ default: module }))
297
+ () => runner.import(remote.file).then((module) => ({ default: module }))
289
298
  ])
290
299
  );
291
300
  },
@@ -314,7 +323,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
314
323
  if (!manifest_data.params) return {};
315
324
 
316
325
  const url = path.resolve(root, manifest_data.params);
317
- const module = await vite.ssrLoadModule(url, { fixStacktrace: true });
326
+ const module = await runner.import(url);
318
327
 
319
328
  if (!module.params) {
320
329
  throw new Error(
@@ -334,48 +343,37 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
334
343
  return;
335
344
  }
336
345
 
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
- }
346
+ let end = 0;
347
+
348
+ error.stack = error.stack
349
+ .replaceAll('\0', '') // remove null bytes from e.g. virtual module IDs, or the response will fail
350
+ .split('\n')
351
+ .map((line, i) => {
352
+ const match = /^ {4}at (?:[^ ]+ \((.+)\)|(.+))$/.exec(line);
353
+ if (!match) {
354
+ end = i + 1;
355
+ return line;
356
+ }
343
357
 
344
- if (error.stack) {
345
- let end = 0;
358
+ const loc = match[1] ?? match[2];
359
+ const file = loc.replace(/:\d+:\d+$/, '');
346
360
 
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) {
361
+ if (fs.existsSync(file)) {
362
+ if (!file.includes('node_modules') && !file.includes(SRC_ROOT)) {
353
363
  end = i + 1;
354
- return line;
355
364
  }
356
365
 
357
- const loc = match[1] ?? match[2];
358
- const file = loc.replace(/:\d+:\d+$/, '');
366
+ return line.replace(file, path.relative(process.cwd(), file));
367
+ }
359
368
 
360
- if (fs.existsSync(file)) {
361
- if (!file.includes('node_modules') && !file.includes(SRC_ROOT)) {
362
- end = i + 1;
363
- }
369
+ return line;
370
+ })
371
+ .slice(0, end)
372
+ .join('\n');
364
373
 
365
- return line.replace(file, path.posix.relative(process.cwd(), file));
366
- }
367
-
368
- return line;
369
- })
370
- .slice(0, end)
371
- .join('\n');
372
-
373
- return error.stack;
374
- }
374
+ return error.stack;
375
375
  }
376
376
 
377
- await update_manifest();
378
-
379
377
  const params_file = resolve_entry(svelte_config.kit.files.params);
380
378
 
381
379
  /**
@@ -386,6 +384,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
386
384
  vite.watcher.on(event, (file) => {
387
385
  if (
388
386
  file.startsWith(svelte_config.kit.files.routes + path.sep) ||
387
+ file.startsWith(svelte_config.kit.files.assets + path.sep) ||
389
388
  (params_file && file === params_file) ||
390
389
  remote_module_pattern.test(file) ||
391
390
  // in contrast to server hooks, client hooks are written to the client manifest
@@ -413,6 +412,11 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
413
412
  watch('add', () => debounce(update_manifest));
414
413
  watch('unlink', () => debounce(update_manifest));
415
414
  watch('change', (file) => {
415
+ // `manifest_data` is populated lazily on the first request (see `update_manifest`
416
+ // call in the middleware below), so it may still be undefined if a file changes
417
+ // before the dev server has served a request. In that case there's nothing to
418
+ // update — the manifest will be created from scratch on the first request.
419
+ if (!manifest_data) return;
416
420
  // Don't run for a single file if the whole manifest is about to get updated
417
421
  // Unless it's a file where the trailing slash page option might have changed
418
422
  if (timeout || !/\+(page|layout|server).*$/.test(file)) return;
@@ -427,7 +431,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
427
431
  if (appTemplate !== 'index.html') {
428
432
  vite.watcher.on('change', (file) => {
429
433
  if (file === appTemplate) {
430
- vite.ws.send({ type: 'full-reload' });
434
+ vite.hot.send({ type: 'full-reload' });
431
435
  }
432
436
  });
433
437
  }
@@ -477,6 +481,9 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
477
481
  const env = loadEnv(vite_config.mode, svelte_config.kit.env.dir, '');
478
482
  const emulator = await svelte_config.kit.adapter?.emulate?.();
479
483
 
484
+ /** @type {Promise<void> | undefined} */
485
+ let init_manifest;
486
+
480
487
  return () => {
481
488
  const serve_static_middleware = vite.middlewares.stack.find(
482
489
  (middleware) =>
@@ -488,6 +495,11 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
488
495
  remove_static_middlewares(vite.middlewares);
489
496
 
490
497
  vite.middlewares.use(async (req, res) => {
498
+ // Vite throws a Cannot read properties of undefined (reading 'wrapDynamicImport')
499
+ // if you try to run ssr.runner.import before the server has started so
500
+ // we do it inside here to avoid that
501
+ await (init_manifest ??= update_manifest());
502
+
491
503
  // Vite's base middleware strips out the base path. Restore it
492
504
  const original_url = req.url;
493
505
  req.url = req.originalUrl;
@@ -536,27 +548,36 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
536
548
  return;
537
549
  }
538
550
 
551
+ // resolve the instrumentation file per request so that changes to it
552
+ // are picked up on new requests
539
553
  const resolved_instrumentation = resolve_entry(
540
554
  path.join(svelte_config.kit.files.src, 'instrumentation.server')
541
555
  );
542
556
 
543
557
  if (resolved_instrumentation) {
544
- await vite.ssrLoadModule(resolved_instrumentation);
558
+ if (
559
+ svelte_config.kit.adapter &&
560
+ !svelte_config.kit.adapter.supports?.instrumentation?.()
561
+ ) {
562
+ throw new Error(
563
+ `${resolved_instrumentation} is unsupported in ${svelte_config.kit.adapter.name}.`
564
+ );
565
+ }
566
+
567
+ await runner.import(resolved_instrumentation);
545
568
  }
546
569
 
547
570
  // we have to import `Server` before calling `set_assets`
548
- const { Server } = /** @type {import('types').ServerModule} */ (
549
- await vite.ssrLoadModule(`${get_runtime_base(root)}/server/index.js`, {
550
- fixStacktrace: true
551
- })
571
+ const { Server } = /** @type {ServerModule} */ (
572
+ await runner.import(`${get_runtime_base(root)}/server/index.js`)
552
573
  );
553
574
 
554
- const { set_fix_stack_trace } = await vite.ssrLoadModule(
575
+ const { set_fix_stack_trace } = await runner.import(
555
576
  `${get_runtime_base(root)}/server/internal.js`
556
577
  );
557
578
  set_fix_stack_trace(fix_stack_trace);
558
579
 
559
- const { set_assets } = await vite.ssrLoadModule('$app/paths/internal/server');
580
+ const { set_assets } = await runner.import('$app/paths/internal/server');
560
581
  set_assets(assets);
561
582
 
562
583
  const server = new Server(manifest);
@@ -628,6 +649,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
628
649
  const error = coalesce_to_error(e);
629
650
  res.statusCode = 500;
630
651
  fix_stack_trace(error);
652
+ console.error(styleText(['bold', 'red'], String(error)));
631
653
  res.end(error.stack || error.message); // handle `stackless` errors
632
654
  }
633
655
  });
@@ -648,17 +670,17 @@ function remove_static_middlewares(server) {
648
670
  }
649
671
 
650
672
  /**
651
- * @param {import('vite').ViteDevServer} vite
652
- * @param {import('vite').ModuleNode | import('vite').EnvironmentModuleNode} node
653
- * @param {Set<import('vite').ModuleNode | import('vite').EnvironmentModuleNode>} deps
673
+ * @param {ViteDevServer} vite
674
+ * @param {EnvironmentModuleNode} node
675
+ * @param {Set<EnvironmentModuleNode>} deps
654
676
  */
655
677
  async function find_deps(vite, node, deps) {
656
- // since `ssrTransformResult.deps` contains URLs instead of `ModuleNode`s, this process is asynchronous.
678
+ // since `transformResult.deps` contains URLs instead of `ModuleNode`s, this process is asynchronous.
657
679
  // instead of using `await`, we resolve all branches in parallel.
658
680
  /** @type {Promise<void>[]} */
659
681
  const branches = [];
660
682
 
661
- /** @param {import('vite').ModuleNode | import('vite').EnvironmentModuleNode} node */
683
+ /** @param {EnvironmentModuleNode} node */
662
684
  async function add(node) {
663
685
  if (!deps.has(node)) {
664
686
  deps.add(node);
@@ -668,23 +690,20 @@ async function find_deps(vite, node, deps) {
668
690
 
669
691
  /** @param {string} url */
670
692
  async function add_by_url(url) {
671
- const node = await get_server_module_by_url(vite, url);
693
+ const node = await vite.environments.ssr.moduleGraph.getModuleByUrl(url);
672
694
 
673
695
  if (node) {
674
696
  await add(node);
675
697
  }
676
698
  }
677
699
 
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)));
700
+ if (node.transformResult) {
701
+ if (node.transformResult.deps) {
702
+ node.transformResult.deps.forEach((url) => branches.push(add_by_url(url)));
684
703
  }
685
704
 
686
- if (transform_result.dynamicDeps) {
687
- transform_result.dynamicDeps.forEach((url) => branches.push(add_by_url(url)));
705
+ if (node.transformResult.dynamicDeps) {
706
+ node.transformResult.dynamicDeps.forEach((url) => branches.push(add_by_url(url)));
688
707
  }
689
708
  } else {
690
709
  node.importedModules.forEach((node) => branches.push(add(node)));
@@ -693,16 +712,6 @@ async function find_deps(vite, node, deps) {
693
712
  await Promise.all(branches);
694
713
  }
695
714
 
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
715
  /**
707
716
  * Determine if a file is being requested with the correct case,
708
717
  * to ensure consistent behaviour between dev and prod and across
@@ -723,3 +732,19 @@ function has_correct_case(file, assets) {
723
732
 
724
733
  return false;
725
734
  }
735
+
736
+ /**
737
+ * Invalidates a module in all environments.
738
+ * @param {ViteDevServer} server
739
+ * @param {string} id
740
+ * @returns {void}
741
+ */
742
+ export function invalidate_module(server, id) {
743
+ for (const environment in server.environments) {
744
+ const module = server.environments[environment].moduleGraph.getModuleById(id);
745
+ if (module) {
746
+ server.environments[environment].moduleGraph.invalidateModule(module);
747
+ void server.environments[environment].reloadModule(module);
748
+ }
749
+ }
750
+ }