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

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 (144) hide show
  1. package/package.json +33 -21
  2. package/src/constants.js +2 -0
  3. package/src/core/adapt/builder.js +32 -49
  4. package/src/core/adapt/index.js +1 -4
  5. package/src/core/config/index.js +137 -71
  6. package/src/core/config/options.js +236 -244
  7. package/src/core/config/types.d.ts +1 -1
  8. package/src/core/env.js +121 -5
  9. package/src/core/generate_manifest/index.js +12 -15
  10. package/src/core/postbuild/analyse.js +7 -19
  11. package/src/core/postbuild/crawl.js +22 -6
  12. package/src/core/postbuild/fallback.js +2 -1
  13. package/src/core/postbuild/prerender.js +114 -48
  14. package/src/core/postbuild/queue.js +2 -1
  15. package/src/core/sync/create_manifest_data/index.js +53 -50
  16. package/src/core/sync/sync.js +0 -2
  17. package/src/core/sync/write_client_manifest.js +8 -15
  18. package/src/core/sync/write_env.js +2 -1
  19. package/src/core/sync/write_non_ambient.js +12 -9
  20. package/src/core/sync/write_server.js +14 -15
  21. package/src/core/sync/write_tsconfig.js +16 -8
  22. package/src/core/sync/write_tsconfig_test/package.json +7 -0
  23. package/src/core/sync/write_types/index.js +28 -24
  24. package/src/core/utils.js +30 -5
  25. package/src/exports/env/index.js +77 -0
  26. package/src/exports/hooks/index.js +3 -9
  27. package/src/exports/hooks/sequence.js +3 -2
  28. package/src/exports/index.js +35 -13
  29. package/src/exports/internal/client.js +5 -0
  30. package/src/exports/internal/env.js +8 -5
  31. package/src/exports/internal/index.js +1 -90
  32. package/src/exports/internal/{event.js → server/event.js} +1 -2
  33. package/src/exports/internal/server/index.js +33 -0
  34. package/src/exports/internal/shared.js +89 -0
  35. package/src/exports/node/index.js +62 -15
  36. package/src/exports/params.js +68 -0
  37. package/src/exports/public.d.ts +363 -189
  38. package/src/exports/url.js +86 -0
  39. package/src/exports/vite/build/build_server.js +53 -59
  40. package/src/exports/vite/build/remote.js +18 -11
  41. package/src/exports/vite/build/utils.js +0 -8
  42. package/src/exports/vite/dev/index.js +92 -50
  43. package/src/exports/vite/index.js +903 -681
  44. package/src/exports/vite/module_ids.js +0 -2
  45. package/src/exports/vite/preview/index.js +32 -24
  46. package/src/exports/vite/static_analysis/index.js +2 -4
  47. package/src/exports/vite/static_analysis/types.d.ts +14 -0
  48. package/src/exports/vite/utils.js +40 -35
  49. package/src/runtime/app/env/internal.js +4 -4
  50. package/src/runtime/app/env/types.d.ts +1 -1
  51. package/src/runtime/app/environment/index.js +3 -3
  52. package/src/runtime/app/forms.js +24 -7
  53. package/src/runtime/app/paths/client.js +4 -10
  54. package/src/runtime/app/paths/internal/client.js +4 -2
  55. package/src/runtime/app/paths/internal/server.js +2 -23
  56. package/src/runtime/app/paths/public.d.ts +0 -28
  57. package/src/runtime/app/paths/server.js +10 -6
  58. package/src/runtime/app/server/index.js +1 -1
  59. package/src/runtime/app/server/remote/command.js +0 -3
  60. package/src/runtime/app/server/remote/form.js +27 -15
  61. package/src/runtime/app/server/remote/prerender.js +29 -36
  62. package/src/runtime/app/server/remote/query.js +101 -99
  63. package/src/runtime/app/server/remote/requested.js +22 -14
  64. package/src/runtime/app/server/remote/shared.js +30 -26
  65. package/src/runtime/app/state/client.js +1 -2
  66. package/src/runtime/app/stores.js +13 -76
  67. package/src/runtime/client/bundle.js +1 -1
  68. package/src/runtime/client/client-entry.js +3 -0
  69. package/src/runtime/client/client.js +411 -318
  70. package/src/runtime/client/entry.js +24 -3
  71. package/src/runtime/client/fetcher.js +3 -2
  72. package/src/runtime/client/ndjson.js +6 -33
  73. package/src/runtime/client/payload.js +17 -0
  74. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  75. package/src/runtime/client/remote-functions/command.svelte.js +7 -32
  76. package/src/runtime/client/remote-functions/form.svelte.js +183 -112
  77. package/src/runtime/client/remote-functions/prerender.svelte.js +29 -8
  78. package/src/runtime/client/remote-functions/query/index.js +7 -14
  79. package/src/runtime/client/remote-functions/query/instance.svelte.js +63 -18
  80. package/src/runtime/client/remote-functions/query/proxy.js +3 -3
  81. package/src/runtime/client/remote-functions/query-batch.svelte.js +60 -68
  82. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +60 -18
  83. package/src/runtime/client/remote-functions/query-live/iterator.js +36 -55
  84. package/src/runtime/client/remote-functions/shared.svelte.js +88 -62
  85. package/src/runtime/client/sse.js +32 -0
  86. package/src/runtime/client/state.svelte.js +65 -49
  87. package/src/runtime/client/stream.js +39 -0
  88. package/src/runtime/client/types.d.ts +11 -7
  89. package/src/runtime/client/utils.js +0 -96
  90. package/src/runtime/components/root.svelte +66 -0
  91. package/src/runtime/env/dynamic/private.js +7 -0
  92. package/src/runtime/env/dynamic/public.js +7 -0
  93. package/src/runtime/env/static/private.js +6 -0
  94. package/src/runtime/env/static/public.js +6 -0
  95. package/src/runtime/form-utils.js +100 -22
  96. package/src/runtime/server/cookie.js +69 -52
  97. package/src/runtime/server/csrf.js +65 -0
  98. package/src/runtime/server/data/index.js +15 -19
  99. package/src/runtime/server/env_module.js +0 -5
  100. package/src/runtime/server/errors.js +135 -0
  101. package/src/runtime/server/fetch.js +1 -1
  102. package/src/runtime/server/index.js +22 -12
  103. package/src/runtime/server/internal.js +25 -0
  104. package/src/runtime/server/page/actions.js +43 -27
  105. package/src/runtime/server/page/data_serializer.js +10 -10
  106. package/src/runtime/server/page/index.js +12 -17
  107. package/src/runtime/server/page/load_data.js +2 -2
  108. package/src/runtime/server/page/render.js +114 -193
  109. package/src/runtime/server/page/respond_with_error.js +11 -11
  110. package/src/runtime/server/page/server_routing.js +27 -18
  111. package/src/runtime/server/remote-functions.js +596 -0
  112. package/src/runtime/server/respond.js +100 -68
  113. package/src/runtime/server/sourcemaps.js +183 -0
  114. package/src/runtime/server/utils.js +2 -134
  115. package/src/runtime/shared.js +83 -13
  116. package/src/runtime/telemetry/otel.js +1 -1
  117. package/src/runtime/types.d.ts +8 -0
  118. package/src/types/ambient-private.d.ts +2 -0
  119. package/src/types/ambient.d.ts +10 -5
  120. package/src/types/global-private.d.ts +13 -25
  121. package/src/types/internal.d.ts +95 -80
  122. package/src/types/private.d.ts +41 -1
  123. package/src/utils/error.js +24 -4
  124. package/src/utils/escape.js +9 -25
  125. package/src/utils/features.js +1 -1
  126. package/src/utils/fork.js +7 -2
  127. package/src/utils/import.js +6 -1
  128. package/src/utils/imports.js +83 -0
  129. package/src/utils/mime.js +9 -0
  130. package/src/utils/page_nodes.js +3 -5
  131. package/src/utils/params.js +66 -0
  132. package/src/utils/routing.js +90 -44
  133. package/src/utils/shared-iterator.js +5 -0
  134. package/src/utils/streaming.js +14 -4
  135. package/src/utils/url.js +20 -2
  136. package/src/version.js +1 -1
  137. package/types/index.d.ts +537 -557
  138. package/types/index.d.ts.map +24 -39
  139. package/src/core/sync/write_root.js +0 -148
  140. package/src/exports/internal/server.js +0 -22
  141. package/src/runtime/server/remote.js +0 -457
  142. package/src/runtime/shared-server.js +0 -7
  143. package/src/types/synthetic/$lib.md +0 -5
  144. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
@@ -0,0 +1,86 @@
1
+ import { DEV } from 'esm-env';
2
+ // we use the export subpath to conditionally import the client/server `get_origin`
3
+ // so that `node:async_hooks` isn't pulled into the client build
4
+ import { get_origin } from '#internal';
5
+ import { matches_external_allowlist_entry } from '../utils/url.js';
6
+
7
+ // See https://datatracker.ietf.org/doc/html/rfc2606 - no domains under the .invalid TLD can be registered
8
+ const REDIRECT_BASE = 'https://sveltekit-redirect.invalid';
9
+
10
+ /**
11
+ * Whether a redirect location is absolute, i.e. not a root-relative or path-relative URL,
12
+ * and not pointing to the same origin as we're currently on (if determineable).
13
+ * @param {string} location
14
+ */
15
+ export function is_external_location(location) {
16
+ const origin = get_origin();
17
+
18
+ try {
19
+ return !matches_external_allowlist_entry(location, origin ?? REDIRECT_BASE);
20
+ } catch {
21
+ return true;
22
+ }
23
+ }
24
+
25
+ const javascript_protocols = new Set(['javascript:', 'data:']);
26
+
27
+ /**
28
+ * @param {string} location
29
+ */
30
+ function is_javascript_location(location) {
31
+ try {
32
+ return javascript_protocols.has(new URL(location, REDIRECT_BASE).protocol);
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * @param {string} location
40
+ * @param {{ external?: boolean | string[] }} [options]
41
+ */
42
+ export function validate_redirect_location(location, options) {
43
+ if (!is_external_location(location)) return;
44
+
45
+ const external = options?.external;
46
+
47
+ if (!external) {
48
+ throw new Error(
49
+ DEV
50
+ ? `Cannot redirect to external URL ${JSON.stringify(location)}. ` +
51
+ 'To redirect to an external URL, pass `{ external: true }` or an allowlist of permitted origins as the third argument to `redirect`'
52
+ : 'Cannot redirect to external URL unless explicitly allowed'
53
+ );
54
+ }
55
+
56
+ if (external === true) {
57
+ if (is_javascript_location(location)) {
58
+ throw new Error(
59
+ DEV
60
+ ? `Cannot redirect to ${JSON.stringify(location)} with \`{ external: true }\`. ` +
61
+ 'The `javascript:` and `data:` protocols must be explicitly listed in the `external` allowlist'
62
+ : 'Cannot redirect to external URL unless explicitly allowed'
63
+ );
64
+ }
65
+
66
+ return;
67
+ }
68
+
69
+ if (Array.isArray(external)) {
70
+ if (!external.some((allowed) => matches_external_allowlist_entry(location, allowed))) {
71
+ throw new Error(
72
+ DEV
73
+ ? `Cannot redirect to ${JSON.stringify(location)}: URL origin is not included in the \`external\` allowlist`
74
+ : 'Cannot redirect to external URL unless explicitly allowed'
75
+ );
76
+ }
77
+
78
+ return;
79
+ }
80
+
81
+ throw new Error(
82
+ DEV
83
+ ? '`redirect` options.external must be `true` or an array of allowed origins'
84
+ : 'Invalid redirect options.external value'
85
+ );
86
+ }
@@ -16,39 +16,16 @@ import { fix_css_urls } from '../../../utils/css.js';
16
16
  import { escape_for_interpolation } from '../../../utils/escape.js';
17
17
 
18
18
  /**
19
- * @overload Build without the client manifest so we can analyse the nodes.
20
19
  * @param {string} out
21
20
  * @param {ValidatedKitConfig} kit
22
21
  * @param {ManifestData} manifest_data
23
22
  * @param {Manifest} server_manifest
24
- * @param {null} client_manifest
25
- * @param {null} assets_path
26
- * @param {null} client_chunks
27
- * @param {string} root
28
- * @returns {void}
29
- */
30
- /**
31
- * @overload Or build with the client manifest
32
- * @param {string} out
33
- * @param {ValidatedKitConfig} kit
34
- * @param {ManifestData} manifest_data
35
- * @param {Manifest} server_manifest
36
- * @param {Manifest} client_manifest
23
+ * @param {Manifest | null} client_manifest
37
24
  * @param {string} assets_path
38
- * @param {Rolldown.RolldownOutput['output']} client_chunks
25
+ * @param {(Rolldown.OutputAsset | Rolldown.OutputChunk)[]} chunks
39
26
  * @param {string} root
40
27
  * @returns {void}
41
28
  */
42
- /**
43
- * @param {string} out
44
- * @param {ValidatedKitConfig} kit
45
- * @param {ManifestData} manifest_data
46
- * @param {Manifest} server_manifest
47
- * @param {Manifest | null} client_manifest
48
- * @param {string | null} assets_path
49
- * @param {Rolldown.RolldownOutput['output'] | null} client_chunks
50
- * @param {string} root
51
- */
52
29
  export function build_server_nodes(
53
30
  out,
54
31
  kit,
@@ -56,7 +33,7 @@ export function build_server_nodes(
56
33
  server_manifest,
57
34
  client_manifest,
58
35
  assets_path,
59
- client_chunks,
36
+ chunks,
60
37
  root
61
38
  ) {
62
39
  mkdirp(`${out}/server/nodes`);
@@ -75,8 +52,8 @@ export function build_server_nodes(
75
52
  */
76
53
  let prepare_css_for_inlining = (css) => s(css);
77
54
 
78
- if (client_chunks && kit.inlineStyleThreshold > 0 && kit.output.bundleStrategy === 'split') {
79
- for (const chunk of client_chunks) {
55
+ if (chunks && kit.inlineStyleThreshold > 0 && kit.output.bundleStrategy === 'split') {
56
+ for (const chunk of chunks) {
80
57
  if (chunk.type !== 'asset' || !chunk.fileName.endsWith('.css')) {
81
58
  continue;
82
59
  }
@@ -132,6 +109,9 @@ export function build_server_nodes(
132
109
  }
133
110
  }
134
111
 
112
+ /** path to the `.svelte-kit` directory */
113
+ const out_dir = normalizePath(kit.outDir);
114
+
135
115
  for (let i = 0; i < manifest_data.nodes.length; i++) {
136
116
  const node = manifest_data.nodes[i];
137
117
 
@@ -155,7 +135,11 @@ export function build_server_nodes(
155
135
  /** @type {Set<string>} */
156
136
  const eager_assets = new Set();
157
137
 
158
- if (node.component && client_manifest) {
138
+ const uses_server_component = node.child_pages
139
+ ? node.child_pages.some((child) => child.page_options?.ssr !== false)
140
+ : node.page_options?.ssr !== false;
141
+
142
+ if (node.component && uses_server_component) {
159
143
  exports.push(
160
144
  'let component_cache;',
161
145
  `export const component = async () => component_cache ??= (await import('../${
@@ -165,7 +149,12 @@ export function build_server_nodes(
165
149
  }
166
150
 
167
151
  if (node.universal) {
168
- if (!!node.page_options && node.page_options.ssr === false) {
152
+ if (
153
+ (kit.router.type === 'hash' &&
154
+ node.page_options !== null &&
155
+ node.page_options?.ssr === undefined) ||
156
+ (node.page_options && node.page_options.ssr === false)
157
+ ) {
169
158
  exports.push(`export const universal = ${s(node.page_options, null, 2)};`);
170
159
  } else {
171
160
  imports.push(
@@ -185,18 +174,7 @@ export function build_server_nodes(
185
174
  exports.push(`export const server_id = ${s(node.server)};`);
186
175
  }
187
176
 
188
- if (
189
- client_manifest &&
190
- (node.universal || node.component) &&
191
- kit.output.bundleStrategy === 'split'
192
- ) {
193
- const entry_path = `${normalizePath(kit.outDir)}/generated/client-optimized/nodes/${i}.js`;
194
- const entry = find_deps(client_manifest, entry_path, true, root);
195
-
196
- // Eagerly load client stylesheets and fonts imported by the SSR-ed page to avoid FOUC.
197
- // However, if it is not used during SSR (not present in the server manifest),
198
- // then it can be lazily loaded in the browser.
199
-
177
+ if ((node.universal || node.component) && kit.output.bundleStrategy === 'split') {
200
178
  /** @type {AssetDependencies | undefined} */
201
179
  let component;
202
180
  if (node.component) {
@@ -209,25 +187,41 @@ export function build_server_nodes(
209
187
  universal = find_deps(server_manifest, node.universal, true, root);
210
188
  }
211
189
 
212
- /** @type {Set<string>} */
213
- const eager_css = new Set();
190
+ if (client_manifest) {
191
+ const entry_path = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
192
+ const entry = find_deps(client_manifest, entry_path, true, root);
214
193
 
215
- entry.stylesheet_map.forEach((value, filepath) => {
216
- // pages and layouts are renamed to node indexes when optimised for the client
217
- // so we use the original filename instead to check against the server manifest
218
- if (filepath === entry_path) {
219
- filepath = node.component ?? filepath;
220
- }
194
+ // Eagerly load client stylesheets and fonts imported by the SSR-ed page to avoid FOUC.
195
+ // However, if it is not used during SSR (not present in the server manifest),
196
+ // then it can be lazily loaded in the browser.
221
197
 
222
- if (component?.stylesheet_map.has(filepath) || universal?.stylesheet_map.has(filepath)) {
223
- value.css.forEach((file) => eager_css.add(file));
224
- value.assets.forEach((file) => eager_assets.add(file));
225
- }
226
- });
198
+ /** @type {Set<string>} */
199
+ const eager_css = new Set();
200
+
201
+ entry.stylesheet_map.forEach((value, filepath) => {
202
+ // pages and layouts are renamed to node indexes when optimised for the client
203
+ // so we use the original filename instead to check against the server manifest
204
+ if (filepath === entry_path) {
205
+ filepath = node.component ?? filepath;
206
+ }
207
+
208
+ if (component?.stylesheet_map.has(filepath) || universal?.stylesheet_map.has(filepath)) {
209
+ value.css.forEach((file) => eager_css.add(file));
210
+ value.assets.forEach((file) => eager_assets.add(file));
211
+ }
212
+ });
227
213
 
228
- imported = entry.imports;
229
- stylesheets = Array.from(eager_css);
230
- fonts = filter_fonts(Array.from(eager_assets));
214
+ imported = entry.imports;
215
+ stylesheets = Array.from(eager_css);
216
+ fonts = filter_fonts(Array.from(eager_assets));
217
+ } else {
218
+ for (const entry of [component, universal]) {
219
+ if (!entry) continue;
220
+ imported.push(...entry.imports);
221
+ stylesheets.push(...entry.stylesheets);
222
+ fonts.push(...entry.fonts);
223
+ }
224
+ }
231
225
  }
232
226
 
233
227
  exports.push(
@@ -244,7 +238,7 @@ export function build_server_nodes(
244
238
 
245
239
  // Keep track of Vite asset filenames so that we avoid touching unrelated ones
246
240
  // when adjusting the inlined CSS
247
- if (stylesheets_to_inline.size && assets_path && eager_assets.size) {
241
+ if (stylesheets_to_inline.size && eager_assets.size) {
248
242
  vite_assets = new Set(
249
243
  Array.from(eager_assets).map((asset) => {
250
244
  return decodeURIComponent(asset.replace(`${assets_path}/`, ''));
@@ -13,7 +13,7 @@ import { posixify } from '../../../utils/os.js';
13
13
  * @param {Array<{ hash: string, file: string }>} remotes
14
14
  * @param {ServerMetadata} metadata
15
15
  * @param {string} cwd
16
- * @param {Rolldown.RolldownOutput} server_bundle
16
+ * @param {(Rolldown.OutputAsset | Rolldown.OutputChunk)[]} server_chunks
17
17
  * @param {NonNullable<import('vitest/config').ViteUserConfig['build']>['sourcemap']} sourcemap
18
18
  */
19
19
  export async function treeshake_prerendered_remotes(
@@ -22,11 +22,14 @@ export async function treeshake_prerendered_remotes(
22
22
  remotes,
23
23
  metadata,
24
24
  cwd,
25
- server_bundle,
25
+ server_chunks,
26
26
  sourcemap
27
27
  ) {
28
28
  if (remotes.length === 0) return;
29
29
 
30
+ /** @type {string[]} */
31
+ const chunk_paths = [];
32
+
30
33
  for (const remote of remotes) {
31
34
  const exports_map = metadata.remotes.get(remote.hash);
32
35
  if (!exports_map) continue;
@@ -45,7 +48,7 @@ export async function treeshake_prerendered_remotes(
45
48
  // remove file extension
46
49
  const remote_filename = path.basename(remote.file).split('.').slice(0, -1).join('.');
47
50
 
48
- const remote_chunk = Object.values(server_bundle).find((chunk) => {
51
+ const remote_chunk = server_chunks.find((chunk) => {
49
52
  return chunk.name === remote_filename;
50
53
  });
51
54
 
@@ -88,8 +91,13 @@ export async function treeshake_prerendered_remotes(
88
91
 
89
92
  const stubbed = modified_code.toString();
90
93
  fs.writeFileSync(chunk_path, stubbed);
94
+ chunk_paths.push(chunk_path);
95
+ }
96
+
97
+ if (!chunk_paths.length) return;
91
98
 
92
- const bundle = /** @type {import('vite').Rolldown.RolldownOutput} */ (
99
+ for (const chunk_path of chunk_paths) {
100
+ const bundle = /** @type {Rolldown.RolldownOutput} */ (
93
101
  await vite.build({
94
102
  configFile: false,
95
103
  build: {
@@ -97,7 +105,7 @@ export async function treeshake_prerendered_remotes(
97
105
  ssr: true,
98
106
  target: 'esnext',
99
107
  sourcemap,
100
- rollupOptions: {
108
+ rolldownOptions: {
101
109
  // avoid resolving imports
102
110
  external: (id) => !id.endsWith(chunk_path),
103
111
  input: {
@@ -108,14 +116,13 @@ export async function treeshake_prerendered_remotes(
108
116
  })
109
117
  );
110
118
 
111
- const chunk = bundle.output.find(
112
- (output) => output.type === 'chunk' && output.name === 'treeshaken'
113
- );
114
- if (chunk && chunk.type === 'chunk') {
115
- fs.writeFileSync(chunk_path, chunk.code);
119
+ for (const output of bundle.output) {
120
+ if (output.type !== 'chunk' || output.name !== 'treeshaken') return;
121
+
122
+ fs.writeFileSync(chunk_path, output.code);
116
123
 
117
124
  const chunk_sourcemap = bundle.output.find(
118
- (output) => output.type === 'asset' && output.fileName === chunk.fileName + '.map'
125
+ (o) => o.type === 'asset' && o.fileName === output.fileName + '.map'
119
126
  );
120
127
  if (chunk_sourcemap && chunk_sourcemap.type === 'asset') {
121
128
  fs.writeFileSync(chunk_path + '.map', chunk_sourcemap.source);
@@ -124,14 +124,6 @@ export function filter_fonts(assets) {
124
124
  return assets.filter((asset) => /\.(woff2?|ttf|otf)$/.test(asset));
125
125
  }
126
126
 
127
- /**
128
- * @param {import('types').ValidatedKitConfig} config
129
- * @returns {string}
130
- */
131
- export function assets_base(config) {
132
- return (config.paths.assets || config.paths.base || '.') + '/';
133
- }
134
-
135
127
  /**
136
128
  * Writes a function with arguments used by a template literal.
137
129
  * This helps us store strings in a module and inject values at runtime.
@@ -1,5 +1,6 @@
1
1
  /** @import { RequestEvent } from '@sveltejs/kit' */
2
2
  /** @import { PrerenderOption, UniversalNode } from 'types' */
3
+ import process from 'node:process';
3
4
  import fs from 'node:fs';
4
5
  import path from 'node:path';
5
6
  import { URL } from 'node:url';
@@ -10,14 +11,21 @@ import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite';
10
11
  import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js';
11
12
  import { coalesce_to_error } from '../../../utils/error.js';
12
13
  import { resolve_entry } from '../../../utils/filesystem.js';
14
+ import { load_and_validate_params } from '../../../utils/params.js';
13
15
  import { from_fs, to_fs } from '../../../utils/vite.js';
14
16
  import { posixify } from '../../../utils/os.js';
15
17
  import { load_error_page } from '../../../core/config/index.js';
16
- import { SVELTE_KIT_ASSETS } from '../../../constants.js';
18
+ import { SRC_ROOT, SVELTE_KIT_ASSETS } from '../../../constants.js';
17
19
  import * as sync from '../../../core/sync/sync.js';
18
20
  import { get_mime_lookup, get_runtime_base } from '../../../core/utils.js';
21
+ import '../../../utils/mime.js'; // extend mrmime with additional types (affects sirv too)
19
22
  import { compact } from '../../../utils/array.js';
20
- import { is_chrome_devtools_request, not_found } from '../utils.js';
23
+ import {
24
+ is_chrome_devtools_request,
25
+ log_response,
26
+ not_found,
27
+ remote_module_pattern
28
+ } from '../utils.js';
21
29
  import { SCHEME } from '../../../utils/url.js';
22
30
  import { check_feature } from '../../../utils/features.js';
23
31
  import { escape_html } from '../../../utils/escape.js';
@@ -31,10 +39,9 @@ const vite_css_query_regex = /(?:\?|&)(?:raw|url|inline)(?:&|$)/;
31
39
  * @param {import('types').ValidatedConfig} svelte_config
32
40
  * @param {() => Array<{ hash: string, file: string }>} get_remotes
33
41
  * @param {string} root The project root directory
34
- * @param {import('@sveltejs/kit').Adapter | undefined} adapter
35
42
  * @return {Promise<Promise<() => void>>}
36
43
  */
37
- export async function dev(vite, vite_config, svelte_config, get_remotes, root, adapter) {
44
+ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
38
45
  /** @type {AsyncLocalStorage<{ event: RequestEvent, config: any, prerender: PrerenderOption }>} */
39
46
  const async_local_storage = new AsyncLocalStorage();
40
47
 
@@ -42,7 +49,12 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
42
49
  const context = async_local_storage.getStore();
43
50
  if (!context || context.prerender === true) return;
44
51
 
45
- check_feature(/** @type {string} */ (context.event.route.id), context.config, label, adapter);
52
+ check_feature(
53
+ /** @type {string} */ (context.event.route.id),
54
+ context.config,
55
+ label,
56
+ svelte_config.kit.adapter
57
+ );
46
58
  };
47
59
 
48
60
  const fetch = globalThis.fetch;
@@ -79,6 +91,10 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
79
91
  vite.config.logger.error(msg, { error: err });
80
92
  }
81
93
 
94
+ // 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
96
+ // to all connected clients, even ones that are unaffected by the error.
97
+ // we need a more considered approach
82
98
  vite.ws.send({
83
99
  type: 'error',
84
100
  err: /** @type {import('vite').ErrorPayload['err']} */ ({
@@ -106,10 +122,17 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
106
122
  return { module, module_node, url };
107
123
  }
108
124
 
109
- function update_manifest() {
125
+ async function update_manifest() {
110
126
  try {
111
127
  ({ manifest_data } = sync.create(svelte_config, root));
112
128
 
129
+ await load_and_validate_params({
130
+ routes: manifest_data.routes,
131
+ params_path: manifest_data.params,
132
+ root,
133
+ load: (file) => loud_ssr_load_module(file)
134
+ });
135
+
113
136
  if (manifest_error) {
114
137
  manifest_error = null;
115
138
  vite.ws.send({ type: 'full-reload' });
@@ -288,22 +311,18 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
288
311
  })
289
312
  ),
290
313
  matchers: async () => {
291
- /** @type {Record<string, import('@sveltejs/kit').ParamMatcher>} */
292
- const matchers = {};
293
-
294
- for (const key in manifest_data.matchers) {
295
- const file = manifest_data.matchers[key];
296
- const url = path.resolve(root, file);
297
- const module = await vite.ssrLoadModule(url, { fixStacktrace: true });
298
-
299
- if (module.match) {
300
- matchers[key] = module.match;
301
- } else {
302
- throw new Error(`${file} does not export a \`match\` function`);
303
- }
314
+ if (!manifest_data.params) return {};
315
+
316
+ const url = path.resolve(root, manifest_data.params);
317
+ const module = await vite.ssrLoadModule(url, { fixStacktrace: true });
318
+
319
+ if (!module.params) {
320
+ throw new Error(
321
+ `${manifest_data.params} does not export \`params\` from \`defineParams\``
322
+ );
304
323
  }
305
324
 
306
- return matchers;
325
+ return module.params;
307
326
  }
308
327
  }
309
328
  };
@@ -311,16 +330,53 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
311
330
 
312
331
  /** @param {Error} error */
313
332
  function fix_stack_trace(error) {
333
+ if (!error.stack) {
334
+ return;
335
+ }
336
+
314
337
  try {
315
338
  vite.ssrFixStacktrace(error);
316
339
  } catch {
317
340
  // ssrFixStacktrace can fail on StackBlitz web containers and we don't know why
318
341
  // by ignoring it the line numbers are wrong, but at least we can show the error
319
342
  }
320
- return error.stack?.replaceAll('\0', ''); // remove null bytes from e.g. virtual module IDs, or the response will fail
343
+
344
+ if (error.stack) {
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
+ }
356
+
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
+ }
367
+
368
+ return line;
369
+ })
370
+ .slice(0, end)
371
+ .join('\n');
372
+
373
+ return error.stack;
374
+ }
321
375
  }
322
376
 
323
- update_manifest();
377
+ await update_manifest();
378
+
379
+ const params_file = resolve_entry(svelte_config.kit.files.params);
324
380
 
325
381
  /**
326
382
  * @param {string} event
@@ -330,8 +386,8 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
330
386
  vite.watcher.on(event, (file) => {
331
387
  if (
332
388
  file.startsWith(svelte_config.kit.files.routes + path.sep) ||
333
- file.startsWith(svelte_config.kit.files.params + path.sep) ||
334
- svelte_config.kit.moduleExtensions.some((ext) => file.endsWith(`.remote${ext}`)) ||
389
+ (params_file && file === params_file) ||
390
+ remote_module_pattern.test(file) ||
335
391
  // in contrast to server hooks, client hooks are written to the client manifest
336
392
  // and therefore need rebuilding when they are added/removed
337
393
  file.startsWith(svelte_config.kit.files.hooks.client)
@@ -351,9 +407,6 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
351
407
  }, 100);
352
408
  };
353
409
 
354
- // flag to skip watchers if server is already restarting
355
- let restarting = false;
356
-
357
410
  // Debounce add/unlink events because in case of folder deletion or moves
358
411
  // they fire in rapid succession, causing needless invocations.
359
412
  // These watchers only run for routes, param matchers, and client hooks.
@@ -362,7 +415,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
362
415
  watch('change', (file) => {
363
416
  // Don't run for a single file if the whole manifest is about to get updated
364
417
  // Unless it's a file where the trailing slash page option might have changed
365
- if (timeout || restarting || !/\+(page|layout|server).*$/.test(file)) return;
418
+ if (timeout || !/\+(page|layout|server).*$/.test(file)) return;
366
419
  sync.update(svelte_config, manifest_data, file, root);
367
420
  });
368
421
 
@@ -373,7 +426,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
373
426
  // send the vite client a full-reload event without path being set
374
427
  if (appTemplate !== 'index.html') {
375
428
  vite.watcher.on('change', (file) => {
376
- if (file === appTemplate && !restarting) {
429
+ if (file === appTemplate) {
377
430
  vite.ws.send({ type: 'full-reload' });
378
431
  }
379
432
  });
@@ -390,26 +443,12 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
390
443
  }
391
444
  });
392
445
 
393
- vite.watcher.on('change', async (file) => {
394
- // changing the svelte config requires restarting the dev server
395
- // the config is only read on start and passed on to vite-plugin-svelte
396
- // which needs up-to-date values to operate correctly
397
- if (file.match(/[/\\]svelte\.config\.[jt]s$/)) {
398
- console.log(`svelte config changed, restarting vite dev-server. changed file: ${file}`);
399
- restarting = true;
400
- await vite.restart();
401
- }
402
- });
403
-
404
446
  const assets = svelte_config.kit.paths.assets ? SVELTE_KIT_ASSETS : svelte_config.kit.paths.base;
405
447
  const asset_server = sirv(svelte_config.kit.files.assets, {
406
448
  dev: true,
407
449
  etag: true,
408
450
  maxAge: 0,
409
- extensions: [],
410
- setHeaders: (res) => {
411
- res.setHeader('access-control-allow-origin', '*');
412
- }
451
+ extensions: []
413
452
  });
414
453
 
415
454
  vite.middlewares.use((req, res, next) => {
@@ -436,7 +475,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
436
475
  });
437
476
 
438
477
  const env = loadEnv(vite_config.mode, svelte_config.kit.env.dir, '');
439
- const emulator = await adapter?.emulate?.();
478
+ const emulator = await svelte_config.kit.adapter?.emulate?.();
440
479
 
441
480
  return () => {
442
481
  const serve_static_middleware = vite.middlewares.stack.find(
@@ -513,7 +552,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
513
552
  );
514
553
 
515
554
  const { set_fix_stack_trace } = await vite.ssrLoadModule(
516
- `${get_runtime_base(root)}/shared-server.js`
555
+ `${get_runtime_base(root)}/server/internal.js`
517
556
  );
518
557
  set_fix_stack_trace(fix_stack_trace);
519
558
 
@@ -527,7 +566,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
527
566
  read: (file) => createReadableStream(from_fs(file))
528
567
  });
529
568
 
530
- const request = await getRequest({
569
+ const request = getRequest({
531
570
  base,
532
571
  request: req
533
572
  });
@@ -578,15 +617,18 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root, a
578
617
  if (rendered.status === 404) {
579
618
  // @ts-expect-error
580
619
  serve_static_middleware.handle(req, res, () => {
581
- void setResponse(res, rendered);
620
+ log_response(rendered.status, request);
621
+ setResponse(res, rendered);
582
622
  });
583
623
  } else {
584
- void setResponse(res, rendered);
624
+ log_response(rendered.status, request);
625
+ setResponse(res, rendered);
585
626
  }
586
627
  } catch (e) {
587
628
  const error = coalesce_to_error(e);
588
629
  res.statusCode = 500;
589
- res.end(fix_stack_trace(error) || error.message); // handle `stackless` errors
630
+ fix_stack_trace(error);
631
+ res.end(error.stack || error.message); // handle `stackless` errors
590
632
  }
591
633
  });
592
634
  };