Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] adding returned stuff from pages to $page store #3252

Merged
merged 6 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fair-pets-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Add returned stuff from pages into \$page store
2 changes: 1 addition & 1 deletion documentation/docs/03-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

This only applies to layout components, _not_ page components.
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.
2 changes: 1 addition & 1 deletion documentation/docs/05-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
60 changes: 44 additions & 16 deletions packages/kit/src/runtime/client/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,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;

Expand Down Expand Up @@ -420,12 +427,13 @@ export class Renderer {
* @param {{
* url: URL;
* params: Record<string, string>;
* stuff: Record<string, any>;
* branch: Array<import('./types').BranchNode | undefined>;
* 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);

Expand All @@ -449,7 +457,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
/**
Expand Down Expand Up @@ -716,6 +724,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) {
Expand All @@ -741,7 +756,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
});
}

/**
Expand All @@ -761,19 +783,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
});
}
}
1 change: 1 addition & 0 deletions packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: []
Expand Down
6 changes: 4 additions & 2 deletions packages/kit/src/runtime/server/page/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { s } from '../../../utils/misc.js';
* error?: Error,
* url: URL;
* params: Record<string, string>
* stuff: Record<string, any>;
* }} opts
*/
export async function render_response({
Expand All @@ -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);
Expand Down Expand Up @@ -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)
};

Expand Down
6 changes: 4 additions & 2 deletions packages/kit/src/runtime/server/page/respond.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand Down Expand Up @@ -158,6 +158,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);
Expand Down Expand Up @@ -199,6 +200,7 @@ export async function respond(opts) {
return with_cookies(
await render_response({
...opts,
stuff,
url: request.url,
page_config,
status,
Expand Down
43 changes: 20 additions & 23 deletions packages/kit/src/runtime/server/page/respond_with_error.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ export async function respond_with_error({ request, options, state, $session, st
/** @type {Record<string, string>} */
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,
Expand All @@ -44,26 +43,23 @@ export async function respond_with_error({ request, options, state, $session, st
})
);

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_error: true,
status,
error
})
)
];
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_error: true,
status,
error
})
);

return await render_response({
options,
Expand All @@ -73,9 +69,10 @@ export async function respond_with_error({ request, options, state, $session, st
router: options.router,
ssr: options.ssr
},
stuff: error_loaded.stuff,
status,
error,
branch,
branch: [layout_loaded, error_loaded],
url: request.url,
params
});
Expand Down
16 changes: 16 additions & 0 deletions packages/kit/test/apps/basics/src/routes/store/stuff/[item].svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script context="module">
/** @type {import('@sveltejs/kit').Load} */
export function load({ params }) {
if (params.item === 'xxx') {
throw new Error('Params = xxx');
}

return {
stuff: {
page: params.item,
value: 456
},
error: params.item === 'yyy' ? 'Params = yyy' : undefined
};
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script context="module">
/** @type {import('@sveltejs/kit').ErrorLoad} */
export function load({ error }) {
return {
stuff: {
error: error?.message,
value: 789
}
};
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script context="module">
export function load() {
return {
stuff: {
name: 'SvelteKit',
value: 123
}
};
}
</script>

<script>
import { page } from '$app/stores';
</script>

<div id="store-stuff">{JSON.stringify($page.stuff)}</div>

<nav><a href="/store/stuff/xxx">xxx</a> <a href="/store/stuff/yyy">yyy</a> <a href="/store/stuff/zzz">zzz</a></nav>

<slot />
23 changes: 23 additions & 0 deletions packages/kit/test/apps/basics/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,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');

Expand Down
1 change: 1 addition & 0 deletions packages/kit/types/ambient-modules.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ declare module '$app/stores' {
export const page: Readable<{
url: URL;
params: Record<string, string>;
stuff: Record<string, any>;
status: number;
error: Error | null;
}>;
Expand Down