From 1e24a282c0cc94f2060d2ad8c2cdaee19b5fe3a7 Mon Sep 17 00:00:00 2001 From: tanhauhau Date: Sun, 9 Jan 2022 16:12:22 +0800 Subject: [PATCH 1/4] add returned stuff to $page --- documentation/docs/03-loading.md | 2 +- documentation/docs/05-modules.md | 2 +- packages/kit/src/runtime/client/renderer.js | 60 ++++++++++++++----- packages/kit/src/runtime/server/index.js | 1 + .../kit/src/runtime/server/page/render.js | 6 +- .../kit/src/runtime/server/page/respond.js | 6 +- .../runtime/server/page/respond_with_error.js | 44 +++++++------- .../src/routes/store/stuff/[item].svelte | 16 +++++ .../src/routes/store/stuff/__error.svelte | 11 ++++ .../src/routes/store/stuff/__layout.svelte | 20 +++++++ packages/kit/test/apps/basics/test/test.js | 23 +++++++ packages/kit/types/ambient-modules.d.ts | 1 + 12 files changed, 148 insertions(+), 44 deletions(-) create mode 100644 packages/kit/test/apps/basics/src/routes/store/stuff/[item].svelte create mode 100644 packages/kit/test/apps/basics/src/routes/store/stuff/__error.svelte create mode 100644 packages/kit/test/apps/basics/src/routes/store/stuff/__layout.svelte diff --git a/documentation/docs/03-loading.md b/documentation/docs/03-loading.md index e460734d1a0c..016ae7881a25 100644 --- a/documentation/docs/03-loading.md +++ b/documentation/docs/03-loading.md @@ -153,4 +153,4 @@ If the `load` function returns a `props` object, the props will be passed to the This will be merged with any existing `stuff` and passed to the `load` functions of subsequent layout and page components. -This only applies to layout components, _not_ page components. +The final stuff will be available as part of the [page store](#modules-$app-stores) diff --git a/documentation/docs/05-modules.md b/documentation/docs/05-modules.md index 297d978a4507..6cefc0074f31 100644 --- a/documentation/docs/05-modules.md +++ b/documentation/docs/05-modules.md @@ -58,7 +58,7 @@ Because of that, the stores are not free-floating objects: they must be accessed The stores themselves attach to the correct context at the point of subscription, which means you can import and use them directly in components without boilerplate. However, it still needs to be called synchronously on component or page initialisation when the `$`-prefix isn't used. Use `getStores` to safely `.subscribe` asynchronously instead. - `navigating` is a [readable store](https://svelte.dev/tutorial/readable-stores). When navigating starts, its value is `{ from, to }`, where `from` and `to` are both [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) instances. When navigating finishes, its value reverts to `null`. -- `page` contains an object with the current [`url`](https://developer.mozilla.org/en-US/docs/Web/API/URL) and [`params`](#loading-input-params). +- `page` contains an object with the current [`url`](https://developer.mozilla.org/en-US/docs/Web/API/URL), [`params`](#loading-input-params) and [`stuff`](#loading-output-stuff). - `session` is a [writable store](https://svelte.dev/tutorial/writable-stores) whose initial value is whatever was returned from [`getSession`](#hooks-getsession). It can be written to, but this will _not_ cause changes to persist on the server — this is something you must implement yourself. ### $lib diff --git a/packages/kit/src/runtime/client/renderer.js b/packages/kit/src/runtime/client/renderer.js index 7568965657ae..124dec6548e8 100644 --- a/packages/kit/src/runtime/client/renderer.js +++ b/packages/kit/src/runtime/client/renderer.js @@ -193,7 +193,14 @@ export class Renderer { result = error_args ? await this._load_error(error_args) - : await this._get_navigation_result_from_branch({ url, params, branch, status, error }); + : await this._get_navigation_result_from_branch({ + url, + params, + stuff, + branch, + status, + error + }); } catch (e) { if (error) throw e; @@ -417,12 +424,13 @@ export class Renderer { * @param {{ * url: URL; * params: Record; + * stuff: Record; * branch: Array; * status: number; * error?: Error; * }} opts */ - async _get_navigation_result_from_branch({ url, params, branch, status, error }) { + async _get_navigation_result_from_branch({ url, params, stuff, branch, status, error }) { const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean)); const redirect = filtered.find((f) => f.loaded && f.loaded.redirect); @@ -446,7 +454,7 @@ export class Renderer { } if (!this.current.url || url.href !== this.current.url.href) { - result.props.page = { url, params, status, error }; + result.props.page = { url, params, status, error, stuff }; // TODO remove this for 1.0 /** @@ -715,6 +723,13 @@ export class Renderer { continue; } + if (error_loaded && error_loaded.loaded && error_loaded.loaded.stuff) { + stuff = { + ...stuff, + ...error_loaded.loaded.stuff + }; + } + branch = branch.slice(0, j + 1).concat(error_loaded); break load; } catch (e) { @@ -740,7 +755,14 @@ export class Renderer { } } - return await this._get_navigation_result_from_branch({ url, params, branch, status, error }); + return await this._get_navigation_result_from_branch({ + url, + params, + stuff, + branch, + status, + error + }); } /** @@ -760,19 +782,25 @@ export class Renderer { params, stuff: {} }); + const error_node = await this._load_node({ + status, + error, + module: await this.fallback[1], + url, + params, + stuff: (node && node.loaded && node.loaded.stuff) || {} + }); - const branch = [ - node, - await this._load_node({ - status, - error, - module: await this.fallback[1], - url, - params, - stuff: (node && node.loaded && node.loaded.stuff) || {} - }) - ]; + const branch = [node, error_node]; + const stuff = { ...node?.loaded?.stuff, ...error_node?.loaded?.stuff }; - return await this._get_navigation_result_from_branch({ url, params, branch, status, error }); + return await this._get_navigation_result_from_branch({ + url, + params, + stuff, + branch, + status, + error + }); } } diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 86fa172a3195..de263e4d9593 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -70,6 +70,7 @@ export async function respond(incoming, options, state = {}) { params: request.params, options, $session: await options.hooks.getSession(request), + stuff: {}, page_config: { ssr: false, router: true, hydrate: true }, status: 200, branch: [] diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 059db4305fe1..58e38e0f89a4 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -17,6 +17,7 @@ import { s } from '../../../utils/misc.js'; * error?: Error, * url: URL; * params: Record + * stuff: Record; * }} opts */ export async function render_response({ @@ -27,7 +28,8 @@ export async function render_response({ status, error, url, - params + params, + stuff }) { const css = new Set(options.manifest._.entry.css); const js = new Set(options.manifest._.entry.js); @@ -68,7 +70,7 @@ export async function render_response({ navigating: writable(null), session }, - page: { url, params, status, error }, + page: { url, params, status, error, stuff }, components: branch.map(({ node }) => node.module.default) }; diff --git a/packages/kit/src/runtime/server/page/respond.js b/packages/kit/src/runtime/server/page/respond.js index 9b99ab2ed7f5..81331d062663 100644 --- a/packages/kit/src/runtime/server/page/respond.js +++ b/packages/kit/src/runtime/server/page/respond.js @@ -73,9 +73,9 @@ export async function respond(opts) { /** @type {string[]} */ let set_cookie_headers = []; - ssr: if (page_config.ssr) { - let stuff = {}; + let stuff = {}; + ssr: if (page_config.ssr) { for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; @@ -160,6 +160,7 @@ export async function respond(opts) { page_config = get_page_config(error_node.module, options); branch = branch.slice(0, j + 1).concat(error_loaded); + stuff = { ...node_loaded.stuff, ...error_loaded.stuff }; break ssr; } catch (err) { const e = coalesce_to_error(err); @@ -201,6 +202,7 @@ export async function respond(opts) { return with_cookies( await render_response({ ...opts, + stuff, url: request.url, page_config, status, diff --git a/packages/kit/src/runtime/server/page/respond_with_error.js b/packages/kit/src/runtime/server/page/respond_with_error.js index 0a84d48a7c8c..dcbb24b088cf 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -27,7 +27,7 @@ export async function respond_with_error({ request, options, state, $session, st const params = {}; // error page has no params // error pages don't fall through, so we know it's not undefined - const loaded = /** @type {Loaded} */ ( + const layout_loaded = /** @type {Loaded} */ ( await load_node({ request, options, @@ -43,28 +43,27 @@ export async function respond_with_error({ request, options, state, $session, st is_error: false }) ); + const error_loaded = /** @type {Loaded} */ ( + await load_node({ + request, + options, + state, + route: null, + url: request.url, + params, + node: default_error, + $session, + stuff: layout_loaded ? layout_loaded.stuff : {}, + prerender_enabled: is_prerender_enabled(options, default_error, state), + is_leaf: false, + is_error: true, + status, + error + }) + ); - const branch = [ - loaded, - /** @type {Loaded} */ ( - await load_node({ - request, - options, - state, - route: null, - url: request.url, - params, - node: default_error, - $session, - stuff: loaded ? loaded.stuff : {}, - prerender_enabled: is_prerender_enabled(options, default_error, state), - is_leaf: false, - is_error: true, - status, - error - }) - ) - ]; + const branch = [layout_loaded, error_loaded]; + const stuff = { ...layout_loaded?.stuff, ...error_loaded?.stuff }; try { return await render_response({ @@ -75,6 +74,7 @@ export async function respond_with_error({ request, options, state, $session, st router: options.router, ssr: options.ssr }, + stuff, status, error, branch, diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/[item].svelte b/packages/kit/test/apps/basics/src/routes/store/stuff/[item].svelte new file mode 100644 index 000000000000..fa9f0b950739 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/stuff/[item].svelte @@ -0,0 +1,16 @@ + diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/__error.svelte b/packages/kit/test/apps/basics/src/routes/store/stuff/__error.svelte new file mode 100644 index 000000000000..0d311e876dc7 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/stuff/__error.svelte @@ -0,0 +1,11 @@ + diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/__layout.svelte b/packages/kit/test/apps/basics/src/routes/store/stuff/__layout.svelte new file mode 100644 index 000000000000..37307d9844c6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/stuff/__layout.svelte @@ -0,0 +1,20 @@ + + + + +
{JSON.stringify($page.stuff)}
+ + + + diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index df2fce51dae1..71131535de5f 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1105,6 +1105,29 @@ test.describe.parallel('$app/stores', () => { expect(oops).toBeUndefined(); }); + test('page store contains stuff', async ({ page, clicknav }) => { + await page.goto('/store/stuff/www'); + + expect(await page.textContent('#store-stuff')).toBe( + JSON.stringify({ name: 'SvelteKit', value: 456, page: 'www' }) + ); + + await clicknav('a[href="/store/stuff/zzz"]'); + expect(await page.textContent('#store-stuff')).toBe( + JSON.stringify({ name: 'SvelteKit', value: 456, page: 'zzz' }) + ); + + await clicknav('a[href="/store/stuff/xxx"]'); + expect(await page.textContent('#store-stuff')).toBe( + JSON.stringify({ name: 'SvelteKit', value: 789, error: 'Params = xxx' }) + ); + + await clicknav('a[href="/store/stuff/yyy"]'); + expect(await page.textContent('#store-stuff')).toBe( + JSON.stringify({ name: 'SvelteKit', value: 789, error: 'Params = yyy' }) + ); + }); + test('navigating store contains from and to', async ({ app, page, javaScriptEnabled }) => { await page.goto('/store/navigating/a'); diff --git a/packages/kit/types/ambient-modules.d.ts b/packages/kit/types/ambient-modules.d.ts index 4c679bd63d8e..608456d856b3 100644 --- a/packages/kit/types/ambient-modules.d.ts +++ b/packages/kit/types/ambient-modules.d.ts @@ -110,6 +110,7 @@ declare module '$app/stores' { export const page: Readable<{ url: URL; params: Record; + stuff: Record; status: number; error: Error | null; }>; From 563783d3633810b3c2c2ddade0aaf5e577d4c097 Mon Sep 17 00:00:00 2001 From: tanhauhau Date: Sun, 9 Jan 2022 16:26:47 +0800 Subject: [PATCH 2/4] add changeset --- .changeset/fair-pets-bake.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fair-pets-bake.md diff --git a/.changeset/fair-pets-bake.md b/.changeset/fair-pets-bake.md new file mode 100644 index 000000000000..ed105b6e47bc --- /dev/null +++ b/.changeset/fair-pets-bake.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Add returned stuff from pages into \$page store From 666019680215ab090b4c49ccc618621e5613c136 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sun, 9 Jan 2022 20:16:19 +0800 Subject: [PATCH 3/4] Update documentation/docs/03-loading.md Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- documentation/docs/03-loading.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/03-loading.md b/documentation/docs/03-loading.md index 016ae7881a25..c63fac7c333e 100644 --- a/documentation/docs/03-loading.md +++ b/documentation/docs/03-loading.md @@ -153,4 +153,4 @@ If the `load` function returns a `props` object, the props will be passed to the This will be merged with any existing `stuff` and passed to the `load` functions of subsequent layout and page components. -The final stuff will be available as part of the [page store](#modules-$app-stores) +The final stuff will be available as part of the [page store](#modules-$app-stores) providing a way to pass data upward to the layouts. From f8d82f9b0b24a484eb9eeffbc6d34bded55bd902 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 10 Jan 2022 16:32:44 -0500 Subject: [PATCH 4/4] tweak docs slightly --- documentation/docs/03-loading.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/03-loading.md b/documentation/docs/03-loading.md index 15667e881d39..8673c1f705a4 100644 --- a/documentation/docs/03-loading.md +++ b/documentation/docs/03-loading.md @@ -157,4 +157,4 @@ If the `load` function returns a `props` object, the props will be passed to the This will be merged with any existing `stuff` and passed to the `load` functions of subsequent layout and page components. -The final stuff will be available as part of the [page store](#modules-$app-stores) providing a way to pass data upward to the layouts. +The combined `stuff` is available to components using the [page store](#modules-$app-stores) as `$page.stuff`, providing a mechanism for pages to pass data 'upward' to layouts.