@sveltejs/kit 1.0.0-next.476 → 1.0.0-next.479

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.476",
3
+ "version": "1.0.0-next.479",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -2,7 +2,7 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
- <title>%sveltekit.message%</title>
5
+ <title>%sveltekit.error.message%</title>
6
6
 
7
7
  <style>
8
8
  body {
@@ -49,7 +49,7 @@
49
49
  <div class="error">
50
50
  <span class="status">%sveltekit.status%</span>
51
51
  <div class="message">
52
- <h1>%sveltekit.message%</h1>
52
+ <h1>%sveltekit.error.message%</h1>
53
53
  </div>
54
54
  </div>
55
55
  </body>
@@ -77,8 +77,13 @@ function process_config(config, { cwd = process.cwd() } = {}) {
77
77
  // TODO remove for 1.0
78
78
  if (key === 'template') continue;
79
79
 
80
- // @ts-expect-error this is typescript at its stupidest
81
- validated.kit.files[key] = path.resolve(cwd, validated.kit.files[key]);
80
+ if (key === 'hooks') {
81
+ validated.kit.files.hooks.client = path.resolve(cwd, validated.kit.files.hooks.client);
82
+ validated.kit.files.hooks.server = path.resolve(cwd, validated.kit.files.hooks.server);
83
+ } else {
84
+ // @ts-expect-error
85
+ validated.kit.files[key] = path.resolve(cwd, validated.kit.files[key]);
86
+ }
82
87
  }
83
88
 
84
89
  if (!fs.existsSync(validated.kit.files.errorTemplate)) {
@@ -141,7 +141,19 @@ const options = object(
141
141
 
142
142
  files: object({
143
143
  assets: string('static'),
144
- hooks: string(join('src', 'hooks')),
144
+ hooks: (input, keypath) => {
145
+ // TODO remove this for the 1.0 release
146
+ if (typeof input === 'string') {
147
+ throw new Error(
148
+ `${keypath} is an object with { server: string, client: string } now. See the PR for more information: https://github.com/sveltejs/kit/pull/6586`
149
+ );
150
+ }
151
+
152
+ return object({
153
+ client: string(join('src', 'hooks.client')),
154
+ server: string(join('src', 'hooks.server'))
155
+ })(input, keypath);
156
+ },
145
157
  lib: string(join('src', 'lib')),
146
158
  params: string(join('src', 'params')),
147
159
  routes: string(join('src', 'routes')),
@@ -26,7 +26,7 @@ export async function create(config) {
26
26
 
27
27
  const output = path.join(config.kit.outDir, 'generated');
28
28
 
29
- write_client_manifest(manifest_data, output);
29
+ write_client_manifest(config, manifest_data, output);
30
30
  write_root(manifest_data, output);
31
31
  write_matchers(manifest_data, output);
32
32
  await write_all_types(config, manifest_data);
@@ -1,14 +1,16 @@
1
1
  import { relative } from 'path';
2
+ import { posixify, resolve_entry } from '../../utils/filesystem.js';
2
3
  import { s } from '../../utils/misc.js';
3
4
  import { trim, write_if_changed } from './utils.js';
4
5
 
5
6
  /**
6
7
  * Writes the client manifest to disk. The manifest is used to power the router. It contains the
7
8
  * list of routes and corresponding Svelte components (i.e. pages and layouts).
9
+ * @param {import('types').ValidatedConfig} config
8
10
  * @param {import('types').ManifestData} manifest_data
9
11
  * @param {string} output
10
12
  */
11
- export function write_client_manifest(manifest_data, output) {
13
+ export function write_client_manifest(config, manifest_data, output) {
12
14
  /**
13
15
  * Creates a module that exports a `CSRPageNode`
14
16
  * @param {import('types').PageNode} node
@@ -78,10 +80,14 @@ export function write_client_manifest(manifest_data, output) {
78
80
  .join(',\n\t\t')}
79
81
  }`.replace(/^\t/gm, '');
80
82
 
83
+ const hooks_file = resolve_entry(config.kit.files.hooks.client);
84
+
81
85
  // String representation of __GENERATED__/client-manifest.js
82
86
  write_if_changed(
83
87
  `${output}/client-manifest.js`,
84
88
  trim(`
89
+ ${hooks_file ? `import * as client_hooks from '${posixify(relative(output, hooks_file))}';` : ''}
90
+
85
91
  export { matchers } from './client-matchers.js';
86
92
 
87
93
  export const nodes = [${nodes}];
@@ -89,6 +95,12 @@ export function write_client_manifest(manifest_data, output) {
89
95
  export const server_loads = [${[...layouts_with_server_load].join(',')}];
90
96
 
91
97
  export const dictionary = ${dictionary};
98
+
99
+ export const hooks = {
100
+ handleError: ${
101
+ hooks_file ? 'client_hooks.handleError || ' : ''
102
+ }(({ error }) => { console.error(error); return { message: 'Internal Error' }; }),
103
+ };
92
104
  `)
93
105
  );
94
106
  }
@@ -1,22 +1,17 @@
1
1
  import { HttpError, Redirect, ValidationError } from '../runtime/control.js';
2
2
 
3
+ // For some reason we need to type the params as well here,
4
+ // JSdoc doesn't seem to like @type with function overloads
3
5
  /**
4
- * Creates an `HttpError` object with an HTTP status code and an optional message.
5
- * This object, if thrown during request handling, will cause SvelteKit to
6
- * return an error response without invoking `handleError`
6
+ * @type {import('@sveltejs/kit').error}
7
7
  * @param {number} status
8
- * @param {string | undefined} [message]
8
+ * @param {any} message
9
9
  */
10
10
  export function error(status, message) {
11
11
  return new HttpError(status, message);
12
12
  }
13
13
 
14
- /**
15
- * Creates a `Redirect` object. If thrown during request handling, SvelteKit will
16
- * return a redirect response.
17
- * @param {number} status
18
- * @param {string} location
19
- */
14
+ /** @type {import('@sveltejs/kit').redirect} */
20
15
  export function redirect(status, location) {
21
16
  if (isNaN(status) || status < 300 || status > 399) {
22
17
  throw new Error('Invalid status code');
@@ -25,11 +20,7 @@ export function redirect(status, location) {
25
20
  return new Redirect(status, location);
26
21
  }
27
22
 
28
- /**
29
- * Generates a JSON `Response` object from the supplied data.
30
- * @param {any} data
31
- * @param {ResponseInit} [init]
32
- */
23
+ /** @type {import('@sveltejs/kit').json} */
33
24
  export function json(data, init) {
34
25
  // TODO deprecate this in favour of `Response.json` when it's
35
26
  // more widely supported
@@ -1,7 +1,10 @@
1
1
  import * as set_cookie_parser from 'set-cookie-parser';
2
2
 
3
- /** @param {import('http').IncomingMessage} req */
4
- function get_raw_body(req) {
3
+ /**
4
+ * @param {import('http').IncomingMessage} req
5
+ * @param {number} [body_size_limit]
6
+ */
7
+ function get_raw_body(req, body_size_limit) {
5
8
  const h = req.headers;
6
9
 
7
10
  if (!h['content-type']) {
@@ -11,11 +14,26 @@ function get_raw_body(req) {
11
14
  const length = Number(h['content-length']);
12
15
 
13
16
  // check if no request body
14
- // https://github.com/jshttp/type-is/blob/c1f4388c71c8a01f79934e68f630ca4a15fffcd6/index.js#L81-L95
15
- if (isNaN(length) && h['transfer-encoding'] == null) {
17
+ if (
18
+ (req.httpVersionMajor === 1 && isNaN(length) && h['transfer-encoding'] == null) ||
19
+ length === 0
20
+ ) {
16
21
  return null;
17
22
  }
18
23
 
24
+ if (body_size_limit) {
25
+ if (!length) {
26
+ throw new Error(
27
+ `Received content-length of ${length}. content-length must be provided when body size limit is specified.`
28
+ );
29
+ }
30
+ if (length > body_size_limit) {
31
+ throw new Error(
32
+ `Received content-length of ${length}, but only accept up to ${body_size_limit} bytes.`
33
+ );
34
+ }
35
+ }
36
+
19
37
  if (req.destroyed) {
20
38
  const readable = new ReadableStream();
21
39
  readable.cancel();
@@ -65,9 +83,9 @@ function get_raw_body(req) {
65
83
  }
66
84
 
67
85
  /** @type {import('@sveltejs/kit/node').getRequest} */
68
- export async function getRequest(base, req) {
69
- let headers = /** @type {Record<string, string>} */ (req.headers);
70
- if (req.httpVersionMajor === 2) {
86
+ export async function getRequest({ request, base, bodySizeLimit }) {
87
+ let headers = /** @type {Record<string, string>} */ (request.headers);
88
+ if (request.httpVersionMajor === 2) {
71
89
  // we need to strip out the HTTP/2 pseudo-headers because node-fetch's
72
90
  // Request implementation doesn't like them
73
91
  // TODO is this still true with Node 18
@@ -78,10 +96,10 @@ export async function getRequest(base, req) {
78
96
  delete headers[':scheme'];
79
97
  }
80
98
 
81
- return new Request(base + req.url, {
82
- method: req.method,
99
+ return new Request(base + request.url, {
100
+ method: request.method,
83
101
  headers,
84
- body: get_raw_body(req)
102
+ body: get_raw_body(request, bodySizeLimit)
85
103
  });
86
104
  }
87
105
 
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { mkdirp, posixify } from '../../../utils/filesystem.js';
4
- import { get_vite_config, merge_vite_configs, resolve_entry } from '../utils.js';
3
+ import { mkdirp, posixify, resolve_entry } from '../../../utils/filesystem.js';
4
+ import { get_vite_config, merge_vite_configs } from '../utils.js';
5
5
  import { load_error_page, load_template } from '../../../core/config/index.js';
6
6
  import { runtime_directory } from '../../../core/utils.js';
7
7
  import { create_build, find_deps, get_default_build_config, is_http_method } from './utils.js';
@@ -33,7 +33,7 @@ const app_template = ({ head, body, assets, nonce }) => ${s(template)
33
33
 
34
34
  const error_template = ({ status, message }) => ${s(error_page)
35
35
  .replace(/%sveltekit\.status%/g, '" + status + "')
36
- .replace(/%sveltekit\.message%/g, '" + message + "')};
36
+ .replace(/%sveltekit\.error\.message%/g, '" + message + "')};
37
37
 
38
38
  let read = null;
39
39
 
@@ -58,9 +58,8 @@ export class Server {
58
58
  check_origin: ${s(config.kit.csrf.checkOrigin)},
59
59
  },
60
60
  dev: false,
61
- get_stack: error => String(error), // for security
62
61
  handle_error: (error, event) => {
63
- this.options.hooks.handleError({
62
+ return this.options.hooks.handleError({
64
63
  error,
65
64
  event,
66
65
 
@@ -69,8 +68,7 @@ export class Server {
69
68
  get request() {
70
69
  throw new Error('request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details');
71
70
  }
72
- });
73
- error.stack = this.options.get_stack(error);
71
+ }) ?? { message: 'Internal Error' };
74
72
  },
75
73
  hooks: null,
76
74
  manifest,
@@ -158,7 +156,24 @@ export async function build_server(options, client) {
158
156
  service_worker_entry_file
159
157
  } = options;
160
158
 
161
- let hooks_file = resolve_entry(config.kit.files.hooks);
159
+ let hooks_file = resolve_entry(config.kit.files.hooks.server);
160
+
161
+ // TODO remove for 1.0
162
+ if (!hooks_file) {
163
+ const old_file = resolve_entry(path.join(process.cwd(), 'src', 'hooks'));
164
+ if (old_file && fs.existsSync(old_file)) {
165
+ throw new Error(
166
+ `Rename your server hook file from ${posixify(
167
+ path.relative(process.cwd(), old_file)
168
+ )} to ${posixify(
169
+ path.relative(process.cwd(), config.kit.files.hooks.server)
170
+ )}${path.extname(
171
+ old_file
172
+ )} (because there's also client hooks now). See the PR for more information: https://github.com/sveltejs/kit/pull/6586`
173
+ );
174
+ }
175
+ }
176
+
162
177
  if (!hooks_file || !fs.existsSync(hooks_file)) {
163
178
  hooks_file = path.join(config.kit.outDir, 'build/hooks.js');
164
179
  fs.writeFileSync(hooks_file, '');
@@ -6,12 +6,12 @@ import { URL } from 'url';
6
6
  import { getRequest, setResponse } from '../../../exports/node/index.js';
7
7
  import { installPolyfills } from '../../../exports/node/polyfills.js';
8
8
  import { coalesce_to_error } from '../../../utils/error.js';
9
- import { posixify } from '../../../utils/filesystem.js';
9
+ import { posixify, resolve_entry } from '../../../utils/filesystem.js';
10
10
  import { load_error_page, load_template } from '../../../core/config/index.js';
11
11
  import { SVELTE_KIT_ASSETS } from '../../../constants.js';
12
12
  import * as sync from '../../../core/sync/sync.js';
13
13
  import { get_mime_lookup, runtime_base, runtime_prefix } from '../../../core/utils.js';
14
- import { prevent_illegal_vite_imports, resolve_entry } from '../utils.js';
14
+ import { prevent_illegal_vite_imports } from '../utils.js';
15
15
  import { compact } from '../../../utils/array.js';
16
16
  import { normalizePath } from 'vite';
17
17
 
@@ -311,11 +311,28 @@ export async function dev(vite, vite_config, svelte_config) {
311
311
  );
312
312
  }
313
313
 
314
- /** @type {Partial<import('types').Hooks>} */
315
- const user_hooks = resolve_entry(svelte_config.kit.files.hooks)
316
- ? await vite.ssrLoadModule(`/${svelte_config.kit.files.hooks}`)
314
+ const hooks_file = svelte_config.kit.files.hooks.server;
315
+ /** @type {Partial<import('types').ServerHooks>} */
316
+ const user_hooks = resolve_entry(hooks_file)
317
+ ? await vite.ssrLoadModule(`/${hooks_file}`)
317
318
  : {};
318
319
 
320
+ // TODO remove for 1.0
321
+ if (!resolve_entry(hooks_file)) {
322
+ const old_file = resolve_entry(path.join(process.cwd(), 'src', 'hooks'));
323
+ if (old_file && fs.existsSync(old_file)) {
324
+ throw new Error(
325
+ `Rename your server hook file from ${posixify(
326
+ path.relative(process.cwd(), old_file)
327
+ )} to ${posixify(
328
+ path.relative(process.cwd(), svelte_config.kit.files.hooks.server)
329
+ )}${path.extname(
330
+ old_file
331
+ )} (because there's also client hooks now). See the PR for more information: https://github.com/sveltejs/kit/pull/6586`
332
+ );
333
+ }
334
+ }
335
+
319
336
  const handle = user_hooks.handle || (({ event, resolve }) => resolve(event));
320
337
 
321
338
  // TODO remove for 1.0
@@ -326,12 +343,13 @@ export async function dev(vite, vite_config, svelte_config) {
326
343
  );
327
344
  }
328
345
 
329
- /** @type {import('types').Hooks} */
346
+ /** @type {import('types').ServerHooks} */
330
347
  const hooks = {
331
348
  handle,
332
349
  handleError:
333
350
  user_hooks.handleError ||
334
- (({ /** @type {Error & { frame?: string }} */ error }) => {
351
+ (({ error: e }) => {
352
+ const error = /** @type {Error & { frame?: string }} */ (e);
335
353
  console.error(colors.bold().red(error.message));
336
354
  if (error.frame) {
337
355
  console.error(colors.gray(error.frame));
@@ -373,10 +391,13 @@ export async function dev(vite, vite_config, svelte_config) {
373
391
  let request;
374
392
 
375
393
  try {
376
- request = await getRequest(base, req);
394
+ request = await getRequest({
395
+ base,
396
+ request: req
397
+ });
377
398
  } catch (/** @type {any} */ err) {
378
399
  res.statusCode = err.status || 400;
379
- return res.end(err.reason || 'Invalid request body');
400
+ return res.end(err.message || 'Invalid request body');
380
401
  }
381
402
 
382
403
  const template = load_template(cwd, svelte_config);
@@ -390,28 +411,29 @@ export async function dev(vite, vite_config, svelte_config) {
390
411
  check_origin: svelte_config.kit.csrf.checkOrigin
391
412
  },
392
413
  dev: true,
393
- get_stack: (error) => fix_stack_trace(error),
394
414
  handle_error: (error, event) => {
395
- hooks.handleError({
396
- error: new Proxy(error, {
397
- get: (target, property) => {
398
- if (property === 'stack') {
399
- return fix_stack_trace(error);
415
+ return (
416
+ hooks.handleError({
417
+ error: new Proxy(error, {
418
+ get: (target, property) => {
419
+ if (property === 'stack') {
420
+ return fix_stack_trace(error);
421
+ }
422
+
423
+ return Reflect.get(target, property, target);
400
424
  }
401
-
402
- return Reflect.get(target, property, target);
425
+ }),
426
+ event,
427
+
428
+ // TODO remove for 1.0
429
+ // @ts-expect-error
430
+ get request() {
431
+ throw new Error(
432
+ 'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
433
+ );
403
434
  }
404
- }),
405
- event,
406
-
407
- // TODO remove for 1.0
408
- // @ts-expect-error
409
- get request() {
410
- throw new Error(
411
- 'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
412
- );
413
- }
414
- });
435
+ }) ?? { message: 'Internal Error' }
436
+ );
415
437
  },
416
438
  hooks,
417
439
  manifest,
@@ -436,7 +458,7 @@ export async function dev(vite, vite_config, svelte_config) {
436
458
  error_template: ({ status, message }) => {
437
459
  return error_page
438
460
  .replace(/%sveltekit\.status%/g, String(status))
439
- .replace(/%sveltekit\.message%/g, message);
461
+ .replace(/%sveltekit\.error\.message%/g, message);
440
462
  },
441
463
  trailing_slash: svelte_config.kit.trailingSlash
442
464
  },
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import colors from 'kleur';
5
5
  import { svelte } from '@sveltejs/vite-plugin-svelte';
6
6
  import * as vite from 'vite';
7
- import { mkdirp, posixify, rimraf } from '../../utils/filesystem.js';
7
+ import { mkdirp, posixify, resolve_entry, rimraf } from '../../utils/filesystem.js';
8
8
  import * as sync from '../../core/sync/sync.js';
9
9
  import { build_server } from './build/build_server.js';
10
10
  import { build_service_worker } from './build/build_service_worker.js';
@@ -14,7 +14,7 @@ import { generate_manifest } from '../../core/generate_manifest/index.js';
14
14
  import { runtime_directory, logger } from '../../core/utils.js';
15
15
  import { find_deps, get_default_build_config } from './build/utils.js';
16
16
  import { preview } from './preview/index.js';
17
- import { get_aliases, resolve_entry, prevent_illegal_rollup_imports, get_env } from './utils.js';
17
+ import { get_aliases, prevent_illegal_rollup_imports, get_env } from './utils.js';
18
18
  import { fileURLToPath } from 'node:url';
19
19
  import { create_static_module, create_dynamic_module } from '../../core/env.js';
20
20
 
@@ -220,6 +220,21 @@ function kit() {
220
220
  return new_config;
221
221
  }
222
222
 
223
+ const allow = new Set([
224
+ svelte_config.kit.files.lib,
225
+ svelte_config.kit.files.routes,
226
+ svelte_config.kit.outDir,
227
+ path.resolve(cwd, 'src'), // TODO this isn't correct if user changed all his files to sth else than src (like in test/options)
228
+ path.resolve(cwd, 'node_modules'),
229
+ path.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules')
230
+ ]);
231
+ // We can only add directories to the allow list, so we find out
232
+ // if there's a client hooks file and pass its directory
233
+ const client_hooks = resolve_entry(svelte_config.kit.files.hooks.client);
234
+ if (client_hooks) {
235
+ allow.add(path.dirname(client_hooks));
236
+ }
237
+
223
238
  // dev and preview config can be shared
224
239
  /** @type {import('vite').UserConfig} */
225
240
  const result = {
@@ -243,16 +258,7 @@ function kit() {
243
258
  root: cwd,
244
259
  server: {
245
260
  fs: {
246
- allow: [
247
- ...new Set([
248
- svelte_config.kit.files.lib,
249
- svelte_config.kit.files.routes,
250
- svelte_config.kit.outDir,
251
- path.resolve(cwd, 'src'),
252
- path.resolve(cwd, 'node_modules'),
253
- path.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules')
254
- ])
255
- ]
261
+ allow: [...allow]
256
262
  },
257
263
  watch: {
258
264
  ignored: [
@@ -131,10 +131,13 @@ export async function preview(vite, vite_config, svelte_config) {
131
131
  let request;
132
132
 
133
133
  try {
134
- request = await getRequest(`${protocol}://${host}`, req);
134
+ request = await getRequest({
135
+ base: `${protocol}://${host}`,
136
+ request: req
137
+ });
135
138
  } catch (/** @type {any} */ err) {
136
139
  res.statusCode = err.status || 400;
137
- return res.end(err.reason || 'Invalid request body');
140
+ return res.end(err.message || 'Invalid request body');
138
141
  }
139
142
 
140
143
  setResponse(