@sveltejs/kit 1.5.2 → 1.5.5

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.5",
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
@@ -632,7 +632,15 @@ export function set_assets(path) {
632
632
  // CLI args
633
633
  mode: vite_config_env.mode,
634
634
  logLevel: vite_config.logLevel,
635
- clearScreen: vite_config.clearScreen
635
+ clearScreen: vite_config.clearScreen,
636
+ build: {
637
+ minify: vite_config.build.minify,
638
+ assetsInlineLimit: vite_config.build.assetsInlineLimit,
639
+ sourcemap: vite_config.build.sourcemap
640
+ },
641
+ optimizeDeps: {
642
+ force: vite_config.optimizeDeps.force
643
+ }
636
644
  })
637
645
  );
638
646
 
@@ -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,23 @@ 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) {
1260
+ if (__SVELTEKIT_DEV__) {
1261
+ preload_data(intent).then((result) => {
1262
+ if (result.type === 'loaded' && result.state.error) {
1263
+ console.warn(
1264
+ `Preloading data for ${intent.url.pathname} failed with the following error: ${result.state.error.message}\n` +
1265
+ 'If this error is transient, you can ignore it. Otherwise, consider disabling preloading for this route. ' +
1266
+ 'This route was preloaded due to a data-sveltekit-preload-data attribute. ' +
1267
+ 'See https://kit.svelte.dev/docs/link-options for more info'
1268
+ );
1269
+ }
1270
+ });
1271
+ } else {
1272
+ preload_data(intent);
1273
+ }
1274
+ }
1265
1275
  } else if (priority <= options.preload_code) {
1266
1276
  preload_code(get_url_path(/** @type {URL} */ (url)));
1267
1277
  }
@@ -1347,7 +1357,13 @@ export function create_client({ target }) {
1347
1357
 
1348
1358
  preload_data: async (href) => {
1349
1359
  const url = new URL(href, get_base_uri(document));
1350
- await preload_data(url);
1360
+ const intent = get_navigation_intent(url, false);
1361
+
1362
+ if (!intent) {
1363
+ throw new Error(`Attempted to preload a URL that does not belong to this app: ${url}`);
1364
+ }
1365
+
1366
+ await preload_data(intent);
1351
1367
  },
1352
1368
 
1353
1369
  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
@@ -304,6 +304,8 @@ export interface KitConfig {
304
304
  * > When `mode` is `'auto'`, SvelteKit will use nonces for dynamically rendered pages and hashes for prerendered pages. Using nonces with prerendered pages is insecure and therefore forbidden.
305
305
  *
306
306
  * > Note that most [Svelte transitions](https://svelte.dev/tutorial/transition) work by creating an inline `<style>` element. If you use these in your app, you must either leave the `style-src` directive unspecified or add `unsafe-inline`.
307
+ *
308
+ * If this level of configuration is insufficient and you have more dynamic requirements, you can use the [`handle` hook](https://kit.svelte.dev/docs/hooks#server-hooks-handle) to roll your own CSP.
307
309
  */
308
310
  csp?: {
309
311
  /**
@@ -542,8 +544,7 @@ export interface KitConfig {
542
544
  */
543
545
  version?: {
544
546
  /**
545
- * The current app version string.
546
- * @default Date.now().toString()
547
+ * 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
548
  */
548
549
  name?: string;
549
550
  /**