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

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

78 lines
2.5 KiB
TypeScript
Raw Normal View History

/**
* @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
*/
refactor(core): update `ng.getDirectiveMetadata` to support Wiz and ACX (#60475) This allows `ng.getDirectiveMetadata` to be implemented by Wiz and ACX with subtly different shapes to match the nuances of those frameworks. Existing usage of `{Component,Directive}DebugMetadata` was moved over to `Angular{Component,Directive}DebugMetadata` as appropriate, since the implementation of `ng` in `@angular/core` is specific to Angular. Only the types support Wiz and ACX. I opted to merge `ComponentDebugMetadata` and `DirectiveDebugMetadata` into a single type of all the frameworks including both components and directives (recall that components extend directives). The reasoning for this is because Wiz does not support directives (you can kind of think of "Wiz Directive" as an abstract class extended by "Wiz Components"). I felt that a `DirectiveDebugMetadata` containing only Angular and ACX types would be a bit of a trap and lead to bugs when used. It's safer to just have the single type containing all the possible results from `ng.getDirectiveMetadata`. I also chose to leave the `ng` type as is internally, since `@angular/core` implements a specific concrete version of it narrowed to Angular types. Separately I defined an expanded `FrameworkAgnosticGlobalUtils` which redefines `ng.getDirectiveMetadata` to include Wiz and ACX. We want this type to exist in the Angular GitHub repo so it can be referenced as a common primitive across all three frameworks. This is sufficient for now, however longer term we will likely want to actually manually define the function types in this framework-agnostic interface and make Angular's version properly implement it rather than extend and overwrite Angular's type. PR Close #60475
2025-03-07 00:00:55 +00:00
import type {ɵFrameworkAgnosticGlobalUtils as GlobalUtils} from '@angular/core';
import {getRoots} from '../component-tree/get-roots';
import {Framework} from '../component-tree/core-enums';
/** Returns a handle to window.ng APIs (global angular debugging). */
refactor(core): update `ng.getDirectiveMetadata` to support Wiz and ACX (#60475) This allows `ng.getDirectiveMetadata` to be implemented by Wiz and ACX with subtly different shapes to match the nuances of those frameworks. Existing usage of `{Component,Directive}DebugMetadata` was moved over to `Angular{Component,Directive}DebugMetadata` as appropriate, since the implementation of `ng` in `@angular/core` is specific to Angular. Only the types support Wiz and ACX. I opted to merge `ComponentDebugMetadata` and `DirectiveDebugMetadata` into a single type of all the frameworks including both components and directives (recall that components extend directives). The reasoning for this is because Wiz does not support directives (you can kind of think of "Wiz Directive" as an abstract class extended by "Wiz Components"). I felt that a `DirectiveDebugMetadata` containing only Angular and ACX types would be a bit of a trap and lead to bugs when used. It's safer to just have the single type containing all the possible results from `ng.getDirectiveMetadata`. I also chose to leave the `ng` type as is internally, since `@angular/core` implements a specific concrete version of it narrowed to Angular types. Separately I defined an expanded `FrameworkAgnosticGlobalUtils` which redefines `ng.getDirectiveMetadata` to include Wiz and ACX. We want this type to exist in the Angular GitHub repo so it can be referenced as a common primitive across all three frameworks. This is sufficient for now, however longer term we will likely want to actually manually define the function types in this framework-agnostic interface and make Angular's version properly implement it rather than extend and overwrite Angular's type. PR Close #60475
2025-03-07 00:00:55 +00:00
export const ngDebugClient = () => (window as any).ng as Partial<GlobalUtils>;
/** Type guard that checks whether a given debug API is supported within window.ng */
refactor(core): update `ng.getDirectiveMetadata` to support Wiz and ACX (#60475) This allows `ng.getDirectiveMetadata` to be implemented by Wiz and ACX with subtly different shapes to match the nuances of those frameworks. Existing usage of `{Component,Directive}DebugMetadata` was moved over to `Angular{Component,Directive}DebugMetadata` as appropriate, since the implementation of `ng` in `@angular/core` is specific to Angular. Only the types support Wiz and ACX. I opted to merge `ComponentDebugMetadata` and `DirectiveDebugMetadata` into a single type of all the frameworks including both components and directives (recall that components extend directives). The reasoning for this is because Wiz does not support directives (you can kind of think of "Wiz Directive" as an abstract class extended by "Wiz Components"). I felt that a `DirectiveDebugMetadata` containing only Angular and ACX types would be a bit of a trap and lead to bugs when used. It's safer to just have the single type containing all the possible results from `ng.getDirectiveMetadata`. I also chose to leave the `ng` type as is internally, since `@angular/core` implements a specific concrete version of it narrowed to Angular types. Separately I defined an expanded `FrameworkAgnosticGlobalUtils` which redefines `ng.getDirectiveMetadata` to include Wiz and ACX. We want this type to exist in the Angular GitHub repo so it can be referenced as a common primitive across all three frameworks. This is sufficient for now, however longer term we will likely want to actually manually define the function types in this framework-agnostic interface and make Angular's version properly implement it rather than extend and overwrite Angular's type. PR Close #60475
2025-03-07 00:00:55 +00:00
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 = getRoots();
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();
// Temporary solution. Convert to `ɵgetLoadedRoutes` when available.
// If there is a Wiz application, make Routes API unavailable.
const roots = getRoots();
return (
!!roots.length &&
!roots.some((el) => {
const comp = ng.getComponent!(el)!;
return ng.getDirectiveMetadata?.(comp)?.framework === Framework.Wiz;
})
);
}