@sveltejs/kit 1.0.0-next.403 → 1.0.0-next.407

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/package.json +24 -25
  2. package/{dist → src}/cli.js +19 -18
  3. package/{dist/chunks/index3.js → src/core/adapt/builder.js} +52 -63
  4. package/src/core/adapt/index.js +19 -0
  5. package/src/core/config/index.js +86 -0
  6. package/{dist/chunks/index.js → src/core/config/options.js} +7 -194
  7. package/src/core/config/types.d.ts +1 -0
  8. package/src/core/constants.js +3 -0
  9. package/src/core/generate_manifest/index.js +99 -0
  10. package/src/core/prerender/crawl.js +194 -0
  11. package/src/core/prerender/prerender.js +378 -0
  12. package/src/core/prerender/queue.js +80 -0
  13. package/src/core/sync/create_manifest_data/index.js +492 -0
  14. package/src/core/sync/create_manifest_data/types.d.ts +40 -0
  15. package/src/core/sync/sync.js +59 -0
  16. package/src/core/sync/utils.js +97 -0
  17. package/src/core/sync/write_ambient.js +87 -0
  18. package/src/core/sync/write_client_manifest.js +82 -0
  19. package/src/core/sync/write_matchers.js +25 -0
  20. package/src/core/sync/write_root.js +88 -0
  21. package/{dist/chunks → src/core/sync}/write_tsconfig.js +24 -108
  22. package/src/core/sync/write_types.js +738 -0
  23. package/src/core/utils.js +58 -0
  24. package/{dist → src}/hooks.js +1 -3
  25. package/src/index/index.js +45 -0
  26. package/src/index/private.js +33 -0
  27. package/src/node/index.js +145 -0
  28. package/src/node/polyfills.js +40 -0
  29. package/src/packaging/index.js +218 -0
  30. package/src/packaging/types.d.ts +8 -0
  31. package/src/packaging/typescript.js +150 -0
  32. package/src/packaging/utils.js +143 -0
  33. package/{assets → src/runtime}/app/env.js +3 -5
  34. package/src/runtime/app/navigation.js +22 -0
  35. package/src/runtime/app/paths.js +1 -0
  36. package/{assets → src/runtime}/app/stores.js +6 -9
  37. package/src/runtime/client/ambient.d.ts +17 -0
  38. package/{assets/client/start.js → src/runtime/client/client.js} +302 -878
  39. package/src/runtime/client/fetcher.js +60 -0
  40. package/src/runtime/client/parse.js +36 -0
  41. package/{assets → src/runtime}/client/singletons.js +2 -4
  42. package/src/runtime/client/start.js +48 -0
  43. package/src/runtime/client/types.d.ts +106 -0
  44. package/src/runtime/client/utils.js +113 -0
  45. package/src/runtime/components/error.svelte +16 -0
  46. package/{assets → src/runtime}/components/layout.svelte +0 -0
  47. package/{assets → src/runtime}/env/dynamic/private.js +0 -0
  48. package/{assets → src/runtime}/env/dynamic/public.js +0 -0
  49. package/{assets → src/runtime}/env-private.js +2 -4
  50. package/{assets → src/runtime}/env-public.js +2 -4
  51. package/src/runtime/env.js +6 -0
  52. package/src/runtime/hash.js +16 -0
  53. package/{assets → src/runtime}/paths.js +3 -5
  54. package/src/runtime/server/endpoint.js +42 -0
  55. package/src/runtime/server/index.js +434 -0
  56. package/src/runtime/server/page/cookie.js +25 -0
  57. package/src/runtime/server/page/crypto.js +239 -0
  58. package/src/runtime/server/page/csp.js +249 -0
  59. package/src/runtime/server/page/fetch.js +265 -0
  60. package/src/runtime/server/page/index.js +418 -0
  61. package/src/runtime/server/page/load_data.js +94 -0
  62. package/src/runtime/server/page/render.js +357 -0
  63. package/src/runtime/server/page/respond_with_error.js +105 -0
  64. package/src/runtime/server/page/types.d.ts +44 -0
  65. package/src/runtime/server/utils.js +116 -0
  66. package/src/utils/error.js +22 -0
  67. package/src/utils/escape.js +104 -0
  68. package/{dist/chunks → src/utils}/filesystem.js +22 -24
  69. package/src/utils/http.js +55 -0
  70. package/src/utils/misc.js +1 -0
  71. package/src/utils/routing.js +107 -0
  72. package/src/utils/url.js +97 -0
  73. package/src/vite/build/build_server.js +335 -0
  74. package/src/vite/build/build_service_worker.js +90 -0
  75. package/src/vite/build/utils.js +153 -0
  76. package/src/vite/dev/index.js +565 -0
  77. package/src/vite/index.js +540 -0
  78. package/src/vite/preview/index.js +186 -0
  79. package/src/vite/types.d.ts +3 -0
  80. package/src/vite/utils.js +335 -0
  81. package/svelte-kit.js +1 -10
  82. package/types/ambient.d.ts +5 -12
  83. package/types/index.d.ts +91 -44
  84. package/types/internal.d.ts +50 -72
  85. package/types/private.d.ts +2 -1
  86. package/assets/app/navigation.js +0 -24
  87. package/assets/app/paths.js +0 -1
  88. package/assets/components/error.svelte +0 -29
  89. package/assets/env.js +0 -8
  90. package/assets/server/index.js +0 -3579
  91. package/dist/chunks/error.js +0 -12
  92. package/dist/chunks/index2.js +0 -15745
  93. package/dist/chunks/multipart-parser.js +0 -458
  94. package/dist/chunks/sync.js +0 -1366
  95. package/dist/chunks/utils.js +0 -66
  96. package/dist/node/polyfills.js +0 -17928
  97. package/dist/node.js +0 -348
  98. package/dist/prerender.js +0 -788
  99. package/dist/vite.js +0 -2520
@@ -0,0 +1,418 @@
1
+ import { negotiate } from '../../../utils/http.js';
2
+ import { render_response } from './render.js';
3
+ import { respond_with_error } from './respond_with_error.js';
4
+ import { method_not_allowed, error_to_pojo, allowed_methods } from '../utils.js';
5
+ import { create_fetch } from './fetch.js';
6
+ import { HttpError, Redirect } from '../../../index/private.js';
7
+ import { error, json } from '../../../index/index.js';
8
+ import { normalize_error } from '../../../utils/error.js';
9
+ import { load_data, load_server_data } from './load_data.js';
10
+
11
+ /**
12
+ * @typedef {import('./types.js').Loaded} Loaded
13
+ * @typedef {import('types').SSRNode} SSRNode
14
+ * @typedef {import('types').SSROptions} SSROptions
15
+ * @typedef {import('types').SSRState} SSRState
16
+ */
17
+
18
+ /**
19
+ * @param {import('types').RequestEvent} event
20
+ * @param {import('types').SSRPage} route
21
+ * @param {import('types').SSROptions} options
22
+ * @param {import('types').SSRState} state
23
+ * @param {import('types').RequiredResolveOptions} resolve_opts
24
+ * @returns {Promise<Response>}
25
+ */
26
+ export async function render_page(event, route, options, state, resolve_opts) {
27
+ if (state.initiator === route) {
28
+ // infinite request cycle detected
29
+ return new Response(`Not found: ${event.url.pathname}`, {
30
+ status: 404
31
+ });
32
+ }
33
+
34
+ const accept = negotiate(event.request.headers.get('accept') || 'text/html', [
35
+ 'text/html',
36
+ 'application/json'
37
+ ]);
38
+
39
+ if (accept === 'application/json') {
40
+ const node = await options.manifest._.nodes[route.leaf]();
41
+ if (node.server) {
42
+ return handle_json_request(event, options, node.server);
43
+ }
44
+ }
45
+
46
+ const $session = await options.hooks.getSession(event);
47
+
48
+ const { fetcher, fetched, cookies } = create_fetch({ event, options, state, route });
49
+
50
+ try {
51
+ const nodes = await Promise.all([
52
+ // we use == here rather than === because [undefined] serializes as "[null]"
53
+ ...route.layouts.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())),
54
+ options.manifest._.nodes[route.leaf]()
55
+ ]);
56
+
57
+ const leaf_node = /** @type {import('types').SSRNode} */ (nodes.at(-1));
58
+
59
+ let status = 200;
60
+
61
+ /** @type {HttpError | Error} */
62
+ let mutation_error;
63
+
64
+ /** @type {Record<string, string> | undefined} */
65
+ let validation_errors;
66
+
67
+ if (leaf_node.server && event.request.method !== 'GET' && event.request.method !== 'HEAD') {
68
+ // for non-GET requests, first call handler in +page.server.js
69
+ // (this also determines status code)
70
+ try {
71
+ const method = /** @type {'POST' | 'PATCH' | 'PUT' | 'DELETE'} */ (event.request.method);
72
+ const handler = leaf_node.server[method];
73
+ if (handler) {
74
+ const result = await handler.call(null, event);
75
+
76
+ if (result?.errors) {
77
+ validation_errors = result.errors;
78
+ status = result.status ?? 400;
79
+ }
80
+
81
+ if (event.request.method === 'POST' && result?.location) {
82
+ return redirect_response(status, result.location);
83
+ }
84
+ } else {
85
+ event.setHeaders({
86
+ allow: allowed_methods(leaf_node.server).join(', ')
87
+ });
88
+
89
+ mutation_error = error(405, 'Method not allowed');
90
+ }
91
+ } catch (e) {
92
+ if (e instanceof Redirect) {
93
+ return redirect_response(e.status, e.location);
94
+ }
95
+
96
+ mutation_error = /** @type {HttpError | Error} */ (e);
97
+ }
98
+ }
99
+
100
+ if (!resolve_opts.ssr) {
101
+ return await render_response({
102
+ branch: [],
103
+ validation_errors: undefined,
104
+ fetched,
105
+ cookies,
106
+ page_config: {
107
+ hydrate: true,
108
+ router: true
109
+ },
110
+ status,
111
+ error: null,
112
+ event,
113
+ options,
114
+ state,
115
+ $session,
116
+ resolve_opts
117
+ });
118
+ }
119
+
120
+ const should_prerender = leaf_node.shared?.prerender ?? options.prerender.default;
121
+ if (should_prerender) {
122
+ const mod = leaf_node.server;
123
+ if (mod && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) {
124
+ throw new Error('Cannot prerender pages that have endpoints with mutative methods');
125
+ }
126
+ } else if (state.prerendering) {
127
+ // if the page isn't marked as prerenderable (or is explicitly
128
+ // marked NOT prerenderable, if `prerender.default` is `true`),
129
+ // then bail out at this point
130
+ if (!should_prerender) {
131
+ return new Response(undefined, {
132
+ status: 204
133
+ });
134
+ }
135
+ }
136
+
137
+ /** @type {Array<Loaded | null>} */
138
+ let branch = [];
139
+
140
+ /** @type {Error | null} */
141
+ let load_error = null;
142
+
143
+ /** @type {Array<Promise<import('types').JSONObject | null>>} */
144
+ const server_promises = nodes.map((node, i) => {
145
+ if (load_error) {
146
+ // if an error happens immediately, don't bother with the rest of the nodes
147
+ throw load_error;
148
+ }
149
+
150
+ return Promise.resolve().then(async () => {
151
+ try {
152
+ if (node === leaf_node && mutation_error) {
153
+ // we wait until here to throw the error so that we can use
154
+ // any nested +error.svelte components that were defined
155
+ throw mutation_error;
156
+ }
157
+
158
+ return await load_server_data({
159
+ event,
160
+ node,
161
+ parent: async () => {
162
+ /** @type {import('types').JSONObject} */
163
+ const data = {};
164
+ for (let j = 0; j < i; j += 1) {
165
+ Object.assign(data, await server_promises[j]);
166
+ }
167
+ return data;
168
+ }
169
+ });
170
+ } catch (e) {
171
+ load_error = /** @type {Error} */ (e);
172
+ throw load_error;
173
+ }
174
+ });
175
+ });
176
+
177
+ /** @type {Array<Promise<Record<string, any> | null>>} */
178
+ const load_promises = nodes.map((node, i) => {
179
+ if (load_error) throw load_error;
180
+ return Promise.resolve().then(async () => {
181
+ try {
182
+ return await load_data({
183
+ $session,
184
+ event,
185
+ fetcher,
186
+ node,
187
+ options,
188
+ parent: async () => {
189
+ const data = {};
190
+ for (let j = 0; j < i; j += 1) {
191
+ Object.assign(data, await load_promises[j]);
192
+ }
193
+ return data;
194
+ },
195
+ server_data_promise: server_promises[i],
196
+ state
197
+ });
198
+ } catch (e) {
199
+ load_error = /** @type {Error} */ (e);
200
+ throw load_error;
201
+ }
202
+ });
203
+ });
204
+
205
+ // if we don't do this, rejections will be unhandled
206
+ for (const p of server_promises) p.catch(() => {});
207
+ for (const p of load_promises) p.catch(() => {});
208
+
209
+ for (let i = 0; i < nodes.length; i += 1) {
210
+ const node = nodes[i];
211
+
212
+ if (node) {
213
+ try {
214
+ const server_data = await server_promises[i];
215
+ const data = await load_promises[i];
216
+
217
+ branch.push({ node, server_data, data });
218
+ } catch (e) {
219
+ const error = normalize_error(e);
220
+
221
+ if (error instanceof Redirect) {
222
+ return redirect_response(error.status, error.location);
223
+ }
224
+
225
+ if (!(error instanceof HttpError)) {
226
+ options.handle_error(/** @type {Error} */ (error), event);
227
+ }
228
+
229
+ const status = error instanceof HttpError ? error.status : 500;
230
+
231
+ while (i--) {
232
+ if (route.errors[i]) {
233
+ const index = /** @type {number} */ (route.errors[i]);
234
+ const node = await options.manifest._.nodes[index]();
235
+
236
+ let j = i;
237
+ while (!branch[j]) j -= 1;
238
+
239
+ return await render_response({
240
+ event,
241
+ options,
242
+ state,
243
+ $session,
244
+ resolve_opts,
245
+ page_config: { router: true, hydrate: true },
246
+ status,
247
+ error,
248
+ branch: compact(branch.slice(0, j + 1)).concat({
249
+ node,
250
+ data: null,
251
+ server_data: null
252
+ }),
253
+ fetched,
254
+ cookies,
255
+ validation_errors: undefined
256
+ });
257
+ }
258
+ }
259
+
260
+ // if we're still here, it means the error happened in the root layout,
261
+ // which means we have to fall back to a plain text response
262
+ // TODO since the requester is expecting HTML, maybe it makes sense to
263
+ // doll this up a bit
264
+ return new Response(
265
+ error instanceof HttpError ? error.message : options.get_stack(error),
266
+ { status }
267
+ );
268
+ }
269
+ } else {
270
+ // push an empty slot so we can rewind past gaps to the
271
+ // layout that corresponds with an +error.svelte page
272
+ branch.push(null);
273
+ }
274
+ }
275
+
276
+ // generate __data.json files when prerendering
277
+ if (state.prerendering && nodes.some((node) => node?.server)) {
278
+ const pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`;
279
+
280
+ const dependency = {
281
+ response: new Response(undefined),
282
+ body: JSON.stringify({
283
+ type: 'data',
284
+ nodes: branch.map((branch_node) => ({ data: branch_node?.server_data }))
285
+ })
286
+ };
287
+
288
+ state.prerendering.dependencies.set(pathname, dependency);
289
+ }
290
+
291
+ // TODO use validation_errors
292
+
293
+ return await render_response({
294
+ event,
295
+ options,
296
+ state,
297
+ $session,
298
+ resolve_opts,
299
+ page_config: get_page_config(leaf_node, options),
300
+ status,
301
+ error: null,
302
+ branch: compact(branch),
303
+ validation_errors,
304
+ fetched,
305
+ cookies
306
+ });
307
+ } catch (error) {
308
+ // if we end up here, it means the data loaded successfull
309
+ // but the page failed to render
310
+ options.handle_error(/** @type {Error} */ (error), event);
311
+
312
+ return await respond_with_error({
313
+ event,
314
+ options,
315
+ state,
316
+ $session,
317
+ status: 500,
318
+ error: /** @type {Error} */ (error),
319
+ resolve_opts
320
+ });
321
+ }
322
+ }
323
+
324
+ /**
325
+ * @param {import('types').SSRNode} leaf
326
+ * @param {SSROptions} options
327
+ */
328
+ function get_page_config(leaf, options) {
329
+ // TODO we can reinstate this now that it's in the module
330
+ if (leaf.shared && 'ssr' in leaf.shared) {
331
+ throw new Error(
332
+ '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle'
333
+ );
334
+ }
335
+
336
+ return {
337
+ router: leaf.shared?.router ?? options.router,
338
+ hydrate: leaf.shared?.hydrate ?? options.hydrate
339
+ };
340
+ }
341
+
342
+ /**
343
+ * @param {import('types').RequestEvent} event
344
+ * @param {import('types').SSROptions} options
345
+ * @param {import('types').SSRNode['server']} mod
346
+ */
347
+ export async function handle_json_request(event, options, mod) {
348
+ const method = /** @type {import('types').HttpMethod} */ (event.request.method);
349
+ const handler = mod[method === 'HEAD' || method === 'GET' ? 'load' : method];
350
+
351
+ if (!handler) {
352
+ return method_not_allowed(mod, method);
353
+ }
354
+
355
+ try {
356
+ // @ts-ignore
357
+ const result = await handler.call(null, event);
358
+
359
+ if (method === 'HEAD') {
360
+ return new Response();
361
+ }
362
+
363
+ if (method === 'GET') {
364
+ return json(result);
365
+ }
366
+
367
+ if (result?.errors) {
368
+ // @ts-ignore
369
+ return json({ errors: result.errors }, { status: result.status || 400 });
370
+ }
371
+
372
+ return new Response(undefined, {
373
+ status: 204,
374
+ // @ts-ignore
375
+ headers: result?.location ? { location: result.location } : undefined
376
+ });
377
+ } catch (e) {
378
+ const error = normalize_error(e);
379
+
380
+ if (error instanceof Redirect) {
381
+ return redirect_response(error.status, error.location);
382
+ }
383
+
384
+ if (!(error instanceof HttpError)) {
385
+ options.handle_error(error, event);
386
+ }
387
+
388
+ return json(error_to_pojo(error, options.get_stack), {
389
+ status: error instanceof HttpError ? error.status : 500
390
+ });
391
+ }
392
+ }
393
+
394
+ /**
395
+ * @param {number} status
396
+ * @param {string} location
397
+ */
398
+ function redirect_response(status, location) {
399
+ return new Response(undefined, {
400
+ status,
401
+ headers: { location }
402
+ });
403
+ }
404
+
405
+ /**
406
+ * @template T
407
+ * @param {Array<T | null>} array
408
+ * @returns {T[]}
409
+ */
410
+ function compact(array) {
411
+ const compacted = [];
412
+ for (const item of array) {
413
+ if (item) {
414
+ compacted.push(item);
415
+ }
416
+ }
417
+ return compacted;
418
+ }
@@ -0,0 +1,94 @@
1
+ import { LoadURL, PrerenderingURL } from '../../../utils/url.js';
2
+
3
+ /**
4
+ * Calls the user's `load` function.
5
+ * @param {{
6
+ * event: import('types').RequestEvent;
7
+ * node: import('types').SSRNode | undefined;
8
+ * parent: () => Promise<import('types').JSONObject | null>;
9
+ * }} opts
10
+ */
11
+ export async function load_server_data({ event, node, parent }) {
12
+ if (!node?.server) return null;
13
+
14
+ const server_data = await node.server.load?.call(null, {
15
+ // can't use destructuring here because it will always
16
+ // invoke event.clientAddress, which breaks prerendering
17
+ get clientAddress() {
18
+ return event.clientAddress;
19
+ },
20
+ locals: event.locals,
21
+ params: event.params,
22
+ parent,
23
+ platform: event.platform,
24
+ request: event.request,
25
+ routeId: event.routeId,
26
+ setHeaders: event.setHeaders,
27
+ url: event.url
28
+ });
29
+
30
+ return server_data ? unwrap_promises(server_data) : null;
31
+ }
32
+
33
+ /**
34
+ * Calls the user's `load` function.
35
+ * @param {{
36
+ * $session: Record<string, any>;
37
+ * event: import('types').RequestEvent;
38
+ * fetcher: typeof fetch;
39
+ * node: import('types').SSRNode | undefined;
40
+ * options: import('types').SSROptions;
41
+ * parent: () => Promise<Record<string, any>>;
42
+ * server_data_promise: Promise<import('types').JSONObject | null>;
43
+ * state: import('types').SSRState;
44
+ * }} opts
45
+ */
46
+ export async function load_data({
47
+ $session,
48
+ event,
49
+ fetcher,
50
+ node,
51
+ options,
52
+ parent,
53
+ server_data_promise,
54
+ state
55
+ }) {
56
+ const server_data = await server_data_promise;
57
+
58
+ if (!node?.shared?.load) {
59
+ return server_data;
60
+ }
61
+
62
+ const data = await node.shared.load.call(null, {
63
+ url: state.prerendering ? new PrerenderingURL(event.url) : new LoadURL(event.url),
64
+ params: event.params,
65
+ data: server_data,
66
+ routeId: event.routeId,
67
+ get session() {
68
+ if (node.shared.prerender ?? options.prerender.default) {
69
+ throw Error(
70
+ 'Attempted to access session from a prerendered page. Session would never be populated.'
71
+ );
72
+ }
73
+ return $session;
74
+ },
75
+ fetch: fetcher,
76
+ setHeaders: event.setHeaders,
77
+ depends: () => {},
78
+ parent
79
+ });
80
+
81
+ return data ? unwrap_promises(data) : null;
82
+ }
83
+
84
+ /** @param {Record<string, any>} object */
85
+ async function unwrap_promises(object) {
86
+ /** @type {import('types').JSONObject} */
87
+ const unwrapped = {};
88
+
89
+ for (const key in object) {
90
+ unwrapped[key] = await object[key];
91
+ }
92
+
93
+ return unwrapped;
94
+ }