angular/devtools/projects/ng-devtools-backend/src/lib/ng-debug-api/ng-debug-api.ts
AleksanderBodurri d006721f30 feat(devtools): clean up router tree for stable release (#63081)
Addresses some cleanup items for the router tree:

- No longer loads router ng global APIs as a side effect of importing the router. Rather this is now a runtime step that occurs when provideRouter is called.
- No longer depends on router.navigateByUrl in Angular DevTools. There is now a dedicated global util for this
- Router instance logic no longer depends on token name
- Prevents navigating to lazy or redirect routes (these don't have an associated component)

PR Close #63081
2025-09-02 20:59:15 -07:00

110 lines
3.6 KiB
TypeScript

/**
* @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 type {ɵFrameworkAgnosticGlobalUtils as GlobalUtils} from '@angular/core';
import {getAppRoots} from '../component-tree/get-roots';
import {Framework} from '../component-tree/core-enums';
/** Returns a handle to window.ng APIs (global angular debugging). */
export const ngDebugClient = () => {
if (typeof (window as any).ng === 'undefined') {
throw new Error(
'Angular DevTools: Angular debugging APIs are not available. Ensure that your Angular app is in development mode and does not invoke `enableProdMode()`.',
);
}
return (window as any).ng as Partial<GlobalUtils>;
};
/** Type guard that checks whether a given debug API is supported within window.ng */
export function ngDebugApiIsSupported<T extends Partial<GlobalUtils>, K extends keyof T>(
ng: T,
api: K,
): ng is T & Record<K, NonNullable<T[K]>> {
return typeof ng[api] === 'function';
}
/** Checks whether Dependency Injection debug API is supported within window.ng */
export function ngDebugDependencyInjectionApiIsSupported(): boolean {
const ng = ngDebugClient();
if (!ngDebugApiIsSupported(ng, 'getInjector')) {
return false;
}
if (!ngDebugApiIsSupported(ng, 'ɵgetInjectorResolutionPath')) {
return false;
}
if (!ngDebugApiIsSupported(ng, 'ɵgetDependenciesFromInjectable')) {
return false;
}
if (!ngDebugApiIsSupported(ng, 'ɵgetInjectorProviders')) {
return false;
}
if (!ngDebugApiIsSupported(ng, 'ɵgetInjectorMetadata')) {
return false;
}
return true;
}
/** Checks whether Profiler API is supported within window.ng */
export function ngDebugProfilerApiIsSupported(): boolean {
const ng = ngDebugClient();
// Temporary solution. Convert to an eligible API when available.
// https://github.com/angular/angular/pull/60585#discussion_r2017047132
// If there is a Wiz application, make Profiler API unavailable.
const roots = getAppRoots();
return (
!!roots.length &&
!roots.some((el) => {
const comp = ng.getComponent!(el)!;
return ng.getDirectiveMetadata?.(comp)?.framework === Framework.Wiz;
})
);
}
/** Checks whether Router API is supported within window.ng */
export function ngDebugRoutesApiIsSupported(): boolean {
const ng = ngDebugClient();
return (
ngDebugApiIsSupported(ng, 'ɵgetLoadedRoutes') &&
ngDebugApiIsSupported(ng, 'ɵgetRouterInstance') &&
ngDebugApiIsSupported(ng, 'ɵnavigateByUrl')
);
}
/** Checks whether Signal Graph API is supported within window.ng */
export function ngDebugSignalGraphApiIsSupported(): boolean {
const ng = ngDebugClient();
return ngDebugApiIsSupported(ng, 'ɵgetSignalGraph');
}
/**
* Checks if transfer state is available.
* Transfer state is only relevant when Angular app uses Server-Side Rendering.
*/
export function ngDebugTransferStateApiIsSupported(): boolean {
const ng = ngDebugClient();
return ngDebugApiIsSupported(ng, 'ɵgetTransferState');
}
/** Checks whether signal properties inspection API is supported within window.ng */
export function ngDebugSignalPropertiesInspectionApiIsSupported(): boolean {
const ng = ngDebugClient();
// If all apps are Angular, make the API available.
const roots = getAppRoots();
return (
!!roots.length &&
roots.every((el) => {
const comp = ng.getComponent!(el)!;
const fw = ng.getDirectiveMetadata?.(comp)?.framework;
// `framework` might be optional in the case of `AngularDirectiveDebugMetadata`.
return !fw || fw === Framework.Angular;
})
);
}