Skip to content

Commit

Permalink
refactor(core): do not serialize parent block id for top level blocks
Browse files Browse the repository at this point in the history
This commit updates incremental hydration-related annotation logic to avoid serializing parent block id when it's `null` (for top-level blocks).
  • Loading branch information
AndrewKushnir committed Dec 21, 2024
1 parent 8c5db3c commit a3f9c20
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 8 deletions.
6 changes: 5 additions & 1 deletion packages/core/src/hydration/annotate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,6 @@ function serializeLContainer(

// Add defer block into info context.deferBlocks
const deferBlockInfo: SerializedDeferBlock = {
[DEFER_PARENT_BLOCK_ID]: parentDeferBlockId,
[NUM_ROOT_NODES]: rootNodes.length,
[DEFER_BLOCK_STATE]: lDetails[CURRENT_DEFER_BLOCK_STATE],
};
Expand All @@ -415,6 +414,11 @@ function serializeLContainer(
deferBlockInfo[DEFER_HYDRATE_TRIGGERS] = serializedTriggers;
}

if (parentDeferBlockId !== null) {
// Serialize parent id only when it's present.
deferBlockInfo[DEFER_PARENT_BLOCK_ID] = parentDeferBlockId;
}

context.deferBlocks.set(deferBlockId, deferBlockInfo);

const node = unwrapRNode(lContainer);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hydration/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export interface SerializedDeferBlock {
/**
* This contains the unique id of this defer block's parent, if it exists.
*/
[DEFER_PARENT_BLOCK_ID]: string | null;
[DEFER_PARENT_BLOCK_ID]?: string;

/**
* This field represents a status, based on the `DeferBlockState` enum.
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hydration/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ export function getParentBlockHydrationQueue(
const deferBlockParents = transferState.get(NGH_DEFER_BLOCKS_KEY, {});

let isTopMostDeferBlock = false;
let currentBlockId: string | null = deferBlockId;
let currentBlockId: string | undefined = deferBlockId;
let parentBlockPromise: Promise<void> | null = null;
const hydrationQueue: string[] = [];

Expand Down
39 changes: 34 additions & 5 deletions packages/platform-server/test/incremental_hydration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ describe('platform-server partial hydration integration', () => {
const ssrContents = getAppContents(html);

expect(ssrContents).toContain(
'"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2},"d1":{"p":"d0","r":2,"s":2}}',
'"__nghDeferData__":{"d0":{"r":1,"s":2},"d1":{"r":2,"s":2,"p":"d0"}}',
);
});

Expand Down Expand Up @@ -285,9 +285,38 @@ describe('platform-server partial hydration integration', () => {
const ssrContents = getAppContents(html);

expect(ssrContents).toContain(
'"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2},"d1":{"p":"d0","r":2,"s":2,"t":[2]}}',
'"__nghDeferData__":{"d0":{"r":1,"s":2},"d1":{"r":2,"s":2,"t":[2],"p":"d0"}}',
);
});

it('should not include parent id in serialized data for top-level `@defer` blocks', async () => {
@Component({
selector: 'app',
template: `
@defer (on viewport; hydrate on interaction) {
Hello world!
} @placeholder {
<span>Placeholder</span>
}
`,
})
class SimpleComponent {}

const appId = 'custom-app-id';
const providers = [{provide: APP_ID, useValue: appId}];
const hydrationFeatures = () => [withIncrementalHydration()];

const html = await ssr(SimpleComponent, {
envProviders: providers,
hydrationFeatures,
});

const ssrContents = getAppContents(html);

// Assert that the serialized data doesn't contain the "p" field,
// which contains parent id (which is not needed for top-level blocks).
expect(ssrContents).toContain('"__nghDeferData__":{"d0":{"r":1,"s":2}}}');
});
});

describe('basic hydration behavior', () => {
Expand Down Expand Up @@ -347,7 +376,7 @@ describe('platform-server partial hydration integration', () => {
expect(ssrContents).toContain('<p jsaction="click:;keydown:;" ngb="d1');
// There is an extra annotation in the TransferState data.
expect(ssrContents).toContain(
'"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2},"d1":{"p":"d0","r":1,"s":2}}',
'"__nghDeferData__":{"d0":{"r":1,"s":2},"d1":{"r":1,"s":2,"p":"d0"}}',
);
// Outer defer block is rendered.
expect(ssrContents).toContain('Main defer block rendered');
Expand Down Expand Up @@ -460,7 +489,7 @@ describe('platform-server partial hydration integration', () => {
expect(ssrContents).toContain('<p jsaction="click:;keydown:;" ngb="d1');
// There is an extra annotation in the TransferState data.
expect(ssrContents).toContain(
'"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2},"d1":{"p":"d0","r":1,"s":2}}',
'"__nghDeferData__":{"d0":{"r":1,"s":2},"d1":{"r":1,"s":2,"p":"d0"}}',
);
// Outer defer block is rendered.
expect(ssrContents).toContain('Main defer block rendered');
Expand Down Expand Up @@ -569,7 +598,7 @@ describe('platform-server partial hydration integration', () => {
// <p> is inside a nested defer block -> different namespace.
// expect(ssrContents).toContain('<p jsaction="click:;" ngb="d1');
// There is an extra annotation in the TransferState data.
expect(ssrContents).toContain('"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2}}');
expect(ssrContents).toContain('"__nghDeferData__":{"d0":{"r":1,"s":2}}');
// Outer defer block is rendered.
expect(ssrContents).toContain('Main defer block rendered');
// Inner defer block should only display placeholder.
Expand Down

0 comments on commit a3f9c20

Please # to comment.