@sveltejs/kit 1.0.0-next.520 → 1.0.0-next.522

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.520",
3
+ "version": "1.0.0-next.522",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -80,13 +80,13 @@
80
80
  "node": ">=16.14"
81
81
  },
82
82
  "scripts": {
83
- "build": "npm run types",
83
+ "build": "pnpm types",
84
84
  "lint": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore",
85
85
  "check": "tsc",
86
86
  "check:all": "tsc && pnpm -r --filter=\"./**\" check",
87
- "format": "npm run lint -- --write",
88
- "test": "npm run test:unit && npm run test:integration",
89
- "test:integration": "pnpm run -r --workspace-concurrency 1 --filter=\"./test/**\" test",
87
+ "format": "pnpm lint --write",
88
+ "test": "pnpm test:unit && pnpm test:integration",
89
+ "test:integration": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test",
90
90
  "test:unit": "uvu src \"(spec\\.js|test[\\\\/]index\\.js)\"",
91
91
  "types": "node scripts/extract-types.js",
92
92
  "postinstall": "node postinstall.js"
@@ -88,7 +88,7 @@ const options = object(
88
88
  // TODO: remove this for the 1.0 release
89
89
  amp: error(
90
90
  (keypath) =>
91
- `${keypath} has been removed. See https://kit.svelte.dev/docs/seo#amp for details on how to support AMP`
91
+ `${keypath} has been removed. See https://kit.svelte.dev/docs/seo#manual-setup-amp for details on how to support AMP`
92
92
  ),
93
93
 
94
94
  appDir: validate('_app', (input, keypath) => {
@@ -321,7 +321,7 @@ const options = object(
321
321
  // TODO remove this for 1.0
322
322
  ssr: error(
323
323
  (keypath) =>
324
- `${keypath} has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle`
324
+ `${keypath} has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#server-hooks-handle`
325
325
  ),
326
326
 
327
327
  // TODO remove this for 1.0
@@ -102,54 +102,15 @@ export async function prerender() {
102
102
  });
103
103
 
104
104
  installPolyfills();
105
+
106
+ // TODO remove this for 1.0
105
107
  const { fetch } = globalThis;
106
108
  globalThis.fetch = async (info, init) => {
107
- /** @type {string} */
108
- let url;
109
-
110
- /** @type {RequestInit} */
111
- let opts = {};
112
-
113
- if (info instanceof Request) {
114
- url = info.url;
115
-
116
- opts = {
117
- method: info.method,
118
- headers: info.headers,
119
- body: info.body,
120
- mode: info.mode,
121
- credentials: info.credentials,
122
- cache: info.cache,
123
- redirect: info.redirect,
124
- referrer: info.referrer,
125
- integrity: info.integrity
126
- };
127
- } else {
128
- url = info.toString();
129
- }
109
+ const url = info instanceof Request ? info.url : info.toString();
130
110
 
131
111
  if (url.startsWith(config.prerender.origin + '/')) {
132
- const request = new Request(url, opts);
133
- const response = await server.respond(request, {
134
- getClientAddress,
135
- prerendering: {
136
- dependencies: new Map()
137
- }
138
- });
139
-
140
- const decoded = new URL(url).pathname;
141
-
142
- save(
143
- 'dependencies',
144
- response,
145
- Buffer.from(await response.clone().arrayBuffer()),
146
- decoded,
147
- encodeURI(decoded),
148
- null,
149
- 'fetched'
150
- );
151
-
152
- return response;
112
+ const sliced = url.slice(config.prerender.origin.length);
113
+ throw new Error(`Use \`event.fetch('${sliced}')\` instead of the global \`fetch('${url}')\``);
153
114
  }
154
115
 
155
116
  return fetch(info, init);
@@ -409,7 +370,7 @@ export async function prerender() {
409
370
  for (const [id, prerender] of prerender_map) {
410
371
  if (prerender) {
411
372
  if (id.includes('[')) continue;
412
- const path = `/${id.split('/').filter(affects_path).join('/')}`;
373
+ const path = `${id.split('/').filter(affects_path).join('/')}`;
413
374
  enqueue(null, config.paths.base + path);
414
375
  }
415
376
  }
@@ -207,7 +207,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
207
207
  }
208
208
  };
209
209
 
210
- walk(0, '', '', null);
210
+ walk(0, '/', '', null);
211
211
 
212
212
  if (routes.length === 1) {
213
213
  const root = routes[0];
@@ -223,7 +223,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
223
223
  // If there's no routes directory, we'll just create a single empty route. This ensures the root layout and
224
224
  // error components are included in the manifest, which is needed for subsequent build/dev commands to work
225
225
  routes.push({
226
- id: '',
226
+ id: '/',
227
227
  segment: '',
228
228
  pattern: /^$/,
229
229
  names: [],
@@ -21,11 +21,14 @@ export function enhance(form, submit = () => {}) {
21
21
  * @param {{
22
22
  * action: URL;
23
23
  * result: import('types').ActionResult;
24
+ * reset?: boolean
24
25
  * }} opts
25
26
  */
26
- const fallback_callback = async ({ action, result }) => {
27
+ const fallback_callback = async ({ action, result, reset }) => {
27
28
  if (result.type === 'success') {
28
- form.reset();
29
+ if (reset !== false) {
30
+ form.reset();
31
+ }
29
32
  await invalidateAll();
30
33
  }
31
34
 
@@ -97,7 +100,7 @@ export function enhance(form, submit = () => {}) {
97
100
  action,
98
101
  data,
99
102
  form,
100
- update: () => fallback_callback({ action, result }),
103
+ update: (opts) => fallback_callback({ action, result, reset: opts?.reset }),
101
104
  // @ts-expect-error generic constraints stuff we don't care about
102
105
  result,
103
106
  // TODO remove for 1.0
@@ -199,7 +199,7 @@ export function create_client({ target, base, trailing_slash }) {
199
199
  const intent = get_navigation_intent(url, false);
200
200
 
201
201
  if (!intent) {
202
- throw new Error('Attempted to prefetch a URL that does not belong to this app');
202
+ throw new Error(`Attempted to prefetch a URL that does not belong to this app: ${url}`);
203
203
  }
204
204
 
205
205
  load_cache = { id: intent.id, promise: load_route(intent) };
@@ -29,7 +29,7 @@ if (import.meta.env.DEV) {
29
29
  const heuristic = can_inspect_stack_trace ? stack.includes('load_node') : loading;
30
30
  if (heuristic) {
31
31
  console.warn(
32
- `Loading ${url} using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/load#input-fetch`
32
+ `Loading ${url} using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/load#making-fetch-requests`
33
33
  );
34
34
  }
35
35
 
@@ -16,9 +16,6 @@ export function create_fetch({ event, options, state, get_cookie_header }) {
16
16
 
17
17
  const request_body = init?.body;
18
18
 
19
- /** @type {import('types').PrerenderDependency} */
20
- let dependency;
21
-
22
19
  return await options.hooks.handleFetch({
23
20
  event,
24
21
  request,
@@ -135,11 +132,6 @@ export function create_fetch({ event, options, state, get_cookie_header }) {
135
132
 
136
133
  response = await respond(request, options, state);
137
134
 
138
- if (state.prerendering) {
139
- dependency = { response, body: null };
140
- state.prerendering.dependencies.set(url.pathname, dependency);
141
- }
142
-
143
135
  const set_cookie = response.headers.get('set-cookie');
144
136
  if (set_cookie) {
145
137
  for (const str of set_cookie_parser.splitCookiesString(set_cookie)) {
@@ -106,8 +106,13 @@ export async function load_data({
106
106
  const url = new URL(input instanceof Request ? input.url : input, event.url);
107
107
  const same_origin = url.origin === event.url.origin;
108
108
 
109
- const request_body = init?.body;
110
- const dependency = same_origin && state.prerendering?.dependencies.get(url.pathname);
109
+ /** @type {import('types').PrerenderDependency} */
110
+ let dependency;
111
+
112
+ if (same_origin && state.prerendering) {
113
+ dependency = { response, body: null };
114
+ state.prerendering.dependencies.set(url.pathname, dependency);
115
+ }
111
116
 
112
117
  const proxy = new Proxy(response, {
113
118
  get(response, key, _receiver) {
@@ -127,7 +132,7 @@ export async function load_data({
127
132
  fetched.push({
128
133
  url: same_origin ? url.href.slice(event.url.origin.length) : url.href,
129
134
  method: event.request.method,
130
- request_body: /** @type {string | ArrayBufferView | undefined} */ (request_body),
135
+ request_body: /** @type {string | ArrayBufferView | undefined} */ (init?.body),
131
136
  response_body: body,
132
137
  response: response
133
138
  });
@@ -165,8 +170,6 @@ export async function load_data({
165
170
  };
166
171
  }
167
172
 
168
- // TODO arrayBuffer?
169
-
170
173
  return Reflect.get(response, key, response);
171
174
  }
172
175
  });
@@ -181,7 +184,7 @@ export async function load_data({
181
184
  const included = resolve_opts.filterSerializedResponseHeaders(lower, value);
182
185
  if (!included) {
183
186
  throw new Error(
184
- `Failed to get response header "${lower}" — it must be included by the \`filterSerializedResponseHeaders\` option: https://kit.svelte.dev/docs/hooks#handle (at ${event.routeId})`
187
+ `Failed to get response header "${lower}" — it must be included by the \`filterSerializedResponseHeaders\` option: https://kit.svelte.dev/docs/hooks#server-hooks-handle (at ${event.routeId})`
185
188
  );
186
189
  }
187
190
  }
@@ -179,7 +179,7 @@ export async function render_response({
179
179
  const match = /\[(\d+)\]\.data\.(.+)/.exec(error.path);
180
180
  if (match) {
181
181
  throw new Error(
182
- `Data returned from \`load\` while rendering /${event.routeId} is not serializable: ${error.message} (data.${match[2]})`
182
+ `Data returned from \`load\` while rendering ${event.routeId} is not serializable: ${error.message} (data.${match[2]})`
183
183
  );
184
184
  }
185
185
  throw error;
@@ -83,7 +83,7 @@ export function data_response(data, event) {
83
83
  const error = /** @type {any} */ (e);
84
84
  const match = /\[(\d+)\]\.data\.(.+)/.exec(error.path);
85
85
  const message = match
86
- ? `Data returned from \`load\` while rendering /${event.routeId} is not serializable: ${error.message} (data.${match[2]})`
86
+ ? `Data returned from \`load\` while rendering ${event.routeId} is not serializable: ${error.message} (data.${match[2]})`
87
87
  : error.message;
88
88
  return new Response(JSON.stringify(message), { headers, status: 500 });
89
89
  }
@@ -13,11 +13,12 @@ export function parse_route_id(id) {
13
13
  let add_trailing_slash = true;
14
14
 
15
15
  const pattern =
16
- id === ''
16
+ id === '/'
17
17
  ? /^\/$/
18
18
  : new RegExp(
19
19
  `^${id
20
20
  .split(/(?:\/|$)/)
21
+ .slice(1)
21
22
  .filter(affects_path)
22
23
  .map((segment, i, segments) => {
23
24
  const decoded_segment = decodeURIComponent(segment);
@@ -110,7 +110,11 @@ declare module '$app/forms' {
110
110
  form: HTMLFormElement;
111
111
  action: URL;
112
112
  result: ActionResult<Success, Invalid>;
113
- update: () => Promise<void>;
113
+ /**
114
+ * Call this to get the default behavior of a form submission response.
115
+ * @param options Set `reset: false` if you don't want the `<form>` values to be reset after a successful submission.
116
+ */
117
+ update: (options?: { reset: boolean }) => Promise<void>;
114
118
  }) => void);
115
119
 
116
120
  /**
@@ -133,7 +137,7 @@ declare module '$app/forms' {
133
137
  * If this function or its return value isn't set, it
134
138
  * - falls back to updating the `form` prop with the returned data if the action is one same page as the form
135
139
  * - updates `$page.status`
136
- * - invalidates all data in case of successful submission with no redirect response
140
+ * - resets the `<form>` element and invalidates all data in case of successful submission with no redirect response
137
141
  * - redirects in case of a redirect response
138
142
  * - redirects to the nearest error page in case of an unexpected error
139
143
  *
package/types/index.d.ts CHANGED
@@ -271,18 +271,108 @@ export interface LoadEvent<
271
271
  Data extends Record<string, unknown> | null = Record<string, any> | null,
272
272
  ParentData extends Record<string, unknown> = Record<string, any>
273
273
  > extends NavigationEvent<Params> {
274
+ /**
275
+ * `fetch` is equivalent to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch), with a few additional features:
276
+ *
277
+ * - it can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request
278
+ * - it can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context)
279
+ * - internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call
280
+ * - during server-side rendering, the response will be captured and inlined into the rendered HTML. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://kit.svelte.dev/docs/hooks#server-hooks-handle)
281
+ * - during hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request
282
+ *
283
+ * > Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it.
284
+ */
274
285
  fetch: typeof fetch;
286
+ /**
287
+ * Contains the data returned by the route's server `load` function (in `+layout.server.js` or `+page.server.js`), if any.
288
+ */
275
289
  data: Data;
290
+ /**
291
+ * If you need to set headers for the response, you can do so using the this method. This is useful if you want the page to be cached, for example:
292
+ *
293
+ * ```js
294
+ * /// file: src/routes/blog/+page.js
295
+ * export async function load({ fetch, setHeaders }) {
296
+ * const url = `https://cms.example.com/articles.json`;
297
+ * const response = await fetch(url);
298
+ *
299
+ * setHeaders({
300
+ * age: response.headers.get('age'),
301
+ * 'cache-control': response.headers.get('cache-control')
302
+ * });
303
+ *
304
+ * return response.json();
305
+ * }
306
+ * ```
307
+ *
308
+ * Setting the same header multiple times (even in separate `load` functions) is an error — you can only set a given header once.
309
+ *
310
+ * You cannot add a `set-cookie` header with `setHeaders` — use the [`cookies`](https://kit.svelte.dev/docs/types#sveltejs-kit-cookies) API in a server-only `load` function instead.
311
+ *
312
+ * `setHeaders` has no effect when a `load` function runs in the browser.
313
+ */
276
314
  setHeaders: (headers: Record<string, string>) => void;
315
+ /**
316
+ * `await parent()` returns data from parent `+layout.js` `load` functions.
317
+ * Implicitly, a missing `+layout.js` is treated as a `({ data }) => data` function, meaning that it will return and forward data from parent `+layout.server.js` files.
318
+ *
319
+ * Be careful not to introduce accidental waterfalls when using `await parent()`. If for example you only want to merge parent data into the returned output, call it _after_ fetching your other data.
320
+ */
277
321
  parent: () => Promise<ParentData>;
322
+ /**
323
+ * This function declares that the `load` function has a _dependency_ on one or more URLs or custom identifiers, which can subsequently be used with [`invalidate()`](/docs/modules#$app-navigation-invalidate) to cause `load` to rerun.
324
+ *
325
+ * Most of the time you won't need this, as `fetch` calls `depends` on your behalf — it's only necessary if you're using a custom API client that bypasses `fetch`.
326
+ *
327
+ * URLs can be absolute or relative to the page being loaded, and must be [encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding).
328
+ *
329
+ * Custom identifiers have to be prefixed with one or more lowercase letters followed by a colon to conform to the [URI specification](https://www.rfc-editor.org/rfc/rfc3986.html).
330
+ *
331
+ * The following example shows how to use `depends` to register a dependency on a custom identifier, which is `invalidate`d after a button click, making the `load` function rerun.
332
+ *
333
+ * ```js
334
+ * /// file: src/routes/+page.js
335
+ * let count = 0;
336
+ * export async function load({ depends }) {
337
+ * depends('increase:count');
338
+ *
339
+ * return { count: count++ };
340
+ * }
341
+ * ```
342
+ *
343
+ * ```html
344
+ * /// file: src/routes/+page.svelte
345
+ * <script>
346
+ * import { invalidate } from '$app/navigation';
347
+ *
348
+ * export let data;
349
+ *
350
+ * const increase = async () => {
351
+ * await invalidate('increase:count');
352
+ * }
353
+ * </script>
354
+ *
355
+ * <p>{data.count}<p>
356
+ * <button on:click={increase}>Increase Count</button>
357
+ * ```
358
+ */
278
359
  depends: (...deps: string[]) => void;
279
360
  }
280
361
 
281
362
  export interface NavigationEvent<
282
363
  Params extends Partial<Record<string, string>> = Partial<Record<string, string>>
283
364
  > {
365
+ /**
366
+ * The parameters of the current page - e.g. for a route like `/blog/[slug]`, the `slug` parameter
367
+ */
284
368
  params: Params;
369
+ /**
370
+ * The route ID of the current page - e.g. for `src/routes/blog/[slug]`, it would be `blog/[slug]`
371
+ */
285
372
  routeId: string | null;
373
+ /**
374
+ * The URL of the current page
375
+ */
286
376
  url: URL;
287
377
  }
288
378
 
@@ -342,15 +432,70 @@ export interface ParamMatcher {
342
432
  export interface RequestEvent<
343
433
  Params extends Partial<Record<string, string>> = Partial<Record<string, string>>
344
434
  > {
435
+ /**
436
+ * Get or set cookies related to the current request
437
+ */
345
438
  cookies: Cookies;
439
+ /**
440
+ * `fetch` is equivalent to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch), with a few additional features:
441
+ *
442
+ * - it can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request
443
+ * - it can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context)
444
+ * - internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call
445
+ *
446
+ * > Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it.
447
+ */
346
448
  fetch: typeof fetch;
449
+ /**
450
+ * The client's IP address, set by the adapter.
451
+ */
347
452
  getClientAddress: () => string;
453
+ /**
454
+ * Contains custom data that was added to the request within the [`handle hook`](https://kit.svelte.dev/docs/hooks#server-hooks-handle).
455
+ */
348
456
  locals: App.Locals;
457
+ /**
458
+ * The parameters of the current page or endpoint - e.g. for a route like `/blog/[slug]`, the `slug` parameter
459
+ */
349
460
  params: Params;
461
+ /**
462
+ * Additional data made available through the adapter.
463
+ */
350
464
  platform: Readonly<App.Platform>;
465
+ /**
466
+ * The original request object
467
+ */
351
468
  request: Request;
469
+ /**
470
+ * The route ID of the current page - e.g. for `src/routes/blog/[slug]`, it would be `blog/[slug]`
471
+ */
352
472
  routeId: string | null;
473
+ /**
474
+ * If you need to set headers for the response, you can do so using the this method. This is useful if you want the page to be cached, for example:
475
+ *
476
+ * ```js
477
+ * /// file: src/routes/blog/+page.js
478
+ * export async function load({ fetch, setHeaders }) {
479
+ * const url = `https://cms.example.com/articles.json`;
480
+ * const response = await fetch(url);
481
+ *
482
+ * setHeaders({
483
+ * age: response.headers.get('age'),
484
+ * 'cache-control': response.headers.get('cache-control')
485
+ * });
486
+ *
487
+ * return response.json();
488
+ * }
489
+ * ```
490
+ *
491
+ * Setting the same header multiple times (even in separate `load` functions) is an error — you can only set a given header once.
492
+ *
493
+ * You cannot add a `set-cookie` header with `setHeaders` — use the [`cookies`](https://kit.svelte.dev/docs/types#sveltejs-kit-cookies) API instead.
494
+ */
353
495
  setHeaders: (headers: Record<string, string>) => void;
496
+ /**
497
+ * The URL of the current page or endpoint
498
+ */
354
499
  url: URL;
355
500
  }
356
501
 
@@ -415,7 +560,49 @@ export interface ServerLoadEvent<
415
560
  Params extends Partial<Record<string, string>> = Partial<Record<string, string>>,
416
561
  ParentData extends Record<string, any> = Record<string, any>
417
562
  > extends RequestEvent<Params> {
563
+ /**
564
+ * `await parent()` returns data from parent `+layout.server.js` `load` functions.
565
+ *
566
+ * Be careful not to introduce accidental waterfalls when using `await parent()`. If for example you only want to merge parent data into the returned output, call it _after_ fetching your other data.
567
+ */
418
568
  parent: () => Promise<ParentData>;
569
+ /**
570
+ * This function declares that the `load` function has a _dependency_ on one or more URLs or custom identifiers, which can subsequently be used with [`invalidate()`](/docs/modules#$app-navigation-invalidate) to cause `load` to rerun.
571
+ *
572
+ * Most of the time you won't need this, as `fetch` calls `depends` on your behalf — it's only necessary if you're using a custom API client that bypasses `fetch`.
573
+ *
574
+ * URLs can be absolute or relative to the page being loaded, and must be [encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding).
575
+ *
576
+ * Custom identifiers have to be prefixed with one or more lowercase letters followed by a colon to conform to the [URI specification](https://www.rfc-editor.org/rfc/rfc3986.html).
577
+ *
578
+ * The following example shows how to use `depends` to register a dependency on a custom identifier, which is `invalidate`d after a button click, making the `load` function rerun.
579
+ *
580
+ * ```js
581
+ * /// file: src/routes/+page.js
582
+ * let count = 0;
583
+ * export async function load({ depends }) {
584
+ * depends('increase:count');
585
+ *
586
+ * return { count: count++ };
587
+ * }
588
+ * ```
589
+ *
590
+ * ```html
591
+ * /// file: src/routes/+page.svelte
592
+ * <script>
593
+ * import { invalidate } from '$app/navigation';
594
+ *
595
+ * export let data;
596
+ *
597
+ * const increase = async () => {
598
+ * await invalidate('increase:count');
599
+ * }
600
+ * </script>
601
+ *
602
+ * <p>{data.count}<p>
603
+ * <button on:click={increase}>Increase Count</button>
604
+ * ```
605
+ */
419
606
  depends: (...deps: string[]) => void;
420
607
  }
421
608