diff --git a/.changeset/strong-adults-rest.md b/.changeset/strong-adults-rest.md new file mode 100644 index 000000000000..a929cf675018 --- /dev/null +++ b/.changeset/strong-adults-rest.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: return updated layout data if child uses `parent` diff --git a/packages/kit/src/runtime/server/data/index.js b/packages/kit/src/runtime/server/data/index.js index b278ee31873b..ce5f3966870a 100644 --- a/packages/kit/src/runtime/server/data/index.js +++ b/packages/kit/src/runtime/server/data/index.js @@ -39,6 +39,8 @@ export async function render_data( try { const node_ids = [...route.page.layouts, route.page.leaf]; const invalidated = invalidated_data_nodes ?? node_ids.map(() => true); + /** @type {Set} */ + const parent_invalid = new Set(); let aborted = false; @@ -74,6 +76,8 @@ export async function render_data( if (parent) { Object.assign(data, parent.data); } + + parent_invalid.add(j); } return data; } @@ -96,27 +100,35 @@ export async function render_data( }); let length = promises.length; - const nodes = await Promise.all( - promises.map((p, i) => - p.catch(async (error) => { - if (error instanceof Redirect) { - throw error; - } - // Math.min because array isn't guaranteed to resolve in order - length = Math.min(length, i + 1); + /** + * @param {Promise} p + * @param {number} i + */ + const handle_load_error = (p, i) => + p.catch(async (error) => { + if (error instanceof Redirect) { + throw error; + } + + // Math.min because array isn't guaranteed to resolve in order + length = Math.min(length, i + 1); - return /** @type {import('types').ServerErrorNode} */ ({ - type: 'error', - error: await handle_error_and_jsonify(event, options, error), - status: - error instanceof HttpError || error instanceof SvelteKitError - ? error.status - : undefined - }); - }) - ) - ); + return /** @type {import('types').ServerErrorNode} */ ({ + type: 'error', + error: await handle_error_and_jsonify(event, options, error), + status: + error instanceof HttpError || error instanceof SvelteKitError ? error.status : undefined + }); + }); + + let nodes = await Promise.all(promises.map(handle_load_error)); + + // return updated layout data if `parent` is used + if (parent_invalid.size) { + parent_invalid.forEach((i) => (invalidated[i] = true)); + nodes = await Promise.all(functions.map((fn) => fn()).map(handle_load_error)); + } const { data, chunks } = get_data_json(event, options, nodes); diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index 56b2c96bc744..00cbdb0d7b2c 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -420,7 +420,7 @@ test.describe('Invalidation', () => { await clicknav('[href="/load/unchanged-parent/uses-parent/b"]'); expect(await page.textContent('h1')).toBe('slug: b'); - expect(await page.textContent('h2')).toBe('count: 0'); + expect(await page.textContent('h2')).toBe('count: 1'); // this looks wrong, but is actually the intended behaviour (the increment side-effect in a GET would be a bug in a real app) expect(await page.textContent('h3')).toBe('doubled: 2');