@sveltejs/kit 1.5.2 → 1.5.3

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.5.2",
3
+ "version": "1.5.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -1,7 +1,5 @@
1
- import { fork } from 'node:child_process';
2
1
  import { existsSync, statSync, createReadStream, createWriteStream } from 'node:fs';
3
2
  import { pipeline } from 'node:stream';
4
- import { fileURLToPath } from 'node:url';
5
3
  import { promisify } from 'node:util';
6
4
  import zlib from 'node:zlib';
7
5
  import glob from 'tiny-glob';
@@ -9,6 +7,8 @@ import { copy, rimraf, mkdirp } from '../../utils/filesystem.js';
9
7
  import { generate_manifest } from '../generate_manifest/index.js';
10
8
  import { get_route_segments } from '../../utils/routing.js';
11
9
  import { get_env } from '../../exports/vite/utils.js';
10
+ import generate_fallback from '../postbuild/fallback.js';
11
+ import { write } from '../sync/utils.js';
12
12
 
13
13
  const pipe = promisify(pipeline);
14
14
 
@@ -142,31 +142,16 @@ export function create_builder({
142
142
  }
143
143
  },
144
144
 
145
- generateFallback(dest) {
146
- // do prerendering in a subprocess so any dangling stuff gets killed upon completion
147
- const script = fileURLToPath(new URL('../postbuild/fallback.js', import.meta.url));
148
-
145
+ async generateFallback(dest) {
149
146
  const manifest_path = `${config.kit.outDir}/output/server/manifest-full.js`;
150
-
151
147
  const env = get_env(config.kit.env, 'production');
152
148
 
153
- return new Promise((fulfil, reject) => {
154
- const child = fork(
155
- script,
156
- [dest, manifest_path, JSON.stringify({ ...env.private, ...env.public })],
157
- {
158
- stdio: 'inherit'
159
- }
160
- );
161
-
162
- child.on('exit', (code) => {
163
- if (code) {
164
- reject(new Error(`Could not create a fallback page — failed with code ${code}`));
165
- } else {
166
- fulfil(undefined);
167
- }
168
- });
149
+ const fallback = await generate_fallback({
150
+ manifest_path,
151
+ env: { ...env.private, ...env.public }
169
152
  });
153
+
154
+ write(dest, fallback);
170
155
  },
171
156
 
172
157
  generateManifest: ({ relativePath, routes: subset }) => {
@@ -1,43 +1,54 @@
1
- import { readFileSync, writeFileSync } from 'node:fs';
2
- import { dirname, join } from 'node:path';
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
- import { mkdirp } from '../../utils/filesystem.js';
5
4
  import { installPolyfills } from '../../exports/node/polyfills.js';
6
5
  import { load_config } from '../config/index.js';
6
+ import { forked } from '../../utils/fork.js';
7
7
 
8
- const [, , dest, manifest_path, env] = process.argv;
8
+ export default forked(import.meta.url, generate_fallback);
9
9
 
10
- /** @type {import('types').ValidatedKitConfig} */
11
- const config = (await load_config()).kit;
10
+ /**
11
+ * @param {{
12
+ * manifest_path: string;
13
+ * env: Record<string, string>
14
+ * }} opts
15
+ */
16
+ async function generate_fallback({ manifest_path, env }) {
17
+ /** @type {import('types').ValidatedKitConfig} */
18
+ const config = (await load_config()).kit;
12
19
 
13
- installPolyfills();
20
+ installPolyfills();
14
21
 
15
- const server_root = join(config.outDir, 'output');
22
+ const server_root = join(config.outDir, 'output');
16
23
 
17
- /** @type {import('types').ServerInternalModule} */
18
- const { set_building } = await import(pathToFileURL(`${server_root}/server/internal.js`).href);
24
+ /** @type {import('types').ServerInternalModule} */
25
+ const { set_building } = await import(pathToFileURL(`${server_root}/server/internal.js`).href);
19
26
 
20
- /** @type {import('types').ServerModule} */
21
- const { Server } = await import(pathToFileURL(`${server_root}/server/index.js`).href);
27
+ /** @type {import('types').ServerModule} */
28
+ const { Server } = await import(pathToFileURL(`${server_root}/server/index.js`).href);
22
29
 
23
- /** @type {import('types').SSRManifest} */
24
- const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
30
+ /** @type {import('types').SSRManifest} */
31
+ const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
25
32
 
26
- set_building(true);
33
+ set_building(true);
27
34
 
28
- const server = new Server(manifest);
29
- await server.init({ env: JSON.parse(env) });
35
+ const server = new Server(manifest);
36
+ await server.init({ env });
30
37
 
31
- const rendered = await server.respond(new Request(config.prerender.origin + '/[fallback]'), {
32
- getClientAddress: () => {
33
- throw new Error('Cannot read clientAddress during prerendering');
34
- },
35
- prerendering: {
36
- fallback: true,
37
- dependencies: new Map()
38
- },
39
- read: (file) => readFileSync(join(config.files.assets, file))
40
- });
38
+ const response = await server.respond(new Request(config.prerender.origin + '/[fallback]'), {
39
+ getClientAddress: () => {
40
+ throw new Error('Cannot read clientAddress during prerendering');
41
+ },
42
+ prerendering: {
43
+ fallback: true,
44
+ dependencies: new Map()
45
+ },
46
+ read: (file) => readFileSync(join(config.files.assets, file))
47
+ });
41
48
 
42
- mkdirp(dirname(dest));
43
- writeFileSync(dest, await rendered.text());
49
+ if (response.ok) {
50
+ return await response.text();
51
+ }
52
+
53
+ throw new Error(`Could not create a fallback page — failed with status ${response.status}`);
54
+ }
@@ -1,6 +1,10 @@
1
1
  import { ReadableStream, TransformStream, WritableStream } from 'node:stream/web';
2
+ import buffer from 'node:buffer';
2
3
  import { webcrypto as crypto } from 'node:crypto';
3
- import { fetch, Response, Request, Headers, FormData } from 'undici';
4
+ import { fetch, Response, Request, Headers, FormData, File as UndiciFile } from 'undici';
5
+
6
+ // @ts-expect-error
7
+ const File = buffer.File ?? UndiciFile;
4
8
 
5
9
  /** @type {Record<string, any>} */
6
10
  const globals = {
@@ -12,7 +16,8 @@ const globals = {
12
16
  ReadableStream,
13
17
  TransformStream,
14
18
  WritableStream,
15
- FormData
19
+ FormData,
20
+ File
16
21
  };
17
22
 
18
23
  // exported for dev/preview and node environments
@@ -23,7 +23,7 @@ export const getStores = () => {
23
23
  export const page = {
24
24
  /** @param {(value: any) => void} fn */
25
25
  subscribe(fn) {
26
- const store = getStores().page;
26
+ const store = __SVELTEKIT_DEV__ ? get_store('page') : getStores().page;
27
27
  return store.subscribe(fn);
28
28
  }
29
29
  };
@@ -31,7 +31,7 @@ export const page = {
31
31
  /** @type {typeof import('$app/stores').navigating} */
32
32
  export const navigating = {
33
33
  subscribe(fn) {
34
- const store = getStores().navigating;
34
+ const store = __SVELTEKIT_DEV__ ? get_store('navigating') : getStores().navigating;
35
35
  return store.subscribe(fn);
36
36
  }
37
37
  };
@@ -39,7 +39,7 @@ export const navigating = {
39
39
  /** @type {typeof import('$app/stores').updated} */
40
40
  export const updated = {
41
41
  subscribe(fn) {
42
- const store = getStores().updated;
42
+ const store = __SVELTEKIT_DEV__ ? get_store('updated') : getStores().updated;
43
43
 
44
44
  if (browser) {
45
45
  updated.check = store.check;
@@ -55,3 +55,18 @@ export const updated = {
55
55
  );
56
56
  }
57
57
  };
58
+
59
+ /**
60
+ * @template {keyof ReturnType<typeof getStores>} Name
61
+ * @param {Name} name
62
+ * @returns {ReturnType<typeof getStores>[Name]}
63
+ */
64
+ function get_store(name) {
65
+ try {
66
+ return getStores()[name];
67
+ } catch (e) {
68
+ throw new Error(
69
+ `Cannot subscribe to '${name}' store on the server outside of a Svelte component, as it is bound to the current request via component context. This prevents state from leaking between users.`
70
+ );
71
+ }
72
+ }
@@ -221,14 +221,8 @@ export function create_client({ target }) {
221
221
  });
222
222
  }
223
223
 
224
- /** @param {URL} url */
225
- async function preload_data(url) {
226
- const intent = get_navigation_intent(url, false);
227
-
228
- if (!intent) {
229
- throw new Error(`Attempted to preload a URL that does not belong to this app: ${url}`);
230
- }
231
-
224
+ /** @param {import('./types').NavigationIntent} intent */
225
+ async function preload_data(intent) {
232
226
  load_cache = {
233
227
  id: intent.id,
234
228
  promise: load_route(intent).then((result) => {
@@ -1261,7 +1255,8 @@ export function create_client({ target }) {
1261
1255
 
1262
1256
  if (!options.reload) {
1263
1257
  if (priority <= options.preload_data) {
1264
- preload_data(/** @type {URL} */ (url));
1258
+ const intent = get_navigation_intent(/** @type {URL} */ (url), false);
1259
+ if (intent) preload_data(intent);
1265
1260
  } else if (priority <= options.preload_code) {
1266
1261
  preload_code(get_url_path(/** @type {URL} */ (url)));
1267
1262
  }
@@ -1347,7 +1342,13 @@ export function create_client({ target }) {
1347
1342
 
1348
1343
  preload_data: async (href) => {
1349
1344
  const url = new URL(href, get_base_uri(document));
1350
- await preload_data(url);
1345
+ const intent = get_navigation_intent(url, false);
1346
+
1347
+ if (!intent) {
1348
+ throw new Error(`Attempted to preload a URL that does not belong to this app: ${url}`);
1349
+ }
1350
+
1351
+ await preload_data(intent);
1351
1352
  },
1352
1353
 
1353
1354
  preload_code,
@@ -113,7 +113,32 @@ export async function render_response({
113
113
  form: form_value
114
114
  };
115
115
 
116
- rendered = options.root.render(props);
116
+ if (__SVELTEKIT_DEV__) {
117
+ const fetch = globalThis.fetch;
118
+ let warned = false;
119
+ globalThis.fetch = (info, init) => {
120
+ if (typeof info === 'string' && !/^\w+:\/\//.test(info)) {
121
+ throw new Error(
122
+ `Cannot call \`fetch\` eagerly during server side rendering with relative URL (${info}) — put your \`fetch\` calls inside \`onMount\` or a \`load\` function instead`
123
+ );
124
+ } else if (!warned) {
125
+ console.warn(
126
+ `Avoid calling \`fetch\` eagerly during server side rendering — put your \`fetch\` calls inside \`onMount\` or a \`load\` function instead`
127
+ );
128
+ warned = true;
129
+ }
130
+
131
+ return fetch(info, init);
132
+ };
133
+
134
+ try {
135
+ rendered = options.root.render(props);
136
+ } finally {
137
+ globalThis.fetch = fetch;
138
+ }
139
+ } else {
140
+ rendered = options.root.render(props);
141
+ }
117
142
 
118
143
  for (const { node } of branch) {
119
144
  if (node.imports) {
@@ -41,7 +41,7 @@ export function method_not_allowed(mod, method) {
41
41
  export function allowed_methods(mod) {
42
42
  const allowed = [];
43
43
 
44
- for (const method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
44
+ for (const method of ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
45
45
  if (method in mod) allowed.push(method);
46
46
  }
47
47
 
@@ -292,16 +292,22 @@ declare module '$app/stores' {
292
292
 
293
293
  /**
294
294
  * A readable store whose value contains page data.
295
+ *
296
+ * On the server, this store can only be subscribed to during component initialization. In the browser, it can be subscribed to at any time.
295
297
  */
296
298
  export const page: Readable<Page>;
297
299
  /**
298
300
  * A readable store.
299
301
  * When navigating starts, its value is a `Navigation` object with `from`, `to`, `type` and (if `type === 'popstate'`) `delta` properties.
300
302
  * When navigating finishes, its value reverts to `null`.
303
+ *
304
+ * On the server, this store can only be subscribed to during component initialization. In the browser, it can be subscribed to at any time.
301
305
  */
302
306
  export const navigating: Readable<Navigation | null>;
303
307
  /**
304
- * A readable store whose initial value is `false`. If [`version.pollInterval`](https://kit.svelte.dev/docs/configuration#version) is a non-zero value, SvelteKit will poll for new versions of the app and update the store value to `true` when it detects one. `updated.check()` will force an immediate check, regardless of polling.
308
+ * A readable store whose initial value is `false`. If [`version.pollInterval`](https://kit.svelte.dev/docs/configuration#version) is a non-zero value, SvelteKit will poll for new versions of the app and update the store value to `true` when it detects one. `updated.check()` will force an immediate check, regardless of polling.
309
+ *
310
+ * On the server, this store can only be subscribed to during component initialization. In the browser, it can be subscribed to at any time.
305
311
  */
306
312
  export const updated: Readable<boolean> & { check(): Promise<boolean> };
307
313
 
package/types/index.d.ts CHANGED
@@ -542,8 +542,7 @@ export interface KitConfig {
542
542
  */
543
543
  version?: {
544
544
  /**
545
- * The current app version string.
546
- * @default Date.now().toString()
545
+ * The current app version string. If specified, this must be deterministic (e.g. a commit ref rather than `Math.random()` or Date.now().toString()`), otherwise defaults to a timestamp of the build
547
546
  */
548
547
  name?: string;
549
548
  /**