mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(devtools): enables typescript strict option (#53340)
Enabling `strict` is part of an effort to improve the quality of the devtools code base. One of the direct side effect is to enable `noImplicitAny`, `strictPropertyInitialization` and `strictBindCallApply`. This commit also replaces `fullTemplateTypeCheck` with `stringTemplates`. PR Close #53340
This commit is contained in:
parent
1e3bcfecba
commit
6cd91c675c
90 changed files with 831 additions and 570 deletions
|
|
@ -26,8 +26,8 @@ import {ZippyComponent} from './zippy.component';
|
|||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class DemoAppComponent {
|
||||
@ViewChild(ZippyComponent) zippy: ZippyComponent;
|
||||
@ViewChild('elementReference') elementRef: ElementRef;
|
||||
@ViewChild(ZippyComponent) zippy!: ZippyComponent;
|
||||
@ViewChild('elementReference') elementRef!: ElementRef;
|
||||
|
||||
@Input('input_one') inputOne = 'input one';
|
||||
@Input() inputTwo = 'input two';
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {Component, Input} from '@angular/core';
|
||||
|
||||
const fib = (n: number) => {
|
||||
const fib = (n: number): number => {
|
||||
if (n === 1 || n === 2) {
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export interface Todo {
|
|||
`
|
||||
})
|
||||
export class TodoComponent {
|
||||
@Input() todo: Todo;
|
||||
@Input() todo!: Todo;
|
||||
@Output() update = new EventEmitter();
|
||||
@Output() delete = new EventEmitter();
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export class TodosFilter implements PipeTransform {
|
|||
}
|
||||
}
|
||||
|
||||
const fib = (n: number) => {
|
||||
const fib = (n: number): number => {
|
||||
if (n === 1 || n === 2) {
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ export class TodosComponent implements OnInit, OnDestroy {
|
|||
@Output() delete = new EventEmitter();
|
||||
@Output() add = new EventEmitter();
|
||||
|
||||
private hashListener: EventListenerOrEventListenerObject;
|
||||
private hashListener!: EventListenerOrEventListenerObject;
|
||||
|
||||
constructor(private cdRef: ChangeDetectorRef) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ import {DialogComponent} from './dialog.component';
|
|||
`
|
||||
})
|
||||
export class TodoAppComponent {
|
||||
name: string;
|
||||
animal: string;
|
||||
name!: string;
|
||||
animal!: string;
|
||||
|
||||
constructor(public dialog: MatDialog) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,6 @@ import {Component, Input} from '@angular/core';
|
|||
`
|
||||
})
|
||||
export class ZippyComponent {
|
||||
@Input() title: string;
|
||||
@Input() title!: string;
|
||||
visible = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,5 +47,5 @@ import {IFrameMessageBus} from '../../../../../src/iframe-message-bus';
|
|||
})
|
||||
export class DevToolsComponent {
|
||||
messageBus: IFrameMessageBus|null = null;
|
||||
@ViewChild('ref') iframe: ElementRef;
|
||||
@ViewChild('ref') iframe!: ElementRef;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export interface ComponentInspectorOptions {
|
|||
}
|
||||
|
||||
export class ComponentInspector {
|
||||
private _selectedComponent: {component: Type<unknown>; host: HTMLElement | null};
|
||||
private _selectedComponent!: {component: Type<unknown>; host: HTMLElement | null};
|
||||
private readonly _onComponentEnter;
|
||||
private readonly _onComponentSelect;
|
||||
private readonly _onComponentLeave;
|
||||
|
|
@ -57,7 +57,7 @@ export class ComponentInspector {
|
|||
|
||||
if (this._selectedComponent.component && this._selectedComponent.host) {
|
||||
this._onComponentSelect(
|
||||
initializeOrGetDirectiveForestHooks().getDirectiveId(this._selectedComponent.component));
|
||||
initializeOrGetDirectiveForestHooks().getDirectiveId(this._selectedComponent.component)!);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ export class ComponentInspector {
|
|||
if (this._selectedComponent.component && this._selectedComponent.host) {
|
||||
highlight(this._selectedComponent.host);
|
||||
this._onComponentEnter(
|
||||
initializeOrGetDirectiveForestHooks().getDirectiveId(this._selectedComponent.component));
|
||||
initializeOrGetDirectiveForestHooks().getDirectiveId(this._selectedComponent.component)!);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import {ComponentExplorerViewQuery, DirectiveMetadata, DirectivesProperties, ElementPosition, InjectedService, PropertyQueryTypes, ProviderRecord, SerializedInjectedService, SerializedInjector, SerializedProviderRecord, UpdatedStateData,} from 'protocol';
|
||||
|
||||
import {buildDirectiveTree, getLViewFromDirectiveOrElementInstance} from './directive-forest/index';
|
||||
import {deeplySerializeSelectedProperties, serializeDirectiveState} from './state-serializer/state-serializer';
|
||||
import {deeplySerializeSelectedProperties, serializeDirectiveState,} from './state-serializer/state-serializer';
|
||||
|
||||
// Need to be kept in sync with Angular framework
|
||||
// We can't directly import it from framework now
|
||||
|
|
@ -21,7 +21,7 @@ enum ChangeDetectionStrategy {
|
|||
}
|
||||
|
||||
import {ComponentTreeNode, DirectiveInstanceType, ComponentInstanceType} from './interfaces';
|
||||
import type {ClassProvider, ExistingProvider, FactoryProvider, InjectionToken, Injector, Type, ValueProvider} from '@angular/core';
|
||||
import type {ClassProvider, ExistingProvider, FactoryProvider, InjectionToken, Injector, Type, ValueProvider,} from '@angular/core';
|
||||
|
||||
const ngDebug = () => (window as any).ng;
|
||||
export const injectorToId = new WeakMap<Injector|HTMLElement, string>();
|
||||
|
|
@ -56,8 +56,9 @@ export function ngDebugApiIsSupported(api: string): boolean {
|
|||
return typeof ng[api] === 'function';
|
||||
}
|
||||
|
||||
export function getInjectorMetadata(injector: Injector):
|
||||
{type: string; source: HTMLElement | string | null;}|null {
|
||||
export function getInjectorMetadata(
|
||||
injector: Injector,
|
||||
): {type: string; source: HTMLElement | string | null}|null {
|
||||
return ngDebug().ɵgetInjectorMetadata(injector);
|
||||
}
|
||||
|
||||
|
|
@ -86,59 +87,67 @@ export function getDirectivesFromElement(element: HTMLElement):
|
|||
};
|
||||
}
|
||||
|
||||
export const getLatestComponentState =
|
||||
(query: ComponentExplorerViewQuery, directiveForest?: ComponentTreeNode[]):
|
||||
{directiveProperties: DirectivesProperties;}|
|
||||
undefined => {
|
||||
// if a directive forest is passed in we don't have to build the forest again.
|
||||
directiveForest = directiveForest ?? buildDirectiveForest();
|
||||
export const getLatestComponentState = (
|
||||
query: ComponentExplorerViewQuery,
|
||||
directiveForest?: ComponentTreeNode[],
|
||||
): {directiveProperties: DirectivesProperties}|undefined => {
|
||||
// if a directive forest is passed in we don't have to build the forest again.
|
||||
directiveForest = directiveForest ?? buildDirectiveForest();
|
||||
|
||||
const node = queryDirectiveForest(query.selectedElement, directiveForest);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const node = queryDirectiveForest(query.selectedElement, directiveForest);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const directiveProperties: DirectivesProperties = {};
|
||||
const directiveProperties: DirectivesProperties = {};
|
||||
|
||||
const injector = ngDebug().getInjector(node.nativeElement);
|
||||
const injector = ngDebug().getInjector(node.nativeElement);
|
||||
|
||||
let resolutionPathWithProviders: {injector: Injector; providers: ProviderRecord[];}[] = [];
|
||||
if (hasDiDebugAPIs()) {
|
||||
resolutionPathWithProviders = getInjectorResolutionPath(injector).map(
|
||||
injector => ({injector, providers: getInjectorProviders(injector)}));
|
||||
}
|
||||
let resolutionPathWithProviders: {injector: Injector; providers: ProviderRecord[]}[] = [];
|
||||
if (hasDiDebugAPIs()) {
|
||||
resolutionPathWithProviders =
|
||||
getInjectorResolutionPath(injector).map((injector) => ({
|
||||
injector,
|
||||
providers: getInjectorProviders(injector),
|
||||
}));
|
||||
}
|
||||
|
||||
const populateResultSet = (dir: DirectiveInstanceType|ComponentInstanceType) => {
|
||||
const {instance, name} = dir;
|
||||
const metadata = getDirectiveMetadata(instance);
|
||||
metadata.dependencies = getDependenciesForDirective(
|
||||
injector, resolutionPathWithProviders, instance.constructor);
|
||||
const populateResultSet = (dir: DirectiveInstanceType|ComponentInstanceType) => {
|
||||
const {instance, name} = dir;
|
||||
const metadata = getDirectiveMetadata(instance);
|
||||
metadata.dependencies = getDependenciesForDirective(
|
||||
injector,
|
||||
resolutionPathWithProviders,
|
||||
instance.constructor,
|
||||
);
|
||||
|
||||
if (query.propertyQuery.type === PropertyQueryTypes.All) {
|
||||
directiveProperties[dir.name] = {
|
||||
props: serializeDirectiveState(instance),
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
|
||||
if (query.propertyQuery.type === PropertyQueryTypes.Specified) {
|
||||
directiveProperties[name] = {
|
||||
props: deeplySerializeSelectedProperties(
|
||||
instance, query.propertyQuery.properties[name] || []),
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
if (query.propertyQuery.type === PropertyQueryTypes.All) {
|
||||
directiveProperties[dir.name] = {
|
||||
props: serializeDirectiveState(instance),
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
|
||||
node.directives.forEach((dir) => populateResultSet(dir));
|
||||
if (node.component) {
|
||||
populateResultSet(node.component);
|
||||
}
|
||||
|
||||
return {
|
||||
directiveProperties,
|
||||
if (query.propertyQuery.type === PropertyQueryTypes.Specified) {
|
||||
directiveProperties[name] = {
|
||||
props: deeplySerializeSelectedProperties(
|
||||
instance,
|
||||
query.propertyQuery.properties[name] || [],
|
||||
),
|
||||
metadata,
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
node.directives.forEach((dir) => populateResultSet(dir));
|
||||
if (node.component) {
|
||||
populateResultSet(node.component);
|
||||
}
|
||||
|
||||
return {
|
||||
directiveProperties,
|
||||
};
|
||||
};
|
||||
|
||||
export function serializeElementInjectorWithId(injector: Injector): SerializedInjector|null {
|
||||
let id: string;
|
||||
|
|
@ -241,71 +250,76 @@ export function getInjectorProviders(injector: Injector): ProviderRecord[] {
|
|||
return ngDebug().ɵgetInjectorProviders(injector);
|
||||
}
|
||||
|
||||
const getDependenciesForDirective =
|
||||
(injector: Injector, resolutionPath: {injector: Injector; providers: ProviderRecord[]}[],
|
||||
directive: any): SerializedInjectedService[] => {
|
||||
if (!ngDebugApiIsSupported('ɵgetDependenciesFromInjectable')) {
|
||||
return [];
|
||||
}
|
||||
const getDependenciesForDirective = (
|
||||
injector: Injector,
|
||||
resolutionPath: {injector: Injector; providers: ProviderRecord[]}[],
|
||||
directive: any,
|
||||
): SerializedInjectedService[] => {
|
||||
if (!ngDebugApiIsSupported('ɵgetDependenciesFromInjectable')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let dependencies: InjectedService[] =
|
||||
ngDebug().ɵgetDependenciesFromInjectable(injector, directive).dependencies;
|
||||
const serializedInjectedServices: SerializedInjectedService[] = [];
|
||||
let dependencies: InjectedService[] = ngDebug()
|
||||
.ɵgetDependenciesFromInjectable(
|
||||
injector,
|
||||
directive,
|
||||
)
|
||||
.dependencies;
|
||||
const serializedInjectedServices: SerializedInjectedService[] = [];
|
||||
|
||||
let position = 0;
|
||||
for (const dependency of dependencies) {
|
||||
const providedIn = dependency.providedIn;
|
||||
const foundInjectorIndex = resolutionPath.findIndex(node => node.injector === providedIn);
|
||||
let position = 0;
|
||||
for (const dependency of dependencies) {
|
||||
const providedIn = dependency.providedIn;
|
||||
const foundInjectorIndex = resolutionPath.findIndex((node) => node.injector === providedIn);
|
||||
|
||||
if (foundInjectorIndex === -1) {
|
||||
position++;
|
||||
continue;
|
||||
}
|
||||
if (foundInjectorIndex === -1) {
|
||||
position++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const providers = resolutionPath[foundInjectorIndex].providers;
|
||||
const foundProvider = providers.find(provider => provider.token === dependency.token);
|
||||
const providers = resolutionPath[foundInjectorIndex].providers;
|
||||
const foundProvider = providers.find((provider) => provider.token === dependency.token);
|
||||
|
||||
// the dependency resolution path is
|
||||
// the path from the root injector to the injector that provided the dependency (1)
|
||||
// +
|
||||
// the import path from the providing injector to the feature module that provided the
|
||||
// dependency (2)
|
||||
const dependencyResolutionPath = [
|
||||
// (1)
|
||||
...resolutionPath.slice(0, foundInjectorIndex + 1)
|
||||
.map(node => serializeInjectorWithId(node.injector)),
|
||||
// the dependency resolution path is
|
||||
// the path from the root injector to the injector that provided the dependency (1)
|
||||
// +
|
||||
// the import path from the providing injector to the feature module that provided the
|
||||
// dependency (2)
|
||||
const dependencyResolutionPath = [
|
||||
// (1)
|
||||
...resolutionPath.slice(0, foundInjectorIndex + 1)
|
||||
.map((node) => serializeInjectorWithId(node.injector)),
|
||||
|
||||
// (2)
|
||||
// We slice the import path to remove the first element because this is the same
|
||||
// injector as the last injector in the resolution path.
|
||||
...(foundProvider?.importPath ?? []).slice(1).map(node => {
|
||||
return {type: 'imported-module', name: valueToLabel(node), id: getInjectorId()};
|
||||
})
|
||||
] as SerializedInjector[];
|
||||
// (2)
|
||||
// We slice the import path to remove the first element because this is the same
|
||||
// injector as the last injector in the resolution path.
|
||||
...(foundProvider?.importPath ?? []).slice(1).map((node) => {
|
||||
return {type: 'imported-module', name: valueToLabel(node), id: getInjectorId()};
|
||||
}),
|
||||
] as SerializedInjector[];
|
||||
|
||||
if (dependency.token && isInjectionToken(dependency.token)) {
|
||||
serializedInjectedServices.push({
|
||||
token: dependency.token!.toString(),
|
||||
value: valueToLabel(dependency.value),
|
||||
flags: dependency.flags,
|
||||
position: [position++],
|
||||
resolutionPath: dependencyResolutionPath,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dependency.token && isInjectionToken(dependency.token)) {
|
||||
serializedInjectedServices.push({
|
||||
token: dependency.token!.toString(),
|
||||
value: valueToLabel(dependency.value),
|
||||
flags: dependency.flags,
|
||||
position: [position++],
|
||||
resolutionPath: dependencyResolutionPath
|
||||
});
|
||||
continue;
|
||||
}
|
||||
serializedInjectedServices.push({
|
||||
token: valueToLabel(dependency.token),
|
||||
value: valueToLabel(dependency.value),
|
||||
flags: dependency.flags,
|
||||
position: [position++],
|
||||
resolutionPath: dependencyResolutionPath,
|
||||
});
|
||||
}
|
||||
|
||||
serializedInjectedServices.push({
|
||||
token: valueToLabel(dependency.token),
|
||||
value: valueToLabel(dependency.value),
|
||||
flags: dependency.flags,
|
||||
position: [position++],
|
||||
resolutionPath: dependencyResolutionPath
|
||||
});
|
||||
}
|
||||
|
||||
return serializedInjectedServices;
|
||||
};
|
||||
return serializedInjectedServices;
|
||||
};
|
||||
|
||||
export const valueToLabel = (value: any): string => {
|
||||
if (isInjectionToken(value)) {
|
||||
|
|
@ -371,8 +385,10 @@ export function serializeInjector(injector: Injector): Omit<SerializedInjector,
|
|||
}
|
||||
|
||||
export function serializeProviderRecord(
|
||||
providerRecord: ProviderRecord, index: number,
|
||||
hasImportPath = false): SerializedProviderRecord {
|
||||
providerRecord: ProviderRecord,
|
||||
index: number,
|
||||
hasImportPath = false,
|
||||
): SerializedProviderRecord {
|
||||
let type: 'type'|'class'|'value'|'factory'|'existing' = 'type';
|
||||
let multi = false;
|
||||
|
||||
|
|
@ -392,17 +408,22 @@ export function serializeProviderRecord(
|
|||
}
|
||||
}
|
||||
|
||||
const serializedProvider = {
|
||||
const serializedProvider: {
|
||||
token: string; type: typeof type; multi: boolean; isViewProvider: boolean; index: number;
|
||||
importPath?: string[];
|
||||
} = {
|
||||
token: valueToLabel(providerRecord.token),
|
||||
type,
|
||||
multi,
|
||||
isViewProvider: providerRecord.isViewProvider,
|
||||
index
|
||||
index,
|
||||
};
|
||||
|
||||
if (hasImportPath) {
|
||||
serializedProvider['importPath'] =
|
||||
(providerRecord.importPath ?? []).map(injector => valueToLabel(injector));
|
||||
serializedProvider['importPath'] = (providerRecord.importPath ?? [])
|
||||
.map(
|
||||
(injector) => valueToLabel(injector),
|
||||
);
|
||||
}
|
||||
|
||||
return serializedProvider;
|
||||
|
|
@ -410,7 +431,9 @@ export function serializeProviderRecord(
|
|||
|
||||
function elementToDirectiveNames(element: HTMLElement): string[] {
|
||||
const {component, directives} = getDirectivesFromElement(element);
|
||||
return [component, ...directives].map(dir => dir?.constructor?.name ?? '').filter(dir => !!dir);
|
||||
return [component, ...directives]
|
||||
.map((dir) => dir?.constructor?.name ?? '')
|
||||
.filter((dir) => !!dir);
|
||||
}
|
||||
|
||||
export function getElementInjectorElement(elementInjector: Injector): HTMLElement {
|
||||
|
|
@ -457,8 +480,9 @@ const getRootLViewsHelper = (element: Element, rootLViews = new Set<any>()): Set
|
|||
};
|
||||
|
||||
const getRoots = () => {
|
||||
const roots =
|
||||
Array.from(document.documentElement.querySelectorAll('[ng-version]')) as HTMLElement[];
|
||||
const roots = Array.from(
|
||||
document.documentElement.querySelectorAll('[ng-version]'),
|
||||
) as HTMLElement[];
|
||||
|
||||
const isTopLevel = (element: HTMLElement) => {
|
||||
let parent: HTMLElement|null = element;
|
||||
|
|
@ -483,41 +507,48 @@ export const buildDirectiveForest = (): ComponentTreeNode[] => {
|
|||
|
||||
// Based on an ElementID we return a specific component node.
|
||||
// If we can't find any, we return null.
|
||||
export const queryDirectiveForest =
|
||||
(position: ElementPosition, forest: ComponentTreeNode[]): ComponentTreeNode|null => {
|
||||
if (!position.length) {
|
||||
return null;
|
||||
}
|
||||
let node: null|ComponentTreeNode = null;
|
||||
for (const i of position) {
|
||||
node = forest[i];
|
||||
if (!node) {
|
||||
return null;
|
||||
}
|
||||
forest = node.children;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
export const queryDirectiveForest = (
|
||||
position: ElementPosition,
|
||||
forest: ComponentTreeNode[],
|
||||
): ComponentTreeNode|null => {
|
||||
if (!position.length) {
|
||||
return null;
|
||||
}
|
||||
let node: null|ComponentTreeNode = null;
|
||||
for (const i of position) {
|
||||
node = forest[i];
|
||||
if (!node) {
|
||||
return null;
|
||||
}
|
||||
forest = node.children;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
export const findNodeInForest =
|
||||
(position: ElementPosition, forest: ComponentTreeNode[]): HTMLElement|null => {
|
||||
const foundComponent: ComponentTreeNode|null = queryDirectiveForest(position, forest);
|
||||
return foundComponent ? (foundComponent.nativeElement as HTMLElement) : null;
|
||||
};
|
||||
export const findNodeInForest = (
|
||||
position: ElementPosition,
|
||||
forest: ComponentTreeNode[],
|
||||
): HTMLElement|null => {
|
||||
const foundComponent: ComponentTreeNode|null = queryDirectiveForest(position, forest);
|
||||
return foundComponent ? (foundComponent.nativeElement as HTMLElement) : null;
|
||||
};
|
||||
|
||||
export const findNodeFromSerializedPosition =
|
||||
(serializedPosition: string): ComponentTreeNode|null => {
|
||||
const position: number[] = serializedPosition.split(',').map((index) => parseInt(index, 10));
|
||||
return queryDirectiveForest(position, buildDirectiveForest());
|
||||
};
|
||||
export const findNodeFromSerializedPosition = (
|
||||
serializedPosition: string,
|
||||
): ComponentTreeNode|null => {
|
||||
const position: number[] = serializedPosition.split(',').map((index) => parseInt(index, 10));
|
||||
return queryDirectiveForest(position, buildDirectiveForest());
|
||||
};
|
||||
|
||||
export const updateState = (updatedStateData: UpdatedStateData): void => {
|
||||
const ngd = ngDebug();
|
||||
const node = queryDirectiveForest(updatedStateData.directiveId.element, buildDirectiveForest());
|
||||
if (!node) {
|
||||
console.warn(
|
||||
'Could not update the state of component', updatedStateData,
|
||||
'because the component was not found');
|
||||
'Could not update the state of component',
|
||||
updatedStateData,
|
||||
'because the component was not found',
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (updatedStateData.directiveId.directive !== undefined) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ const TYPE = 1;
|
|||
const ELEMENT = 0;
|
||||
const LVIEW_TVIEW = 1;
|
||||
|
||||
|
||||
// Big oversimplification of the LView structure.
|
||||
type LView = Array<any>;
|
||||
|
||||
export const isLContainer = (value: any): boolean => {
|
||||
return Array.isArray(value) && value[TYPE] === true;
|
||||
};
|
||||
|
|
@ -37,7 +41,7 @@ const isLView = (value: any): boolean => {
|
|||
};
|
||||
|
||||
export const METADATA_PROPERTY_NAME = '__ngContext__';
|
||||
export const getLViewFromDirectiveOrElementInstance = (dir: any): null|{} => {
|
||||
export function getLViewFromDirectiveOrElementInstance(dir: any): null|LView {
|
||||
if (!dir) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -49,7 +53,7 @@ export const getLViewFromDirectiveOrElementInstance = (dir: any): null|{} => {
|
|||
return context;
|
||||
}
|
||||
return context.lView;
|
||||
};
|
||||
}
|
||||
|
||||
export const getDirectiveHostElement = (dir: any) => {
|
||||
if (!dir) {
|
||||
|
|
|
|||
|
|
@ -196,11 +196,11 @@ const insertOrMerge = (lastFrame: ElementProfile, profile: DirectiveProfile) =>
|
|||
current = 0;
|
||||
}
|
||||
d.changeDetection = current + (profile.changeDetection ?? 0);
|
||||
for (const key of Object.keys(profile.lifecycle)) {
|
||||
for (const key of Object.keys(profile.lifecycle) as (keyof LifecycleProfile)[]) {
|
||||
if (!d.lifecycle[key]) {
|
||||
d.lifecycle[key] = 0;
|
||||
}
|
||||
d.lifecycle[key] += profile.lifecycle[key];
|
||||
d.lifecycle[key]! += profile.lifecycle[key]!;
|
||||
}
|
||||
for (const key of Object.keys(profile.outputs)) {
|
||||
if (!d.outputs[key]) {
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ export class PatchingProfiler extends Profiler {
|
|||
// These two abstractions don't have `__ngContext__`, and
|
||||
// currently we won't be able to extract the required
|
||||
// metadata by the UI.
|
||||
if (!this[METADATA_PROPERTY_NAME]) {
|
||||
if (!(this as any)[METADATA_PROPERTY_NAME]) {
|
||||
return;
|
||||
}
|
||||
const id = self._tracker.getDirectiveId(this);
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ type CreationHook =
|
|||
position: ElementPosition) => void;
|
||||
|
||||
type LifecycleStartHook =
|
||||
(componentOrDirective: any, hook: keyof LifecycleProfile|'unknown', node: Node, id: number,
|
||||
(componentOrDirective: any, hook: keyof LifecycleProfile, node: Node, id: number,
|
||||
isComponent: boolean) => void;
|
||||
|
||||
type LifecycleEndHook =
|
||||
(componentOrDirective: any, hook: keyof LifecycleProfile|'unknown', node: Node, id: number,
|
||||
(componentOrDirective: any, hook: keyof LifecycleProfile, node: Node, id: number,
|
||||
isComponent: boolean) => void;
|
||||
|
||||
type ChangeDetectionStartHook =
|
||||
|
|
@ -157,7 +157,7 @@ export abstract class Profiler {
|
|||
this._hooks.forEach((config) => {
|
||||
const cb = config[name];
|
||||
if (typeof cb === 'function') {
|
||||
cb.apply(null, args);
|
||||
(cb as any).apply(null, args);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {parseRoutes} from './router-tree';
|
|||
|
||||
describe('parseRoutes', () => {
|
||||
it('should work without any routes', () => {
|
||||
const routes = [];
|
||||
const routes: any[] = [];
|
||||
const parsedRoutes = parseRoutes(routes as any);
|
||||
expect(parsedRoutes).toEqual({
|
||||
handler: 'no-name',
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
//
|
||||
// We'd have to go through a serialization and deserialization logic
|
||||
// which will add unnecessary complexity.
|
||||
export const getKeys = (obj: {}): string[] => {
|
||||
export function getKeys(obj: {}): string[] {
|
||||
if (!obj) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ export const getKeys = (obj: {}): string[] => {
|
|||
});
|
||||
|
||||
return properties.concat(gettersAndSetters);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper function covers the common scenario as well as the getters and setters
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ export const getPropType = (prop: unknown): PropType => {
|
|||
return PropType.HTMLNode;
|
||||
}
|
||||
const type = typeof prop;
|
||||
if (commonTypes[type] !== undefined) {
|
||||
return commonTypes[type];
|
||||
if (type in commonTypes) {
|
||||
return commonTypes[type as keyof typeof commonTypes];
|
||||
}
|
||||
if (type === 'object') {
|
||||
if (Array.isArray(prop)) {
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ export const createNestedSerializedDescriptor =
|
|||
(instance: {}, propName: string|number, propData: CompositeType, levelOptions: LevelOptions,
|
||||
nodes: NestedProp[],
|
||||
nestedSerializer: (
|
||||
instance: any, propName: string, nodes: NestedProp[], currentLevel: number,
|
||||
instance: any, propName: string|number, nodes: NestedProp[], currentLevel: number,
|
||||
level?: number) => void): Descriptor => {
|
||||
const {type, prop} = propData;
|
||||
|
||||
|
|
@ -227,7 +227,7 @@ export const createNestedSerializedDescriptor =
|
|||
preview: getPreview(propData, getterOrSetter),
|
||||
};
|
||||
|
||||
if (nodes && nodes.length) {
|
||||
if (nodes?.length) {
|
||||
const value = getNestedDescriptorValue(propData, levelOptions, nodes, nestedSerializer);
|
||||
if (value !== undefined) {
|
||||
nestedSerializedDescriptor.value = value;
|
||||
|
|
@ -236,50 +236,48 @@ export const createNestedSerializedDescriptor =
|
|||
return nestedSerializedDescriptor;
|
||||
};
|
||||
|
||||
const getNestedDescriptorValue =
|
||||
(propData: CompositeType, levelOptions: LevelOptions, nodes: NestedProp[],
|
||||
nestedSerializer: (
|
||||
instance: any, propName: string|number, nodes: NestedProp[], currentLevel: number,
|
||||
level?: number) => void) => {
|
||||
const {type, prop} = propData;
|
||||
const {currentLevel} = levelOptions;
|
||||
function getNestedDescriptorValue(
|
||||
propData: CompositeType, levelOptions: LevelOptions, nodes: NestedProp[],
|
||||
nestedSerializer: (
|
||||
instance: any, propName: string|number, nodes: NestedProp[], currentLevel: number,
|
||||
level?: number) => void) {
|
||||
const {type, prop} = propData;
|
||||
const {currentLevel} = levelOptions;
|
||||
|
||||
switch (type) {
|
||||
case PropType.Array:
|
||||
return nodes.map(
|
||||
(nestedProp) =>
|
||||
nestedSerializer(prop, nestedProp.name, nestedProp.children, currentLevel + 1));
|
||||
case PropType.Object:
|
||||
return nodes.reduce((accumulator, nestedProp) => {
|
||||
if (prop.hasOwnProperty(nestedProp.name) && !ignoreList.has(nestedProp.name)) {
|
||||
accumulator[nestedProp.name] =
|
||||
nestedSerializer(prop, nestedProp.name, nestedProp.children, currentLevel + 1);
|
||||
}
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
};
|
||||
switch (type) {
|
||||
case PropType.Array:
|
||||
return nodes.map(
|
||||
(nestedProp) =>
|
||||
nestedSerializer(prop, nestedProp.name, nestedProp.children, currentLevel + 1));
|
||||
case PropType.Object:
|
||||
return nodes.reduce((accumulator, nestedProp) => {
|
||||
if (prop.hasOwnProperty(nestedProp.name) && !ignoreList.has(nestedProp.name)) {
|
||||
accumulator[nestedProp.name] =
|
||||
nestedSerializer(prop, nestedProp.name, nestedProp.children, currentLevel + 1);
|
||||
}
|
||||
return accumulator;
|
||||
}, {} as Record<string, void>);
|
||||
}
|
||||
}
|
||||
|
||||
const getLevelDescriptorValue =
|
||||
(propData: CompositeType, levelOptions: LevelOptions,
|
||||
continuation: (instance: any, propName: string|number, level?: number, max?: number) =>
|
||||
void) => {
|
||||
const {type, prop} = propData;
|
||||
const {currentLevel, level} = levelOptions;
|
||||
function getLevelDescriptorValue(
|
||||
propData: CompositeType, levelOptions: LevelOptions,
|
||||
continuation: (instance: any, propName: string|number, level?: number, max?: number) => void) {
|
||||
const {type, prop} = propData;
|
||||
const {currentLevel, level} = levelOptions;
|
||||
|
||||
switch (type) {
|
||||
case PropType.Array:
|
||||
return prop.map(
|
||||
(_: any, idx: number) => continuation(prop, idx, currentLevel + 1, level));
|
||||
case PropType.Object:
|
||||
return getKeys(prop).reduce((accumulator, propName) => {
|
||||
if (!ignoreList.has(propName)) {
|
||||
accumulator[propName] = continuation(prop, propName, currentLevel + 1, level);
|
||||
}
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
};
|
||||
switch (type) {
|
||||
case PropType.Array:
|
||||
return prop.map((_: any, idx: number) => continuation(prop, idx, currentLevel + 1, level));
|
||||
case PropType.Object:
|
||||
return getKeys(prop).reduce((accumulator, propName) => {
|
||||
if (!ignoreList.has(propName)) {
|
||||
accumulator[propName] = continuation(prop, propName, currentLevel + 1, level);
|
||||
}
|
||||
return accumulator;
|
||||
}, {} as Record<string, void>);
|
||||
}
|
||||
}
|
||||
|
||||
const truncate = (str: string, max = 20): string => {
|
||||
if (str.length > max) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {PropType} from 'protocol';
|
|||
import {getDescriptor, getKeys} from './object-utils';
|
||||
import {deeplySerializeSelectedProperties} from './state-serializer';
|
||||
|
||||
const QUERY_1_1 = [];
|
||||
const QUERY_1_1: any[] = [];
|
||||
|
||||
const QUERY_1_2 = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ const nestedSerializer =
|
|||
};
|
||||
|
||||
const nestedSerializerContinuation = (nodes: NestedProp[], level: number) =>
|
||||
(instance: any, propName: string, nestedLevel: number) => {
|
||||
(instance: any, propName: string|number, nestedLevel?: number, _?: number) => {
|
||||
const idx = nodes.findIndex((v) => v.name === propName);
|
||||
if (idx < 0) {
|
||||
// The property is not specified in the query.
|
||||
|
|
@ -51,38 +51,38 @@ const nestedSerializerContinuation = (nodes: NestedProp[], level: number) =>
|
|||
return nestedSerializer(instance, propName, nodes[idx].children, nestedLevel, level);
|
||||
};
|
||||
|
||||
const levelSerializer =
|
||||
(instance: any, propName: string|number, currentLevel = 0, level = MAX_LEVEL,
|
||||
continuation = levelSerializer): Descriptor => {
|
||||
const serializableInstance = instance[propName];
|
||||
const propData:
|
||||
PropertyData = {prop: serializableInstance, type: getPropType(serializableInstance)};
|
||||
function levelSerializer(
|
||||
instance: any, propName: string|number, currentLevel = 0, level = MAX_LEVEL,
|
||||
continuation = levelSerializer): Descriptor {
|
||||
const serializableInstance = instance[propName];
|
||||
const propData:
|
||||
PropertyData = {prop: serializableInstance, type: getPropType(serializableInstance)};
|
||||
|
||||
switch (propData.type) {
|
||||
case PropType.Array:
|
||||
case PropType.Object:
|
||||
return createLevelSerializedDescriptor(
|
||||
instance, propName, propData, {level, currentLevel}, continuation);
|
||||
default:
|
||||
return createShallowSerializedDescriptor(instance, propName, propData);
|
||||
}
|
||||
};
|
||||
switch (propData.type) {
|
||||
case PropType.Array:
|
||||
case PropType.Object:
|
||||
return createLevelSerializedDescriptor(
|
||||
instance, propName, propData, {level, currentLevel}, continuation);
|
||||
default:
|
||||
return createShallowSerializedDescriptor(instance, propName, propData);
|
||||
}
|
||||
}
|
||||
|
||||
export const serializeDirectiveState =
|
||||
(instance: object, levels = MAX_LEVEL): {[key: string]: Descriptor} => {
|
||||
const result = {};
|
||||
getKeys(instance).forEach((prop) => {
|
||||
if (typeof prop === 'string' && ignoreList.has(prop)) {
|
||||
return;
|
||||
}
|
||||
result[prop] = levelSerializer(instance, prop, null, 0, levels);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
export function serializeDirectiveState(
|
||||
instance: object, levels = MAX_LEVEL): {[key: string]: Descriptor} {
|
||||
const result: Record<string, Descriptor> = {};
|
||||
getKeys(instance).forEach((prop) => {
|
||||
if (typeof prop === 'string' && ignoreList.has(prop)) {
|
||||
return;
|
||||
}
|
||||
result[prop] = levelSerializer(instance, prop, 0, 0);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export const deeplySerializeSelectedProperties =
|
||||
(instance: any, props: NestedProp[]): {[name: string]: Descriptor} => {
|
||||
const result = {};
|
||||
const result: Record<string, Descriptor> = {};
|
||||
getKeys(instance).forEach((prop) => {
|
||||
if (ignoreList.has(prop)) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -43,12 +43,17 @@ ts_test_library(
|
|||
srcs = ["devtools-tabs.spec.ts"],
|
||||
deps = [
|
||||
":devtools-tabs",
|
||||
"//devtools/projects/ng-devtools/src/lib:theme",
|
||||
"//devtools/projects/ng-devtools/src/lib/application-environment",
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer",
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/tab-update",
|
||||
"//devtools/projects/protocol",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"//packages/core/src/util",
|
||||
"//packages/core/testing",
|
||||
"@npm//@angular/material",
|
||||
"@npm//rxjs",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,22 +23,14 @@ export interface InjectorTreeNode {
|
|||
children: InjectorTreeNode[];
|
||||
}
|
||||
|
||||
export interface InjectorTreeD3Node {
|
||||
children: InjectorTreeD3Node[];
|
||||
data: InjectorTreeNode;
|
||||
depth: number;
|
||||
height: number;
|
||||
parent?: InjectorTreeD3Node;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
export type InjectorTreeD3Node = d3.HierarchyPointNode<InjectorTreeNode>;
|
||||
|
||||
export abstract class GraphRenderer<T, U> {
|
||||
abstract render(graph: T): void;
|
||||
abstract getNodeById(id: string): U|null;
|
||||
abstract snapToNode(node: U): void;
|
||||
abstract snapToRoot(): void;
|
||||
abstract zoomScale(scale: number);
|
||||
abstract zoomScale(scale: number): void;
|
||||
abstract root: U|null;
|
||||
abstract get graphElement(): HTMLElement;
|
||||
|
||||
|
|
@ -99,11 +91,12 @@ export class InjectorTreeVisualizer extends GraphRenderer<InjectorTreeNode, Inje
|
|||
private d3 = d3;
|
||||
|
||||
override root: InjectorTreeD3Node|null = null;
|
||||
zoomController: d3.ZoomBehavior<Element, unknown>|null = null;
|
||||
zoomController: d3.ZoomBehavior<HTMLElement, unknown>|null = null;
|
||||
|
||||
override zoomScale(scale: number) {
|
||||
if (this.zoomController) {
|
||||
this.zoomController.scaleTo(this.d3.select(this._containerElement), scale);
|
||||
this.zoomController.scaleTo(
|
||||
this.d3.select<HTMLElement, unknown>(this._containerElement), scale);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +111,7 @@ export class InjectorTreeVisualizer extends GraphRenderer<InjectorTreeNode, Inje
|
|||
const halfWidth = (this._containerElement.clientWidth / 2);
|
||||
const halfHeight = (this._containerElement.clientHeight / 2);
|
||||
const t = d3.zoomIdentity.translate(halfWidth - node.y, halfHeight - node.x).scale(scale);
|
||||
svg.transition().duration(500).call(this.zoomController.transform, t);
|
||||
svg.transition().duration(500).call(this.zoomController!.transform, t);
|
||||
}
|
||||
|
||||
override get graphElement(): HTMLElement {
|
||||
|
|
@ -126,7 +119,8 @@ export class InjectorTreeVisualizer extends GraphRenderer<InjectorTreeNode, Inje
|
|||
}
|
||||
|
||||
override getNodeById(id: string): InjectorTreeD3Node|null {
|
||||
const selection = this.d3.select(this._containerElement).select(`.node[data-id="${id}"]`);
|
||||
const selection = this.d3.select<HTMLElement, InjectorTreeD3Node>(this._containerElement)
|
||||
.select(`.node[data-id="${id}"]`);
|
||||
if (selection.empty()) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -142,12 +136,12 @@ export class InjectorTreeVisualizer extends GraphRenderer<InjectorTreeNode, Inje
|
|||
// cleanup old graph
|
||||
this.cleanup();
|
||||
|
||||
const data = this.d3.hierarchy(injectorGraph, (node: InjectorTreeD3Node) => node.children);
|
||||
const tree = this.d3.tree();
|
||||
const data = this.d3.hierarchy(injectorGraph, (node: InjectorTreeNode) => node.children);
|
||||
const tree = this.d3.tree<InjectorTreeNode>();
|
||||
const svg = this.d3.select(this._containerElement);
|
||||
const g = this.d3.select(this._graphElement);
|
||||
const g = this.d3.select<HTMLElement, InjectorTreeD3Node>(this._graphElement);
|
||||
|
||||
this.zoomController = this.d3.zoom().scaleExtent([0.1, 2]);
|
||||
this.zoomController = this.d3.zoom<HTMLElement, unknown>().scaleExtent([0.1, 2]);
|
||||
this.zoomController.on('start zoom end', (e: {transform: number}) => {
|
||||
g.attr('transform', e.transform);
|
||||
});
|
||||
|
|
@ -253,15 +247,15 @@ export class InjectorTreeVisualizer extends GraphRenderer<InjectorTreeNode, Inje
|
|||
return node.data.injector.id;
|
||||
})
|
||||
.on('click',
|
||||
(pointerEvent, node: InjectorTreeD3Node) => {
|
||||
(pointerEvent: PointerEvent, node: InjectorTreeD3Node) => {
|
||||
this.nodeClickListeners.forEach(listener => listener(pointerEvent, node));
|
||||
})
|
||||
.on('mouseover',
|
||||
(pointerEvent, node: InjectorTreeD3Node) => {
|
||||
(pointerEvent: PointerEvent, node: InjectorTreeD3Node) => {
|
||||
this.nodeMouseoverListeners.forEach(listener => listener(pointerEvent, node));
|
||||
})
|
||||
.on('mouseout',
|
||||
(pointerEvent, node: InjectorTreeD3Node) => {
|
||||
(pointerEvent: PointerEvent, node: InjectorTreeD3Node) => {
|
||||
this.nodeMouseoutListeners.forEach(listener => listener(pointerEvent, node));
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@ import {InjectorTreeNode, InjectorTreeVisualizer} from './injector-tree-visualiz
|
|||
standalone: true
|
||||
})
|
||||
export class ResolutionPathComponent implements OnDestroy, AfterViewInit {
|
||||
@ViewChild('svgContainer', {static: true}) private svgContainer: ElementRef;
|
||||
@ViewChild('mainGroup', {static: true}) private g: ElementRef;
|
||||
@ViewChild('svgContainer', {static: true}) private svgContainer!: ElementRef;
|
||||
@ViewChild('mainGroup', {static: true}) private g!: ElementRef;
|
||||
|
||||
@Input() orientation: 'horizontal'|'vertical' = 'horizontal';
|
||||
|
||||
private injectorTree: InjectorTreeVisualizer;
|
||||
private pathNode: InjectorTreeNode;
|
||||
private injectorTree!: InjectorTreeVisualizer;
|
||||
private pathNode!: InjectorTreeNode;
|
||||
|
||||
@Input()
|
||||
set path(path: SerializedInjector[]) {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@
|
|||
*/
|
||||
|
||||
import {AfterViewInit, Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {MatTabNav} from '@angular/material/tabs';
|
||||
import {Events, MessageBus, Route} from 'protocol';
|
||||
import {Subscription} from 'rxjs';
|
||||
|
||||
import {ApplicationEnvironment} from '../application-environment/index';
|
||||
import {Theme, ThemeService} from '../theme-service';
|
||||
|
|
@ -22,10 +22,10 @@ import {TabUpdate} from './tab-update/index';
|
|||
templateUrl: './devtools-tabs.component.html',
|
||||
styleUrls: ['./devtools-tabs.component.scss'],
|
||||
})
|
||||
export class DevToolsTabsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
export class DevToolsTabsComponent implements OnInit, AfterViewInit {
|
||||
@Input() angularVersion: string|undefined = undefined;
|
||||
@ViewChild(DirectiveExplorerComponent) directiveExplorer: DirectiveExplorerComponent;
|
||||
@ViewChild('navBar', {static: true}) navbar: MatTabNav;
|
||||
@ViewChild(DirectiveExplorerComponent) directiveExplorer!: DirectiveExplorerComponent;
|
||||
@ViewChild('navBar', {static: true}) navbar!: MatTabNav;
|
||||
|
||||
activeTab: 'Components'|'Profiler'|'Router Tree'|'Injector Tree' = 'Components';
|
||||
|
||||
|
|
@ -34,24 +34,25 @@ export class DevToolsTabsComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
showCommentNodes = false;
|
||||
timingAPIEnabled = false;
|
||||
|
||||
private _currentThemeSubscription: Subscription;
|
||||
currentTheme: Theme;
|
||||
currentTheme!: Theme;
|
||||
|
||||
routes: Route[] = [];
|
||||
|
||||
constructor(
|
||||
public tabUpdate: TabUpdate, public themeService: ThemeService,
|
||||
public tabUpdate: TabUpdate,
|
||||
public themeService: ThemeService,
|
||||
private _messageBus: MessageBus<Events>,
|
||||
private _applicationEnvironment: ApplicationEnvironment) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._currentThemeSubscription =
|
||||
this.themeService.currentTheme.subscribe((theme) => (this.currentTheme = theme));
|
||||
private _applicationEnvironment: ApplicationEnvironment,
|
||||
) {
|
||||
this.themeService.currentTheme.pipe(takeUntilDestroyed())
|
||||
.subscribe((theme) => (this.currentTheme = theme));
|
||||
|
||||
this._messageBus.on('updateRouterTree', (routes) => {
|
||||
this.routes = routes || [];
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.navbar.stretchTabs = false;
|
||||
}
|
||||
|
||||
|
|
@ -64,10 +65,6 @@ export class DevToolsTabsComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.navbar.disablePagination = true;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._currentThemeSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
get latestSHA(): string {
|
||||
return this._applicationEnvironment.environment.LATEST_SHA.slice(0, 8);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,26 +6,48 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {Events, MessageBus} from 'protocol';
|
||||
import {Subject} from 'rxjs';
|
||||
|
||||
import {ApplicationEnvironment} from '../application-environment/index';
|
||||
import {Theme, ThemeService} from '../theme-service';
|
||||
|
||||
import {DevToolsTabsComponent} from './devtools-tabs.component';
|
||||
import {DirectiveExplorerComponent} from './directive-explorer/directive-explorer.component';
|
||||
import {TabUpdate} from './tab-update/index';
|
||||
|
||||
@Component({
|
||||
selector: 'ng-directive-explorer',
|
||||
template: '',
|
||||
})
|
||||
export class MockDirectiveExplorerComponent {
|
||||
}
|
||||
|
||||
describe('DevtoolsTabsComponent', () => {
|
||||
let messageBusMock: MessageBus<Events>;
|
||||
let applicationEnvironmentMock: ApplicationEnvironment;
|
||||
let comp: DevToolsTabsComponent;
|
||||
let mockThemeService: any;
|
||||
|
||||
beforeEach(() => {
|
||||
messageBusMock = jasmine.createSpyObj('messageBus', ['on', 'once', 'emit', 'destroy']);
|
||||
applicationEnvironmentMock = jasmine.createSpyObj('applicationEnvironment', ['environment']);
|
||||
mockThemeService = {};
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DevToolsTabsComponent, MockDirectiveExplorerComponent],
|
||||
imports: [MatTooltipModule, MatMenuModule],
|
||||
providers: [
|
||||
TabUpdate,
|
||||
{provide: ThemeService, useFactory: () => ({currentTheme: new Subject<Theme>()})},
|
||||
{provide: MessageBus, useValue: messageBusMock},
|
||||
{provide: ApplicationEnvironment, useValue: applicationEnvironmentMock},
|
||||
],
|
||||
});
|
||||
|
||||
comp = new DevToolsTabsComponent(
|
||||
new TabUpdate(), mockThemeService as any, messageBusMock, applicationEnvironmentMock);
|
||||
const fixture = TestBed.createComponent(DevToolsTabsComponent);
|
||||
comp = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should create instance from class', () => {
|
||||
|
|
|
|||
|
|
@ -50,14 +50,14 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy {
|
|||
@Input() showCommentNodes = false;
|
||||
@Output() toggleInspector = new EventEmitter<void>();
|
||||
|
||||
@ViewChild(DirectiveForestComponent) directiveForest: DirectiveForestComponent;
|
||||
@ViewChild(BreadcrumbsComponent) breadcrumbs: BreadcrumbsComponent;
|
||||
@ViewChild(SplitComponent, {static: true, read: ElementRef}) splitElementRef: ElementRef;
|
||||
@ViewChild(DirectiveForestComponent) directiveForest!: DirectiveForestComponent;
|
||||
@ViewChild(BreadcrumbsComponent) breadcrumbs!: BreadcrumbsComponent;
|
||||
@ViewChild(SplitComponent, {static: true, read: ElementRef}) splitElementRef!: ElementRef;
|
||||
@ViewChild('directiveForestSplitArea', {static: true, read: ElementRef})
|
||||
directiveForestSplitArea: ElementRef;
|
||||
directiveForestSplitArea!: ElementRef;
|
||||
|
||||
currentSelectedElement: IndexedNode|null = null;
|
||||
forest: DevToolsNode[];
|
||||
forest!: DevToolsNode[];
|
||||
splitDirection: 'horizontal'|'vertical' = 'horizontal';
|
||||
parents: FlatNode[]|null = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ ng_module(
|
|||
"//devtools/projects/protocol",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"//packages/core/rxjs-interop",
|
||||
"@npm//@angular/cdk",
|
||||
"@npm//@angular/material",
|
||||
"@npm//@types",
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ import {FlatNode} from '../component-data-source';
|
|||
styleUrls: ['./breadcrumbs.component.scss'],
|
||||
})
|
||||
export class BreadcrumbsComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
@Input() parents: FlatNode[];
|
||||
@Input({required: true}) parents!: FlatNode[];
|
||||
@Output() handleSelect = new EventEmitter();
|
||||
@Output() mouseOverNode = new EventEmitter();
|
||||
@Output() mouseLeaveNode = new EventEmitter();
|
||||
|
||||
@ViewChild('breadcrumbs') breadcrumbsScrollContent: ElementRef;
|
||||
@ViewChild('breadcrumbs') breadcrumbsScrollContent!: ElementRef;
|
||||
|
||||
showScrollLeftButton = false;
|
||||
showScrollRightButton = false;
|
||||
|
|
|
|||
|
|
@ -94,8 +94,10 @@ export class ComponentDataSource extends DataSource<FlatNode> {
|
|||
this._nodeToFlat.set(node, flatNode);
|
||||
return flatNode;
|
||||
},
|
||||
(node) => (node ? node.level : -1), (node) => (node ? node.expandable : false),
|
||||
(node) => (node ? node.children : []));
|
||||
(node) => (node ? node.level : -1),
|
||||
(node) => (node ? node.expandable : false),
|
||||
(node) => (node ? node.children : []),
|
||||
);
|
||||
|
||||
constructor(private _treeControl: FlatTreeControl<FlatNode>) {
|
||||
super();
|
||||
|
|
@ -113,8 +115,10 @@ export class ComponentDataSource extends DataSource<FlatNode> {
|
|||
return this._nodeToFlat.get(indexedNode);
|
||||
}
|
||||
|
||||
update(forest: DevToolsNode[], showCommentNodes: boolean):
|
||||
{newItems: FlatNode[]; movedItems: FlatNode[]; removedItems: FlatNode[]} {
|
||||
update(
|
||||
forest: DevToolsNode[],
|
||||
showCommentNodes: boolean,
|
||||
): {newItems: FlatNode[]; movedItems: FlatNode[]; removedItems: FlatNode[]} {
|
||||
if (!forest) {
|
||||
return {newItems: [], movedItems: [], removedItems: []};
|
||||
}
|
||||
|
|
@ -141,13 +145,16 @@ export class ComponentDataSource extends DataSource<FlatNode> {
|
|||
|
||||
this.data.forEach((i) => (i.newItem = false));
|
||||
|
||||
const expandedNodes = {};
|
||||
const expandedNodes: Record<string, boolean> = {};
|
||||
this.data.forEach((item) => {
|
||||
expandedNodes[item.id] = this._treeControl.isExpanded(item);
|
||||
});
|
||||
|
||||
const {newItems, movedItems, removedItems} =
|
||||
diff<FlatNode>(this._differ, this.data, flattenedCollection);
|
||||
const {newItems, movedItems, removedItems} = diff<FlatNode>(
|
||||
this._differ,
|
||||
this.data,
|
||||
flattenedCollection,
|
||||
);
|
||||
this._treeControl.dataNodes = this.data;
|
||||
this._flattenedData.next(this.data);
|
||||
|
||||
|
|
@ -165,13 +172,22 @@ export class ComponentDataSource extends DataSource<FlatNode> {
|
|||
|
||||
override connect(collectionViewer: CollectionViewer): Observable<FlatNode[]> {
|
||||
const changes = [
|
||||
collectionViewer.viewChange, this._treeControl.expansionModel.changed, this._flattenedData
|
||||
collectionViewer.viewChange,
|
||||
this._treeControl.expansionModel.changed,
|
||||
this._flattenedData,
|
||||
];
|
||||
return merge(...changes).pipe(map(() => {
|
||||
this._expandedData.next(
|
||||
this._treeFlattener.expandFlattenedNodes(this.data, this._treeControl) as FlatNode[]);
|
||||
return this._expandedData.value;
|
||||
}));
|
||||
return merge(...changes)
|
||||
.pipe(
|
||||
map(() => {
|
||||
this._expandedData.next(
|
||||
this._treeFlattener.expandFlattenedNodes(
|
||||
this.data,
|
||||
this._treeControl as FlatTreeControl<FlatNode|undefined>,
|
||||
) as FlatNode[],
|
||||
);
|
||||
return this._expandedData.value;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
override disconnect(): void {}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
|
||||
import {FlatTreeControl} from '@angular/cdk/tree';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output, ViewChild,} from '@angular/core';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {DevToolsNode, ElementPosition, Events, MessageBus} from 'protocol';
|
||||
import {Subscription} from 'rxjs';
|
||||
|
||||
import {TabUpdate} from '../../tab-update/index';
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ import {IndexedNode} from './index-forest';
|
|||
styleUrls: ['./directive-forest.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DirectiveForestComponent implements OnInit, OnDestroy {
|
||||
export class DirectiveForestComponent {
|
||||
@Input()
|
||||
set forest(forest: DevToolsNode[]) {
|
||||
this._latestForest = forest;
|
||||
|
|
@ -35,7 +35,7 @@ export class DirectiveForestComponent implements OnInit, OnDestroy {
|
|||
this._reselectNodeOnUpdate();
|
||||
}
|
||||
}
|
||||
@Input() currentSelectedElement: IndexedNode;
|
||||
@Input({required: true}) currentSelectedElement!: IndexedNode;
|
||||
@Input()
|
||||
set showCommentNodes(show: boolean) {
|
||||
this._showCommentNodes = show;
|
||||
|
|
@ -49,18 +49,17 @@ export class DirectiveForestComponent implements OnInit, OnDestroy {
|
|||
@Output() removeComponentHighlight = new EventEmitter<void>();
|
||||
@Output() toggleInspector = new EventEmitter<void>();
|
||||
|
||||
@ViewChild(CdkVirtualScrollViewport) viewport: CdkVirtualScrollViewport;
|
||||
@ViewChild(CdkVirtualScrollViewport) viewport!: CdkVirtualScrollViewport;
|
||||
|
||||
filterRegex = new RegExp('.^');
|
||||
currentlyMatchedIndex = -1;
|
||||
|
||||
selectedNode: FlatNode|null = null;
|
||||
parents: FlatNode[];
|
||||
parents!: FlatNode[];
|
||||
|
||||
private _highlightIDinTreeFromElement: number|null = null;
|
||||
private _tabUpdateSubscription: Subscription;
|
||||
private _showCommentNodes = false;
|
||||
private _latestForest: DevToolsNode[];
|
||||
private _latestForest!: DevToolsNode[];
|
||||
|
||||
set highlightIDinTreeFromElement(id: number|null) {
|
||||
this._highlightIDinTreeFromElement = id;
|
||||
|
|
@ -68,7 +67,7 @@ export class DirectiveForestComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
readonly treeControl =
|
||||
new FlatTreeControl<FlatNode>((node) => node.level, (node) => node.expandable);
|
||||
new FlatTreeControl<FlatNode>((node) => node!.level, (node) => node.expandable);
|
||||
readonly dataSource = new ComponentDataSource(this.treeControl);
|
||||
readonly itemHeight = 18;
|
||||
|
||||
|
|
@ -76,11 +75,9 @@ export class DirectiveForestComponent implements OnInit, OnDestroy {
|
|||
|
||||
constructor(
|
||||
private _tabUpdate: TabUpdate, private _messageBus: MessageBus<Events>,
|
||||
private _cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
private _cdr: ChangeDetectorRef) {
|
||||
this.subscribeToInspectorEvents();
|
||||
this._tabUpdateSubscription = this._tabUpdate.tabUpdate$.subscribe(() => {
|
||||
this._tabUpdate.tabUpdate$.pipe(takeUntilDestroyed()).subscribe(() => {
|
||||
if (this.viewport) {
|
||||
setTimeout(() => {
|
||||
this.viewport.scrollToIndex(0);
|
||||
|
|
@ -90,12 +87,6 @@ export class DirectiveForestComponent implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this._tabUpdateSubscription) {
|
||||
this._tabUpdateSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
subscribeToInspectorEvents(): void {
|
||||
this._messageBus.on('selectComponent', (id: number) => {
|
||||
this.selectNodeByComponentId(id);
|
||||
|
|
@ -189,7 +180,7 @@ export class DirectiveForestComponent implements OnInit, OnDestroy {
|
|||
return result;
|
||||
}
|
||||
|
||||
populateParents(position: ElementPosition): void {
|
||||
private populateParents(position: ElementPosition): void {
|
||||
this.parents = [];
|
||||
for (let i = 1; i <= position.length; i++) {
|
||||
const current = position.slice(0, i);
|
||||
|
|
|
|||
|
|
@ -12,13 +12,15 @@ import {Property} from './element-property-resolver';
|
|||
|
||||
export const arrayifyProps =
|
||||
(props: {[prop: string]: Descriptor}|Descriptor[], parent: Property|null = null): Property[] =>
|
||||
Object.keys(props).map((name) => ({name, descriptor: props[name], parent})).sort((a, b) => {
|
||||
const parsedA = parseInt(a.name, 10);
|
||||
const parsedB = parseInt(b.name, 10);
|
||||
Object.entries(props)
|
||||
.map(([name, val]) => ({name, descriptor: val, parent}))
|
||||
.sort((a, b) => {
|
||||
const parsedA = parseInt(a.name, 10);
|
||||
const parsedB = parseInt(b.name, 10);
|
||||
|
||||
if (isNaN(parsedA) || isNaN(parsedB)) {
|
||||
return a.name > b.name ? 1 : -1;
|
||||
}
|
||||
if (isNaN(parsedA) || isNaN(parsedB)) {
|
||||
return a.name > b.name ? 1 : -1;
|
||||
}
|
||||
|
||||
return parsedA - parsedB;
|
||||
});
|
||||
return parsedA - parsedB;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -52,7 +52,11 @@ export class DirectivePropertyResolver {
|
|||
constructor(
|
||||
private _messageBus: MessageBus<Events>, private _props: Properties,
|
||||
private _directivePosition: DirectivePosition) {
|
||||
this._initDataSources();
|
||||
const {inputProps, outputProps, stateProps} = this._classifyProperties();
|
||||
|
||||
this._inputsDataSource = this._createDataSourceFromProps(inputProps);
|
||||
this._outputsDataSource = this._createDataSourceFromProps(outputProps);
|
||||
this._stateDataSource = this._createDataSourceFromProps(stateProps);
|
||||
}
|
||||
|
||||
get directiveInputControls(): DirectiveTreeData {
|
||||
|
|
@ -111,14 +115,6 @@ export class DirectivePropertyResolver {
|
|||
node.prop.descriptor.value = newValue;
|
||||
}
|
||||
|
||||
private _initDataSources(): void {
|
||||
const {inputProps, outputProps, stateProps} = this._classifyProperties();
|
||||
|
||||
this._inputsDataSource = this._createDataSourceFromProps(inputProps);
|
||||
this._outputsDataSource = this._createDataSourceFromProps(outputProps);
|
||||
this._stateDataSource = this._createDataSourceFromProps(stateProps);
|
||||
}
|
||||
|
||||
private _createDataSourceFromProps(props: {[name: string]: Descriptor}): PropertyDataSource {
|
||||
return new PropertyDataSource(
|
||||
props, this._treeFlattener, this._treeControl, this._directivePosition, this._messageBus);
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ import {FlatNode} from './element-property-resolver';
|
|||
export const getExpandedDirectiveProperties = (data: FlatNode[]): NestedProp[] => {
|
||||
const getChildren = (prop: Descriptor) => {
|
||||
if ((prop.type === PropType.Object || prop.type === PropType.Array) && prop.value) {
|
||||
return Object.keys(prop.value).map(k => {
|
||||
return Object.entries(prop.value).map(([k, v]: [
|
||||
string, any
|
||||
]): {name: number|string, children: NestedProp[]} => {
|
||||
return {
|
||||
name: prop.type === PropType.Array ? parseInt(k, 10) : k,
|
||||
children: getChildren(prop.value[k]),
|
||||
children: getChildren(v),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
@ -24,7 +26,7 @@ export const getExpandedDirectiveProperties = (data: FlatNode[]): NestedProp[] =
|
|||
};
|
||||
|
||||
const getExpandedProperties = (props: {[name: string]: Descriptor}) => {
|
||||
return Object.keys(props).map(name => {
|
||||
return Object.keys(props).map((name) => {
|
||||
return {
|
||||
name,
|
||||
children: getChildren(props[name]),
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {ElementPropertyResolver} from '../property-resolver/element-property-res
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ComponentMetadataComponent {
|
||||
@Input() currentSelectedComponent: ComponentType;
|
||||
@Input({required: true}) currentSelectedComponent!: ComponentType;
|
||||
|
||||
constructor(private _nestedProps: ElementPropertyResolver) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ import {IndexedNode} from '../directive-forest/index-forest';
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PropertyTabHeaderComponent {
|
||||
@Input() currentSelectedElement: IndexedNode;
|
||||
@Input({required: true}) currentSelectedElement!: IndexedNode;
|
||||
@Input() currentDirectives: string[]|undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {FlatNode} from '../property-resolver/element-property-resolver';
|
|||
selector: 'ng-property-tab',
|
||||
})
|
||||
export class PropertyTabComponent {
|
||||
@Input() currentSelectedElement: IndexedNode;
|
||||
@Input({required: true}) currentSelectedElement!: IndexedNode;
|
||||
@Output() viewSource = new EventEmitter<string>();
|
||||
@Output() inspect = new EventEmitter<{node: FlatNode; directivePosition: DirectivePosition}>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ const parseValue = (value: EditorResult): EditorResult => {
|
|||
styleUrls: ['./property-editor.component.scss'],
|
||||
})
|
||||
export class PropertyEditorComponent implements AfterViewChecked, OnInit {
|
||||
@Input() key: string;
|
||||
@Input() initialValue: EditorResult;
|
||||
@Input({required: true}) key!: string;
|
||||
@Input({required: true}) initialValue!: EditorResult;
|
||||
@Output() updateValue = new EventEmitter<EditorResult>();
|
||||
|
||||
readState = PropertyEditorState.Read;
|
||||
writeState = PropertyEditorState.Write;
|
||||
|
||||
valueToSubmit: EditorResult;
|
||||
valueToSubmit!: EditorResult;
|
||||
currentPropertyState = this.readState;
|
||||
|
||||
constructor(private _cd: ChangeDetectorRef, private _elementRef: ElementRef) {}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {FlatNode} from '../../property-resolver/element-property-resolver';
|
|||
styleUrls: ['./property-preview.component.scss'],
|
||||
})
|
||||
export class PropertyPreviewComponent {
|
||||
@Input() node: FlatNode;
|
||||
@Input({required: true}) node!: FlatNode;
|
||||
@Output() inspect = new EventEmitter<void>();
|
||||
|
||||
get isClickableProp(): boolean {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import {FlatNode} from '../../property-resolver/element-property-resolver';
|
|||
styleUrls: ['./property-tab-body.component.scss'],
|
||||
})
|
||||
export class PropertyTabBodyComponent {
|
||||
@Input() currentSelectedElement: IndexedNode|null;
|
||||
@Input({required: true}) currentSelectedElement!: IndexedNode|null;
|
||||
@Output() inspect = new EventEmitter<{node: FlatNode; directivePosition: DirectivePosition}>();
|
||||
@Output() viewSource = new EventEmitter<string>();
|
||||
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ import {FlatNode} from '../../property-resolver/element-property-resolver';
|
|||
styleUrls: ['./property-view-body.component.scss'],
|
||||
})
|
||||
export class PropertyViewBodyComponent {
|
||||
@Input() controller: DirectivePropertyResolver;
|
||||
@Input() directiveInputControls: DirectiveTreeData;
|
||||
@Input() directiveOutputControls: DirectiveTreeData;
|
||||
@Input() directiveStateControls: DirectiveTreeData;
|
||||
@Input({required: true}) controller!: DirectivePropertyResolver;
|
||||
@Input({required: true}) directiveInputControls!: DirectiveTreeData;
|
||||
@Input({required: true}) directiveOutputControls!: DirectiveTreeData;
|
||||
@Input({required: true}) directiveStateControls!: DirectiveTreeData;
|
||||
|
||||
@Output() inspect = new EventEmitter<{node: FlatNode; directivePosition: DirectivePosition}>();
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ export class PropertyViewBodyComponent {
|
|||
`]
|
||||
})
|
||||
export class DependencyViewerComponent {
|
||||
@Input() dependency: SerializedInjectedService;
|
||||
@Input({required: true}) dependency!: SerializedInjectedService;
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
|
@ -150,13 +150,14 @@ export class DependencyViewerComponent {
|
|||
`]
|
||||
})
|
||||
export class InjectedServicesComponent {
|
||||
@Input() controller: DirectivePropertyResolver;
|
||||
@Input({required: true}) controller!: DirectivePropertyResolver;
|
||||
|
||||
get dependencies(): SerializedInjectedService[] {
|
||||
return this.controller.directiveMetadata?.dependencies ?? [];
|
||||
}
|
||||
|
||||
dependencyPosition(_index, dependency: SerializedInjectedService) {
|
||||
// TODO: remove when migrating to @for block
|
||||
dependencyPosition(_index: any, dependency: SerializedInjectedService) {
|
||||
return dependency.position[0];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
|
|||
styleUrls: ['./property-view-header.component.scss'],
|
||||
})
|
||||
export class PropertyViewHeaderComponent {
|
||||
@Input() directive: string;
|
||||
@Input({required: true}) directive!: string;
|
||||
@Output() viewSource = new EventEmitter<void>();
|
||||
|
||||
// output that emits directive
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import {PropertyDataSource} from '../../property-resolver/property-data-source';
|
|||
styleUrls: ['./property-view-tree.component.scss'],
|
||||
})
|
||||
export class PropertyViewTreeComponent {
|
||||
@Input() dataSource: PropertyDataSource;
|
||||
@Input() treeControl: FlatTreeControl<FlatNode>;
|
||||
@Input({required: true}) dataSource!: PropertyDataSource;
|
||||
@Input({required: true}) treeControl!: FlatTreeControl<FlatNode>;
|
||||
@Output() updateValue = new EventEmitter<any>();
|
||||
@Output() inspect = new EventEmitter<any>();
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import {ElementPropertyResolver, FlatNode} from '../../property-resolver/element
|
|||
styleUrls: ['./property-view.component.scss'],
|
||||
})
|
||||
export class PropertyViewComponent {
|
||||
@Input() directive: string;
|
||||
@Input({required: true}) directive!: string;
|
||||
@Output() inspect = new EventEmitter<{node: FlatNode; directivePosition: DirectivePosition}>();
|
||||
@Output() viewSource = new EventEmitter<void>();
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import {Events, MessageBus, SerializedInjector, SerializedProviderRecord} from '
|
|||
>
|
||||
<mat-option>None</mat-option>
|
||||
@for (type of providerTypes; track type) {
|
||||
<mat-option [value]="type">{{providerTypeToLabel[type]}}</mat-option>
|
||||
<mat-option [value]="type">{{$any(providerTypeToLabel)[type]}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
|
@ -58,7 +58,7 @@ import {Events, MessageBus, SerializedInjector, SerializedProviderRecord} from '
|
|||
@if (provider.type === 'multi') {
|
||||
multi (x{{ provider.index.length }})
|
||||
} @else {
|
||||
{{providerTypeToLabel[provider.type]}}
|
||||
{{$any(providerTypeToLabel)[provider.type]}}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
|
@ -147,7 +147,7 @@ import {Events, MessageBus, SerializedInjector, SerializedProviderRecord} from '
|
|||
],
|
||||
})
|
||||
export class InjectorProvidersComponent {
|
||||
@Input() injector: SerializedInjector;
|
||||
@Input({required: true}) injector!: SerializedInjector;
|
||||
@Input() providers: SerializedProviderRecord[] = [];
|
||||
|
||||
searchToken = signal('');
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ describe('getInjectorIdsToRootFromNode', () => {
|
|||
};
|
||||
|
||||
const child = {
|
||||
parent: root,
|
||||
data: {
|
||||
injector: {
|
||||
id: '2',
|
||||
|
|
@ -33,6 +34,7 @@ describe('getInjectorIdsToRootFromNode', () => {
|
|||
};
|
||||
|
||||
const grandChild = {
|
||||
parent: child,
|
||||
data: {
|
||||
injector: {
|
||||
id: '3',
|
||||
|
|
@ -42,9 +44,6 @@ describe('getInjectorIdsToRootFromNode', () => {
|
|||
},
|
||||
};
|
||||
|
||||
grandChild['parent'] = child;
|
||||
child['parent'] = root;
|
||||
|
||||
expect(getInjectorIdsToRootFromNode(root as InjectorTreeD3Node)).toEqual(['1']);
|
||||
expect(getInjectorIdsToRootFromNode(child as InjectorTreeD3Node)).toEqual(['2', '1']);
|
||||
expect(getInjectorIdsToRootFromNode(grandChild as InjectorTreeD3Node)).toEqual(['3', '2', '1']);
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ export function grabInjectorPathsFromDirectiveForest(directiveForest: DevToolsNo
|
|||
InjectorPath[] {
|
||||
const injectorPaths: InjectorPath[] = [];
|
||||
|
||||
const grabInjectorPaths = (node) => {
|
||||
const grabInjectorPaths = (node: DevToolsNode) => {
|
||||
if (node.resolutionPath) {
|
||||
injectorPaths.push({node, path: node.resolutionPath.slice().reverse()});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@ import {filterOutAngularInjectors, filterOutInjectorsWithNoProviders, generateEd
|
|||
styleUrls: ['./injector-tree.component.scss']
|
||||
})
|
||||
export class InjectorTreeComponent {
|
||||
@ViewChild('svgContainer', {static: false}) private svgContainer: ElementRef;
|
||||
@ViewChild('mainGroup', {static: false}) private g: ElementRef;
|
||||
@ViewChild('svgContainer', {static: false}) private svgContainer!: ElementRef;
|
||||
@ViewChild('mainGroup', {static: false}) private g!: ElementRef;
|
||||
|
||||
@ViewChild('elementSvgContainer', {static: false}) private elementSvgContainer: ElementRef;
|
||||
@ViewChild('elementMainGroup', {static: false}) private elementG: ElementRef;
|
||||
@ViewChild('elementSvgContainer', {static: false}) private elementSvgContainer!: ElementRef;
|
||||
@ViewChild('elementMainGroup', {static: false}) private elementG!: ElementRef;
|
||||
|
||||
private _messageBus = inject(MessageBus) as MessageBus<Events>;
|
||||
zone = inject(NgZone);
|
||||
|
|
@ -48,8 +48,8 @@ export class InjectorTreeComponent {
|
|||
firstRender = true;
|
||||
selectedNode: InjectorTreeD3Node|null = null;
|
||||
rawDirectiveForest: DevToolsNode[] = [];
|
||||
injectorTreeGraph: InjectorTreeVisualizer;
|
||||
elementInjectorTreeGraph: InjectorTreeVisualizer;
|
||||
injectorTreeGraph!: InjectorTreeVisualizer;
|
||||
elementInjectorTreeGraph!: InjectorTreeVisualizer;
|
||||
diDebugAPIsAvailable = false;
|
||||
providers: SerializedProviderRecord[] = [];
|
||||
elementToEnvironmentPath: Map<string, SerializedInjector[]> = new Map();
|
||||
|
|
|
|||
|
|
@ -24,18 +24,54 @@ const PROFILER_VERSION = 1;
|
|||
templateUrl: './profiler.component.html',
|
||||
styleUrls: ['./profiler.component.scss'],
|
||||
})
|
||||
export class ProfilerComponent implements OnInit, OnDestroy {
|
||||
export class ProfilerComponent implements OnInit {
|
||||
state: State = 'idle';
|
||||
stream = new Subject<ProfilerFrame[]>();
|
||||
|
||||
private _fileUploadSubscription: Subscription;
|
||||
|
||||
// We collect this buffer so we can have it available for export.
|
||||
private _buffer: ProfilerFrame[] = [];
|
||||
|
||||
constructor(
|
||||
private _fileApiService: FileApiService, private _messageBus: MessageBus<Events>,
|
||||
public dialog: MatDialog) {}
|
||||
private _fileApiService: FileApiService,
|
||||
private _messageBus: MessageBus<Events>,
|
||||
public dialog: MatDialog,
|
||||
) {
|
||||
this._fileApiService.uploadedData.subscribe((importedFile) => {
|
||||
if (importedFile.error) {
|
||||
console.error('Could not process uploaded file');
|
||||
console.error(importedFile.error);
|
||||
this.dialog.open(ProfilerImportDialogComponent, {
|
||||
width: '600px',
|
||||
data: {status: 'ERROR', errorMessage: importedFile.error},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SUPPORTED_VERSIONS.includes(importedFile.version)) {
|
||||
const processDataDialog = this.dialog.open(ProfilerImportDialogComponent, {
|
||||
width: '600px',
|
||||
data: {
|
||||
importedVersion: importedFile.version,
|
||||
profilerVersion: PROFILER_VERSION,
|
||||
status: 'INVALID_VERSION',
|
||||
},
|
||||
});
|
||||
|
||||
processDataDialog.afterClosed().subscribe((result) => {
|
||||
if (result) {
|
||||
this.state = 'visualizing';
|
||||
this._buffer = importedFile.buffer;
|
||||
setTimeout(() => this.stream.next(importedFile.buffer));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.state = 'visualizing';
|
||||
this._buffer = importedFile.buffer;
|
||||
setTimeout(() => this.stream.next(importedFile.buffer));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startRecording(): void {
|
||||
this.state = 'recording';
|
||||
|
|
@ -60,46 +96,6 @@ export class ProfilerComponent implements OnInit, OnDestroy {
|
|||
this.stream.next([chunkOfRecords]);
|
||||
this._buffer.push(chunkOfRecords);
|
||||
});
|
||||
|
||||
this._fileUploadSubscription = this._fileApiService.uploadedData.subscribe((importedFile) => {
|
||||
if (importedFile.error) {
|
||||
console.error('Could not process uploaded file');
|
||||
console.error(importedFile.error);
|
||||
this.dialog.open(ProfilerImportDialogComponent, {
|
||||
width: '600px',
|
||||
data: {status: 'ERROR', errorMessage: importedFile.error},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SUPPORTED_VERSIONS.includes(importedFile.version)) {
|
||||
const processDataDialog = this.dialog.open(ProfilerImportDialogComponent, {
|
||||
width: '600px',
|
||||
data: {
|
||||
importedVersion: importedFile.version,
|
||||
profilerVersion: PROFILER_VERSION,
|
||||
status: 'INVALID_VERSION'
|
||||
},
|
||||
});
|
||||
|
||||
processDataDialog.afterClosed().subscribe((result) => {
|
||||
if (result) {
|
||||
this.state = 'visualizing';
|
||||
this._buffer = importedFile.buffer;
|
||||
setTimeout(() => this.stream.next(importedFile.buffer));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.state = 'visualizing';
|
||||
this._buffer = importedFile.buffer;
|
||||
setTimeout(() => this.stream.next(importedFile.buffer));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._fileUploadSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
exportProfilerResults(): void {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
*/
|
||||
|
||||
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
|
||||
import {Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild} from '@angular/core';
|
||||
import {Component, DestroyRef, ElementRef, EventEmitter, Input, Output, ViewChild,} from '@angular/core';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
|
||||
import {TabUpdate} from '../../tab-update/index';
|
||||
|
|
@ -21,16 +22,17 @@ const ITEM_WIDTH = 30;
|
|||
templateUrl: './frame-selector.component.html',
|
||||
styleUrls: ['./frame-selector.component.scss'],
|
||||
})
|
||||
export class FrameSelectorComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('barContainer') barContainer: ElementRef;
|
||||
export class FrameSelectorComponent {
|
||||
@ViewChild('barContainer') barContainer!: ElementRef;
|
||||
@Input()
|
||||
set graphData$(graphData: Observable<GraphNode[]>) {
|
||||
this._graphData$ = graphData;
|
||||
this._graphDataSubscription =
|
||||
this._graphData$.subscribe((items) => setTimeout(() => {
|
||||
this.frameCount = items.length;
|
||||
this.viewport.scrollToIndex(items.length);
|
||||
}));
|
||||
this._graphDataSubscription?.unsubscribe();
|
||||
this._graphDataSubscription = this._graphData$.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe((items) => setTimeout(() => {
|
||||
this.frameCount = items.length;
|
||||
this.viewport.scrollToIndex(items.length);
|
||||
}));
|
||||
}
|
||||
|
||||
get graphData$(): Observable<GraphNode[]> {
|
||||
|
|
@ -39,12 +41,12 @@ export class FrameSelectorComponent implements OnInit, OnDestroy {
|
|||
|
||||
@Output() selectFrames = new EventEmitter<{indexes: number[]}>();
|
||||
|
||||
@ViewChild(CdkVirtualScrollViewport) viewport: CdkVirtualScrollViewport;
|
||||
@ViewChild(CdkVirtualScrollViewport) viewport!: CdkVirtualScrollViewport;
|
||||
|
||||
startFrameIndex = -1;
|
||||
endFrameIndex = -1;
|
||||
selectedFrameIndexes = new Set<number>();
|
||||
frameCount: number;
|
||||
frameCount!: number;
|
||||
|
||||
private _viewportScrollState = {scrollLeft: 0, xCoordinate: 0, isDragScrolling: false};
|
||||
|
||||
|
|
@ -52,14 +54,15 @@ export class FrameSelectorComponent implements OnInit, OnDestroy {
|
|||
return ITEM_WIDTH;
|
||||
}
|
||||
|
||||
private _graphData$: Observable<GraphNode[]>;
|
||||
private _graphDataSubscription: Subscription;
|
||||
private _tabUpdateSubscription: Subscription;
|
||||
private _graphData$!: Observable<GraphNode[]>;
|
||||
private _graphDataSubscription?: Subscription;
|
||||
|
||||
constructor(private _tabUpdate: TabUpdate) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._tabUpdateSubscription = this._tabUpdate.tabUpdate$.subscribe(() => {
|
||||
constructor(
|
||||
private _tabUpdate: TabUpdate,
|
||||
private destroyRef: DestroyRef,
|
||||
) {
|
||||
this._tabUpdate.tabUpdate$.pipe(takeUntilDestroyed()).subscribe(() => {
|
||||
if (this.viewport) {
|
||||
setTimeout(() => {
|
||||
this.viewport.scrollToIndex(0);
|
||||
|
|
@ -69,15 +72,6 @@ export class FrameSelectorComponent implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this._tabUpdateSubscription) {
|
||||
this._tabUpdateSubscription.unsubscribe();
|
||||
}
|
||||
if (this._graphDataSubscription) {
|
||||
this._graphDataSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
get selectionLabel(): string {
|
||||
if (this.startFrameIndex === this.endFrameIndex) {
|
||||
return `${this.startFrameIndex + 1}`;
|
||||
|
|
@ -112,9 +106,9 @@ export class FrameSelectorComponent implements OnInit, OnDestroy {
|
|||
|
||||
return groups.filter((group) => group.length > 0)
|
||||
.map(
|
||||
(group) =>
|
||||
(group.length === 1 ? group[0] + 1 :
|
||||
`${group[0] + 1}-${group[group.length - 1] + 1}`))
|
||||
(group) => group.length === 1 ? group[0] + 1 :
|
||||
`${group[0] + 1}-${group[group.length - 1] + 1}`,
|
||||
)
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
|
|
@ -139,8 +133,9 @@ export class FrameSelectorComponent implements OnInit, OnDestroy {
|
|||
|
||||
if (shiftKey) {
|
||||
const [start, end] = [Math.min(this.startFrameIndex, idx), Math.max(this.endFrameIndex, idx)];
|
||||
this.selectedFrameIndexes =
|
||||
new Set(Array.from(Array(end - start + 1), (_, index) => index + start));
|
||||
this.selectedFrameIndexes = new Set(
|
||||
Array.from(Array(end - start + 1), (_, index) => index + start),
|
||||
);
|
||||
} else if (ctrlKey || metaKey) {
|
||||
if (this.selectedFrameIndexes.has(idx)) {
|
||||
if (this.selectedFrameIndexes.size === 1) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {DirectiveProfile, ElementProfile, ProfilerFrame} from 'protocol';
|
||||
import {DirectiveProfile, ElementProfile, LifecycleProfile, ProfilerFrame} from 'protocol';
|
||||
|
||||
const mergeProperty = (mergeInProp: number|undefined, value: number|undefined) => {
|
||||
if (mergeInProp === undefined) {
|
||||
|
|
@ -20,7 +20,8 @@ const mergeProperty = (mergeInProp: number|undefined, value: number|undefined) =
|
|||
|
||||
const mergeDirective = (mergeIn: DirectiveProfile, second: DirectiveProfile) => {
|
||||
mergeIn.changeDetection = mergeProperty(mergeIn.changeDetection, second.changeDetection);
|
||||
Object.keys(mergeIn.lifecycle).forEach((hook) => {
|
||||
Object.keys(mergeIn.lifecycle).forEach((key) => {
|
||||
const hook = key as keyof LifecycleProfile;
|
||||
mergeIn.lifecycle[hook] = mergeProperty(mergeIn.lifecycle[hook], second.lifecycle[hook]);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ElementProfile, ProfilerFrame} from 'protocol';
|
||||
import {DirectiveProfile, ElementProfile, ProfilerFrame} from 'protocol';
|
||||
|
||||
import {RecordFormatter} from './record-formatter';
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ describe('getLabel cases', () => {
|
|||
});
|
||||
|
||||
describe('getDirectiveValue cases', () => {
|
||||
let directive;
|
||||
let directive!: DirectiveProfile;
|
||||
|
||||
it('calculates value with no lifecycle hooks', () => {
|
||||
directive = {
|
||||
|
|
@ -193,7 +193,8 @@ describe('getDirectiveValue cases', () => {
|
|||
isElement: false,
|
||||
isComponent: true,
|
||||
lifecycle: {},
|
||||
name: 'AppComponent'
|
||||
name: 'AppComponent',
|
||||
outputs: {},
|
||||
};
|
||||
expect(formatter.getDirectiveValue(directive)).toBe(10);
|
||||
});
|
||||
|
|
@ -205,6 +206,7 @@ describe('getDirectiveValue cases', () => {
|
|||
name: 'NgForOf',
|
||||
lifecycle: {ngDoCheck: 5},
|
||||
changeDetection: 0,
|
||||
outputs: {},
|
||||
};
|
||||
expect(formatter.getDirectiveValue(directive)).toBe(5);
|
||||
});
|
||||
|
|
@ -216,6 +218,7 @@ describe('getDirectiveValue cases', () => {
|
|||
name: 'NgForOf',
|
||||
lifecycle: {ngDoCheck: 5},
|
||||
changeDetection: 10,
|
||||
outputs: {},
|
||||
};
|
||||
expect(formatter.getDirectiveValue(directive)).toBe(15);
|
||||
});
|
||||
|
|
@ -227,6 +230,7 @@ describe('getDirectiveValue cases', () => {
|
|||
name: 'NgForOf',
|
||||
lifecycle: {ngDoCheck: 5, ngAfterViewInit: 100},
|
||||
changeDetection: 10,
|
||||
outputs: {},
|
||||
};
|
||||
expect(formatter.getDirectiveValue(directive)).toBe(115);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ export abstract class RecordFormatter<T> {
|
|||
current = 0;
|
||||
}
|
||||
result += current;
|
||||
Object.keys(directive.lifecycle).forEach((key) => {
|
||||
const value = parseFloat(directive.lifecycle[key]);
|
||||
Object.values(directive.lifecycle).forEach((lifecycleProfile) => {
|
||||
const value = parseFloat(lifecycleProfile);
|
||||
if (!isNaN(value)) {
|
||||
result += value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ ng_module(
|
|||
"//packages/animations",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"//packages/core/rxjs-interop",
|
||||
"@npm//@angular/material",
|
||||
"@npm//@types",
|
||||
"@npm//ngx-flamegraph",
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ export class BarChartComponent {
|
|||
});
|
||||
}
|
||||
}
|
||||
@Input() color: string;
|
||||
@Input({required: true}) color!: string;
|
||||
@Output() barClick = new EventEmitter<BargraphNode>();
|
||||
|
||||
originalData: BargraphNode[];
|
||||
originalData!: BargraphNode[];
|
||||
internalData: BarData[] = [];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
|
||||
import {Component, EventEmitter, Input, OnDestroy, Output} from '@angular/core';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {ProfilerFrame} from 'protocol';
|
||||
import {Subscription} from 'rxjs';
|
||||
|
||||
import {Theme, ThemeService} from '../../../../theme-service';
|
||||
import {BarGraphFormatter, BargraphNode} from '../record-formatter/bargraph-formatter/index';
|
||||
|
|
@ -21,34 +21,25 @@ import {SelectedDirective, SelectedEntry} from './timeline-visualizer.component'
|
|||
templateUrl: './bargraph-visualizer.component.html',
|
||||
styleUrls: ['./bargraph-visualizer.component.scss'],
|
||||
})
|
||||
export class BargraphVisualizerComponent implements OnInit, OnDestroy {
|
||||
barColor: string;
|
||||
profileRecords: BargraphNode[];
|
||||
export class BargraphVisualizerComponent {
|
||||
barColor!: string;
|
||||
profileRecords!: BargraphNode[];
|
||||
|
||||
@Output() nodeSelect = new EventEmitter<SelectedEntry>();
|
||||
|
||||
private _formatter = new BarGraphFormatter();
|
||||
private _currentThemeSubscription: Subscription;
|
||||
currentTheme: Theme;
|
||||
|
||||
@Input()
|
||||
set frame(data: ProfilerFrame) {
|
||||
this.profileRecords = this._formatter.formatFrame(data);
|
||||
}
|
||||
|
||||
constructor(public themeService: ThemeService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._currentThemeSubscription = this.themeService.currentTheme.subscribe((theme) => {
|
||||
this.currentTheme = theme;
|
||||
constructor(public themeService: ThemeService) {
|
||||
this.themeService.currentTheme.pipe(takeUntilDestroyed()).subscribe((theme) => {
|
||||
this.barColor = theme === 'dark-theme' ? '#073d69' : '#cfe8fc';
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._currentThemeSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
formatEntryData(bargraphNode: BargraphNode): SelectedDirective[] {
|
||||
return formatDirectiveProfile(bargraphNode.directives ?? []);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,5 +16,5 @@ import {SelectedDirective} from './timeline-visualizer.component';
|
|||
styleUrls: ['./execution-details.component.scss'],
|
||||
})
|
||||
export class ExecutionDetailsComponent {
|
||||
@Input() data: SelectedDirective[];
|
||||
@Input({required: true}) data!: SelectedDirective[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ import {SelectedDirective, SelectedEntry} from './timeline-visualizer.component'
|
|||
export class FlamegraphVisualizerComponent implements OnInit, OnDestroy {
|
||||
profilerBars: FlamegraphNode[] = [];
|
||||
view: [number, number] = [235, 200];
|
||||
colors: Color;
|
||||
colors!: Color;
|
||||
|
||||
private _formatter = new FlamegraphFormatter();
|
||||
private _showChangeDetection = false;
|
||||
private _frame: ProfilerFrame;
|
||||
private _currentThemeSubscription: Subscription;
|
||||
currentTheme: Theme;
|
||||
private _frame!: ProfilerFrame;
|
||||
private _currentThemeSubscription!: Subscription;
|
||||
currentTheme!: Theme;
|
||||
|
||||
@Output() nodeSelect = new EventEmitter<SelectedEntry>();
|
||||
|
||||
|
|
|
|||
|
|
@ -37,18 +37,18 @@ export const formatDirectiveProfile = (nodes: DirectiveProfile[]) => {
|
|||
value: parseFloat(changeDetection.toFixed(2)),
|
||||
});
|
||||
}
|
||||
Object.keys(node.lifecycle).forEach((key) => {
|
||||
Object.entries(node.lifecycle).forEach(([key, lifeCycleProfile]) => {
|
||||
graphData.push({
|
||||
directive: node.name,
|
||||
method: key,
|
||||
value: +node.lifecycle[key].toFixed(2),
|
||||
value: +lifeCycleProfile.toFixed(2),
|
||||
});
|
||||
});
|
||||
Object.keys(node.outputs).forEach((key) => {
|
||||
Object.entries(node.outputs).forEach(([key, outputProfile]) => {
|
||||
graphData.push({
|
||||
directive: node.name,
|
||||
method: formatOutput(key),
|
||||
value: +node.outputs[key].toFixed(2),
|
||||
value: +outputProfile.toFixed(2),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ export class TimelineVisualizerComponent {
|
|||
this.selectedDirectives = [];
|
||||
this.parentHierarchy = [];
|
||||
}
|
||||
@Input() frame: ProfilerFrame;
|
||||
@Input() changeDetection: boolean;
|
||||
@Input({required: true}) frame!: ProfilerFrame;
|
||||
@Input({required: true}) changeDetection!: boolean;
|
||||
|
||||
cmpVisualizationModes = VisualizationMode;
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ export class TimelineVisualizerComponent {
|
|||
parentHierarchy: {name: string}[] = [];
|
||||
|
||||
/** @internal */
|
||||
_visualizationMode: VisualizationMode;
|
||||
_visualizationMode!: VisualizationMode;
|
||||
|
||||
handleNodeSelect({entry, parentHierarchy, selectedDirectives}: SelectedEntry): void {
|
||||
this.selectedEntry = entry;
|
||||
|
|
|
|||
|
|
@ -34,13 +34,13 @@ export class TreeMapVisualizerComponent implements OnInit, OnDestroy {
|
|||
constructor(private _ngZone: NgZone) {}
|
||||
|
||||
private resize$ = new Subject<void>();
|
||||
private _throttledResizeSubscription: Subscription;
|
||||
private _throttledResizeSubscription!: Subscription;
|
||||
|
||||
private _resizeObserver: ResizeObserver =
|
||||
new ResizeObserver(() => this._ngZone.run(() => this.resize$.next()));
|
||||
treeMapRecords: TreeMapNode;
|
||||
private treeMapRecords!: TreeMapNode;
|
||||
|
||||
@ViewChild('webTree', {static: true}) tree: ElementRef;
|
||||
@ViewChild('webTree', {static: true}) tree!: ElementRef<HTMLElement>;
|
||||
|
||||
ngOnInit(): void {
|
||||
this._throttledResizeSubscription =
|
||||
|
|
@ -59,7 +59,7 @@ export class TreeMapVisualizerComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
private _removeTree(): void {
|
||||
Array.from(this.tree.nativeElement.children).forEach((child: HTMLElement) => child.remove());
|
||||
Array.from(this.tree.nativeElement.children).forEach((child) => child.remove());
|
||||
}
|
||||
|
||||
private _createTree(): void {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ import {VisualizationMode} from './visualization-mode';
|
|||
})
|
||||
export class TimelineControlsComponent {
|
||||
@Input() record: ProfilerFrame|undefined;
|
||||
@Input() estimatedFrameRate: number;
|
||||
@Input() visualizationMode: VisualizationMode;
|
||||
@Input() empty: boolean;
|
||||
@Input() changeDetection: boolean;
|
||||
@Input({required: true}) estimatedFrameRate!: number;
|
||||
@Input({required: true}) visualizationMode!: VisualizationMode;
|
||||
@Input({required: true}) empty!: boolean;
|
||||
@Input({required: true}) changeDetection!: boolean;
|
||||
@Output() changeVisualizationMode = new EventEmitter<VisualizationMode>();
|
||||
@Output() exportProfile = new EventEmitter<void>();
|
||||
@Output() toggleChangeDetection = new EventEmitter<boolean>();
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export class TimelineComponent implements OnDestroy {
|
|||
|
||||
private _filter: Filter = noopFilter;
|
||||
private _maxDuration = -Infinity;
|
||||
private _subscription: Subscription;
|
||||
private _subscription!: Subscription;
|
||||
private _allRecords: ProfilerFrame[] = [];
|
||||
private _filtered: GraphNode[] = [];
|
||||
private _graphDataSubject = new BehaviorSubject<GraphNode[]>([]);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import {Events, MessageBus, Route} from 'protocol';
|
|||
styleUrls: ['./router-tree.component.scss'],
|
||||
})
|
||||
export class RouterTreeComponent implements AfterViewInit {
|
||||
@ViewChild('svgContainer', {static: true}) private svgContainer: ElementRef;
|
||||
@ViewChild('mainGroup', {static: true}) private g: ElementRef;
|
||||
@ViewChild('svgContainer', {static: true}) private svgContainer!: ElementRef;
|
||||
@ViewChild('mainGroup', {static: true}) private g!: ElementRef;
|
||||
|
||||
@Input()
|
||||
set routes(routes: Route[]) {
|
||||
|
|
@ -26,7 +26,7 @@ export class RouterTreeComponent implements AfterViewInit {
|
|||
}
|
||||
|
||||
private _routes: Route[] = [];
|
||||
private tree: d3.TreeLayout<{}>;
|
||||
private tree!: d3.TreeLayout<{}>;
|
||||
private tooltip: any;
|
||||
|
||||
constructor(private _messageBus: MessageBus<Events>) {}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export class DevToolsComponent implements OnInit, OnDestroy {
|
|||
angularExists: boolean|null = null;
|
||||
angularVersion: string|boolean|undefined = undefined;
|
||||
angularIsInDevMode = true;
|
||||
ivy: boolean;
|
||||
ivy!: boolean;
|
||||
|
||||
private readonly _firefoxStyleName = 'firefox_styles.css';
|
||||
private readonly _chromeStyleName = 'chrome_styles.css';
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ export class SplitComponent implements AfterViewInit, OnDestroy {
|
|||
@Output() gutterClick = new EventEmitter<IOutputData>(false);
|
||||
@Output() gutterDblClick = new EventEmitter<IOutputData>(false);
|
||||
|
||||
private transitionEndSubscriber: Subscriber<IOutputAreaSizes>;
|
||||
private transitionEndSubscriber: Subscriber<IOutputAreaSizes>|null = null;
|
||||
@Output()
|
||||
get transitionEnd(): Observable<IOutputAreaSizes> {
|
||||
return new Observable((subscriber) => (this.transitionEndSubscriber = subscriber))
|
||||
|
|
@ -234,7 +234,7 @@ export class SplitComponent implements AfterViewInit, OnDestroy {
|
|||
public readonly displayedAreas: Array<IArea> = [];
|
||||
private readonly hidedAreas: Array<IArea> = [];
|
||||
|
||||
@ViewChildren('gutterEls') private gutterEls: QueryList<ElementRef>;
|
||||
@ViewChildren('gutterEls') private gutterEls!: QueryList<ElementRef>;
|
||||
|
||||
constructor(
|
||||
private ngZone: NgZone, private elRef: ElementRef, private cdRef: ChangeDetectorRef,
|
||||
|
|
@ -735,7 +735,7 @@ export class SplitComponent implements AfterViewInit, OnDestroy {
|
|||
this.gutterDblClick.emit({gutterNum, sizes});
|
||||
} else if (type === 'transitionEnd') {
|
||||
if (this.transitionEndSubscriber) {
|
||||
this.ngZone.run(() => this.transitionEndSubscriber.next(sizes));
|
||||
this.ngZone.run(() => this.transitionEndSubscriber?.next(sizes));
|
||||
}
|
||||
} else if (type === 'progress') {
|
||||
// Stay outside zone to allow users do what they want about change detection mechanism.
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ export class SplitAreaDirective implements OnInit, OnDestroy {
|
|||
|
||||
////
|
||||
|
||||
private transitionListener: Function;
|
||||
private transitionListener!: Function;
|
||||
private readonly lockListeners: Array<Function> = [];
|
||||
|
||||
constructor(
|
||||
|
|
|
|||
|
|
@ -57,18 +57,16 @@ export class PriorityAwareMessageBus extends MessageBus<Events> {
|
|||
}
|
||||
|
||||
override on<E extends Topic>(topic: E, cb: Events[E]): void {
|
||||
const self = this;
|
||||
return this._bus.on(topic, function(): void {
|
||||
cb.apply(this, arguments);
|
||||
self._afterMessage(topic);
|
||||
return this._bus.on(topic, (...args: any) => {
|
||||
(cb as any)(...args);
|
||||
this._afterMessage(topic);
|
||||
});
|
||||
}
|
||||
|
||||
override once<E extends Topic>(topic: E, cb: Events[E]): void {
|
||||
const self = this;
|
||||
return this._bus.once(topic, function(): void {
|
||||
cb.apply(this, arguments);
|
||||
self._afterMessage(topic);
|
||||
return this._bus.once(topic, (...args: any) => {
|
||||
(cb as any)(...args);
|
||||
this._afterMessage(topic);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
import {arrayEquals} from 'shared-utils';
|
||||
|
||||
describe('arrayEquals', () => {
|
||||
let a;
|
||||
let b;
|
||||
let a: any;
|
||||
let b: any;
|
||||
|
||||
describe('true cases', () => {
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const ports: {
|
|||
|
||||
chrome.runtime.onConnect.addListener((port) => {
|
||||
let tab: string|null = null;
|
||||
let name: string|null = null;
|
||||
let name: 'devtools'|'content-script'|null = null;
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log('Connection event in the background script');
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ const installContentScript = (tabId: number) => {
|
|||
if (isManifestV3) {
|
||||
chrome.scripting.executeScript(
|
||||
{files: ['app/content_script_bundle.js'], target: {tabId}}, () => {
|
||||
chrome.scripting.executeScript({func: () => globalThis.main(), target: {tabId}});
|
||||
chrome.scripting.executeScript({func: () => (globalThis as any).main(), target: {tabId}});
|
||||
});
|
||||
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {ApplicationEnvironment, Environment} from 'ng-devtools';
|
|||
import {environment} from '../environments/environment';
|
||||
|
||||
export class ChromeApplicationEnvironment extends ApplicationEnvironment {
|
||||
get environment(): Environment {
|
||||
override get environment(): Environment {
|
||||
return environment;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {ApplicationOperations} from 'ng-devtools';
|
|||
import {DirectivePosition, ElementPosition} from 'protocol';
|
||||
|
||||
export class ChromeApplicationOperations extends ApplicationOperations {
|
||||
viewSource(position: ElementPosition, directiveIndex: number): void {
|
||||
override viewSource(position: ElementPosition, directiveIndex: number): void {
|
||||
if (chrome.devtools) {
|
||||
chrome.devtools.inspectedWindow.eval(
|
||||
`inspect(inspectedApplication.findConstructorByPosition('${position}', ${
|
||||
|
|
@ -20,14 +20,14 @@ export class ChromeApplicationOperations extends ApplicationOperations {
|
|||
}
|
||||
}
|
||||
|
||||
selectDomElement(position: ElementPosition): void {
|
||||
override selectDomElement(position: ElementPosition): void {
|
||||
if (chrome.devtools) {
|
||||
chrome.devtools.inspectedWindow.eval(
|
||||
`inspect(inspectedApplication.findDomElementByPosition('${position}'))`);
|
||||
}
|
||||
}
|
||||
|
||||
inspect(directivePosition: DirectivePosition, objectPath: string[]): void {
|
||||
override inspect(directivePosition: DirectivePosition, objectPath: string[]): void {
|
||||
if (chrome.devtools) {
|
||||
const args = {
|
||||
directivePosition,
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@ export class ChromeMessageBus extends MessageBus<Events> {
|
|||
};
|
||||
}
|
||||
|
||||
on<E extends keyof Events>(topic: E, cb: Events[E]): () => void {
|
||||
override on<E extends keyof Events>(topic: E, cb: Events[E]): () => void {
|
||||
const listener = (msg: ChromeMessage<Events, keyof Events>): void => {
|
||||
if (msg.topic === topic) {
|
||||
cb.apply(null, msg.args);
|
||||
(cb as any).apply(null, msg.args);
|
||||
}
|
||||
};
|
||||
this._port.onMessage.addListener(listener);
|
||||
|
|
@ -56,17 +56,17 @@ export class ChromeMessageBus extends MessageBus<Events> {
|
|||
};
|
||||
}
|
||||
|
||||
once<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
override once<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
const listener = (msg: ChromeMessage<Events, keyof Events>) => {
|
||||
if (msg.topic === topic) {
|
||||
cb.apply(null, msg.args);
|
||||
(cb as any).apply(null, msg.args);
|
||||
this._port.onMessage.removeListener(listener);
|
||||
}
|
||||
};
|
||||
this._port.onMessage.addListener(listener);
|
||||
}
|
||||
|
||||
emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
override emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
if (this._disconnected) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ export class ChromeMessageBus extends MessageBus<Events> {
|
|||
return true;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
override destroy(): void {
|
||||
this._listeners.forEach((l) => window.removeEventListener('message', l));
|
||||
this._listeners = [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ import {findNodeFromSerializedPosition} from 'ng-devtools-backend';
|
|||
import {buildDirectiveForest, queryDirectiveForest} from '../../../ng-devtools-backend/src/lib/component-tree';
|
||||
|
||||
export const initializeExtendedWindowOperations = () => {
|
||||
extendWindowOperations(window, {inspectedApplication: chromeWindowExtensions});
|
||||
extendWindowOperations(globalThis, {inspectedApplication: chromeWindowExtensions});
|
||||
};
|
||||
|
||||
const extendWindowOperations = <T extends {}>(target, classImpl: T) => {
|
||||
const extendWindowOperations = <T extends {}>(target: any, classImpl: T) => {
|
||||
for (const key of Object.keys(classImpl)) {
|
||||
if (target[key] != null) {
|
||||
console.warn(`A window function or object named ${key} would be overwritten`);
|
||||
|
|
@ -56,7 +56,7 @@ const chromeWindowExtensions = {
|
|||
}
|
||||
return node.nativeElement;
|
||||
},
|
||||
findPropertyByPosition: (args): any => {
|
||||
findPropertyByPosition: (args: any): any => {
|
||||
const {directivePosition, objectPath} = JSON.parse(args);
|
||||
const node = queryDirectiveForest(directivePosition.element, buildDirectiveForest());
|
||||
if (node === null) {
|
||||
|
|
|
|||
|
|
@ -62,4 +62,4 @@ export const main = () => {
|
|||
};
|
||||
|
||||
// expose to use as callback for chrome.tabs.executeScript in background.ts
|
||||
globalThis.main = main;
|
||||
(globalThis as any).main = main;
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ export class SamePageMessageBus extends MessageBus<Events> {
|
|||
};
|
||||
}
|
||||
|
||||
on<E extends keyof Events>(topic: E, cb: Events[E]): () => void {
|
||||
override on<E extends keyof Events>(topic: E, cb: Events[E]): () => void {
|
||||
const listener = (e: MessageEvent): void => {
|
||||
if (e.source !== window || !e.data || e.data.source !== this._destination || !e.data.topic) {
|
||||
return;
|
||||
}
|
||||
if (e.data.topic === topic) {
|
||||
cb.apply(null, e.data.args);
|
||||
(cb as any).apply(null, e.data.args);
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', listener);
|
||||
|
|
@ -49,20 +49,20 @@ export class SamePageMessageBus extends MessageBus<Events> {
|
|||
};
|
||||
}
|
||||
|
||||
once<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
override once<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
const listener = (e: MessageEvent): void => {
|
||||
if (e.source !== window || !e.data || e.data.source !== this._destination || !e.data.topic) {
|
||||
return;
|
||||
}
|
||||
if (e.data.topic === topic) {
|
||||
cb.apply(null, e.data.args);
|
||||
(cb as any).apply(null, e.data.args);
|
||||
}
|
||||
window.removeEventListener('message', listener);
|
||||
};
|
||||
window.addEventListener('message', listener);
|
||||
}
|
||||
|
||||
emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
override emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
window.postMessage(
|
||||
{
|
||||
source: this._source,
|
||||
|
|
@ -74,7 +74,7 @@ export class SamePageMessageBus extends MessageBus<Events> {
|
|||
return true;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
override destroy(): void {
|
||||
this._listeners.forEach((l) => window.removeEventListener('message', l));
|
||||
this._listeners = [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,24 +18,24 @@ export class ZoneAwareChromeMessageBus extends MessageBus<Events> {
|
|||
this._bus = new ChromeMessageBus(port);
|
||||
}
|
||||
|
||||
on<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
this._bus.on(topic, function(): void {
|
||||
this._ngZone.run(() => cb.apply(null, arguments));
|
||||
override on<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
this._bus.on(topic, function(this: ZoneAwareChromeMessageBus): void {
|
||||
this._ngZone.run(() => (cb as any).apply(null, arguments));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
once<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
this._bus.once(topic, function(): void {
|
||||
this._ngZone.run(() => cb.apply(null, arguments));
|
||||
override once<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
this._bus.once(topic, function(this: ZoneAwareChromeMessageBus): void {
|
||||
this._ngZone.run(() => (cb as any).apply(null, arguments));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
override emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
this._ngZone.run(() => this._bus.emit(topic, args));
|
||||
return true;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
override destroy(): void {
|
||||
this._bus.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ import {ZippyComponent} from './zippy.component';
|
|||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class DemoAppComponent {
|
||||
@ViewChild(ZippyComponent) zippy: ZippyComponent;
|
||||
@ViewChild('elementReference') elementRef: ElementRef;
|
||||
@ViewChild(ZippyComponent) zippy!: ZippyComponent;
|
||||
@ViewChild('elementReference') elementRef!: ElementRef;
|
||||
|
||||
@Input('input_one') inputOne = 'input one';
|
||||
@Input() inputTwo = 'input two';
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {Component, Input} from '@angular/core';
|
||||
|
||||
const fib = (n: number) => {
|
||||
const fib = (n: number): number => {
|
||||
if (n === 1 || n === 2) {
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {SampleService} from './sample.service';
|
|||
styles: [''],
|
||||
})
|
||||
export class SamplePropertiesComponent {
|
||||
@ViewChild('elementReference') elementRef: ElementRef;
|
||||
@ViewChild('elementReference') elementRef!: ElementRef;
|
||||
|
||||
exampleService = inject(SampleService);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ export class MyServiceA {
|
|||
viewProviders: [MyServiceA]
|
||||
})
|
||||
export class AppTodoComponent {
|
||||
name: string;
|
||||
animal: string;
|
||||
name!: string;
|
||||
animal!: string;
|
||||
|
||||
constructor(public dialog: MatDialog) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {Todo} from './todo';
|
|||
styleUrls: ['./todo.component.scss'],
|
||||
})
|
||||
export class TodoComponent {
|
||||
@Input() todo: Todo;
|
||||
@Input() todo!: Todo;
|
||||
@Output() update = new EventEmitter();
|
||||
@Output() delete = new EventEmitter();
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output} f
|
|||
import {Todo} from './todo';
|
||||
import {TodoFilter} from './todos.pipe';
|
||||
|
||||
const fib = (n: number) => {
|
||||
const fib = (n: number): number => {
|
||||
if (n === 1 || n === 2) {
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ export class TodosComponent implements OnInit, OnDestroy {
|
|||
@Output() delete = new EventEmitter();
|
||||
@Output() add = new EventEmitter();
|
||||
|
||||
private hashListener: EventListenerOrEventListenerObject;
|
||||
private hashListener!: EventListenerOrEventListenerObject;
|
||||
|
||||
constructor(private cdRef: ChangeDetectorRef) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,6 @@ import {Component, Input} from '@angular/core';
|
|||
styleUrls: ['./zippy.component.scss'],
|
||||
})
|
||||
export class ZippyComponent {
|
||||
@Input() title: string;
|
||||
@Input() title!: string;
|
||||
visible = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,5 +28,5 @@ import {IFrameMessageBus} from '../../iframe-message-bus';
|
|||
})
|
||||
export class DevToolsComponent {
|
||||
messageBus: IFrameMessageBus|null = null;
|
||||
@ViewChild('ref') iframe: ElementRef;
|
||||
@ViewChild('ref') iframe!: ElementRef;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {ApplicationEnvironment, Environment} from 'ng-devtools';
|
|||
import {environment} from './environments/environment';
|
||||
|
||||
export class DemoApplicationEnvironment extends ApplicationEnvironment {
|
||||
get environment(): Environment {
|
||||
override get environment(): Environment {
|
||||
return environment;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ import {ApplicationOperations} from 'ng-devtools';
|
|||
import {DirectivePosition, ElementPosition} from 'protocol';
|
||||
|
||||
export class DemoApplicationOperations extends ApplicationOperations {
|
||||
viewSource(position: ElementPosition): void {
|
||||
override viewSource(position: ElementPosition): void {
|
||||
console.warn('viewSource() is not implemented because the demo app runs in an Iframe');
|
||||
throw new Error('Not implemented in demo app.');
|
||||
}
|
||||
selectDomElement(position: ElementPosition): void {
|
||||
override selectDomElement(position: ElementPosition): void {
|
||||
console.warn('selectDomElement() is not implemented because the demo app runs in an Iframe');
|
||||
throw new Error('Not implemented in demo app.');
|
||||
}
|
||||
inspect(directivePosition: DirectivePosition, keyPath: string[]): void {
|
||||
override inspect(directivePosition: DirectivePosition, keyPath: string[]): void {
|
||||
console.warn('inspect() is not implemented because the demo app runs in an Iframe');
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,13 +33,13 @@ export class IFrameMessageBus extends MessageBus<Events> {
|
|||
};
|
||||
}
|
||||
|
||||
on<E extends keyof Events>(topic: E, cb: Events[E]): () => void {
|
||||
override on<E extends keyof Events>(topic: E, cb: Events[E]): () => void {
|
||||
const listener = (e: MessageEvent) => {
|
||||
if (!e.data || e.data.source !== this._destination || !e.data.topic) {
|
||||
return;
|
||||
}
|
||||
if (e.data.topic === topic) {
|
||||
cb.apply(null, e.data.args);
|
||||
(cb as () => void).apply(null, e.data.args);
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', listener);
|
||||
|
|
@ -50,20 +50,20 @@ export class IFrameMessageBus extends MessageBus<Events> {
|
|||
};
|
||||
}
|
||||
|
||||
once<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
override once<E extends keyof Events>(topic: E, cb: Events[E]): void {
|
||||
const listener = (e: MessageEvent) => {
|
||||
if (!e.data || e.data.source !== this._destination || !e.data.topic) {
|
||||
return;
|
||||
}
|
||||
if (e.data.topic === topic) {
|
||||
cb.apply(null, e.data.args);
|
||||
(cb as any).apply(null, e.data.args);
|
||||
window.removeEventListener('message', listener);
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', listener);
|
||||
}
|
||||
|
||||
emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
override emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
this._docWindow().postMessage(
|
||||
{
|
||||
source: this._source,
|
||||
|
|
@ -80,7 +80,7 @@ export class IFrameMessageBus extends MessageBus<Events> {
|
|||
return true;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
override destroy(): void {
|
||||
this._listeners.forEach((l) => window.removeEventListener('message', l));
|
||||
this._listeners = [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export class ZoneUnawareIFrameMessageBus extends MessageBus<Events> {
|
|||
return result;
|
||||
}
|
||||
|
||||
on<E extends keyof Events>(topic: E, cb: Events[E]): any {
|
||||
override on<E extends keyof Events>(topic: E, cb: Events[E]): any {
|
||||
let result: any;
|
||||
runOutsideAngular(() => {
|
||||
result = this._delegate.on(topic, cb);
|
||||
|
|
@ -45,7 +45,7 @@ export class ZoneUnawareIFrameMessageBus extends MessageBus<Events> {
|
|||
return result;
|
||||
}
|
||||
|
||||
once<E extends keyof Events>(topic: E, cb: Events[E]): any {
|
||||
override once<E extends keyof Events>(topic: E, cb: Events[E]): any {
|
||||
let result: any;
|
||||
runOutsideAngular(() => {
|
||||
result = this._delegate.once(topic, cb);
|
||||
|
|
@ -55,11 +55,11 @@ export class ZoneUnawareIFrameMessageBus extends MessageBus<Events> {
|
|||
|
||||
// Need to be run in the zone because otherwise it won't be caught by the
|
||||
// listener in the extension.
|
||||
emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
override emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean {
|
||||
return this._delegate.emit(topic, args);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
override destroy(): void {
|
||||
this._delegate.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
"outDir": "bazel-out/darwin-fastbuild/bin",
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"strictNullChecks": true,
|
||||
"strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"downlevelIteration": true,
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
},
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictTemplates": true,
|
||||
"strictInjectionParameters": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@
|
|||
"@types/adm-zip": "^0.5.0",
|
||||
"@types/cldrjs": "^0.4.22",
|
||||
"@types/cli-progress": "^3.4.2",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/inquirer": "^9.0.3",
|
||||
"@types/jsdom": "^21.1.5",
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
|
|
|
|||
215
yarn.lock
215
yarn.lock
|
|
@ -3275,6 +3275,216 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/d3-array@*":
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5"
|
||||
integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==
|
||||
|
||||
"@types/d3-axis@*":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-3.0.6.tgz#e760e5765b8188b1defa32bc8bb6062f81e4c795"
|
||||
integrity sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==
|
||||
dependencies:
|
||||
"@types/d3-selection" "*"
|
||||
|
||||
"@types/d3-brush@*":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-3.0.6.tgz#c2f4362b045d472e1b186cdbec329ba52bdaee6c"
|
||||
integrity sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==
|
||||
dependencies:
|
||||
"@types/d3-selection" "*"
|
||||
|
||||
"@types/d3-chord@*":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-3.0.6.tgz#1706ca40cf7ea59a0add8f4456efff8f8775793d"
|
||||
integrity sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==
|
||||
|
||||
"@types/d3-color@*":
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2"
|
||||
integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==
|
||||
|
||||
"@types/d3-contour@*":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-3.0.6.tgz#9ada3fa9c4d00e3a5093fed0356c7ab929604231"
|
||||
integrity sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==
|
||||
dependencies:
|
||||
"@types/d3-array" "*"
|
||||
"@types/geojson" "*"
|
||||
|
||||
"@types/d3-delaunay@*":
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz#185c1a80cc807fdda2a3fe960f7c11c4a27952e1"
|
||||
integrity sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==
|
||||
|
||||
"@types/d3-dispatch@*":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz#096efdf55eb97480e3f5621ff9a8da552f0961e7"
|
||||
integrity sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==
|
||||
|
||||
"@types/d3-drag@*":
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02"
|
||||
integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==
|
||||
dependencies:
|
||||
"@types/d3-selection" "*"
|
||||
|
||||
"@types/d3-dsv@*":
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz#0a351f996dc99b37f4fa58b492c2d1c04e3dac17"
|
||||
integrity sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==
|
||||
|
||||
"@types/d3-ease@*":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b"
|
||||
integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==
|
||||
|
||||
"@types/d3-fetch@*":
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz#c04a2b4f23181aa376f30af0283dbc7b3b569980"
|
||||
integrity sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==
|
||||
dependencies:
|
||||
"@types/d3-dsv" "*"
|
||||
|
||||
"@types/d3-force@*":
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-3.0.9.tgz#dd96ccefba4386fe4ff36b8e4ee4e120c21fcf29"
|
||||
integrity sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==
|
||||
|
||||
"@types/d3-format@*":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-3.0.4.tgz#b1e4465644ddb3fdf3a263febb240a6cd616de90"
|
||||
integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==
|
||||
|
||||
"@types/d3-geo@*":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-3.1.0.tgz#b9e56a079449174f0a2c8684a9a4df3f60522440"
|
||||
integrity sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==
|
||||
dependencies:
|
||||
"@types/geojson" "*"
|
||||
|
||||
"@types/d3-hierarchy@*":
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.6.tgz#8d3638df273ec90da34b3ac89d8784c59708cb0d"
|
||||
integrity sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw==
|
||||
|
||||
"@types/d3-interpolate@*":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c"
|
||||
integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==
|
||||
dependencies:
|
||||
"@types/d3-color" "*"
|
||||
|
||||
"@types/d3-path@*":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.0.2.tgz#4327f4a05d475cf9be46a93fc2e0f8d23380805a"
|
||||
integrity sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==
|
||||
|
||||
"@types/d3-polygon@*":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz#dfae54a6d35d19e76ac9565bcb32a8e54693189c"
|
||||
integrity sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==
|
||||
|
||||
"@types/d3-quadtree@*":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz#d4740b0fe35b1c58b66e1488f4e7ed02952f570f"
|
||||
integrity sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==
|
||||
|
||||
"@types/d3-random@*":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-3.0.3.tgz#ed995c71ecb15e0cd31e22d9d5d23942e3300cfb"
|
||||
integrity sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==
|
||||
|
||||
"@types/d3-scale-chromatic@*":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz#fc0db9c10e789c351f4c42d96f31f2e4df8f5644"
|
||||
integrity sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==
|
||||
|
||||
"@types/d3-scale@*":
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.8.tgz#d409b5f9dcf63074464bf8ddfb8ee5a1f95945bb"
|
||||
integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==
|
||||
dependencies:
|
||||
"@types/d3-time" "*"
|
||||
|
||||
"@types/d3-selection@*":
|
||||
version "3.0.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.10.tgz#98cdcf986d0986de6912b5892e7c015a95ca27fe"
|
||||
integrity sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==
|
||||
|
||||
"@types/d3-shape@*":
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.6.tgz#65d40d5a548f0a023821773e39012805e6e31a72"
|
||||
integrity sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==
|
||||
dependencies:
|
||||
"@types/d3-path" "*"
|
||||
|
||||
"@types/d3-time-format@*":
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz#d6bc1e6b6a7db69cccfbbdd4c34b70632d9e9db2"
|
||||
integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==
|
||||
|
||||
"@types/d3-time@*":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.3.tgz#3c186bbd9d12b9d84253b6be6487ca56b54f88be"
|
||||
integrity sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==
|
||||
|
||||
"@types/d3-timer@*":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70"
|
||||
integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==
|
||||
|
||||
"@types/d3-transition@*":
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.8.tgz#677707f5eed5b24c66a1918cde05963021351a8f"
|
||||
integrity sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==
|
||||
dependencies:
|
||||
"@types/d3-selection" "*"
|
||||
|
||||
"@types/d3-zoom@*":
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b"
|
||||
integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==
|
||||
dependencies:
|
||||
"@types/d3-interpolate" "*"
|
||||
"@types/d3-selection" "*"
|
||||
|
||||
"@types/d3@^7.4.3":
|
||||
version "7.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.4.3.tgz#d4550a85d08f4978faf0a4c36b848c61eaac07e2"
|
||||
integrity sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==
|
||||
dependencies:
|
||||
"@types/d3-array" "*"
|
||||
"@types/d3-axis" "*"
|
||||
"@types/d3-brush" "*"
|
||||
"@types/d3-chord" "*"
|
||||
"@types/d3-color" "*"
|
||||
"@types/d3-contour" "*"
|
||||
"@types/d3-delaunay" "*"
|
||||
"@types/d3-dispatch" "*"
|
||||
"@types/d3-drag" "*"
|
||||
"@types/d3-dsv" "*"
|
||||
"@types/d3-ease" "*"
|
||||
"@types/d3-fetch" "*"
|
||||
"@types/d3-force" "*"
|
||||
"@types/d3-format" "*"
|
||||
"@types/d3-geo" "*"
|
||||
"@types/d3-hierarchy" "*"
|
||||
"@types/d3-interpolate" "*"
|
||||
"@types/d3-path" "*"
|
||||
"@types/d3-polygon" "*"
|
||||
"@types/d3-quadtree" "*"
|
||||
"@types/d3-random" "*"
|
||||
"@types/d3-scale" "*"
|
||||
"@types/d3-scale-chromatic" "*"
|
||||
"@types/d3-selection" "*"
|
||||
"@types/d3-shape" "*"
|
||||
"@types/d3-time" "*"
|
||||
"@types/d3-time-format" "*"
|
||||
"@types/d3-timer" "*"
|
||||
"@types/d3-transition" "*"
|
||||
"@types/d3-zoom" "*"
|
||||
|
||||
"@types/diff@^5.0.0":
|
||||
version "5.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.0.7.tgz#471bad8be0d911ed04d115863402920f3a84079c"
|
||||
|
|
@ -3355,6 +3565,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.31.tgz#a5a256646bd98209baf9aa32073047f84f4c3f3f"
|
||||
integrity sha512-12df1utOvPC80+UaVoOO1d81X8pa5MefHNS+gWX9R2ucSESpMz9K5QwlTWDGKASrzCpSFwj7NPYh+nTsolgEGA==
|
||||
|
||||
"@types/geojson@*":
|
||||
version "7946.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.13.tgz#e6e77ea9ecf36564980a861e24e62a095988775e"
|
||||
integrity sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==
|
||||
|
||||
"@types/glob@*":
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc"
|
||||
|
|
|
|||
Loading…
Reference in a new issue