-
Notifications
You must be signed in to change notification settings - Fork 26.2k
/
Copy pathregistry.ts
116 lines (103 loc) · 3.84 KB
/
registry.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {inject} from '../di';
import {InjectionToken} from '../di/injection_token';
import {ɵɵdefineInjectable} from '../di/interface/defs';
import {
EventContractDetails,
JSACTION_EVENT_CONTRACT,
removeListenersFromBlocks,
} from '../event_delegation_utils';
import {JSACTION_BLOCK_ELEMENT_MAP} from '../hydration/tokens';
import {DehydratedDeferBlock} from './interfaces';
/**
* An internal injection token to reference `DehydratedBlockRegistry` implementation
* in a tree-shakable way.
*/
export const DEHYDRATED_BLOCK_REGISTRY = new InjectionToken<DehydratedBlockRegistry>(
ngDevMode ? 'DEHYDRATED_BLOCK_REGISTRY' : '',
);
/**
* The DehydratedBlockRegistry is used for incremental hydration purposes. It keeps
* track of the Defer Blocks that need hydration so we can effectively
* navigate up to the top dehydrated defer block and fire appropriate cleanup
* functions post hydration.
*/
export class DehydratedBlockRegistry {
private registry = new Map<string, DehydratedDeferBlock>();
private cleanupFns = new Map<string, Function[]>();
private jsActionMap: Map<string, Set<Element>> = inject(JSACTION_BLOCK_ELEMENT_MAP);
private contract: EventContractDetails = inject(JSACTION_EVENT_CONTRACT);
add(blockId: string, info: DehydratedDeferBlock) {
this.registry.set(blockId, info);
// It's possible that hydration is queued that's waiting for the
// resolution of a lazy loaded route. In this case, we ensure
// the callback function is called to continue the hydration process
// for the queued block set.
if (this.awaitingCallbacks.has(blockId)) {
const awaitingCallbacks = this.awaitingCallbacks.get(blockId)!;
for (const cb of awaitingCallbacks) {
cb();
}
}
}
get(blockId: string): DehydratedDeferBlock | null {
return this.registry.get(blockId) ?? null;
}
has(blockId: string): boolean {
return this.registry.has(blockId);
}
cleanup(hydratedBlocks: string[]) {
removeListenersFromBlocks(hydratedBlocks, this.jsActionMap);
for (let blockId of hydratedBlocks) {
this.registry.delete(blockId);
this.jsActionMap.delete(blockId);
this.invokeTriggerCleanupFns(blockId);
this.hydrating.delete(blockId);
this.awaitingCallbacks.delete(blockId);
}
if (this.size === 0) {
this.contract.instance?.cleanUp();
}
}
get size(): number {
return this.registry.size;
}
// we have to leave the lowest block Id in the registry
// unless that block has no children
addCleanupFn(blockId: string, fn: Function) {
let cleanupFunctions: Function[] = [];
if (this.cleanupFns.has(blockId)) {
cleanupFunctions = this.cleanupFns.get(blockId)!;
}
cleanupFunctions.push(fn);
this.cleanupFns.set(blockId, cleanupFunctions);
}
invokeTriggerCleanupFns(blockId: string) {
const fns = this.cleanupFns.get(blockId) ?? [];
for (let fn of fns) {
fn();
}
this.cleanupFns.delete(blockId);
}
// Blocks that are being hydrated.
hydrating = new Map<string, PromiseWithResolvers<void>>();
// Blocks that are awaiting a defer instruction finish.
private awaitingCallbacks = new Map<string, Function[]>();
awaitParentBlock(topmostParentBlock: string, callback: Function) {
const parentBlockAwaitCallbacks = this.awaitingCallbacks.get(topmostParentBlock) ?? [];
parentBlockAwaitCallbacks.push(callback);
this.awaitingCallbacks.set(topmostParentBlock, parentBlockAwaitCallbacks);
}
/** @nocollapse */
static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({
token: DehydratedBlockRegistry,
providedIn: null,
factory: () => new DehydratedBlockRegistry(),
});
}