@sveltejs/kit 1.0.0-next.359 → 1.0.0-next.361

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.
@@ -1140,7 +1140,11 @@ function create_client({ target, session, base, trailing_slash }) {
1140
1140
  props = res.status === 204 ? {} : await res.json();
1141
1141
  } else {
1142
1142
  status = res.status;
1143
- error = new Error('Failed to load data');
1143
+ try {
1144
+ error = await res.json();
1145
+ } catch (e) {
1146
+ error = new Error('Failed to load data');
1147
+ }
1144
1148
  }
1145
1149
  }
1146
1150
 
@@ -20,6 +20,60 @@ function to_headers(object) {
20
20
  return headers;
21
21
  }
22
22
 
23
+ /**
24
+ * Given an Accept header and a list of possible content types, pick
25
+ * the most suitable one to respond with
26
+ * @param {string} accept
27
+ * @param {string[]} types
28
+ */
29
+ function negotiate(accept, types) {
30
+ const parts = accept
31
+ .split(',')
32
+ .map((str, i) => {
33
+ const match = /([^/]+)\/([^;]+)(?:;q=([0-9.]+))?/.exec(str);
34
+ if (match) {
35
+ const [, type, subtype, q = '1'] = match;
36
+ return { type, subtype, q: +q, i };
37
+ }
38
+
39
+ throw new Error(`Invalid Accept header: ${accept}`);
40
+ })
41
+ .sort((a, b) => {
42
+ if (a.q !== b.q) {
43
+ return b.q - a.q;
44
+ }
45
+
46
+ if ((a.subtype === '*') !== (b.subtype === '*')) {
47
+ return a.subtype === '*' ? 1 : -1;
48
+ }
49
+
50
+ if ((a.type === '*') !== (b.type === '*')) {
51
+ return a.type === '*' ? 1 : -1;
52
+ }
53
+
54
+ return a.i - b.i;
55
+ });
56
+
57
+ let accepted;
58
+ let min_priority = Infinity;
59
+
60
+ for (const mimetype of types) {
61
+ const [type, subtype] = mimetype.split('/');
62
+ const priority = parts.findIndex(
63
+ (part) =>
64
+ (part.type === type || part.type === '*') &&
65
+ (part.subtype === subtype || part.subtype === '*')
66
+ );
67
+
68
+ if (priority !== -1 && priority < min_priority) {
69
+ accepted = mimetype;
70
+ min_priority = priority;
71
+ }
72
+ }
73
+
74
+ return accepted;
75
+ }
76
+
23
77
  /**
24
78
  * Hash using djb2
25
79
  * @param {import('types').StrictBody} value
@@ -95,6 +149,47 @@ function normalize_request_method(event) {
95
149
  return method === 'delete' ? 'del' : method; // 'delete' is a reserved word
96
150
  }
97
151
 
152
+ /**
153
+ * Serialize an error into a JSON string, by copying its `name`, `message`
154
+ * and (in dev) `stack`, plus any custom properties, plus recursively
155
+ * serialized `cause` properties. This is necessary because
156
+ * `JSON.stringify(error) === '{}'`
157
+ * @param {Error} error
158
+ * @param {(error: Error) => string | undefined} get_stack
159
+ */
160
+ function serialize_error(error, get_stack) {
161
+ return JSON.stringify(clone_error(error, get_stack));
162
+ }
163
+
164
+ /**
165
+ * @param {Error} error
166
+ * @param {(error: Error) => string | undefined} get_stack
167
+ */
168
+ function clone_error(error, get_stack) {
169
+ const {
170
+ name,
171
+ message,
172
+ // this should constitute 'using' a var, since it affects `custom`
173
+ // eslint-disable-next-line
174
+ stack,
175
+ // @ts-expect-error i guess typescript doesn't know about error.cause yet
176
+ cause,
177
+ ...custom
178
+ } = error;
179
+
180
+ /** @type {Record<string, any>} */
181
+ const object = { name, message, stack: get_stack(error) };
182
+
183
+ if (cause) object.cause = clone_error(cause, get_stack);
184
+
185
+ for (const key in custom) {
186
+ // @ts-expect-error
187
+ object[key] = custom[key];
188
+ }
189
+
190
+ return object;
191
+ }
192
+
98
193
  /** @param {string} body */
99
194
  function error(body) {
100
195
  return new Response(body, {
@@ -132,9 +227,10 @@ function is_text(content_type) {
132
227
  /**
133
228
  * @param {import('types').RequestEvent} event
134
229
  * @param {{ [method: string]: import('types').RequestHandler }} mod
230
+ * @param {import('types').SSROptions} options
135
231
  * @returns {Promise<Response>}
136
232
  */
137
- async function render_endpoint(event, mod) {
233
+ async function render_endpoint(event, mod, options) {
138
234
  const method = normalize_request_method(event);
139
235
 
140
236
  /** @type {import('types').RequestHandler} */
@@ -193,9 +289,12 @@ async function render_endpoint(event, mod) {
193
289
 
194
290
  const type = headers.get('content-type');
195
291
 
196
- if (!is_text(type) && !(body instanceof Uint8Array || is_string(body))) {
292
+ if (
293
+ !is_text(type) &&
294
+ !(body instanceof Uint8Array || body instanceof ReadableStream || is_string(body))
295
+ ) {
197
296
  return error(
198
- `${preface}: body must be an instance of string or Uint8Array if content-type is not a supported textual content-type`
297
+ `${preface}: body must be an instance of string, Uint8Array or ReadableStream if content-type is not a supported textual content-type`
199
298
  );
200
299
  }
201
300
 
@@ -204,7 +303,8 @@ async function render_endpoint(event, mod) {
204
303
 
205
304
  if (is_pojo(body) && (!type || type.startsWith('application/json'))) {
206
305
  headers.set('content-type', 'application/json; charset=utf-8');
207
- normalized_body = JSON.stringify(body);
306
+ normalized_body =
307
+ body instanceof Error ? serialize_error(body, options.get_stack) : JSON.stringify(body);
208
308
  } else {
209
309
  normalized_body = /** @type {import('types').StrictBody} */ (body);
210
310
  }
@@ -1182,10 +1282,14 @@ async function render_response({
1182
1282
  }
1183
1283
  }
1184
1284
 
1185
- const stylesheets = new Set(options.manifest._.entry.css);
1186
- const modulepreloads = new Set(options.manifest._.entry.js);
1285
+ const { entry } = options.manifest._;
1286
+
1287
+ const stylesheets = new Set(entry.stylesheets);
1288
+ const modulepreloads = new Set(entry.imports);
1289
+
1187
1290
  /** @type {Map<string, string>} */
1188
- const styles = new Map();
1291
+ // TODO if we add a client entry point one day, we will need to include inline_styles with the entry, otherwise stylesheets will be linked even if they are below inlineStyleThreshold
1292
+ const inline_styles = new Map();
1189
1293
 
1190
1294
  /** @type {Array<import('./types').Fetched>} */
1191
1295
  const serialized_data = [];
@@ -1203,10 +1307,18 @@ async function render_response({
1203
1307
  }
1204
1308
 
1205
1309
  if (resolve_opts.ssr) {
1206
- branch.forEach(({ node, props, loaded, fetched, uses_credentials }) => {
1207
- if (node.css) node.css.forEach((url) => stylesheets.add(url));
1208
- if (node.js) node.js.forEach((url) => modulepreloads.add(url));
1209
- if (node.styles) Object.entries(node.styles).forEach(([k, v]) => styles.set(k, v));
1310
+ for (const { node, props, loaded, fetched, uses_credentials } of branch) {
1311
+ if (node.imports) {
1312
+ node.imports.forEach((url) => modulepreloads.add(url));
1313
+ }
1314
+
1315
+ if (node.stylesheets) {
1316
+ node.stylesheets.forEach((url) => stylesheets.add(url));
1317
+ }
1318
+
1319
+ if (node.inline_styles) {
1320
+ Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
1321
+ }
1210
1322
 
1211
1323
  // TODO probably better if `fetched` wasn't populated unless `hydrate`
1212
1324
  if (fetched && page_config.hydrate) serialized_data.push(...fetched);
@@ -1214,7 +1326,7 @@ async function render_response({
1214
1326
 
1215
1327
  cache = loaded?.cache;
1216
1328
  is_private = cache?.private ?? uses_credentials;
1217
- });
1329
+ }
1218
1330
 
1219
1331
  const session = writable($session);
1220
1332
 
@@ -1275,8 +1387,6 @@ async function render_response({
1275
1387
 
1276
1388
  let { head, html: body } = rendered;
1277
1389
 
1278
- const inlined_style = Array.from(styles.values()).join('\n');
1279
-
1280
1390
  await csp_ready;
1281
1391
  const csp = new Csp(options.csp, {
1282
1392
  dev: options.dev,
@@ -1288,7 +1398,7 @@ async function render_response({
1288
1398
 
1289
1399
  // prettier-ignore
1290
1400
  const init_app = `
1291
- import { start } from ${s(options.prefix + options.manifest._.entry.file)};
1401
+ import { start } from ${s(options.prefix + entry.file)};
1292
1402
  start({
1293
1403
  target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode,
1294
1404
  paths: ${s(options.paths)},
@@ -1300,7 +1410,7 @@ async function render_response({
1300
1410
  trailing_slash: ${s(options.trailing_slash)},
1301
1411
  hydrate: ${resolve_opts.ssr && page_config.hydrate ? `{
1302
1412
  status: ${status},
1303
- error: ${serialize_error(error)},
1413
+ error: ${error && serialize_error(error, e => e.stack)},
1304
1414
  nodes: [${branch.map(({ node }) => node.index).join(', ')}],
1305
1415
  params: ${devalue(event.params)},
1306
1416
  routeId: ${s(event.routeId)}
@@ -1316,14 +1426,16 @@ async function render_response({
1316
1426
  }
1317
1427
  `;
1318
1428
 
1319
- if (inlined_style) {
1429
+ if (inline_styles.size > 0) {
1430
+ const content = Array.from(inline_styles.values()).join('\n');
1431
+
1320
1432
  const attributes = [];
1321
1433
  if (options.dev) attributes.push(' data-sveltekit');
1322
1434
  if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`);
1323
1435
 
1324
- csp.add_style(inlined_style);
1436
+ csp.add_style(content);
1325
1437
 
1326
- head += `\n\t<style${attributes.join('')}>${inlined_style}</style>`;
1438
+ head += `\n\t<style${attributes.join('')}>${content}</style>`;
1327
1439
  }
1328
1440
 
1329
1441
  // prettier-ignore
@@ -1338,7 +1450,7 @@ async function render_response({
1338
1450
  attributes.push(`nonce="${csp.nonce}"`);
1339
1451
  }
1340
1452
 
1341
- if (styles.has(dep)) {
1453
+ if (inline_styles.has(dep)) {
1342
1454
  // don't load stylesheets that are already inlined
1343
1455
  // include them in disabled state so that Vite can detect them and doesn't try to add them
1344
1456
  attributes.push('disabled', 'media="(max-width: 0)"');
@@ -1419,10 +1531,6 @@ async function render_response({
1419
1531
  headers.set('cache-control', `${is_private ? 'private' : 'public'}, max-age=${cache.maxage}`);
1420
1532
  }
1421
1533
 
1422
- if (!options.floc) {
1423
- headers.set('permissions-policy', 'interest-cohort=()');
1424
- }
1425
-
1426
1534
  if (!state.prerendering) {
1427
1535
  const csp_header = csp.get_header();
1428
1536
  if (csp_header) {
@@ -1449,22 +1557,6 @@ function try_serialize(data, fail) {
1449
1557
  }
1450
1558
  }
1451
1559
 
1452
- // Ensure we return something truthy so the client will not re-render the page over the error
1453
-
1454
- /** @param {(Error & {frame?: string} & {loc?: object}) | undefined | null} error */
1455
- function serialize_error(error) {
1456
- if (!error) return null;
1457
- let serialized = try_serialize(error);
1458
- if (!serialized) {
1459
- const { name, message, stack } = error;
1460
- serialized = try_serialize({ ...error, name, message, stack });
1461
- }
1462
- if (!serialized) {
1463
- serialized = '{}';
1464
- }
1465
- return serialized;
1466
- }
1467
-
1468
1560
  /*!
1469
1561
  * cookie
1470
1562
  * Copyright(c) 2012-2014 Roman Shtylman
@@ -2163,6 +2255,7 @@ async function load_node({
2163
2255
  for (const [key, value] of event.request.headers) {
2164
2256
  if (
2165
2257
  key !== 'authorization' &&
2258
+ key !== 'connection' &&
2166
2259
  key !== 'cookie' &&
2167
2260
  key !== 'host' &&
2168
2261
  key !== 'if-none-match' &&
@@ -2395,7 +2488,7 @@ async function load_node({
2395
2488
 
2396
2489
  if (!loaded) {
2397
2490
  // TODO do we still want to enforce this now that there's no fallthrough?
2398
- throw new Error(`load function must return a value${options.dev ? ` (${node.entry})` : ''}`);
2491
+ throw new Error(`load function must return a value${options.dev ? ` (${node.file})` : ''}`);
2399
2492
  }
2400
2493
  } else if (shadow.body) {
2401
2494
  loaded = {
@@ -2469,22 +2562,23 @@ async function load_shadow_data(route, event, options, prerender) {
2469
2562
  };
2470
2563
 
2471
2564
  if (!is_get) {
2472
- const result = await handler(event);
2473
-
2474
- // TODO remove for 1.0
2475
- // @ts-expect-error
2476
- if (result.fallthrough) {
2477
- throw new Error(
2478
- 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching'
2479
- );
2480
- }
2481
-
2482
- const { status, headers, body } = validate_shadow_output(result);
2565
+ const { status, headers, body } = validate_shadow_output(await handler(event));
2566
+ add_cookies(/** @type {string[]} */ (data.cookies), headers);
2483
2567
  data.status = status;
2484
2568
 
2485
- add_cookies(/** @type {string[]} */ (data.cookies), headers);
2569
+ // explicit errors cause an error page...
2570
+ if (body instanceof Error) {
2571
+ if (status < 400) {
2572
+ data.status = 500;
2573
+ data.error = new Error('A non-error status code was returned with an error body');
2574
+ } else {
2575
+ data.error = body;
2576
+ }
2486
2577
 
2487
- // Redirects are respected...
2578
+ return data;
2579
+ }
2580
+
2581
+ // ...redirects are respected...
2488
2582
  if (status >= 300 && status < 400) {
2489
2583
  data.redirect = /** @type {string} */ (
2490
2584
  headers instanceof Headers ? headers.get('location') : headers.location
@@ -2500,20 +2594,21 @@ async function load_shadow_data(route, event, options, prerender) {
2500
2594
 
2501
2595
  const get = (method === 'head' && mod.head) || mod.get;
2502
2596
  if (get) {
2503
- const result = await get(event);
2504
-
2505
- // TODO remove for 1.0
2506
- // @ts-expect-error
2507
- if (result.fallthrough) {
2508
- throw new Error(
2509
- 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching'
2510
- );
2511
- }
2512
-
2513
- const { status, headers, body } = validate_shadow_output(result);
2597
+ const { status, headers, body } = validate_shadow_output(await get(event));
2514
2598
  add_cookies(/** @type {string[]} */ (data.cookies), headers);
2515
2599
  data.status = status;
2516
2600
 
2601
+ if (body instanceof Error) {
2602
+ if (status < 400) {
2603
+ data.status = 500;
2604
+ data.error = new Error('A non-error status code was returned with an error body');
2605
+ } else {
2606
+ data.error = body;
2607
+ }
2608
+
2609
+ return data;
2610
+ }
2611
+
2517
2612
  if (status >= 400) {
2518
2613
  data.error = new Error('Failed to load data');
2519
2614
  return data;
@@ -2560,6 +2655,14 @@ function add_cookies(target, headers) {
2560
2655
  * @param {import('types').ShadowEndpointOutput} result
2561
2656
  */
2562
2657
  function validate_shadow_output(result) {
2658
+ // TODO remove for 1.0
2659
+ // @ts-expect-error
2660
+ if (result.fallthrough) {
2661
+ throw new Error(
2662
+ 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching'
2663
+ );
2664
+ }
2665
+
2563
2666
  const { status = 200, body = {} } = result;
2564
2667
  let headers = result.headers || {};
2565
2668
 
@@ -2574,7 +2677,9 @@ function validate_shadow_output(result) {
2574
2677
  }
2575
2678
 
2576
2679
  if (!is_pojo(body)) {
2577
- throw new Error('Body returned from endpoint request handler must be a plain object');
2680
+ throw new Error(
2681
+ 'Body returned from endpoint request handler must be a plain object or an Error'
2682
+ );
2578
2683
  }
2579
2684
 
2580
2685
  return { status, headers, body };
@@ -2967,7 +3072,7 @@ async function render_page(event, route, options, state, resolve_opts) {
2967
3072
  ]);
2968
3073
 
2969
3074
  if (type === 'application/json') {
2970
- return render_endpoint(event, await route.shadow());
3075
+ return render_endpoint(event, await route.shadow(), options);
2971
3076
  }
2972
3077
  }
2973
3078
 
@@ -2983,58 +3088,6 @@ async function render_page(event, route, options, state, resolve_opts) {
2983
3088
  });
2984
3089
  }
2985
3090
 
2986
- /**
2987
- * @param {string} accept
2988
- * @param {string[]} types
2989
- */
2990
- function negotiate(accept, types) {
2991
- const parts = accept
2992
- .split(',')
2993
- .map((str, i) => {
2994
- const match = /([^/]+)\/([^;]+)(?:;q=([0-9.]+))?/.exec(str);
2995
- if (match) {
2996
- const [, type, subtype, q = '1'] = match;
2997
- return { type, subtype, q: +q, i };
2998
- }
2999
-
3000
- throw new Error(`Invalid Accept header: ${accept}`);
3001
- })
3002
- .sort((a, b) => {
3003
- if (a.q !== b.q) {
3004
- return b.q - a.q;
3005
- }
3006
-
3007
- if ((a.subtype === '*') !== (b.subtype === '*')) {
3008
- return a.subtype === '*' ? 1 : -1;
3009
- }
3010
-
3011
- if ((a.type === '*') !== (b.type === '*')) {
3012
- return a.type === '*' ? 1 : -1;
3013
- }
3014
-
3015
- return a.i - b.i;
3016
- });
3017
-
3018
- let accepted;
3019
- let min_priority = Infinity;
3020
-
3021
- for (const mimetype of types) {
3022
- const [type, subtype] = mimetype.split('/');
3023
- const priority = parts.findIndex(
3024
- (part) =>
3025
- (part.type === type || part.type === '*') &&
3026
- (part.subtype === subtype || part.subtype === '*')
3027
- );
3028
-
3029
- if (priority !== -1 && priority < min_priority) {
3030
- accepted = mimetype;
3031
- min_priority = priority;
3032
- }
3033
- }
3034
-
3035
- return accepted;
3036
- }
3037
-
3038
3091
  /**
3039
3092
  * @param {RegExpMatchArray} match
3040
3093
  * @param {string[]} names
@@ -3265,7 +3318,7 @@ async function respond(request, options, state) {
3265
3318
  let response;
3266
3319
 
3267
3320
  if (is_data_request && route.type === 'page' && route.shadow) {
3268
- response = await render_endpoint(event, await route.shadow());
3321
+ response = await render_endpoint(event, await route.shadow(), options);
3269
3322
 
3270
3323
  // loading data for a client-side transition is a special case
3271
3324
  if (request.headers.has('x-sveltekit-load')) {
@@ -3287,7 +3340,7 @@ async function respond(request, options, state) {
3287
3340
  } else {
3288
3341
  response =
3289
3342
  route.type === 'endpoint'
3290
- ? await render_endpoint(event, await route.load())
3343
+ ? await render_endpoint(event, await route.load(), options)
3291
3344
  : await render_page(event, route, options, state, resolve_opts);
3292
3345
  }
3293
3346
 
@@ -3371,6 +3424,18 @@ async function respond(request, options, state) {
3371
3424
 
3372
3425
  options.handle_error(error, event);
3373
3426
 
3427
+ const type = negotiate(event.request.headers.get('accept') || 'text/html', [
3428
+ 'text/html',
3429
+ 'application/json'
3430
+ ]);
3431
+
3432
+ if (is_data_request || type === 'application/json') {
3433
+ return new Response(serialize_error(error, options.get_stack), {
3434
+ status: 500,
3435
+ headers: { 'content-type': 'application/json; charset=utf-8' }
3436
+ });
3437
+ }
3438
+
3374
3439
  try {
3375
3440
  const $session = await options.hooks.getSession(event);
3376
3441
  return await respond_with_error({
@@ -239,8 +239,6 @@ const options = object(
239
239
  template: string(join('src', 'app.html'))
240
240
  }),
241
241
 
242
- floc: boolean(false),
243
-
244
242
  // TODO: remove this for the 1.0 release
245
243
  headers: error(
246
244
  (keypath) =>
package/dist/cli.js CHANGED
@@ -18,7 +18,7 @@ function handle_error(e) {
18
18
  process.exit(1);
19
19
  }
20
20
 
21
- const prog = sade('svelte-kit').version('1.0.0-next.359');
21
+ const prog = sade('svelte-kit').version('1.0.0-next.361');
22
22
 
23
23
  prog
24
24
  .command('package')
package/dist/vite.js CHANGED
@@ -4,7 +4,7 @@ import path__default, { join, dirname, resolve as resolve$1, normalize } from 'p
4
4
  import { a as load_template, $, c as coalesce_to_error, l as load_config } from './chunks/error.js';
5
5
  import { svelte } from '@sveltejs/vite-plugin-svelte';
6
6
  import * as vite from 'vite';
7
- import { searchForWorkspaceRoot } from 'vite';
7
+ import { loadConfigFromFile, searchForWorkspaceRoot } from 'vite';
8
8
  import { p as posixify, m as mkdirp, r as rimraf } from './chunks/write_tsconfig.js';
9
9
  import { g as get_runtime_path, s, i as init, u as update, a as get_mime_lookup, p as parse_route_id, b as all, l as logger } from './chunks/sync.js';
10
10
  import { pathToFileURL, URL as URL$1 } from 'url';
@@ -39,16 +39,15 @@ import 'node:fs';
39
39
  import 'node:path';
40
40
 
41
41
  /**
42
+ * @param {import('vite').ConfigEnv} config_env
42
43
  * @return {Promise<import('vite').UserConfig>}
43
44
  */
44
- async function get_vite_config() {
45
- for (const file of ['vite.config.js', 'vite.config.mjs', 'vite.config.cjs']) {
46
- if (fs__default.existsSync(file)) {
47
- const config = await import(pathToFileURL(file).toString());
48
- return config.default || config;
49
- }
45
+ async function get_vite_config(config_env) {
46
+ const config = (await loadConfigFromFile(config_env))?.config;
47
+ if (!config) {
48
+ throw new Error('Could not load Vite config');
50
49
  }
51
- throw new Error('Could not find vite.config.js');
50
+ return config;
52
51
  }
53
52
 
54
53
  /**
@@ -199,24 +198,47 @@ async function create_build(config) {
199
198
 
200
199
  /**
201
200
  * Adds transitive JS and CSS dependencies to the js and css inputs.
202
- * @param {string} file
203
201
  * @param {import('vite').Manifest} manifest
204
- * @param {Set<string>} css
205
- * @param {Set<string>} js
202
+ * @param {string} entry
203
+ * @param {boolean} add_dynamic_css
206
204
  */
207
- function find_deps$1(file, manifest, js, css) {
208
- const chunk = manifest[file];
205
+ function find_deps$1(manifest, entry, add_dynamic_css) {
206
+ /** @type {Set<string>} */
207
+ const imports = new Set();
209
208
 
210
- if (js.has(chunk.file)) return;
211
- js.add(chunk.file);
209
+ /** @type {Set<string>} */
210
+ const stylesheets = new Set();
212
211
 
213
- if (chunk.css) {
214
- chunk.css.forEach((file) => css.add(file));
215
- }
212
+ /**
213
+ * @param {string} file
214
+ * @param {boolean} add_js
215
+ */
216
+ function traverse(file, add_js) {
217
+ const chunk = manifest[file];
218
+
219
+ if (imports.has(chunk.file)) return;
220
+ if (add_js) imports.add(chunk.file);
221
+
222
+ if (chunk.css) {
223
+ chunk.css.forEach((file) => stylesheets.add(file));
224
+ }
225
+
226
+ if (chunk.imports) {
227
+ chunk.imports.forEach((file) => traverse(file, add_js));
228
+ }
216
229
 
217
- if (chunk.imports) {
218
- chunk.imports.forEach((file) => find_deps$1(file, manifest, js, css));
230
+ if (add_dynamic_css && chunk.dynamicImports) {
231
+ chunk.dynamicImports.forEach((file) => traverse(file, false));
232
+ }
219
233
  }
234
+
235
+ traverse(entry, true);
236
+
237
+ return {
238
+ file: manifest[entry].file,
239
+ imports: Array.from(imports),
240
+ stylesheets: Array.from(stylesheets)
241
+ };
220
242
  }
221
243
 
222
244
  /**
@@ -331,7 +353,6 @@ export class Server {
331
353
  this.options = {
332
354
  csp: ${s(config.kit.csp)},
333
355
  dev: false,
334
- floc: ${config.kit.floc},
335
356
  get_stack: error => String(error), // for security
336
357
  handle_error: (error, event) => {
337
358
  this.options.hooks.handleError({
@@ -390,6 +411,7 @@ export class Server {
390
411
  * @param {{
391
412
  * cwd: string;
392
413
  * config: import('types').ValidatedConfig
414
+ * vite_config_env: import('vite').ConfigEnv
393
415
  * manifest_data: import('types').ManifestData
394
416
  * build_dir: string;
395
417
  * output_dir: string;
@@ -398,7 +420,15 @@ export class Server {
398
420
  * @param {{ vite_manifest: import('vite').Manifest, assets: import('rollup').OutputAsset[] }} client
399
421
  */
400
422
  async function build_server(options, client) {
401
- const { cwd, config, manifest_data, build_dir, output_dir, service_worker_entry_file } = options;
423
+ const {
424
+ cwd,
425
+ config,
426
+ vite_config_env,
427
+ manifest_data,
428
+ build_dir,
429
+ output_dir,
430
+ service_worker_entry_file
431
+ } = options;
402
432
 
403
433
  let hooks_file = resolve_entry(config.kit.files.hooks);
404
434
  if (!hooks_file || !fs__default.existsSync(hooks_file)) {
@@ -457,7 +487,7 @@ async function build_server(options, client) {
457
487
  })
458
488
  );
459
489
 
460
- const vite_config = await get_vite_config();
490
+ const vite_config = await get_vite_config(vite_config_env);
461
491
 
462
492
  const merged_config = merge_vite_configs(
463
493
  get_default_config({ config, input, ssr: true, outDir: `${output_dir}/server` }),
@@ -491,26 +521,22 @@ async function build_server(options, client) {
491
521
  });
492
522
 
493
523
  manifest_data.components.forEach((component, i) => {
494
- const file = `${output_dir}/server/nodes/${i}.js`;
495
-
496
- const js = new Set();
497
- const css = new Set();
498
- find_deps$1(component, client.vite_manifest, js, css);
524
+ const entry = find_deps$1(client.vite_manifest, component, true);
499
525
 
500
526
  const imports = [`import * as module from '../${vite_manifest[component].file}';`];
501
527
 
502
528
  const exports = [
503
529
  'export { module };',
504
530
  `export const index = ${i};`,
505
- `export const entry = '${client.vite_manifest[component].file}';`,
506
- `export const js = ${s(Array.from(js))};`,
507
- `export const css = ${s(Array.from(css))};`
531
+ `export const file = '${entry.file}';`,
532
+ `export const imports = ${s(entry.imports)};`,
533
+ `export const stylesheets = ${s(entry.stylesheets)};`
508
534
  ];
509
535
 
510
536
  /** @type {string[]} */
511
537
  const styles = [];
512
538
 
513
- css.forEach((file) => {
539
+ entry.stylesheets.forEach((file) => {
514
540
  if (stylesheet_lookup.has(file)) {
515
541
  const index = stylesheet_lookup.get(file);
516
542
  const name = `stylesheet_${index}`;
@@ -520,10 +546,11 @@ async function build_server(options, client) {
520
546
  });
521
547
 
522
548
  if (styles.length > 0) {
523
- exports.push(`export const styles = {\n${styles.join(',\n')}\n};`);
549
+ exports.push(`export const inline_styles = () => ({\n${styles.join(',\n')}\n});`);
524
550
  }
525
551
 
526
- fs__default.writeFileSync(file, `${imports.join('\n')}\n\n${exports.join('\n')}\n`);
552
+ const out = `${output_dir}/server/nodes/${i}.js`;
553
+ fs__default.writeFileSync(out, `${imports.join('\n')}\n\n${exports.join('\n')}\n`);
527
554
  });
528
555
 
529
556
  return {
@@ -630,6 +657,7 @@ function normalize_path(path, trailing_slash) {
630
657
  /**
631
658
  * @param {{
632
659
  * config: import('types').ValidatedConfig;
660
+ * vite_config_env: import('vite').ConfigEnv;
633
661
  * manifest_data: import('types').ManifestData;
634
662
  * output_dir: string;
635
663
  * service_worker_entry_file: string | null;
@@ -638,7 +666,7 @@ function normalize_path(path, trailing_slash) {
638
666
  * @param {import('vite').Manifest} client_manifest
639
667
  */
640
668
  async function build_service_worker(
641
- { config, manifest_data, output_dir, service_worker_entry_file },
669
+ { config, vite_config_env, manifest_data, output_dir, service_worker_entry_file },
642
670
  prerendered,
643
671
  client_manifest
644
672
  ) {
@@ -687,7 +715,7 @@ async function build_service_worker(
687
715
  .trim()
688
716
  );
689
717
 
690
- const vite_config = await get_vite_config();
718
+ const vite_config = await get_vite_config(vite_config_env);
691
719
  const merged_config = merge_vite_configs(vite_config, {
692
720
  base: assets_base(config.kit),
693
721
  build: {
@@ -2038,8 +2066,8 @@ async function dev(vite, svelte_config) {
2038
2066
  _: {
2039
2067
  entry: {
2040
2068
  file: `/@fs${runtime}/client/start.js`,
2041
- css: [],
2042
- js: []
2069
+ imports: [],
2070
+ stylesheets: []
2043
2071
  },
2044
2072
  nodes: manifest_data.components.map((id, index) => {
2045
2073
  return async () => {
@@ -2048,43 +2076,46 @@ async function dev(vite, svelte_config) {
2048
2076
  const module = /** @type {import('types').SSRComponent} */ (
2049
2077
  await vite.ssrLoadModule(url, { fixStacktrace: false })
2050
2078
  );
2051
- const node = await vite.moduleGraph.getModuleByUrl(url);
2052
-
2053
- if (!node) throw new Error(`Could not find node for ${url}`);
2054
-
2055
- const deps = new Set();
2056
- await find_deps(vite, node, deps);
2057
-
2058
- /** @type {Record<string, string>} */
2059
- const styles = {};
2060
-
2061
- for (const dep of deps) {
2062
- const parsed = new URL$1(dep.url, 'http://localhost/');
2063
- const query = parsed.searchParams;
2064
-
2065
- if (
2066
- style_pattern.test(dep.file) ||
2067
- (query.has('svelte') && query.get('type') === 'style')
2068
- ) {
2069
- try {
2070
- const mod = await vite.ssrLoadModule(dep.url, { fixStacktrace: false });
2071
- styles[dep.url] = mod.default;
2072
- } catch {
2073
- // this can happen with dynamically imported modules, I think
2074
- // because the Vite module graph doesn't distinguish between
2075
- // static and dynamic imports? TODO investigate, submit fix
2076
- }
2077
- }
2078
- }
2079
2079
 
2080
2080
  return {
2081
2081
  module,
2082
2082
  index,
2083
- entry: url.endsWith('.svelte') ? url : url + '?import',
2084
- css: [],
2085
- js: [],
2083
+ file: url.endsWith('.svelte') ? url : url + '?import',
2084
+ imports: [],
2085
+ stylesheets: [],
2086
2086
  // in dev we inline all styles to avoid FOUC
2087
- styles
2087
+ inline_styles: async () => {
2088
+ const node = await vite.moduleGraph.getModuleByUrl(url);
2089
+
2090
+ if (!node) throw new Error(`Could not find node for ${url}`);
2091
+
2092
+ const deps = new Set();
2093
+ await find_deps(vite, node, deps);
2094
+
2095
+ /** @type {Record<string, string>} */
2096
+ const styles = {};
2097
+
2098
+ for (const dep of deps) {
2099
+ const parsed = new URL$1(dep.url, 'http://localhost/');
2100
+ const query = parsed.searchParams;
2101
+
2102
+ if (
2103
+ style_pattern.test(dep.file) ||
2104
+ (query.has('svelte') && query.get('type') === 'style')
2105
+ ) {
2106
+ try {
2107
+ const mod = await vite.ssrLoadModule(dep.url, { fixStacktrace: false });
2108
+ styles[dep.url] = mod.default;
2109
+ } catch {
2110
+ // this can happen with dynamically imported modules, I think
2111
+ // because the Vite module graph doesn't distinguish between
2112
+ // static and dynamic imports? TODO investigate, submit fix
2113
+ }
2114
+ }
2115
+ }
2116
+
2117
+ return styles;
2118
+ }
2088
2119
  };
2089
2120
  };
2090
2121
  }),
@@ -2280,7 +2311,6 @@ async function dev(vite, svelte_config) {
2280
2311
  {
2281
2312
  csp: svelte_config.kit.csp,
2282
2313
  dev: true,
2283
- floc: svelte_config.kit.floc,
2284
2314
  get_stack: (error) => {
2285
2315
  return fix_stack_trace(error);
2286
2316
  },
@@ -2418,6 +2448,10 @@ async function find_deps(vite, node, deps) {
2418
2448
  if (node.ssrTransformResult.deps) {
2419
2449
  node.ssrTransformResult.deps.forEach((url) => branches.push(add_by_url(url)));
2420
2450
  }
2451
+
2452
+ if (node.ssrTransformResult.dynamicDeps) {
2453
+ node.ssrTransformResult.dynamicDeps.forEach((url) => branches.push(add_by_url(url)));
2454
+ }
2421
2455
  } else {
2422
2456
  node.importedModules.forEach((node) => branches.push(add(node)));
2423
2457
  }
@@ -2794,9 +2828,15 @@ function kit() {
2794
2828
  /** @type {import('vite').UserConfig} */
2795
2829
  let vite_config;
2796
2830
 
2831
+ /** @type {import('vite').ConfigEnv} */
2832
+ let vite_config_env;
2833
+
2797
2834
  /** @type {import('types').ManifestData} */
2798
2835
  let manifest_data;
2799
2836
 
2837
+ /** @type {boolean} */
2838
+ let is_build;
2839
+
2800
2840
  /**
2801
2841
  * @type {{
2802
2842
  * build_dir: string;
@@ -2836,9 +2876,11 @@ function kit() {
2836
2876
  return {
2837
2877
  name: 'vite-plugin-svelte-kit',
2838
2878
 
2839
- async config(config, { command }) {
2879
+ async config(config, config_env) {
2840
2880
  vite_config = config;
2881
+ vite_config_env = config_env;
2841
2882
  svelte_config = await load_config();
2883
+ is_build = config_env.command === 'build';
2842
2884
 
2843
2885
  paths = {
2844
2886
  build_dir: `${svelte_config.kit.outDir}/build`,
@@ -2846,7 +2888,7 @@ function kit() {
2846
2888
  client_out_dir: `${svelte_config.kit.outDir}/output/client/${svelte_config.kit.appDir}`
2847
2889
  };
2848
2890
 
2849
- if (command === 'build') {
2891
+ if (is_build) {
2850
2892
  process.env.VITE_SVELTEKIT_APP_VERSION = svelte_config.kit.version.name;
2851
2893
  process.env.VITE_SVELTEKIT_APP_VERSION_FILE = `${svelte_config.kit.appDir}/version.json`;
2852
2894
  process.env.VITE_SVELTEKIT_APP_VERSION_POLL_INTERVAL = `${svelte_config.kit.version.pollInterval}`;
@@ -2906,17 +2948,24 @@ function kit() {
2906
2948
  },
2907
2949
 
2908
2950
  buildStart() {
2909
- rimraf(paths.build_dir);
2910
- mkdirp(paths.build_dir);
2951
+ if (is_build) {
2952
+ rimraf(paths.build_dir);
2953
+ mkdirp(paths.build_dir);
2911
2954
 
2912
- rimraf(paths.output_dir);
2913
- mkdirp(paths.output_dir);
2955
+ rimraf(paths.output_dir);
2956
+ mkdirp(paths.output_dir);
2957
+ }
2914
2958
  },
2915
2959
 
2916
2960
  async writeBundle(_options, bundle) {
2917
2961
  const verbose = vite_config.logLevel === 'info';
2918
2962
  const log = logger({ verbose });
2919
2963
 
2964
+ fs__default.writeFileSync(
2965
+ `${paths.client_out_dir}/version.json`,
2966
+ JSON.stringify({ version: process.env.VITE_SVELTEKIT_APP_VERSION })
2967
+ );
2968
+
2920
2969
  /** @type {import('rollup').OutputChunk[]} */
2921
2970
  const chunks = [];
2922
2971
  /** @type {import('rollup').OutputAsset[]} */
@@ -2935,25 +2984,14 @@ function kit() {
2935
2984
  fs__default.readFileSync(`${paths.client_out_dir}/immutable/manifest.json`, 'utf-8')
2936
2985
  );
2937
2986
 
2938
- const entry = posixify(
2987
+ const entry_id = posixify(
2939
2988
  path__default.relative(cwd, `${get_runtime_path(svelte_config.kit)}/client/start.js`)
2940
2989
  );
2941
- const entry_js = new Set();
2942
- const entry_css = new Set();
2943
- find_deps$1(entry, vite_manifest, entry_js, entry_css);
2944
2990
 
2945
- fs__default.writeFileSync(
2946
- `${paths.client_out_dir}/version.json`,
2947
- JSON.stringify({ version: process.env.VITE_SVELTEKIT_APP_VERSION })
2948
- );
2949
2991
  const client = {
2950
2992
  assets,
2951
2993
  chunks,
2952
- entry: {
2953
- file: vite_manifest[entry].file,
2954
- js: Array.from(entry_js),
2955
- css: Array.from(entry_css)
2956
- },
2994
+ entry: find_deps$1(vite_manifest, entry_id, false),
2957
2995
  vite_manifest
2958
2996
  };
2959
2997
  log.info(`Client build completed. Wrote ${chunks.length} chunks and ${assets.length} assets`);
@@ -2961,6 +2999,7 @@ function kit() {
2961
2999
  const options = {
2962
3000
  cwd,
2963
3001
  config: svelte_config,
3002
+ vite_config_env,
2964
3003
  build_dir: paths.build_dir, // TODO just pass `paths`
2965
3004
  manifest_data,
2966
3005
  output_dir: paths.output_dir,
@@ -3044,7 +3083,7 @@ function kit() {
3044
3083
  },
3045
3084
 
3046
3085
  closeBundle() {
3047
- if (svelte_config.kit.prerender.enabled) {
3086
+ if (is_build && svelte_config.kit.prerender.enabled) {
3048
3087
  // this is necessary to close any open db connections, etc.
3049
3088
  // TODO: prerender in a subprocess so we can exit in isolation
3050
3089
  // https://github.com/sveltejs/kit/issues/5306
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.359",
3
+ "version": "1.0.0-next.361",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -81,7 +81,7 @@
81
81
  },
82
82
  "types": "types/index.d.ts",
83
83
  "engines": {
84
- "node": ">=16.7"
84
+ "node": ">=16.9"
85
85
  },
86
86
  "scripts": {
87
87
  "build": "rollup -c && node scripts/cp.js src/runtime/components assets/components && npm run types",
package/types/index.d.ts CHANGED
@@ -117,7 +117,6 @@ export interface KitConfig {
117
117
  serviceWorker?: string;
118
118
  template?: string;
119
119
  };
120
- floc?: boolean;
121
120
  inlineStyleThreshold?: number;
122
121
  methodOverride?: {
123
122
  parameter?: string;
@@ -268,7 +267,7 @@ export interface ResolveOptions {
268
267
  transformPage?: ({ html }: { html: string }) => MaybePromise<string>;
269
268
  }
270
269
 
271
- export type ResponseBody = JSONValue | Uint8Array | ReadableStream;
270
+ export type ResponseBody = JSONValue | Uint8Array | ReadableStream | Error;
272
271
 
273
272
  export class Server {
274
273
  constructor(manifest: SSRManifest);
@@ -284,8 +283,8 @@ export interface SSRManifest {
284
283
  _: {
285
284
  entry: {
286
285
  file: string;
287
- js: string[];
288
- css: string[];
286
+ imports: string[];
287
+ stylesheets: string[];
289
288
  };
290
289
  nodes: SSRNodeLoader[];
291
290
  routes: SSRRoute[];
@@ -51,8 +51,8 @@ export interface BuildData {
51
51
  chunks: OutputChunk[];
52
52
  entry: {
53
53
  file: string;
54
- js: string[];
55
- css: string[];
54
+ imports: string[];
55
+ stylesheets: string[];
56
56
  };
57
57
  vite_manifest: import('vite').Manifest;
58
58
  };
@@ -176,13 +176,6 @@ export interface ShadowEndpointOutput<Output extends JSONObject = JSONObject> {
176
176
  body?: Output;
177
177
  }
178
178
 
179
- /**
180
- * The route key of a page with a matching endpoint — used to ensure the
181
- * client loads data from the right endpoint during client-side navigation
182
- * rather than a different route that happens to match the path
183
- */
184
- type ShadowKey = string;
185
-
186
179
  export interface ShadowRequestHandler<Output extends JSONObject = JSONObject> {
187
180
  (event: RequestEvent): MaybePromise<ShadowEndpointOutput<Output>>;
188
181
  }
@@ -230,13 +223,13 @@ export interface SSRNode {
230
223
  /** index into the `components` array in client-manifest.js */
231
224
  index: number;
232
225
  /** client-side module URL for this component */
233
- entry: string;
234
- /** external CSS files */
235
- css: string[];
226
+ file: string;
236
227
  /** external JS files */
237
- js: string[];
228
+ imports: string[];
229
+ /** external CSS files */
230
+ stylesheets: string[];
238
231
  /** inlined styles */
239
- styles?: Record<string, string>;
232
+ inline_styles?: () => MaybePromise<Record<string, string>>;
240
233
  }
241
234
 
242
235
  export type SSRNodeLoader = () => Promise<SSRNode>;
@@ -244,7 +237,6 @@ export type SSRNodeLoader = () => Promise<SSRNode>;
244
237
  export interface SSROptions {
245
238
  csp: ValidatedConfig['kit']['csp'];
246
239
  dev: boolean;
247
- floc: boolean;
248
240
  get_stack: (error: Error) => string | undefined;
249
241
  handle_error(error: Error & { frame?: string }, event: RequestEvent): void;
250
242
  hooks: Hooks;