Skip to content

Commit 2fabe24

Browse files
committed
refactor(devtools): combine element name and nativeElement into element object
This refactoring combines the 'element' (tagName string) and 'nativeElement' (DOM Node) properties of DevToolsNode into a single 'element' object. This cleans up the interface and makes it clearer what is serializable.
1 parent e695379 commit 2fabe24

16 files changed

Lines changed: 111 additions & 83 deletions

File tree

devtools/projects/ng-devtools-backend/src/lib/client-event-subscribers.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ const getSignalNestedPropertiesCallback =
255255
return emitEmpty();
256256
}
257257

258-
const injector = getInjectorFromElementNode(node.nativeElement!);
258+
const injector = getInjectorFromElementNode(node.element!.node!);
259259
if (!injector) {
260260
return emitEmpty();
261261
}
@@ -375,7 +375,12 @@ export interface SerializableComponentTreeNode extends DevToolsNode<
375375
SerializableComponentInstanceType
376376
> {
377377
children: SerializableComponentTreeNode[];
378-
nativeElement?: never;
378+
element:
379+
| {
380+
tagName: string;
381+
node?: never;
382+
}
383+
| undefined;
379384
// Since the nativeElement is not serializable, we will use this boolean as backup
380385
hasNativeElement: boolean;
381386
}
@@ -384,11 +389,11 @@ function getRouterInstance() {
384389
const forest = initializeOrGetDirectiveForestHooks().getIndexedDirectiveForest();
385390
const rootNode = forest[0];
386391

387-
if (!rootNode || !rootNode.nativeElement) {
392+
if (!rootNode || !rootNode.element?.node) {
388393
return null;
389394
}
390395

391-
const injector = getInjectorFromElementNode(rootNode.nativeElement);
396+
const injector = getInjectorFromElementNode(rootNode.element.node);
392397
if (!injector) {
393398
return null;
394399
}
@@ -407,7 +412,11 @@ const prepareForestForSerialization = (
407412
const serializedNodes: SerializableComponentTreeNode[] = [];
408413
for (const node of roots) {
409414
const serializedNode: SerializableComponentTreeNode = {
410-
element: node.element,
415+
element: node.element
416+
? {
417+
tagName: node.element.tagName,
418+
}
419+
: undefined,
411420
component: node.component
412421
? {
413422
name: node.component.name,
@@ -425,7 +434,7 @@ const prepareForestForSerialization = (
425434
changeDetection: node.component ? getDirectiveCdStrategy(node.component) : undefined,
426435

427436
// native elements are not serializable
428-
hasNativeElement: !!node.nativeElement,
437+
hasNativeElement: !!node.element?.node,
429438
};
430439
serializedNodes.push(serializedNode);
431440

@@ -439,11 +448,11 @@ const prepareForestForSerialization = (
439448

440449
function getNodeDIResolutionPath(node: ComponentTreeNode): SerializedInjector[] | undefined {
441450
// Some nodes are not linked to HTMLElements, for example @defer blocks
442-
if (!node.nativeElement) {
451+
if (!node.element?.node) {
443452
return undefined;
444453
}
445454

446-
const nodeInjector = getInjectorFromElementNode(node.nativeElement);
455+
const nodeInjector = getInjectorFromElementNode(node.element.node);
447456
if (!nodeInjector) {
448457
return [];
449458
}
@@ -568,12 +577,12 @@ const getTransferStateCallback = (messageBus: MessageBus<Events>) => () => {
568577
}
569578

570579
const rootNode = forest[0];
571-
if (!rootNode || !rootNode.nativeElement) {
580+
if (!rootNode || !rootNode.element?.node) {
572581
messageBus.emit('transferStateData', [null]);
573582
return;
574583
}
575584

576-
const injector = getInjectorFromElementNode(rootNode.nativeElement);
585+
const injector = getInjectorFromElementNode(rootNode.element.node);
577586
if (!injector) {
578587
messageBus.emit('transferStateData', [null]);
579588
return;
@@ -629,7 +638,7 @@ const getSignalGraphCallback = (messageBus: MessageBus<Events>) => (element: Ele
629638
return;
630639
}
631640

632-
const injector = node.injector ?? getInjectorFromElementNode(node.nativeElement!);
641+
const injector = node.injector ?? getInjectorFromElementNode(node.element!.node!);
633642

634643
if (!injector) {
635644
messageBus.emit('latestSignalGraph', [null]);

devtools/projects/ng-devtools-backend/src/lib/component-inspector/component-inspector.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ function findNodesForHydrationOverlay(
144144
return forest.flatMap((node) => {
145145
if (node?.hydration?.status) {
146146
// We highlight first level
147-
return {node: node.nativeElement!, status: node.hydration};
147+
return {node: node.element!.node!, status: node.hydration};
148148
}
149149
if (node.children.length) {
150150
return findNodesForHydrationOverlay(node.children);
@@ -159,7 +159,7 @@ function findErrorNodesForHydrationOverlay(
159159
return forest.flatMap((node) => {
160160
if (node?.hydration?.status === 'mismatched') {
161161
// We highlight first level
162-
return {node: node.nativeElement!, status: node.hydration};
162+
return {node: node.element!.node!, status: node.hydration};
163163
}
164164
if (node.children.length) {
165165
return findNodesForHydrationOverlay(node.children);

devtools/projects/ng-devtools-backend/src/lib/component-tree/component-tree.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export const getLatestComponentState = (
135135

136136
const directiveProperties: DirectivesProperties = {};
137137

138-
const injector = getInjectorFromElementNode(node.nativeElement!);
138+
const injector = getInjectorFromElementNode(node.element!.node!);
139139

140140
const injectors = injector ? getInjectorResolutionPath(injector) : [];
141141
const resolutionPathWithProviders = !ngDebugDependencyInjectionApiIsSupported()
@@ -685,7 +685,18 @@ export const buildDirectiveForest = (): ComponentTreeNode[] => {
685685
const frontier = [...forest];
686686
while (frontier.length) {
687687
const node = frontier.pop()!;
688-
node.element ??= node.nativeElement?.nodeName.toLowerCase() ?? '';
688+
const rawNode = node as any;
689+
if (rawNode.element && typeof rawNode.element === 'string') {
690+
node.element = {
691+
tagName: rawNode.element,
692+
node: rawNode.nativeElement,
693+
};
694+
} else if (!node.element && rawNode.nativeElement) {
695+
node.element = {
696+
tagName: rawNode.nativeElement.nodeName.toLowerCase() ?? '',
697+
node: rawNode.nativeElement,
698+
};
699+
}
689700
node.component!.isElement ??= false;
690701
for (const child of node.children) {
691702
frontier.push(child);
@@ -724,7 +735,7 @@ export const findNodeInForest = (
724735
forest: ComponentTreeNode[],
725736
): HTMLElement | null => {
726737
const foundComponent: ComponentTreeNode | null = queryDirectiveForest(position, forest);
727-
return foundComponent ? (foundComponent.nativeElement as HTMLElement) : null;
738+
return foundComponent ? (foundComponent.element?.node as HTMLElement) : null;
728739
};
729740

730741
export const findNodeFromSerializedPosition = (

devtools/projects/ng-devtools-backend/src/lib/directive-forest/control-flow.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,10 @@ export function createControlFlowTreeNode(
8080
children,
8181
component: null,
8282
directives: [],
83-
element: ELEMENT_NAME_MAP[controlFlowBlock.type],
84-
nativeElement: undefined,
83+
element: {
84+
tagName: ELEMENT_NAME_MAP[controlFlowBlock.type],
85+
node: undefined,
86+
},
8587
controlFlowBlock: mapToDevtoolsControlFlowModel(controlFlowBlock, iteratorCurrentIdx, rootId),
8688
};
8789
}

devtools/projects/ng-devtools-backend/src/lib/directive-forest/ltree.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ export class LTreeStrategy {
8989
const element = (node.tagName || node.nodeName).toLowerCase();
9090
if (!tNode) {
9191
return {
92-
nativeElement: node,
92+
element: {
93+
tagName: element,
94+
node,
95+
},
9396
children: [],
94-
element,
9597
directives: [],
9698
component: null,
9799
controlFlowBlock: null, // neither there will be any control flow block
@@ -114,9 +116,11 @@ export class LTreeStrategy {
114116
}
115117
}
116118
return {
117-
nativeElement: node,
119+
element: {
120+
tagName: element,
121+
node,
122+
},
118123
children: [],
119-
element,
120124
directives,
121125
component,
122126
controlFlowBlock: null, // neither there will be any control flow block

devtools/projects/ng-devtools-backend/src/lib/directive-forest/render-tree.spec.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,25 +170,29 @@ describe('render tree extraction', () => {
170170
expect(rtree.length).toBe(1);
171171

172172
const appRTreeNode = rtree[0];
173-
expect(appRTreeNode.children.map((c) => c.element)).toEqual(['@defer', 'child']);
173+
expect(appRTreeNode.children.map((c) => c.element?.tagName)).toEqual(['@defer', 'child']);
174174

175175
const outerDefer = appRTreeNode.children[0];
176176
expect(outerDefer.children.length).toBe(2);
177177

178178
const [deferChild, innerDefer] = outerDefer.children;
179179
expect(deferChild).toEqual(
180180
jasmine.objectContaining({
181-
element: 'defer-child',
182-
nativeElement: deferChildNode,
181+
element: {
182+
tagName: 'defer-child',
183+
node: deferChildNode,
184+
},
183185
}),
184186
);
185-
expect(innerDefer.element).toEqual('@defer');
187+
expect(innerDefer.element?.tagName).toEqual('@defer');
186188

187189
expect(innerDefer.children.length).toBe(1);
188190
expect(innerDefer.children[0]).toEqual(
189191
jasmine.objectContaining({
190-
element: 'nested-defer-child',
191-
nativeElement: nestedDeferChildNode,
192+
element: {
193+
tagName: 'nested-defer-child',
194+
node: nestedDeferChildNode,
195+
},
192196
}),
193197
);
194198
});

devtools/projects/ng-devtools-backend/src/lib/directive-forest/render-tree.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ function extractViewTree(
5858
name: dir.constructor.name,
5959
};
6060
}),
61-
element: domNode.nodeName.toLowerCase(),
62-
nativeElement: domNode,
61+
element: {
62+
tagName: domNode.nodeName.toLowerCase(),
63+
node: domNode,
64+
},
6365
hydration: hydrationStatus(domNode),
6466
controlFlowBlock: null,
6567
};

devtools/projects/ng-devtools-backend/src/lib/hooks/identity-tracker.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ const indexTree = <T extends DevToolsNode<DirectiveInstanceType, ComponentInstan
170170
component: node.component,
171171
directives: node.directives?.map((d) => ({position, ...d})),
172172
children: node.children.map((n, i) => indexTree(n, i, position)),
173-
nativeElement: node.nativeElement,
174173
hydration: node.hydration,
175174
controlFlowBlock: node.controlFlowBlock,
176175
injector: node.injector,

devtools/projects/ng-devtools-backend/src/lib/set-console-reference.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ const setDirectiveKey = (node: ComponentTreeNode | null, key: string) => {
5757
if (node?.component) {
5858
return node.component.instance;
5959
}
60-
if (node?.nativeElement) {
61-
return node.nativeElement;
60+
if (node?.element?.node) {
61+
return node.element.node;
6262
}
6363
return node;
6464
},

devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
[class.selected]="$last"
1717
class="breadcrumb"
1818
>
19-
{{ node.original.component?.name || node.original.element }}
19+
{{ node.original.component?.name || node.original.element?.tagName }}
2020
</button>
2121
}
2222
</div>

0 commit comments

Comments
 (0)