@sveltejs/kit 1.0.0-next.44 → 1.0.0-next.442

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 (153) hide show
  1. package/README.md +12 -9
  2. package/package.json +94 -63
  3. package/src/cli.js +112 -0
  4. package/src/core/adapt/builder.js +223 -0
  5. package/src/core/adapt/index.js +19 -0
  6. package/src/core/config/index.js +86 -0
  7. package/src/core/config/options.js +488 -0
  8. package/src/core/config/types.d.ts +1 -0
  9. package/src/core/constants.js +5 -0
  10. package/src/core/env.js +97 -0
  11. package/src/core/generate_manifest/index.js +78 -0
  12. package/src/core/prerender/crawl.js +194 -0
  13. package/src/core/prerender/prerender.js +380 -0
  14. package/src/core/prerender/queue.js +80 -0
  15. package/src/core/sync/create_manifest_data/index.js +452 -0
  16. package/src/core/sync/create_manifest_data/types.d.ts +37 -0
  17. package/src/core/sync/sync.js +59 -0
  18. package/src/core/sync/utils.js +33 -0
  19. package/src/core/sync/write_ambient.js +27 -0
  20. package/src/core/sync/write_client_manifest.js +92 -0
  21. package/src/core/sync/write_matchers.js +25 -0
  22. package/src/core/sync/write_root.js +91 -0
  23. package/src/core/sync/write_tsconfig.js +195 -0
  24. package/src/core/sync/write_types/index.js +580 -0
  25. package/src/core/sync/write_types/test/layout/+layout.js +5 -0
  26. package/src/core/sync/write_types/test/layout/+layout.server.js +5 -0
  27. package/src/core/sync/write_types/test/layout/+layout.svelte +0 -0
  28. package/src/core/sync/write_types/test/layout/+page.js +5 -0
  29. package/src/core/sync/write_types/test/layout/+page.server.js +5 -0
  30. package/src/core/sync/write_types/test/layout/+page.svelte +0 -0
  31. package/src/core/sync/write_types/test/layout/_expected/$types.d.ts +67 -0
  32. package/src/core/sync/write_types/test/layout-advanced/(main)/+layout.server.js +5 -0
  33. package/src/core/sync/write_types/test/layout-advanced/(main)/+layout.svelte +0 -0
  34. package/src/core/sync/write_types/test/layout-advanced/(main)/+page.js +5 -0
  35. package/src/core/sync/write_types/test/layout-advanced/(main)/+page@.svelte +0 -0
  36. package/src/core/sync/write_types/test/layout-advanced/(main)/sub/+page.js +5 -0
  37. package/src/core/sync/write_types/test/layout-advanced/(main)/sub/+page.svelte +0 -0
  38. package/src/core/sync/write_types/test/layout-advanced/+layout.js +5 -0
  39. package/src/core/sync/write_types/test/layout-advanced/+layout.svelte +0 -0
  40. package/src/core/sync/write_types/test/layout-advanced/_expected/$types.d.ts +32 -0
  41. package/src/core/sync/write_types/test/layout-advanced/_expected/(main)/$types.d.ts +42 -0
  42. package/src/core/sync/write_types/test/layout-advanced/_expected/(main)/sub/$types.d.ts +33 -0
  43. package/src/core/sync/write_types/test/simple-page-server-and-shared/+page.js +5 -0
  44. package/src/core/sync/write_types/test/simple-page-server-and-shared/+page.server.js +5 -0
  45. package/src/core/sync/write_types/test/simple-page-server-and-shared/+page.svelte +0 -0
  46. package/src/core/sync/write_types/test/simple-page-server-and-shared/_expected/$types.d.ts +44 -0
  47. package/src/core/sync/write_types/test/simple-page-server-only/+page.server.js +5 -0
  48. package/src/core/sync/write_types/test/simple-page-server-only/+page.svelte +0 -0
  49. package/src/core/sync/write_types/test/simple-page-server-only/_expected/$types.d.ts +30 -0
  50. package/src/core/sync/write_types/test/simple-page-shared-only/+page.js +5 -0
  51. package/src/core/sync/write_types/test/simple-page-shared-only/+page.svelte +0 -0
  52. package/src/core/sync/write_types/test/simple-page-shared-only/_expected/$types.d.ts +33 -0
  53. package/src/core/sync/write_types/test/slugs/+layout.js +1 -0
  54. package/src/core/sync/write_types/test/slugs/+layout.svelte +1 -0
  55. package/src/core/sync/write_types/test/slugs/[...rest]/+page.js +3 -0
  56. package/src/core/sync/write_types/test/slugs/[...rest]/+page.svelte +0 -0
  57. package/src/core/sync/write_types/test/slugs/[slug]/+page.js +3 -0
  58. package/src/core/sync/write_types/test/slugs/[slug]/+page.svelte +0 -0
  59. package/src/core/sync/write_types/test/slugs/_expected/$types.d.ts +32 -0
  60. package/src/core/sync/write_types/test/slugs/_expected/[...rest]/$types.d.ts +29 -0
  61. package/src/core/sync/write_types/test/slugs/_expected/[slug]/$types.d.ts +29 -0
  62. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/+layout.js +1 -0
  63. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/+layout.svelte +1 -0
  64. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/_expected/$types.d.ts +30 -0
  65. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/_expected/nested/$types.d.ts +32 -0
  66. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/_expected/nested/[...rest]/$types.d.ts +37 -0
  67. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/_expected/nested/[slug]/$types.d.ts +17 -0
  68. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/+layout.js +1 -0
  69. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/+layout.svelte +1 -0
  70. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/[...rest]/+page.js +3 -0
  71. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/[...rest]/+page.svelte +0 -0
  72. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/[slug]/+page@.svelte +0 -0
  73. package/src/core/utils.js +70 -0
  74. package/src/hooks.js +26 -0
  75. package/src/index/index.js +45 -0
  76. package/src/index/private.js +33 -0
  77. package/src/node/index.js +145 -0
  78. package/src/node/polyfills.js +40 -0
  79. package/src/runtime/app/env.js +11 -0
  80. package/src/runtime/app/navigation.js +22 -0
  81. package/src/runtime/app/paths.js +1 -0
  82. package/src/runtime/app/stores.js +102 -0
  83. package/src/runtime/client/ambient.d.ts +18 -0
  84. package/src/runtime/client/client.js +1362 -0
  85. package/src/runtime/client/fetcher.js +60 -0
  86. package/src/runtime/client/parse.js +41 -0
  87. package/src/runtime/client/singletons.js +21 -0
  88. package/src/runtime/client/start.js +46 -0
  89. package/src/runtime/client/types.d.ts +86 -0
  90. package/src/runtime/client/utils.js +113 -0
  91. package/src/runtime/components/error.svelte +16 -0
  92. package/{assets → src/runtime}/components/layout.svelte +0 -0
  93. package/src/runtime/env/dynamic/private.js +1 -0
  94. package/src/runtime/env/dynamic/public.js +1 -0
  95. package/src/runtime/env-private.js +7 -0
  96. package/src/runtime/env-public.js +7 -0
  97. package/src/runtime/env.js +6 -0
  98. package/src/runtime/hash.js +16 -0
  99. package/src/runtime/paths.js +11 -0
  100. package/src/runtime/server/endpoint.js +50 -0
  101. package/src/runtime/server/index.js +492 -0
  102. package/src/runtime/server/page/cookie.js +25 -0
  103. package/src/runtime/server/page/crypto.js +239 -0
  104. package/src/runtime/server/page/csp.js +249 -0
  105. package/src/runtime/server/page/fetch.js +266 -0
  106. package/src/runtime/server/page/index.js +408 -0
  107. package/src/runtime/server/page/load_data.js +168 -0
  108. package/src/runtime/server/page/render.js +361 -0
  109. package/src/runtime/server/page/respond_with_error.js +95 -0
  110. package/src/runtime/server/page/types.d.ts +44 -0
  111. package/src/runtime/server/utils.js +116 -0
  112. package/src/utils/array.js +9 -0
  113. package/src/utils/error.js +22 -0
  114. package/src/utils/escape.js +104 -0
  115. package/src/utils/filesystem.js +108 -0
  116. package/src/utils/functions.js +16 -0
  117. package/src/utils/http.js +55 -0
  118. package/src/utils/misc.js +1 -0
  119. package/src/utils/routing.js +146 -0
  120. package/src/utils/url.js +142 -0
  121. package/src/vite/build/build_server.js +348 -0
  122. package/src/vite/build/build_service_worker.js +90 -0
  123. package/src/vite/build/utils.js +160 -0
  124. package/src/vite/dev/index.js +543 -0
  125. package/src/vite/index.js +578 -0
  126. package/src/vite/preview/index.js +186 -0
  127. package/src/vite/types.d.ts +3 -0
  128. package/src/vite/utils.js +345 -0
  129. package/svelte-kit.js +1 -1
  130. package/types/ambient.d.ts +366 -0
  131. package/types/index.d.ts +345 -0
  132. package/types/internal.d.ts +374 -0
  133. package/types/private.d.ts +209 -0
  134. package/CHANGELOG.md +0 -437
  135. package/assets/components/error.svelte +0 -13
  136. package/assets/runtime/app/env.js +0 -5
  137. package/assets/runtime/app/navigation.js +0 -41
  138. package/assets/runtime/app/paths.js +0 -1
  139. package/assets/runtime/app/stores.js +0 -93
  140. package/assets/runtime/chunks/utils.js +0 -19
  141. package/assets/runtime/internal/singletons.js +0 -23
  142. package/assets/runtime/internal/start.js +0 -770
  143. package/assets/runtime/paths.js +0 -12
  144. package/dist/chunks/index.js +0 -3521
  145. package/dist/chunks/index2.js +0 -587
  146. package/dist/chunks/index3.js +0 -246
  147. package/dist/chunks/index4.js +0 -545
  148. package/dist/chunks/index5.js +0 -761
  149. package/dist/chunks/index6.js +0 -322
  150. package/dist/chunks/standard.js +0 -99
  151. package/dist/chunks/utils.js +0 -83
  152. package/dist/cli.js +0 -546
  153. package/dist/ssr.js +0 -2580
@@ -0,0 +1,168 @@
1
+ import { disable_search, make_trackable } from '../../../utils/url.js';
2
+
3
+ /**
4
+ * Calls the user's `load` function.
5
+ * @param {{
6
+ * dev: boolean;
7
+ * event: import('types').RequestEvent;
8
+ * state: import('types').SSRState;
9
+ * node: import('types').SSRNode | undefined;
10
+ * parent: () => Promise<Record<string, any>>;
11
+ * }} opts
12
+ * @returns {Promise<import('types').ServerDataNode | null>}
13
+ */
14
+ export async function load_server_data({ dev, event, state, node, parent }) {
15
+ if (!node?.server) return null;
16
+
17
+ const uses = {
18
+ dependencies: new Set(),
19
+ params: new Set(),
20
+ parent: false,
21
+ url: false
22
+ };
23
+
24
+ const url = make_trackable(event.url, () => {
25
+ uses.url = true;
26
+ });
27
+
28
+ if (state.prerendering) {
29
+ disable_search(url);
30
+ }
31
+
32
+ const result = await node.server.load?.call(null, {
33
+ ...event,
34
+ /** @param {string[]} deps */
35
+ depends: (...deps) => {
36
+ for (const dep of deps) {
37
+ const { href } = new URL(dep, event.url);
38
+ uses.dependencies.add(href);
39
+ }
40
+ },
41
+ params: new Proxy(event.params, {
42
+ get: (target, key) => {
43
+ uses.params.add(key);
44
+ return target[/** @type {string} */ (key)];
45
+ }
46
+ }),
47
+ parent: async () => {
48
+ uses.parent = true;
49
+ return parent();
50
+ },
51
+ url
52
+ });
53
+
54
+ const data = result ? await unwrap_promises(result) : null;
55
+
56
+ if (dev) {
57
+ check_serializability(data, /** @type {string} */ (node.server_id), 'data');
58
+ }
59
+
60
+ return {
61
+ type: 'data',
62
+ data,
63
+ uses: {
64
+ dependencies: uses.dependencies.size > 0 ? Array.from(uses.dependencies) : undefined,
65
+ params: uses.params.size > 0 ? Array.from(uses.params) : undefined,
66
+ parent: uses.parent ? 1 : undefined,
67
+ url: uses.url ? 1 : undefined
68
+ }
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Calls the user's `load` function.
74
+ * @param {{
75
+ * event: import('types').RequestEvent;
76
+ * fetcher: typeof fetch;
77
+ * node: import('types').SSRNode | undefined;
78
+ * parent: () => Promise<Record<string, any>>;
79
+ * server_data_promise: Promise<import('types').ServerDataNode | null>;
80
+ * state: import('types').SSRState;
81
+ * }} opts
82
+ * @returns {Promise<Record<string, any> | null>}
83
+ */
84
+ export async function load_data({ event, fetcher, node, parent, server_data_promise }) {
85
+ const server_data_node = await server_data_promise;
86
+
87
+ if (!node?.shared?.load) {
88
+ return server_data_node?.data ?? null;
89
+ }
90
+
91
+ const load_event = {
92
+ url: event.url,
93
+ params: event.params,
94
+ data: server_data_node?.data ?? null,
95
+ routeId: event.routeId,
96
+ fetch: fetcher,
97
+ setHeaders: event.setHeaders,
98
+ depends: () => {},
99
+ parent
100
+ };
101
+
102
+ // TODO remove this for 1.0
103
+ Object.defineProperties(load_event, {
104
+ session: {
105
+ get() {
106
+ throw new Error(
107
+ 'session is no longer available. See https://github.com/sveltejs/kit/discussions/5883'
108
+ );
109
+ },
110
+ enumerable: false
111
+ }
112
+ });
113
+
114
+ const data = await node.shared.load.call(null, load_event);
115
+
116
+ return data ? unwrap_promises(data) : null;
117
+ }
118
+
119
+ /** @param {Record<string, any>} object */
120
+ async function unwrap_promises(object) {
121
+ /** @type {Record<string, any>} */
122
+ const unwrapped = {};
123
+
124
+ for (const key in object) {
125
+ unwrapped[key] = await object[key];
126
+ }
127
+
128
+ return unwrapped;
129
+ }
130
+
131
+ /**
132
+ * Check that the data can safely be serialized to JSON
133
+ * @param {any} value
134
+ * @param {string} id
135
+ * @param {string} path
136
+ */
137
+ function check_serializability(value, id, path) {
138
+ const type = typeof value;
139
+
140
+ if (type === 'string' || type === 'boolean' || type === 'number' || type === 'undefined') {
141
+ // primitives are fine
142
+ return;
143
+ }
144
+
145
+ if (type === 'object') {
146
+ // nulls are fine...
147
+ if (!value) return;
148
+
149
+ // ...so are plain arrays...
150
+ if (Array.isArray(value)) {
151
+ value.forEach((child, i) => {
152
+ check_serializability(child, id, `${path}[${i}]`);
153
+ });
154
+ return;
155
+ }
156
+
157
+ // ...and objects
158
+ const tag = Object.prototype.toString.call(value);
159
+ if (tag === '[object Object]') {
160
+ for (const key in value) {
161
+ check_serializability(value[key], id, `${path}.${key}`);
162
+ }
163
+ return;
164
+ }
165
+ }
166
+
167
+ throw new Error(`${path} returned from 'load' in ${id} cannot be serialized as JSON`);
168
+ }
@@ -0,0 +1,361 @@
1
+ import devalue from 'devalue';
2
+ import { readable, writable } from 'svelte/store';
3
+ import * as cookie from 'cookie';
4
+ import { hash } from '../../hash.js';
5
+ import { render_json_payload_script } from '../../../utils/escape.js';
6
+ import { s } from '../../../utils/misc.js';
7
+ import { Csp } from './csp.js';
8
+ import { serialize_error } from '../utils.js';
9
+ import { HttpError } from '../../../index/private.js';
10
+
11
+ // TODO rename this function/module
12
+
13
+ const updated = {
14
+ ...readable(false),
15
+ check: () => false
16
+ };
17
+
18
+ /**
19
+ * Creates the HTML response.
20
+ * @param {{
21
+ * branch: Array<import('./types').Loaded>;
22
+ * fetched: Array<import('./types').Fetched>;
23
+ * cookies: import('set-cookie-parser').Cookie[];
24
+ * options: import('types').SSROptions;
25
+ * state: import('types').SSRState;
26
+ * page_config: { hydrate: boolean, router: boolean };
27
+ * status: number;
28
+ * error: HttpError | Error | null;
29
+ * event: import('types').RequestEvent;
30
+ * resolve_opts: import('types').RequiredResolveOptions;
31
+ * validation_errors: Record<string, string> | undefined;
32
+ * }} opts
33
+ */
34
+ export async function render_response({
35
+ branch,
36
+ fetched,
37
+ cookies,
38
+ options,
39
+ state,
40
+ page_config,
41
+ status,
42
+ error = null,
43
+ event,
44
+ resolve_opts,
45
+ validation_errors
46
+ }) {
47
+ if (state.prerendering) {
48
+ if (options.csp.mode === 'nonce') {
49
+ throw new Error('Cannot use prerendering if config.kit.csp.mode === "nonce"');
50
+ }
51
+
52
+ if (options.template_contains_nonce) {
53
+ throw new Error('Cannot use prerendering if page template contains %sveltekit.nonce%');
54
+ }
55
+ }
56
+
57
+ const { entry } = options.manifest._;
58
+
59
+ const stylesheets = new Set(entry.stylesheets);
60
+ const modulepreloads = new Set(entry.imports);
61
+
62
+ /** @type {Set<string>} */
63
+ const link_header_preloads = new Set();
64
+
65
+ /** @type {Map<string, string>} */
66
+ // 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
67
+ const inline_styles = new Map();
68
+
69
+ let rendered;
70
+
71
+ const stack = error instanceof HttpError ? undefined : error?.stack;
72
+
73
+ if (error && options.dev && !(error instanceof HttpError)) {
74
+ error.stack = options.get_stack(error);
75
+ }
76
+
77
+ if (resolve_opts.ssr) {
78
+ /** @type {Record<string, any>} */
79
+ const props = {
80
+ stores: {
81
+ page: writable(null),
82
+ navigating: writable(null),
83
+ updated
84
+ },
85
+ components: await Promise.all(branch.map(({ node }) => node.component()))
86
+ };
87
+
88
+ let data = {};
89
+
90
+ // props_n (instead of props[n]) makes it easy to avoid
91
+ // unnecessary updates for layout components
92
+ for (let i = 0; i < branch.length; i += 1) {
93
+ data = { ...data, ...branch[i].data };
94
+ props[`data_${i}`] = data;
95
+ }
96
+
97
+ props.page = {
98
+ error,
99
+ params: /** @type {Record<string, any>} */ (event.params),
100
+ routeId: event.routeId,
101
+ status,
102
+ url: event.url,
103
+ data
104
+ };
105
+
106
+ if (validation_errors) {
107
+ props.errors = validation_errors;
108
+ }
109
+
110
+ // TODO remove this for 1.0
111
+ /**
112
+ * @param {string} property
113
+ * @param {string} replacement
114
+ */
115
+ const print_error = (property, replacement) => {
116
+ Object.defineProperty(props.page, property, {
117
+ get: () => {
118
+ throw new Error(`$page.${property} has been replaced by $page.url.${replacement}`);
119
+ }
120
+ });
121
+ };
122
+
123
+ print_error('origin', 'origin');
124
+ print_error('path', 'pathname');
125
+ print_error('query', 'searchParams');
126
+
127
+ rendered = options.root.render(props);
128
+
129
+ for (const { node } of branch) {
130
+ if (node.imports) {
131
+ node.imports.forEach((url) => modulepreloads.add(url));
132
+ }
133
+
134
+ if (node.stylesheets) {
135
+ node.stylesheets.forEach((url) => stylesheets.add(url));
136
+ }
137
+
138
+ if (node.inline_styles) {
139
+ Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
140
+ }
141
+ }
142
+ } else {
143
+ rendered = { head: '', html: '', css: { code: '', map: null } };
144
+ }
145
+
146
+ let { head, html: body } = rendered;
147
+
148
+ const csp = new Csp(options.csp, {
149
+ dev: options.dev,
150
+ prerender: !!state.prerendering
151
+ });
152
+
153
+ const target = hash(body);
154
+
155
+ /**
156
+ * The prefix to use for static assets. Replaces `%sveltekit.assets%` in the template
157
+ * @type {string}
158
+ */
159
+ let assets;
160
+
161
+ if (options.paths.assets) {
162
+ // if an asset path is specified, use it
163
+ assets = options.paths.assets;
164
+ } else if (state.prerendering?.fallback) {
165
+ // if we're creating a fallback page, asset paths need to be root-relative
166
+ assets = options.paths.base;
167
+ } else {
168
+ // otherwise we want asset paths to be relative to the page, so that they
169
+ // will work in odd contexts like IPFS, the internet archive, and so on
170
+ const segments = event.url.pathname.slice(options.paths.base.length).split('/').slice(2);
171
+ assets = segments.length > 0 ? segments.map(() => '..').join('/') : '.';
172
+ }
173
+
174
+ /** @param {string} path */
175
+ const prefixed = (path) => (path.startsWith('/') ? path : `${assets}/${path}`);
176
+
177
+ // prettier-ignore
178
+ const init_app = `
179
+ import { set_public_env, start } from ${s(prefixed(entry.file))};
180
+
181
+ set_public_env(${s(options.public_env)});
182
+
183
+ start({
184
+ target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode,
185
+ paths: ${s(options.paths)},
186
+ route: ${!!page_config.router},
187
+ spa: ${!resolve_opts.ssr},
188
+ trailing_slash: ${s(options.trailing_slash)},
189
+ hydrate: ${resolve_opts.ssr && page_config.hydrate ? `{
190
+ status: ${status},
191
+ error: ${error && serialize_error(error, e => e.stack)},
192
+ node_ids: [${branch.map(({ node }) => node.index).join(', ')}],
193
+ params: ${devalue(event.params)},
194
+ routeId: ${s(event.routeId)}
195
+ }` : 'null'}
196
+ });
197
+ `;
198
+
199
+ // we use an anonymous function instead of an arrow function to support
200
+ // older browsers (https://github.com/sveltejs/kit/pull/5417)
201
+ const init_service_worker = `
202
+ if ('serviceWorker' in navigator) {
203
+ addEventListener('load', function () {
204
+ navigator.serviceWorker.register('${options.service_worker}');
205
+ });
206
+ }
207
+ `;
208
+
209
+ if (inline_styles.size > 0) {
210
+ const content = Array.from(inline_styles.values()).join('\n');
211
+
212
+ const attributes = [];
213
+ if (options.dev) attributes.push(' data-sveltekit');
214
+ if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`);
215
+
216
+ csp.add_style(content);
217
+
218
+ head += `\n\t<style${attributes.join('')}>${content}</style>`;
219
+ }
220
+
221
+ for (const dep of stylesheets) {
222
+ const path = prefixed(dep);
223
+ const attributes = [];
224
+
225
+ if (csp.style_needs_nonce) {
226
+ attributes.push(`nonce="${csp.nonce}"`);
227
+ }
228
+
229
+ if (inline_styles.has(dep)) {
230
+ // don't load stylesheets that are already inlined
231
+ // include them in disabled state so that Vite can detect them and doesn't try to add them
232
+ attributes.push('disabled', 'media="(max-width: 0)"');
233
+ } else {
234
+ const preload_atts = ['rel="preload"', 'as="style"'].concat(attributes);
235
+ link_header_preloads.add(`<${encodeURI(path)}>; ${preload_atts.join(';')}; nopush`);
236
+ }
237
+
238
+ attributes.unshift('rel="stylesheet"');
239
+ head += `\n\t<link href="${path}" ${attributes.join(' ')}>`;
240
+ }
241
+
242
+ if (page_config.router || page_config.hydrate) {
243
+ for (const dep of modulepreloads) {
244
+ const path = prefixed(dep);
245
+ link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
246
+ if (state.prerendering) {
247
+ head += `\n\t<link rel="modulepreload" href="${path}">`;
248
+ }
249
+ }
250
+
251
+ const attributes = ['type="module"', `data-sveltekit-hydrate="${target}"`];
252
+
253
+ csp.add_script(init_app);
254
+
255
+ if (csp.script_needs_nonce) {
256
+ attributes.push(`nonce="${csp.nonce}"`);
257
+ }
258
+
259
+ body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`;
260
+ }
261
+
262
+ if (resolve_opts.ssr && page_config.hydrate) {
263
+ /** @type {string[]} */
264
+ const serialized_data = [];
265
+
266
+ for (const { url, body, response } of fetched) {
267
+ serialized_data.push(
268
+ render_json_payload_script(
269
+ { type: 'data', url, body: typeof body === 'string' ? hash(body) : undefined },
270
+ response
271
+ )
272
+ );
273
+ }
274
+
275
+ if (branch.some((node) => node.server_data)) {
276
+ serialized_data.push(
277
+ render_json_payload_script(
278
+ { type: 'server_data' },
279
+ branch.map(({ server_data }) => server_data)
280
+ )
281
+ );
282
+ }
283
+
284
+ if (validation_errors) {
285
+ serialized_data.push(
286
+ render_json_payload_script({ type: 'validation_errors' }, validation_errors)
287
+ );
288
+ }
289
+
290
+ body += `\n\t${serialized_data.join('\n\t')}`;
291
+ }
292
+
293
+ if (options.service_worker) {
294
+ // always include service worker unless it's turned off explicitly
295
+ csp.add_script(init_service_worker);
296
+
297
+ head += `
298
+ <script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
299
+ }
300
+
301
+ if (state.prerendering) {
302
+ // TODO read headers set with setHeaders and convert into http-equiv where possible
303
+ const http_equiv = [];
304
+
305
+ const csp_headers = csp.csp_provider.get_meta();
306
+ if (csp_headers) {
307
+ http_equiv.push(csp_headers);
308
+ }
309
+
310
+ if (state.prerendering.cache) {
311
+ http_equiv.push(`<meta http-equiv="cache-control" content="${state.prerendering.cache}">`);
312
+ }
313
+
314
+ if (http_equiv.length > 0) {
315
+ head = http_equiv.join('\n') + head;
316
+ }
317
+ }
318
+
319
+ // TODO flush chunks as early as we can
320
+ const html =
321
+ (await resolve_opts.transformPageChunk({
322
+ html: options.template({ head, body, assets, nonce: /** @type {string} */ (csp.nonce) }),
323
+ done: true
324
+ })) || '';
325
+
326
+ const headers = new Headers({
327
+ 'content-type': 'text/html',
328
+ etag: `"${hash(html)}"`
329
+ });
330
+
331
+ if (!state.prerendering) {
332
+ const csp_header = csp.csp_provider.get_header();
333
+ if (csp_header) {
334
+ headers.set('content-security-policy', csp_header);
335
+ }
336
+ const report_only_header = csp.report_only_provider.get_header();
337
+ if (report_only_header) {
338
+ headers.set('content-security-policy-report-only', report_only_header);
339
+ }
340
+
341
+ for (const new_cookie of cookies) {
342
+ const { name, value, ...options } = new_cookie;
343
+ // @ts-expect-error
344
+ headers.append('set-cookie', cookie.serialize(name, value, options));
345
+ }
346
+
347
+ if (link_header_preloads.size) {
348
+ headers.set('link', Array.from(link_header_preloads).join(', '));
349
+ }
350
+ }
351
+
352
+ if (error && options.dev && !(error instanceof HttpError)) {
353
+ // reset stack, otherwise it may be 'fixed' a second time
354
+ error.stack = stack;
355
+ }
356
+
357
+ return new Response(html, {
358
+ status,
359
+ headers
360
+ });
361
+ }
@@ -0,0 +1,95 @@
1
+ import { render_response } from './render.js';
2
+ import { load_data, load_server_data } from './load_data.js';
3
+ import { coalesce_to_error } from '../../../utils/error.js';
4
+ import { GENERIC_ERROR } from '../utils.js';
5
+ import { create_fetch } from './fetch.js';
6
+
7
+ /**
8
+ * @typedef {import('./types.js').Loaded} Loaded
9
+ * @typedef {import('types').SSROptions} SSROptions
10
+ * @typedef {import('types').SSRState} SSRState
11
+ */
12
+
13
+ /**
14
+ * @param {{
15
+ * event: import('types').RequestEvent;
16
+ * options: SSROptions;
17
+ * state: SSRState;
18
+ * status: number;
19
+ * error: Error;
20
+ * resolve_opts: import('types').RequiredResolveOptions;
21
+ * }} opts
22
+ */
23
+ export async function respond_with_error({ event, options, state, status, error, resolve_opts }) {
24
+ const { fetcher, fetched, cookies } = create_fetch({
25
+ event,
26
+ options,
27
+ state,
28
+ route: GENERIC_ERROR
29
+ });
30
+
31
+ try {
32
+ const branch = [];
33
+
34
+ if (resolve_opts.ssr) {
35
+ const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
36
+
37
+ const server_data_promise = load_server_data({
38
+ dev: options.dev,
39
+ event,
40
+ state,
41
+ node: default_layout,
42
+ parent: async () => ({})
43
+ });
44
+
45
+ const server_data = await server_data_promise;
46
+
47
+ const data = await load_data({
48
+ event,
49
+ fetcher,
50
+ node: default_layout,
51
+ parent: async () => ({}),
52
+ server_data_promise,
53
+ state
54
+ });
55
+
56
+ branch.push(
57
+ {
58
+ node: default_layout,
59
+ server_data,
60
+ data
61
+ },
62
+ {
63
+ node: await options.manifest._.nodes[1](), // 1 is always the root error
64
+ data: null,
65
+ server_data: null
66
+ }
67
+ );
68
+ }
69
+
70
+ return await render_response({
71
+ options,
72
+ state,
73
+ page_config: {
74
+ hydrate: options.hydrate,
75
+ router: options.router
76
+ },
77
+ status,
78
+ error,
79
+ branch,
80
+ fetched,
81
+ cookies,
82
+ event,
83
+ resolve_opts,
84
+ validation_errors: undefined
85
+ });
86
+ } catch (err) {
87
+ const error = coalesce_to_error(err);
88
+
89
+ options.handle_error(error, event);
90
+
91
+ return new Response(error.stack, {
92
+ status: 500
93
+ });
94
+ }
95
+ }
@@ -0,0 +1,44 @@
1
+ import { ResponseHeaders, SSRNode, CspDirectives } from 'types';
2
+ import { HttpError } from '../../../index/private';
3
+
4
+ export interface Fetched {
5
+ url: string;
6
+ body?: string | null;
7
+ response: {
8
+ status: number;
9
+ statusText: string;
10
+ headers: ResponseHeaders;
11
+ body: string;
12
+ };
13
+ }
14
+
15
+ export interface FetchState {
16
+ fetched: Fetched[];
17
+ cookies: string[];
18
+ new_cookies: string[];
19
+ }
20
+
21
+ export type Loaded = {
22
+ node: SSRNode;
23
+ data: Record<string, any> | null;
24
+ server_data: any;
25
+ };
26
+
27
+ type CspMode = 'hash' | 'nonce' | 'auto';
28
+
29
+ export interface CspConfig {
30
+ mode: CspMode;
31
+ directives: CspDirectives;
32
+ reportOnly: CspDirectives;
33
+ }
34
+
35
+ export interface CspOpts {
36
+ dev: boolean;
37
+ prerender: boolean;
38
+ }
39
+
40
+ export interface SerializedHttpError extends Pick<HttpError, 'message' | 'status'> {
41
+ name: 'HttpError';
42
+ stack: '';
43
+ __is_http_error: true;
44
+ }