Skip to content

Commit a94c912

Browse files
JeanMecheAndrewKushnir
authored andcommitted
refactor(devtools): Make every ng function optional in the typings (angular#60208)
This will help in the future guarding against old apps what don't have the new APIs implemented PR Close angular#60208
1 parent 1a0b561 commit a94c912

File tree

6 files changed

+41
-50
lines changed

6 files changed

+41
-50
lines changed

devtools/projects/ng-devtools-backend/src/lib/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ ts_library(
4141
name = "highlighter",
4242
srcs = ["highlighter.ts"],
4343
deps = [
44+
"//devtools/projects/ng-devtools-backend/src/lib/ng-debug-api",
4445
"//devtools/projects/protocol",
4546
"//packages/core",
4647
],
@@ -98,6 +99,10 @@ ts_library(
9899
ts_library(
99100
name = "utils",
100101
srcs = ["utils.ts"],
102+
deps = [
103+
"//devtools/projects/ng-devtools-backend/src/lib/ng-debug-api",
104+
"//packages/core",
105+
],
101106
)
102107

103108
ts_library(

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,16 @@ export function getInjectorId() {
6464
}
6565

6666
export function getInjectorMetadata(injector: Injector) {
67-
return ngDebugClient().ɵgetInjectorMetadata!(injector);
67+
return ngDebugClient().ɵgetInjectorMetadata?.(injector) ?? null;
6868
}
6969

7070
export function getInjectorResolutionPath(injector: Injector): Injector[] {
71-
if (!ngDebugApiIsSupported('ɵgetInjectorResolutionPath')) {
71+
const ng = ngDebugClient();
72+
if (!ngDebugApiIsSupported(ng, 'ɵgetInjectorResolutionPath')) {
7273
return [];
7374
}
7475

75-
return ngDebugClient().ɵgetInjectorResolutionPath!(injector);
76+
return ng.ɵgetInjectorResolutionPath(injector) ?? [];
7677
}
7778

7879
export function getInjectorFromElementNode(element: Node): Injector | null {
@@ -83,9 +84,10 @@ function getDirectivesFromElement(element: HTMLElement): {
8384
component: unknown | null;
8485
directives: unknown[];
8586
} {
87+
const ng = ngDebugClient();
8688
let component = null;
87-
if (element instanceof Element) {
88-
component = ngDebugClient().getComponent!(element);
89+
if (element instanceof Element && ngDebugApiIsSupported(ng, 'getComponent')) {
90+
component = ng.getComponent(element);
8991
}
9092

9193
return {
@@ -108,7 +110,7 @@ export const getLatestComponentState = (
108110

109111
const directiveProperties: DirectivesProperties = {};
110112

111-
const injector = getInjectorFromElementNode!(node.nativeElement!);
113+
const injector = getInjectorFromElementNode(node.nativeElement!);
112114

113115
const injectors = injector ? getInjectorResolutionPath(injector) : [];
114116
const resolutionPathWithProviders = !ngDebugDependencyInjectionApiIsSupported()
@@ -265,12 +267,12 @@ const getDependenciesForDirective = (
265267
resolutionPath: {injector: Injector; providers: ProviderRecord[]}[],
266268
directive: any,
267269
): SerializedInjectedService[] => {
268-
if (!ngDebugApiIsSupported('ɵgetDependenciesFromInjectable')) {
270+
const ng = ngDebugClient();
271+
if (!ngDebugApiIsSupported(ng, 'ɵgetDependenciesFromInjectable')) {
269272
return [];
270273
}
271274

272-
let dependencies =
273-
ngDebugClient().ɵgetDependenciesFromInjectable!(injector, directive)?.dependencies ?? [];
275+
let dependencies = ng.ɵgetDependenciesFromInjectable(injector, directive)?.dependencies ?? [];
274276
const uniqueServices = new Set<string>();
275277
const serializedInjectedServices: SerializedInjectedService[] = [];
276278

@@ -580,7 +582,9 @@ export const updateState = (updatedStateData: UpdatedStateData): void => {
580582
if (updatedStateData.directiveId.directive !== undefined) {
581583
const directive = node.directives[updatedStateData.directiveId.directive].instance;
582584
mutateComponentOrDirective(updatedStateData, directive);
583-
ng.applyChanges?.(ng.getOwningComponent!(directive)!);
585+
if (ngDebugApiIsSupported(ng, 'getOwningComponent')) {
586+
ng.applyChanges?.(ng.getOwningComponent(directive)!);
587+
}
584588
return;
585589
}
586590
if (node.component) {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {isCustomElement} from '../utils';
1616
const extractViewTree = (
1717
domNode: Node | Element,
1818
result: ComponentTreeNode[],
19-
getComponent: (element: Element) => {} | null,
19+
getComponent?: (element: Element) => {} | null,
2020
getDirectives?: (node: Node) => {}[],
2121
): ComponentTreeNode[] => {
2222
// Ignore DOM Node if it came from a different frame. Use instanceof Node to check this.
@@ -45,7 +45,7 @@ const extractViewTree = (
4545
result.push(componentTreeNode);
4646
return result;
4747
}
48-
const component = getComponent(domNode);
48+
const component = getComponent?.(domNode);
4949
if (component) {
5050
componentTreeNode.component = {
5151
instance: component,
@@ -98,7 +98,7 @@ export class RTreeStrategy {
9898
while (element.parentElement) {
9999
element = element.parentElement;
100100
}
101-
const getComponent = ngDebugClient().getComponent!;
101+
const getComponent = ngDebugClient().getComponent;
102102
const getDirectives = ngDebugClient().getDirectives;
103103
return extractViewTree(element, [], getComponent, getDirectives);
104104
}

devtools/projects/ng-devtools-backend/src/lib/highlighter.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import type {ɵGlobalDevModeUtils as GlobalDevModeUtils, Type} from '@angular/core';
9+
import type {Type} from '@angular/core';
1010
import {HydrationStatus} from 'protocol';
11+
import {ngDebugClient} from './ng-debug-api/ng-debug-api';
1112

1213
let hydrationOverlayItems: HTMLElement[] = [];
1314
let selectedElementOverlay: HTMLElement | null = null;
1415

15-
declare const ng: GlobalDevModeUtils['ng'];
16-
1716
const DEV_TOOLS_HIGHLIGHT_NODE_ID = '____ngDevToolsHighlight';
1817

1918
const OVERLAY_CONTENT_MARGIN = 4;
@@ -61,11 +60,12 @@ export function findComponentAndHost(el: Node | undefined): {
6160
component: any;
6261
host: HTMLElement | null;
6362
} {
63+
const ng = ngDebugClient();
6464
if (!el) {
6565
return {component: null, host: null};
6666
}
6767
while (el) {
68-
const component = el instanceof HTMLElement && ng.getComponent(el);
68+
const component = el instanceof HTMLElement && ng.getComponent!(el);
6969
if (component) {
7070
return {component, host: el as HTMLElement};
7171
}

devtools/projects/ng-devtools-backend/src/lib/ng-debug-api/ng-debug-api.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ import type {ɵGlobalDevModeUtils as GlobalDevModeUtils} from '@angular/core';
1616
export const ngDebugClient = () => (window as any).ng as Partial<GlobalDevModeUtils['ng']>;
1717

1818
/**
19-
* Checks whether a given debug API is supported within window.ng
19+
* Type guard that checks whether a given debug API is supported within window.ng
2020
*
21-
* @returns boolean
21+
* @returns whether the ng object includes the given debug API
2222
*/
23-
export function ngDebugApiIsSupported(api: keyof GlobalDevModeUtils['ng']): boolean {
24-
const ng = ngDebugClient();
23+
export function ngDebugApiIsSupported<
24+
T extends Partial<GlobalDevModeUtils['ng']>,
25+
K extends keyof T,
26+
>(ng: T, api: K): ng is T & Record<K, NonNullable<T[K]>> {
2527
return typeof ng[api] === 'function';
2628
}
2729

@@ -31,19 +33,20 @@ export function ngDebugApiIsSupported(api: keyof GlobalDevModeUtils['ng']): bool
3133
* @returns boolean
3234
*/
3335
export function ngDebugDependencyInjectionApiIsSupported(): boolean {
34-
if (!ngDebugApiIsSupported('getInjector')) {
36+
const ng = ngDebugClient();
37+
if (!ngDebugApiIsSupported(ng, 'getInjector')) {
3538
return false;
3639
}
37-
if (!ngDebugApiIsSupported('ɵgetInjectorResolutionPath')) {
40+
if (!ngDebugApiIsSupported(ng, 'ɵgetInjectorResolutionPath')) {
3841
return false;
3942
}
40-
if (!ngDebugApiIsSupported('ɵgetDependenciesFromInjectable')) {
43+
if (!ngDebugApiIsSupported(ng, 'ɵgetDependenciesFromInjectable')) {
4144
return false;
4245
}
43-
if (!ngDebugApiIsSupported('ɵgetInjectorProviders')) {
46+
if (!ngDebugApiIsSupported(ng, 'ɵgetInjectorProviders')) {
4447
return false;
4548
}
46-
if (!ngDebugApiIsSupported('ɵgetInjectorMetadata')) {
49+
if (!ngDebugApiIsSupported(ng, 'ɵgetInjectorMetadata')) {
4750
return false;
4851
}
4952

devtools/projects/ng-devtools-backend/src/lib/utils.ts

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
export const ngDebug = () => (window as any).ng;
9+
import {ngDebugApiIsSupported, ngDebugClient} from './ng-debug-api/ng-debug-api';
1010

1111
export const runOutsideAngular = (f: () => void): void => {
1212
const w = window as any;
@@ -37,30 +37,9 @@ export const isCustomElement = (node: Node) => {
3737
return !!customElements.get(tagName);
3838
};
3939

40-
export function hasDiDebugAPIs(): boolean {
41-
if (!ngDebugApiIsSupported('ɵgetInjectorResolutionPath')) {
42-
return false;
43-
}
44-
if (!ngDebugApiIsSupported('ɵgetDependenciesFromInjectable')) {
45-
return false;
46-
}
47-
if (!ngDebugApiIsSupported('ɵgetInjectorProviders')) {
48-
return false;
49-
}
50-
if (!ngDebugApiIsSupported('ɵgetInjectorMetadata')) {
51-
return false;
52-
}
53-
54-
return true;
55-
}
56-
57-
export function ngDebugApiIsSupported(api: string): boolean {
58-
const ng = ngDebug();
59-
return typeof ng[api] === 'function';
60-
}
61-
6240
export function isSignal(prop: unknown): prop is (() => unknown) & {set: (value: unknown) => void} {
63-
if (!ngDebugApiIsSupported('isSignal')) {
41+
const ng = ngDebugClient();
42+
if (!ngDebugApiIsSupported(ng, 'isSignal')) {
6443
return false;
6544
}
6645
return (window as any).ng.isSignal(prop);

0 commit comments

Comments
 (0)