@sveltejs/kit 1.0.0-next.492 → 1.0.0-next.494

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.492",
3
+ "version": "1.0.0-next.494",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -225,97 +225,115 @@ function create_routes_and_nodes(cwd, config, fallback) {
225
225
  walk(0, '', '', null);
226
226
 
227
227
  const root = /** @type {import('types').RouteData} */ (route_map.get(''));
228
-
229
- // TODO remove for 1.0
230
228
  if (route_map.size === 1) {
231
229
  if (!root.leaf && !root.error && !root.layout && !root.endpoint) {
232
230
  throw new Error(
231
+ // TODO adjust this error message for 1.0
232
+ // 'No routes found. If you are using a custom src/routes directory, make sure it is specified in svelte.config.js'
233
233
  'The filesystem router API has changed, see https://github.com/sveltejs/kit/discussions/5774 for details'
234
234
  );
235
235
  }
236
236
  }
237
+ } else {
238
+ // If there's no routes directory, we'll just create a single empty route. This ensures the root layout and
239
+ // error components are included in the manifest, which is needed for subsequent build/dev commands to work
240
+ route_map.set('', {
241
+ id: '',
242
+ segment: '',
243
+ pattern: /^$/,
244
+ names: [],
245
+ types: [],
246
+ parent: null,
247
+ layout: null,
248
+ error: null,
249
+ leaf: null,
250
+ page: null,
251
+ endpoint: null
252
+ });
253
+ }
237
254
 
238
- if (!root.layout?.component) {
239
- if (!root.layout) root.layout = { depth: 0, child_pages: [] };
240
- root.layout.component = posixify(path.relative(cwd, `${fallback}/layout.svelte`));
241
- }
255
+ const root = /** @type {import('types').RouteData} */ (route_map.get(''));
242
256
 
243
- if (!root.error?.component) {
244
- if (!root.error) root.error = { depth: 0 };
245
- root.error.component = posixify(path.relative(cwd, `${fallback}/error.svelte`));
246
- }
257
+ if (!root.layout?.component) {
258
+ if (!root.layout) root.layout = { depth: 0, child_pages: [] };
259
+ root.layout.component = posixify(path.relative(cwd, `${fallback}/layout.svelte`));
260
+ }
247
261
 
248
- // we do layouts/errors first as they are more likely to be reused,
249
- // and smaller indexes take fewer bytes. also, this guarantees that
250
- // the default error/layout are 0/1
251
- route_map.forEach((route) => {
252
- if (route.layout) nodes.push(route.layout);
253
- if (route.error) nodes.push(route.error);
254
- });
262
+ if (!root.error?.component) {
263
+ if (!root.error) root.error = { depth: 0 };
264
+ root.error.component = posixify(path.relative(cwd, `${fallback}/error.svelte`));
265
+ }
255
266
 
256
- /** @type {Map<string, string>} */
257
- const conflicts = new Map();
267
+ // we do layouts/errors first as they are more likely to be reused,
268
+ // and smaller indexes take fewer bytes. also, this guarantees that
269
+ // the default error/layout are 0/1
270
+ route_map.forEach((route) => {
271
+ if (route.layout) nodes.push(route.layout);
272
+ if (route.error) nodes.push(route.error);
273
+ });
258
274
 
259
- route_map.forEach((route) => {
260
- if (!route.leaf) return;
275
+ /** @type {Map<string, string>} */
276
+ const conflicts = new Map();
261
277
 
262
- nodes.push(route.leaf);
278
+ route_map.forEach((route) => {
279
+ if (!route.leaf) return;
263
280
 
264
- const normalized = route.id.split('/').filter(affects_path).join('/');
281
+ nodes.push(route.leaf);
265
282
 
266
- if (conflicts.has(normalized)) {
267
- throw new Error(`${conflicts.get(normalized)} and ${route.id} occupy the same route`);
268
- }
283
+ const normalized = route.id.split('/').filter(affects_path).join('/');
269
284
 
270
- conflicts.set(normalized, route.id);
271
- });
285
+ if (conflicts.has(normalized)) {
286
+ throw new Error(`${conflicts.get(normalized)} and ${route.id} occupy the same route`);
287
+ }
272
288
 
273
- const indexes = new Map(nodes.map((node, i) => [node, i]));
289
+ conflicts.set(normalized, route.id);
290
+ });
274
291
 
275
- route_map.forEach((route) => {
276
- if (!route.leaf) return;
292
+ const indexes = new Map(nodes.map((node, i) => [node, i]));
277
293
 
278
- route.page = {
279
- layouts: [],
280
- errors: [],
281
- leaf: /** @type {number} */ (indexes.get(route.leaf))
282
- };
294
+ route_map.forEach((route) => {
295
+ if (!route.leaf) return;
283
296
 
284
- /** @type {import('types').RouteData | null} */
285
- let current_route = route;
286
- let current_node = route.leaf;
287
- let parent_id = route.leaf.parent_id;
288
-
289
- while (current_route) {
290
- if (parent_id === undefined || current_route.segment === parent_id) {
291
- if (current_route.layout || current_route.error) {
292
- route.page.layouts.unshift(
293
- current_route.layout ? indexes.get(current_route.layout) : undefined
294
- );
295
- route.page.errors.unshift(
296
- current_route.error ? indexes.get(current_route.error) : undefined
297
- );
298
- }
297
+ route.page = {
298
+ layouts: [],
299
+ errors: [],
300
+ leaf: /** @type {number} */ (indexes.get(route.leaf))
301
+ };
299
302
 
300
- if (current_route.layout) {
301
- /** @type {import('types').PageNode[]} */ (current_route.layout.child_pages).push(
302
- route.leaf
303
- );
304
- current_node.parent = current_node = current_route.layout;
305
- parent_id = current_node.parent_id;
306
- } else {
307
- parent_id = undefined;
308
- }
303
+ /** @type {import('types').RouteData | null} */
304
+ let current_route = route;
305
+ let current_node = route.leaf;
306
+ let parent_id = route.leaf.parent_id;
307
+
308
+ while (current_route) {
309
+ if (parent_id === undefined || current_route.segment === parent_id) {
310
+ if (current_route.layout || current_route.error) {
311
+ route.page.layouts.unshift(
312
+ current_route.layout ? indexes.get(current_route.layout) : undefined
313
+ );
314
+ route.page.errors.unshift(
315
+ current_route.error ? indexes.get(current_route.error) : undefined
316
+ );
309
317
  }
310
318
 
311
- current_route = current_route.parent;
319
+ if (current_route.layout) {
320
+ /** @type {import('types').PageNode[]} */ (current_route.layout.child_pages).push(
321
+ route.leaf
322
+ );
323
+ current_node.parent = current_node = current_route.layout;
324
+ parent_id = current_node.parent_id;
325
+ } else {
326
+ parent_id = undefined;
327
+ }
312
328
  }
313
329
 
314
- if (parent_id !== undefined) {
315
- throw new Error(`${current_node.component} references missing segment "${parent_id}"`);
316
- }
317
- });
318
- }
330
+ current_route = current_route.parent;
331
+ }
332
+
333
+ if (parent_id !== undefined) {
334
+ throw new Error(`${current_node.component} references missing segment "${parent_id}"`);
335
+ }
336
+ });
319
337
 
320
338
  const routes = Array.from(route_map.values()).sort((a, b) => compare(a, b, segment_map));
321
339
 
@@ -82,7 +82,7 @@ export class Server {
82
82
  public_env: {},
83
83
  read,
84
84
  root,
85
- service_worker: ${has_service_worker ? "base + '/service-worker.js'" : 'null'},
85
+ service_worker: ${has_service_worker},
86
86
  app_template,
87
87
  app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
88
88
  error_template,
@@ -460,6 +460,7 @@ export async function dev(vite, vite_config, svelte_config) {
460
460
  .replace(/%sveltekit\.status%/g, String(status))
461
461
  .replace(/%sveltekit\.error\.message%/g, message);
462
462
  },
463
+ service_worker: false,
463
464
  trailing_slash: svelte_config.kit.trailingSlash
464
465
  },
465
466
  {
@@ -521,22 +521,15 @@ export function create_client({ target, base, trailing_slash }) {
521
521
  }
522
522
  }
523
523
 
524
- /** @type {Record<string, string>} */
525
- const uses_params = {};
526
- for (const key in params) {
527
- Object.defineProperty(uses_params, key, {
528
- get() {
529
- uses.params.add(key);
530
- return params[key];
531
- },
532
- enumerable: true
533
- });
534
- }
535
-
536
524
  /** @type {import('types').LoadEvent} */
537
525
  const load_input = {
538
526
  routeId,
539
- params: uses_params,
527
+ params: new Proxy(params, {
528
+ get: (target, key) => {
529
+ uses.params.add(/** @type {string} */ (key));
530
+ return target[/** @type {string} */ (key)];
531
+ }
532
+ }),
540
533
  data: server_data_node?.data ?? null,
541
534
  url: make_trackable(url, () => {
542
535
  uses.url = true;
@@ -544,9 +537,7 @@ export function create_client({ target, base, trailing_slash }) {
544
537
  async fetch(resource, init) {
545
538
  let requested;
546
539
 
547
- if (typeof resource === 'string') {
548
- requested = resource;
549
- } else {
540
+ if (resource instanceof Request) {
550
541
  requested = resource.url;
551
542
 
552
543
  // we're not allowed to modify the received `Request` object, so in order
@@ -571,6 +562,8 @@ export function create_client({ target, base, trailing_slash }) {
571
562
  signal: resource.signal,
572
563
  ...init
573
564
  };
565
+ } else {
566
+ requested = resource;
574
567
  }
575
568
 
576
569
  // we must fixup relative urls so they are resolved from the target page
@@ -640,20 +633,21 @@ export function create_client({ target, base, trailing_slash }) {
640
633
  }
641
634
 
642
635
  /**
643
- * @param {import('types').Uses | undefined} uses
636
+ * @param {boolean} url_changed
644
637
  * @param {boolean} parent_changed
645
- * @param {{ url: boolean, params: string[] }} changed
638
+ * @param {import('types').Uses | undefined} uses
639
+ * @param {Record<string, string>} params
646
640
  */
647
- function has_changed(changed, parent_changed, uses) {
641
+ function has_changed(url_changed, parent_changed, uses, params) {
648
642
  if (force_invalidation) return true;
649
643
 
650
644
  if (!uses) return false;
651
645
 
652
646
  if (uses.parent && parent_changed) return true;
653
- if (changed.url && uses.url) return true;
647
+ if (uses.url && url_changed) return true;
654
648
 
655
- for (const param of changed.params) {
656
- if (uses.params.has(param)) return true;
649
+ for (const param of uses.params) {
650
+ if (params[param] !== current.params[param]) return true;
657
651
  }
658
652
 
659
653
  for (const href of uses.dependencies) {
@@ -697,11 +691,6 @@ export function create_client({ target, base, trailing_slash }) {
697
691
 
698
692
  const { errors, layouts, leaf } = route;
699
693
 
700
- const changed = current.url && {
701
- url: id !== current.url.pathname + current.url.search,
702
- params: Object.keys(params).filter((key) => current.params[key] !== params[key])
703
- };
704
-
705
694
  const loaders = [...layouts, leaf];
706
695
 
707
696
  // preload modules to avoid waterfall, but handle rejections
@@ -713,12 +702,15 @@ export function create_client({ target, base, trailing_slash }) {
713
702
  /** @type {import('types').ServerData | null} */
714
703
  let server_data = null;
715
704
 
705
+ const url_changed = current.url ? id !== current.url.pathname + current.url.search : false;
706
+
716
707
  const invalid_server_nodes = loaders.reduce((acc, loader, i) => {
717
708
  const previous = current.branch[i];
709
+
718
710
  const invalid =
719
711
  !!loader?.[0] &&
720
712
  (previous?.loader !== loader[1] ||
721
- has_changed(changed, acc.some(Boolean), previous.server?.uses));
713
+ has_changed(url_changed, acc.some(Boolean), previous.server?.uses, params));
722
714
 
723
715
  acc.push(invalid);
724
716
  return acc;
@@ -757,7 +749,7 @@ export function create_client({ target, base, trailing_slash }) {
757
749
  const valid =
758
750
  (!server_data_node || server_data_node.type === 'skip') &&
759
751
  loader[1] === previous?.loader &&
760
- !has_changed(changed, parent_changed, previous.shared?.uses);
752
+ !has_changed(url_changed, parent_changed, previous.shared?.uses, params);
761
753
  if (valid) return previous;
762
754
 
763
755
  parent_changed = true;
@@ -62,12 +62,12 @@ const cache = new Map();
62
62
  /**
63
63
  * Should be called on the initial run of load functions that hydrate the page.
64
64
  * Saves any requests with cache-control max-age to the cache.
65
- * @param {RequestInfo} resource
65
+ * @param {RequestInfo | URL} resource
66
66
  * @param {string} resolved
67
67
  * @param {RequestInit} [opts]
68
68
  */
69
69
  export function initial_fetch(resource, resolved, opts) {
70
- const url = JSON.stringify(typeof resource === 'string' ? resource : resource.url);
70
+ const url = JSON.stringify(resource instanceof Request ? resource.url : resource);
71
71
 
72
72
  let selector = `script[data-sveltekit-fetched][data-url=${url}]`;
73
73
 
@@ -1,5 +1,5 @@
1
1
  import { escape_html_attr } from '../../../utils/escape.js';
2
- import { sha256, base64 } from './crypto.js';
2
+ import { base64, sha256 } from './crypto.js';
3
3
 
4
4
  const array = new Uint8Array(16);
5
5
 
@@ -15,7 +15,8 @@ const quoted = new Set([
15
15
  'unsafe-inline',
16
16
  'none',
17
17
  'strict-dynamic',
18
- 'report-sample'
18
+ 'report-sample',
19
+ 'wasm-unsafe-eval'
19
20
  ]);
20
21
 
21
22
  const crypto_pattern = /^(nonce|sha\d\d\d)-/;
@@ -172,21 +172,21 @@ export function create_fetch({ event, options, state, route, prerender_default,
172
172
  state.prerendering.dependencies.set(url.pathname, dependency);
173
173
  }
174
174
 
175
+ const set_cookie = response.headers.get('set-cookie');
176
+ if (set_cookie) {
177
+ set_cookies.push(
178
+ ...set_cookie_parser.splitCookiesString(set_cookie).map((str) => {
179
+ const { name, value, ...options } = set_cookie_parser.parseString(str);
180
+ // options.sameSite is string, something more specific is required - type cast is safe
181
+ return /** @type{import('./types').Cookie} */ ({ name, value, options });
182
+ })
183
+ );
184
+ }
185
+
175
186
  return response;
176
187
  }
177
188
  });
178
189
 
179
- const set_cookie = response.headers.get('set-cookie');
180
- if (set_cookie) {
181
- set_cookies.push(
182
- ...set_cookie_parser.splitCookiesString(set_cookie).map((str) => {
183
- const { name, value, ...options } = set_cookie_parser.parseString(str);
184
- // options.sameSite is string, something more specific is required - type cast is safe
185
- return /** @type{import('./types').Cookie} */ ({ name, value, options });
186
- })
187
- );
188
- }
189
-
190
190
  const proxy = new Proxy(response, {
191
191
  get(response, key, _receiver) {
192
192
  async function text() {
@@ -187,37 +187,6 @@ export async function render_response({
187
187
  serialized.form = devalue(form_value);
188
188
  }
189
189
 
190
- // prettier-ignore
191
- const init_app = `
192
- import { start } from ${s(prefixed(entry.file))};
193
-
194
- start({
195
- env: ${s(options.public_env)},
196
- hydrate: ${page_config.ssr ? `{
197
- status: ${status},
198
- error: ${s(error)},
199
- node_ids: [${branch.map(({ node }) => node.index).join(', ')}],
200
- params: ${devalue(event.params)},
201
- routeId: ${s(event.routeId)},
202
- data: ${serialized.data},
203
- form: ${serialized.form}
204
- }` : 'null'},
205
- paths: ${s(options.paths)},
206
- target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode,
207
- trailing_slash: ${s(options.trailing_slash)}
208
- });
209
- `;
210
-
211
- // we use an anonymous function instead of an arrow function to support
212
- // older browsers (https://github.com/sveltejs/kit/pull/5417)
213
- const init_service_worker = `
214
- if ('serviceWorker' in navigator) {
215
- addEventListener('load', function () {
216
- navigator.serviceWorker.register('${options.service_worker}');
217
- });
218
- }
219
- `;
220
-
221
190
  if (inline_styles.size > 0) {
222
191
  const content = Array.from(inline_styles.values()).join('\n');
223
192
 
@@ -248,15 +217,36 @@ export async function render_response({
248
217
  }
249
218
 
250
219
  attributes.unshift('rel="stylesheet"');
251
- head += `\n\t<link href="${path}" ${attributes.join(' ')}>`;
220
+ head += `\n\t\t<link href="${path}" ${attributes.join(' ')}>`;
252
221
  }
253
222
 
254
223
  if (page_config.csr) {
224
+ // prettier-ignore
225
+ const init_app = `
226
+ import { start } from ${s(prefixed(entry.file))};
227
+
228
+ start({
229
+ env: ${s(options.public_env)},
230
+ hydrate: ${page_config.ssr ? `{
231
+ status: ${status},
232
+ error: ${s(error)},
233
+ node_ids: [${branch.map(({ node }) => node.index).join(', ')}],
234
+ params: ${devalue(event.params)},
235
+ routeId: ${s(event.routeId)},
236
+ data: ${serialized.data},
237
+ form: ${serialized.form}
238
+ }` : 'null'},
239
+ paths: ${s(options.paths)},
240
+ target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode,
241
+ trailing_slash: ${s(options.trailing_slash)}
242
+ });
243
+ `;
244
+
255
245
  for (const dep of modulepreloads) {
256
246
  const path = prefixed(dep);
257
247
  link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
258
248
  if (state.prerendering) {
259
- head += `\n\t<link rel="modulepreload" href="${path}">`;
249
+ head += `\n\t\t<link rel="modulepreload" href="${path}">`;
260
250
  }
261
251
  }
262
252
 
@@ -280,11 +270,21 @@ export async function render_response({
280
270
  }
281
271
 
282
272
  if (options.service_worker) {
273
+ // we use an anonymous function instead of an arrow function to support
274
+ // older browsers (https://github.com/sveltejs/kit/pull/5417)
275
+ const init_service_worker = `
276
+ if ('serviceWorker' in navigator) {
277
+ addEventListener('load', function () {
278
+ navigator.serviceWorker.register('${prefixed('service-worker.js')}');
279
+ });
280
+ }
281
+ `;
282
+
283
283
  // always include service worker unless it's turned off explicitly
284
284
  csp.add_script(init_service_worker);
285
285
 
286
286
  head += `
287
- <script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
287
+ <script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
288
288
  }
289
289
 
290
290
  if (state.prerendering) {
package/types/index.d.ts CHANGED
@@ -13,7 +13,8 @@ import {
13
13
  PrerenderOnErrorValue,
14
14
  RequestOptions,
15
15
  RouteDefinition,
16
- TrailingSlash
16
+ TrailingSlash,
17
+ UniqueInterface
17
18
  } from './private.js';
18
19
  import { SSRNodeLoader, SSRRoute, ValidatedConfig } from './internal.js';
19
20
  import { HttpError, Redirect } from '../src/runtime/control.js';
@@ -52,7 +53,8 @@ type OptionalUnion<
52
53
  > = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
53
54
 
54
55
  // Needs to be here, else ActionData will be resolved to unknown - probably because of "d.ts file imports .js file" in combination with allowJs
55
- interface ValidationError<T extends Record<string, unknown> | undefined = undefined> {
56
+ export interface ValidationError<T extends Record<string, unknown> | undefined = undefined>
57
+ extends UniqueInterface {
56
58
  status: number;
57
59
  data: T;
58
60
  }
@@ -263,7 +265,7 @@ export interface LoadEvent<
263
265
  Data extends Record<string, unknown> | null = Record<string, any> | null,
264
266
  ParentData extends Record<string, unknown> = Record<string, any>
265
267
  > extends NavigationEvent<Params> {
266
- fetch(info: RequestInfo, init?: RequestInit): Promise<Response>;
268
+ fetch: typeof fetch;
267
269
  data: Data;
268
270
  setHeaders: (headers: Record<string, string>) => void;
269
271
  parent: () => Promise<ParentData>;
@@ -298,7 +298,7 @@ export interface SSROptions {
298
298
  public_env: Record<string, string>;
299
299
  read(file: string): Buffer;
300
300
  root: SSRComponent['default'];
301
- service_worker?: string;
301
+ service_worker: boolean;
302
302
  app_template({
303
303
  head,
304
304
  body,
@@ -53,7 +53,13 @@ export interface AdapterEntry {
53
53
 
54
54
  export namespace Csp {
55
55
  type ActionSource = 'strict-dynamic' | 'report-sample';
56
- type BaseSource = 'self' | 'unsafe-eval' | 'unsafe-hashes' | 'unsafe-inline' | 'none';
56
+ type BaseSource =
57
+ | 'self'
58
+ | 'unsafe-eval'
59
+ | 'unsafe-hashes'
60
+ | 'unsafe-inline'
61
+ | 'wasm-unsafe-eval'
62
+ | 'none';
57
63
  type CryptoSource = `${'nonce' | 'sha256' | 'sha384' | 'sha512'}-${string}`;
58
64
  type FrameSource = HostSource | SchemeSource | 'self' | 'none';
59
65
  type HostNameScheme = `${string}.${string}` | 'localhost';
@@ -207,3 +213,12 @@ export interface RouteSegment {
207
213
  }
208
214
 
209
215
  export type TrailingSlash = 'never' | 'always' | 'ignore';
216
+
217
+ /**
218
+ * This doesn't actually exist, it's a way to better distinguish the type
219
+ */
220
+ declare const uniqueSymbol: unique symbol;
221
+
222
+ export interface UniqueInterface {
223
+ readonly [uniqueSymbol]: unknown;
224
+ }