@sveltejs/kit 1.0.0-next.501 → 1.0.0-next.503

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.501",
3
+ "version": "1.0.0-next.503",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
package/postinstall.js CHANGED
@@ -4,35 +4,44 @@ import glob from 'tiny-glob/sync.js';
4
4
  import { load_config } from './src/core/config/index.js';
5
5
  import * as sync from './src/core/sync/sync.js';
6
6
 
7
- const cwd = process.env.INIT_CWD ?? process.cwd();
8
- process.chdir(cwd);
7
+ try {
8
+ const cwd = process.env.INIT_CWD ?? process.cwd();
9
+ process.chdir(cwd);
9
10
 
10
- const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
11
+ if (fs.existsSync('package.json')) {
12
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
11
13
 
12
- const directories = [];
14
+ const directories = [];
13
15
 
14
- if (pkg.workspaces) {
15
- for (const directory of pkg.workspaces) {
16
- directories.push(...glob(directory, { cwd }).map((dir) => path.resolve(cwd, dir)));
17
- }
18
- } else {
19
- directories.push(cwd);
20
- }
16
+ if (pkg.workspaces) {
17
+ // we have to do this because of https://classic.yarnpkg.com/blog/2018/02/15/nohoist/
18
+ const packages = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages;
21
19
 
22
- for (const cwd of directories) {
23
- process.chdir(cwd);
20
+ for (const directory of packages) {
21
+ directories.push(...glob(directory, { cwd }).map((dir) => path.resolve(cwd, dir)));
22
+ }
23
+ } else {
24
+ directories.push(cwd);
25
+ }
26
+
27
+ for (const cwd of directories) {
28
+ process.chdir(cwd);
24
29
 
25
- if (!fs.existsSync('package.json')) continue;
26
- if (!fs.existsSync('svelte.config.js')) continue;
30
+ if (!fs.existsSync('package.json')) continue;
31
+ if (!fs.existsSync('svelte.config.js')) continue;
27
32
 
28
- const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
29
- if (!pkg.dependencies?.['@sveltejs/kit'] && !pkg.devDependencies?.['@sveltejs/kit']) continue;
33
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
34
+ if (!pkg.dependencies?.['@sveltejs/kit'] && !pkg.devDependencies?.['@sveltejs/kit']) continue;
30
35
 
31
- try {
32
- const config = await load_config();
33
- await sync.all(config, 'development');
34
- } catch (e) {
35
- console.log('Error while trying to sync SvelteKit config');
36
- console.log(e);
36
+ try {
37
+ const config = await load_config();
38
+ await sync.all(config, 'development');
39
+ } catch (error) {
40
+ console.log('Error while trying to sync SvelteKit config');
41
+ console.log(error.stack);
42
+ }
43
+ }
37
44
  }
45
+ } catch (error) {
46
+ console.error(error.stack);
38
47
  }
@@ -85,6 +85,9 @@ export async function prerender() {
85
85
  /** @type {import('types').PrerenderMap} */
86
86
  const prerender_map = new Map();
87
87
 
88
+ /** @type {Set<string>} */
89
+ const prerendered_routes = new Set();
90
+
88
91
  /** @type {import('types').ValidatedKitConfig} */
89
92
  const config = (await load_config()).kit;
90
93
 
@@ -302,6 +305,9 @@ export async function prerender() {
302
305
 
303
306
  if (written.has(file)) return;
304
307
 
308
+ const route_id = response.headers.get('x-sveltekit-routeid');
309
+ if (route_id !== null) prerendered_routes.add(route_id);
310
+
305
311
  if (response_type === REDIRECT) {
306
312
  const location = headers['location'];
307
313
 
@@ -410,6 +416,23 @@ export async function prerender() {
410
416
 
411
417
  await q.done();
412
418
 
419
+ /** @type {string[]} */
420
+ const not_prerendered = [];
421
+
422
+ for (const [route_id, prerender] of prerender_map) {
423
+ if (prerender === true && !prerendered_routes.has(route_id)) {
424
+ not_prerendered.push(route_id);
425
+ }
426
+ }
427
+
428
+ if (not_prerendered.length > 0) {
429
+ throw new Error(
430
+ `The following routes were marked as prerenderable, but were not prerendered:\n${not_prerendered.map(
431
+ (id) => ` - ${id}`
432
+ )}\n\nSee https://kit.svelte.dev/docs/page-options#prerender-troubleshooting for more info`
433
+ );
434
+ }
435
+
413
436
  const rendered = await server.respond(new Request(config.prerender.origin + '/[fallback]'), {
414
437
  getClientAddress,
415
438
  prerendering: {
@@ -46,11 +46,82 @@ export async function write_all_types(config, manifest_data) {
46
46
  }
47
47
  }
48
48
 
49
+ // Read/write meta data on each invocation, not once per node process,
50
+ // it could be invoked by another process in the meantime.
51
+ const meta_data_file = `${types_dir}/route_meta_data.json`;
52
+ const has_meta_data = fs.existsSync(meta_data_file);
53
+ let meta_data = has_meta_data
54
+ ? /** @type {Record<string, string[]>} */ (JSON.parse(fs.readFileSync(meta_data_file, 'utf-8')))
55
+ : {};
49
56
  const routes_map = create_routes_map(manifest_data);
50
57
  // For each directory, write $types.d.ts
51
58
  for (const route of manifest_data.routes) {
52
- update_types(config, routes_map, route);
59
+ if (!route.leaf && !route.layout && !route.endpoint) continue; // nothing to do
60
+
61
+ const outdir = path.join(config.kit.outDir, 'types', routes_dir, route.id);
62
+
63
+ // check if the types are out of date
64
+ /** @type {string[]} */
65
+ const input_files = [];
66
+
67
+ /** @type {import('types').PageNode | null} */
68
+ let node = route.leaf;
69
+ while (node) {
70
+ if (node.shared) input_files.push(node.shared);
71
+ if (node.server) input_files.push(node.server);
72
+ node = node.parent ?? null;
73
+ }
74
+
75
+ /** @type {import('types').PageNode | null} */
76
+ node = route.layout;
77
+ while (node) {
78
+ if (node.shared) input_files.push(node.shared);
79
+ if (node.server) input_files.push(node.server);
80
+ node = node.parent ?? null;
81
+ }
82
+
83
+ if (route.endpoint) {
84
+ input_files.push(route.endpoint.file);
85
+ }
86
+
87
+ try {
88
+ fs.mkdirSync(outdir, { recursive: true });
89
+ } catch {}
90
+
91
+ const output_files = compact(
92
+ fs.readdirSync(outdir).map((name) => {
93
+ const stats = fs.statSync(path.join(outdir, name));
94
+ if (stats.isDirectory()) return;
95
+ return {
96
+ name,
97
+ updated: stats.mtimeMs
98
+ };
99
+ })
100
+ );
101
+
102
+ const source_last_updated = Math.max(
103
+ // ctimeMs includes move operations whereas mtimeMs does not
104
+ ...input_files.map((file) => fs.statSync(file).ctimeMs)
105
+ );
106
+ const types_last_updated = Math.max(...output_files.map((file) => file.updated));
107
+
108
+ const should_generate =
109
+ // source files were generated more recently than the types
110
+ source_last_updated > types_last_updated ||
111
+ // no meta data file exists yet
112
+ !has_meta_data ||
113
+ // some file was deleted
114
+ !meta_data[route.id]?.every((file) => input_files.includes(file));
115
+
116
+ if (should_generate) {
117
+ // track which old files end up being surplus to requirements
118
+ const to_delete = new Set(output_files.map((file) => file.name));
119
+ update_types(config, routes_map, route, to_delete);
120
+ meta_data[route.id] = input_files;
121
+ }
53
122
  }
123
+
124
+ fs.writeFileSync(meta_data_file, JSON.stringify(meta_data, null, '\t'));
54
125
  }
55
126
 
56
127
  /**
@@ -72,6 +143,7 @@ export async function write_types(config, manifest_data, file) {
72
143
 
73
144
  const route = manifest_data.routes.find((route) => route.id === id);
74
145
  if (!route) return; // this shouldn't ever happen
146
+ if (!route.leaf && !route.layout && !route.endpoint) return; // nothing to do
75
147
 
76
148
  update_types(config, create_routes_map(manifest_data), route);
77
149
  }
@@ -96,60 +168,12 @@ function create_routes_map(manifest_data) {
96
168
  * @param {import('types').ValidatedConfig} config
97
169
  * @param {Map<import('types').PageNode, import('types').RouteData>} routes
98
170
  * @param {import('types').RouteData} route
171
+ * @param {Set<string>} [to_delete]
99
172
  */
100
- function update_types(config, routes, route) {
101
- if (!route.leaf && !route.layout && !route.endpoint) return; // nothing to do
102
-
173
+ function update_types(config, routes, route, to_delete = new Set()) {
103
174
  const routes_dir = posixify(path.relative('.', config.kit.files.routes));
104
175
  const outdir = path.join(config.kit.outDir, 'types', routes_dir, route.id);
105
176
 
106
- // first, check if the types are out of date
107
- const input_files = [];
108
-
109
- /** @type {import('types').PageNode | null} */
110
- let node = route.leaf;
111
- while (node) {
112
- if (node.shared) input_files.push(node.shared);
113
- if (node.server) input_files.push(node.server);
114
- node = node.parent ?? null;
115
- }
116
-
117
- /** @type {import('types').PageNode | null} */
118
- node = route.layout;
119
- while (node) {
120
- if (node.shared) input_files.push(node.shared);
121
- if (node.server) input_files.push(node.server);
122
- node = node.parent ?? null;
123
- }
124
-
125
- if (route.endpoint) {
126
- input_files.push(route.endpoint.file);
127
- }
128
-
129
- try {
130
- fs.mkdirSync(outdir, { recursive: true });
131
- } catch {}
132
-
133
- const output_files = compact(
134
- fs.readdirSync(outdir).map((name) => {
135
- const stats = fs.statSync(path.join(outdir, name));
136
- if (stats.isDirectory()) return;
137
- return {
138
- name,
139
- updated: stats.mtimeMs
140
- };
141
- })
142
- );
143
-
144
- const source_last_updated = Math.max(...input_files.map((file) => fs.statSync(file).mtimeMs));
145
- const types_last_updated = Math.max(...output_files.map((file) => file?.updated));
146
-
147
- // types were generated more recently than the source files, so don't regenerate
148
- if (types_last_updated > source_last_updated) return;
149
-
150
- // track which old files end up being surplus to requirements
151
- const to_delete = new Set(output_files.map((file) => file.name));
152
-
153
177
  // now generate new types
154
178
  const imports = [`import type * as Kit from '@sveltejs/kit';`];
155
179
 
@@ -180,7 +204,7 @@ function update_types(config, routes, route) {
180
204
  `type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>`
181
205
  );
182
206
  // null & {} == null, we need to prevent that in some situations
183
- declarations.push(`type EnsureParentData<T> = T extends null | undefined ? {} : T;`);
207
+ declarations.push(`type EnsureDefined<T> = T extends null | undefined ? {} : T;`);
184
208
  }
185
209
 
186
210
  if (route.leaf) {
@@ -308,7 +332,7 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
308
332
  ? `./proxy${replace_ext_with_js(basename)}`
309
333
  : path_to_original(outdir, node.server);
310
334
 
311
- type = `Expand<Kit.AwaitedActions<typeof import('${from}').actions>>`;
335
+ type = `Expand<Kit.AwaitedActions<typeof import('${from}').actions>> | undefined`;
312
336
  }
313
337
  }
314
338
  exports.push(`export type ActionData = ${type};`);
@@ -329,9 +353,13 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
329
353
  written_proxies.push(`proxy${path.basename(node.shared)}`);
330
354
  }
331
355
 
332
- const type = get_data_type(node.shared, `${parent_type} & ${prefix}ServerData`, proxy);
356
+ const type = get_data_type(
357
+ node.shared,
358
+ `${parent_type} & EnsureDefined<${prefix}ServerData>`,
359
+ proxy
360
+ );
333
361
 
334
- data = `Expand<Omit<${parent_type}, keyof ${type}> & ${type}>`;
362
+ data = `Expand<Omit<${parent_type}, keyof ${type}> & EnsureDefined<${type}>>`;
335
363
 
336
364
  const output_data_shape =
337
365
  !is_page && all_pages_have_load
@@ -345,7 +373,7 @@ function process_node(node, outdir, is_page, all_pages_have_load = true) {
345
373
  } else if (server_data === 'null') {
346
374
  data = `Expand<${parent_type}>`;
347
375
  } else {
348
- data = `Expand<Omit<${parent_type}, keyof ${prefix}ServerData> & ${prefix}ServerData>`;
376
+ data = `Expand<Omit<${parent_type}, keyof ${prefix}ServerData> & EnsureDefined<${prefix}ServerData>>`;
349
377
  }
350
378
 
351
379
  exports.push(`export type ${prefix}Data = ${data};`);
@@ -396,14 +424,14 @@ function get_parent_type(node, type) {
396
424
  parent = parent.parent;
397
425
  }
398
426
 
399
- let parent_str = `EnsureParentData<${parent_imports[0] || '{}'}>`;
427
+ let parent_str = `EnsureDefined<${parent_imports[0] || '{}'}>`;
400
428
  for (let i = 1; i < parent_imports.length; i++) {
401
429
  // Omit is necessary because a parent could have a property with the same key which would
402
430
  // cause a type conflict. At runtime the child overwrites the parent property in this case,
403
431
  // so reflect that in the type definition.
404
- // EnsureParentData is necessary because {something: string} & null becomes null.
432
+ // EnsureDefined is necessary because {something: string} & null becomes null.
405
433
  // Output types of server loads can be null but when passed in through the `parent` parameter they are the empty object instead.
406
- parent_str = `Omit<${parent_str}, keyof ${parent_imports[i]}> & EnsureParentData<${parent_imports[i]}>`;
434
+ parent_str = `Omit<${parent_str}, keyof ${parent_imports[i]}> & EnsureDefined<${parent_imports[i]}>`;
407
435
  }
408
436
  return parent_str;
409
437
  }
@@ -46,7 +46,7 @@ export async function preview(vite, vite_config, svelte_config) {
46
46
 
47
47
  const server = new Server(manifest);
48
48
  await server.init({
49
- env: loadEnv(vite_config.mode, process.cwd(), '')
49
+ env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, '')
50
50
  });
51
51
 
52
52
  return () => {
@@ -73,7 +73,8 @@ export function enhance(form, submit = () => {}) {
73
73
  const response = await fetch(action, {
74
74
  method: 'POST',
75
75
  headers: {
76
- accept: 'application/json'
76
+ accept: 'application/json',
77
+ 'x-sveltekit-action': 'true'
77
78
  },
78
79
  body: data,
79
80
  signal: controller.signal
@@ -438,7 +438,11 @@ export function create_client({ target, base, trailing_slash }) {
438
438
  }
439
439
 
440
440
  const page_changed =
441
- !current.url || url.href !== current.url.href || current.error !== error || data_changed;
441
+ !current.url ||
442
+ url.href !== current.url.href ||
443
+ current.error !== error ||
444
+ form !== undefined ||
445
+ data_changed;
442
446
 
443
447
  if (page_changed) {
444
448
  result.props.page = {
@@ -447,6 +451,7 @@ export function create_client({ target, base, trailing_slash }) {
447
451
  routeId: route && route.id,
448
452
  status,
449
453
  url,
454
+ form,
450
455
  // The whole page store is updated, but this way the object reference stays the same
451
456
  data: data_changed ? data : page.data
452
457
  };
@@ -1203,13 +1208,10 @@ export function create_client({ target, base, trailing_slash }) {
1203
1208
  goto(result.location, {}, []);
1204
1209
  } else {
1205
1210
  /** @type {Record<string, any>} */
1206
- const props = { form: result.data };
1207
-
1208
- if (result.status !== page.status) {
1209
- page = { ...page, status: result.status };
1210
- props.page = page;
1211
- }
1212
-
1211
+ const props = {
1212
+ form: result.data,
1213
+ page: { ...page, form: result.data, status: result.status }
1214
+ };
1213
1215
  const post_update = pre_update();
1214
1216
  root.$set(props);
1215
1217
  post_update();
@@ -54,7 +54,6 @@ export async function render_endpoint(event, mod, state) {
54
54
  }
55
55
 
56
56
  if (state.prerendering) {
57
- response.headers.set('x-sveltekit-routeid', /** @type {string} */ (event.routeId));
58
57
  response.headers.set('x-sveltekit-prerender', String(prerender));
59
58
  }
60
59
 
@@ -77,13 +76,16 @@ export async function render_endpoint(event, mod, state) {
77
76
  * @param {import('types').RequestEvent} event
78
77
  */
79
78
  export function is_endpoint_request(event) {
80
- const { method } = event.request;
79
+ const { method, headers } = event.request;
81
80
 
82
81
  if (method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
83
82
  // These methods exist exclusively for endpoints
84
83
  return true;
85
84
  }
86
85
 
86
+ // use:enhance uses a custom header to disambiguate
87
+ if (method === 'POST' && headers.get('x-sveltekit-action') === 'true') return false;
88
+
87
89
  // GET/POST requests may be for endpoints or pages. We prefer endpoints if this isn't a text/html request
88
90
  const accept = event.request.headers.get('accept') ?? '*/*';
89
91
  return negotiate(accept, ['*', 'text/html']) !== 'text/html';
@@ -296,6 +296,11 @@ export async function respond(request, options, state) {
296
296
  }
297
297
  }
298
298
  add_cookies_to_headers(response.headers, Array.from(new_cookies.values()));
299
+
300
+ if (state.prerendering && event.routeId !== null) {
301
+ response.headers.set('x-sveltekit-routeid', event.routeId);
302
+ }
303
+
299
304
  return response;
300
305
  }),
301
306
  // TODO remove for 1.0
@@ -98,7 +98,8 @@ export async function render_response({
98
98
  routeId: event.routeId,
99
99
  status,
100
100
  url: event.url,
101
- data
101
+ data,
102
+ form: form_value
102
103
  };
103
104
 
104
105
  // TODO remove this for 1.0
package/types/index.d.ts CHANGED
@@ -302,6 +302,7 @@ export interface Page<Params extends Record<string, string> = Record<string, str
302
302
  status: number;
303
303
  error: App.Error | null;
304
304
  data: App.PageData & Record<string, any>;
305
+ form: any;
305
306
  }
306
307
 
307
308
  export interface ParamMatcher {