From 6cd91c675c8d248736cfe447f2a5d6dc488093b8 Mon Sep 17 00:00:00 2001 From: Matthieu Riegler Date: Mon, 4 Dec 2023 20:44:53 +0100 Subject: [PATCH] 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 --- .../src/app/demo-app/demo-app.component.ts | 4 +- .../src/app/demo-app/heavy.component.ts | 2 +- .../app/demo-app/todo/home/todo.component.ts | 2 +- .../app/demo-app/todo/home/todos.component.ts | 4 +- .../app/demo-app/todo/todo-app.component.ts | 4 +- .../src/app/demo-app/zippy.component.ts | 2 +- .../devtools-app/devtools-app.component.ts | 2 +- .../component-inspector.ts | 6 +- .../src/lib/component-tree.ts | 311 ++++++++++-------- .../src/lib/directive-forest/ltree.ts | 8 +- .../src/lib/hooks/capture.ts | 4 +- .../src/lib/hooks/profiler/polyfill.ts | 2 +- .../src/lib/hooks/profiler/shared.ts | 6 +- .../src/lib/router-tree.spec.ts | 2 +- .../src/lib/state-serializer/object-utils.ts | 4 +- .../src/lib/state-serializer/prop-type.ts | 4 +- .../serialized-descriptor-factory.ts | 84 +++-- .../state-serializer/state-serializer.spec.ts | 2 +- .../lib/state-serializer/state-serializer.ts | 56 ++-- .../src/lib/devtools-tabs/BUILD.bazel | 5 + .../injector-tree-visualizer.ts | 36 +- .../resolution-path.component.ts | 8 +- .../devtools-tabs/devtools-tabs.component.ts | 29 +- .../lib/devtools-tabs/devtools-tabs.spec.ts | 30 +- .../directive-explorer.component.ts | 10 +- .../directive-forest/BUILD.bazel | 1 + .../breadcrumbs/breadcrumbs.component.ts | 4 +- .../component-data-source/index.ts | 42 ++- .../directive-forest.component.ts | 29 +- .../property-resolver/arrayify-props.ts | 18 +- .../directive-property-resolver.ts | 14 +- .../property-expanded-directive-properties.ts | 8 +- .../component-metadata.component.ts | 2 +- .../property-tab-header.component.ts | 2 +- .../property-tab/property-tab.component.ts | 2 +- .../property-editor.component.ts | 6 +- .../property-preview.component.ts | 2 +- .../property-tab-body.component.ts | 2 +- .../property-view-body.component.ts | 15 +- .../property-view-header.component.ts | 2 +- .../property-view-tree.component.ts | 4 +- .../property-view/property-view.component.ts | 2 +- .../injector-providers.component.ts | 6 +- .../injector-tree/injector-tree-fns.spec.ts | 5 +- .../injector-tree/injector-tree-fns.ts | 2 +- .../injector-tree/injector-tree.component.ts | 12 +- .../profiler/profiler.component.ts | 86 +++-- .../timeline/frame-selector.component.ts | 55 ++-- .../timeline/record-formatter/frame-merger.ts | 5 +- .../record-formatter/record-formatter.spec.ts | 10 +- .../record-formatter/record-formatter.ts | 4 +- .../timeline/recording-visualizer/BUILD.bazel | 1 + .../bar-chart.component.ts | 4 +- .../bargraph-visualizer.component.ts | 23 +- .../execution-details.component.ts | 2 +- .../flamegraph-visualizer.component.ts | 8 +- .../recording-visualizer/profile-formatter.ts | 8 +- .../timeline-visualizer.component.ts | 6 +- .../tree-map-visualizer.component.ts | 8 +- .../timeline/timeline-controls.component.ts | 8 +- .../profiler/timeline/timeline.component.ts | 2 +- .../router-tree/router-tree.component.ts | 6 +- .../ng-devtools/src/lib/devtools.component.ts | 2 +- .../lib/component/split.component.ts | 6 +- .../lib/component/splitArea.directive.ts | 2 +- .../src/lib/priority-aware-message-bus.ts | 14 +- .../shared-utils/src/lib/shared-utils.spec.ts | 4 +- .../shell-browser/src/app/background.ts | 4 +- .../src/app/chrome-application-environment.ts | 2 +- .../src/app/chrome-application-operations.ts | 6 +- .../src/app/chrome-message-bus.ts | 12 +- .../src/app/chrome-window-extensions.ts | 6 +- .../shell-browser/src/app/content-script.ts | 2 +- .../src/app/same-page-message-bus.ts | 12 +- .../src/app/zone-aware-chrome-message-bus.ts | 16 +- .../src/app/demo-app/demo-app.component.ts | 4 +- devtools/src/app/demo-app/heavy.component.ts | 2 +- .../demo-app/sample-properties.component.ts | 2 +- .../app/demo-app/todo/app-todo.component.ts | 4 +- .../app/demo-app/todo/home/todo.component.ts | 2 +- .../app/demo-app/todo/home/todos.component.ts | 4 +- devtools/src/app/demo-app/zippy.component.ts | 2 +- .../devtools-app/devtools-app.component.ts | 2 +- devtools/src/demo-application-environment.ts | 2 +- devtools/src/demo-application-operations.ts | 6 +- devtools/src/iframe-message-bus.ts | 12 +- .../src/zone-unaware-iframe-message-bus.ts | 8 +- devtools/tsconfig.json | 4 +- package.json | 1 + yarn.lock | 215 ++++++++++++ 90 files changed, 831 insertions(+), 570 deletions(-) diff --git a/devtools/projects/demo-standalone/src/app/demo-app/demo-app.component.ts b/devtools/projects/demo-standalone/src/app/demo-app/demo-app.component.ts index 636a311c182..86b33cd9ad8 100644 --- a/devtools/projects/demo-standalone/src/app/demo-app/demo-app.component.ts +++ b/devtools/projects/demo-standalone/src/app/demo-app/demo-app.component.ts @@ -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'; diff --git a/devtools/projects/demo-standalone/src/app/demo-app/heavy.component.ts b/devtools/projects/demo-standalone/src/app/demo-app/heavy.component.ts index bca53cb54f1..49367eb8f3b 100644 --- a/devtools/projects/demo-standalone/src/app/demo-app/heavy.component.ts +++ b/devtools/projects/demo-standalone/src/app/demo-app/heavy.component.ts @@ -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; } diff --git a/devtools/projects/demo-standalone/src/app/demo-app/todo/home/todo.component.ts b/devtools/projects/demo-standalone/src/app/demo-app/todo/home/todo.component.ts index 34c62bf47e9..3fcf2a470af 100644 --- a/devtools/projects/demo-standalone/src/app/demo-app/todo/home/todo.component.ts +++ b/devtools/projects/demo-standalone/src/app/demo-app/todo/home/todo.component.ts @@ -45,7 +45,7 @@ export interface Todo { ` }) export class TodoComponent { - @Input() todo: Todo; + @Input() todo!: Todo; @Output() update = new EventEmitter(); @Output() delete = new EventEmitter(); diff --git a/devtools/projects/demo-standalone/src/app/demo-app/todo/home/todos.component.ts b/devtools/projects/demo-standalone/src/app/demo-app/todo/home/todos.component.ts index ffc009cf24e..a7abcbcb61b 100644 --- a/devtools/projects/demo-standalone/src/app/demo-app/todo/home/todos.component.ts +++ b/devtools/projects/demo-standalone/src/app/demo-app/todo/home/todos.component.ts @@ -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) {} diff --git a/devtools/projects/demo-standalone/src/app/demo-app/todo/todo-app.component.ts b/devtools/projects/demo-standalone/src/app/demo-app/todo/todo-app.component.ts index 87cbca71a9a..3d61b6e4730 100644 --- a/devtools/projects/demo-standalone/src/app/demo-app/todo/todo-app.component.ts +++ b/devtools/projects/demo-standalone/src/app/demo-app/todo/todo-app.component.ts @@ -52,8 +52,8 @@ import {DialogComponent} from './dialog.component'; ` }) export class TodoAppComponent { - name: string; - animal: string; + name!: string; + animal!: string; constructor(public dialog: MatDialog) {} diff --git a/devtools/projects/demo-standalone/src/app/demo-app/zippy.component.ts b/devtools/projects/demo-standalone/src/app/demo-app/zippy.component.ts index c01a08d9ed8..44ef01fcbaa 100644 --- a/devtools/projects/demo-standalone/src/app/demo-app/zippy.component.ts +++ b/devtools/projects/demo-standalone/src/app/demo-app/zippy.component.ts @@ -42,6 +42,6 @@ import {Component, Input} from '@angular/core'; ` }) export class ZippyComponent { - @Input() title: string; + @Input() title!: string; visible = false; } diff --git a/devtools/projects/demo-standalone/src/app/devtools-app/devtools-app.component.ts b/devtools/projects/demo-standalone/src/app/devtools-app/devtools-app.component.ts index 912c699c6ca..e87c840eb6c 100644 --- a/devtools/projects/demo-standalone/src/app/devtools-app/devtools-app.component.ts +++ b/devtools/projects/demo-standalone/src/app/devtools-app/devtools-app.component.ts @@ -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; } diff --git a/devtools/projects/ng-devtools-backend/src/lib/component-inspector/component-inspector.ts b/devtools/projects/ng-devtools-backend/src/lib/component-inspector/component-inspector.ts index e7cd06070b6..02e09de685c 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/component-inspector/component-inspector.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/component-inspector/component-inspector.ts @@ -23,7 +23,7 @@ export interface ComponentInspectorOptions { } export class ComponentInspector { - private _selectedComponent: {component: Type; host: HTMLElement | null}; + private _selectedComponent!: {component: Type; 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)!); } } diff --git a/devtools/projects/ng-devtools-backend/src/lib/component-tree.ts b/devtools/projects/ng-devtools-backend/src/lib/component-tree.ts index 7dcd9167dbd..4f6146c5c73 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/component-tree.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/component-tree.ts @@ -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(); @@ -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 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()): 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) { diff --git a/devtools/projects/ng-devtools-backend/src/lib/directive-forest/ltree.ts b/devtools/projects/ng-devtools-backend/src/lib/directive-forest/ltree.ts index 3c90a297628..20b7e8a5082 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/directive-forest/ltree.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/directive-forest/ltree.ts @@ -28,6 +28,10 @@ const TYPE = 1; const ELEMENT = 0; const LVIEW_TVIEW = 1; + +// Big oversimplification of the LView structure. +type LView = Array; + 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) { diff --git a/devtools/projects/ng-devtools-backend/src/lib/hooks/capture.ts b/devtools/projects/ng-devtools-backend/src/lib/hooks/capture.ts index d3bd013c34b..ed469603904 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/hooks/capture.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/hooks/capture.ts @@ -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]) { diff --git a/devtools/projects/ng-devtools-backend/src/lib/hooks/profiler/polyfill.ts b/devtools/projects/ng-devtools-backend/src/lib/hooks/profiler/polyfill.ts index 4b89f984bc7..e5d23af36ee 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/hooks/profiler/polyfill.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/hooks/profiler/polyfill.ts @@ -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); diff --git a/devtools/projects/ng-devtools-backend/src/lib/hooks/profiler/shared.ts b/devtools/projects/ng-devtools-backend/src/lib/hooks/profiler/shared.ts index 3b47c34abc2..8189084128d 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/hooks/profiler/shared.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/hooks/profiler/shared.ts @@ -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); } }); } diff --git a/devtools/projects/ng-devtools-backend/src/lib/router-tree.spec.ts b/devtools/projects/ng-devtools-backend/src/lib/router-tree.spec.ts index 8bb7b4ffe8c..a03d7036d89 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/router-tree.spec.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/router-tree.spec.ts @@ -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', diff --git a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/object-utils.ts b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/object-utils.ts index ff42ce7472e..555a95f0f86 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/object-utils.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/object-utils.ts @@ -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 diff --git a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/prop-type.ts b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/prop-type.ts index f9ff4ab2d79..d810853bcf9 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/prop-type.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/prop-type.ts @@ -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)) { diff --git a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/serialized-descriptor-factory.ts b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/serialized-descriptor-factory.ts index 6d75e4f0bdc..538b19a5c5e 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/serialized-descriptor-factory.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/serialized-descriptor-factory.ts @@ -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); + } +} -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); + } +} const truncate = (str: string, max = 20): string => { if (str.length > max) { diff --git a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.spec.ts b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.spec.ts index 22ef95fb704..f1505422494 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.spec.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.spec.ts @@ -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 = [ { diff --git a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.ts b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.ts index eddef98a724..815b5729c6d 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.ts @@ -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 = {}; + 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 = {}; getKeys(instance).forEach((prop) => { if (ignoreList.has(prop)) { return; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/BUILD.bazel index 6444fba8baf..ad3c3232f88 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/BUILD.bazel @@ -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", ], ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection/injector-tree-visualizer.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection/injector-tree-visualizer.ts index 60f38f7cc6e..0ef0b3acd7d 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection/injector-tree-visualizer.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection/injector-tree-visualizer.ts @@ -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; export abstract class GraphRenderer { 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|null = null; + zoomController: d3.ZoomBehavior|null = null; override zoomScale(scale: number) { if (this.zoomController) { - this.zoomController.scaleTo(this.d3.select(this._containerElement), scale); + this.zoomController.scaleTo( + this.d3.select(this._containerElement), scale); } } @@ -118,7 +111,7 @@ export class InjectorTreeVisualizer extends GraphRenderer(this._containerElement) + .select(`.node[data-id="${id}"]`); if (selection.empty()) { return null; } @@ -142,12 +136,12 @@ export class InjectorTreeVisualizer extends GraphRenderer node.children); - const tree = this.d3.tree(); + const data = this.d3.hierarchy(injectorGraph, (node: InjectorTreeNode) => node.children); + const tree = this.d3.tree(); const svg = this.d3.select(this._containerElement); - const g = this.d3.select(this._graphElement); + const g = this.d3.select(this._graphElement); - this.zoomController = this.d3.zoom().scaleExtent([0.1, 2]); + this.zoomController = this.d3.zoom().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 { + (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)); }) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection/resolution-path.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection/resolution-path.component.ts index ee67e6ada28..0ac31bdc53f 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection/resolution-path.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection/resolution-path.component.ts @@ -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[]) { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.ts index ae5a49d92a5..70c81b07780 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.ts @@ -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, - 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); } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.spec.ts index 6182290b5bd..62d4298d81c 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.spec.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.spec.ts @@ -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; 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()})}, + {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', () => { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts index bb4311325c1..258b53defe8 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts @@ -50,14 +50,14 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy { @Input() showCommentNodes = false; @Output() toggleInspector = new EventEmitter(); - @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; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/BUILD.bazel index d4661b9e96e..30c0e6b4beb 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/BUILD.bazel @@ -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", diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.ts index 804b19114fc..c8fb388b3b3 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.ts @@ -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; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source/index.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source/index.ts index 1be8b478d6a..a026aff1cf1 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source/index.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source/index.ts @@ -94,8 +94,10 @@ export class ComponentDataSource extends DataSource { 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) { super(); @@ -113,8 +115,10 @@ export class ComponentDataSource extends DataSource { 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 { this.data.forEach((i) => (i.newItem = false)); - const expandedNodes = {}; + const expandedNodes: Record = {}; this.data.forEach((item) => { expandedNodes[item.id] = this._treeControl.isExpanded(item); }); - const {newItems, movedItems, removedItems} = - diff(this._differ, this.data, flattenedCollection); + const {newItems, movedItems, removedItems} = diff( + this._differ, + this.data, + flattenedCollection, + ); this._treeControl.dataNodes = this.data; this._flattenedData.next(this.data); @@ -165,13 +172,22 @@ export class ComponentDataSource extends DataSource { override connect(collectionViewer: CollectionViewer): Observable { 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, + ) as FlatNode[], + ); + return this._expandedData.value; + }), + ); } override disconnect(): void {} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.ts index f51b45e5ea9..16c7cab2142 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.ts @@ -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(); @Output() toggleInspector = new EventEmitter(); - @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((node) => node.level, (node) => node.expandable); + new FlatTreeControl((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, - 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); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/arrayify-props.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/arrayify-props.ts index 31060754181..1c1e96fd531 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/arrayify-props.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/arrayify-props.ts @@ -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; + }); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts index 6547b2f2c2b..a6dc0b1c3e8 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts @@ -52,7 +52,11 @@ export class DirectivePropertyResolver { constructor( private _messageBus: MessageBus, 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); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-expanded-directive-properties.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-expanded-directive-properties.ts index 238823ed6a3..c93d45ab87a 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-expanded-directive-properties.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-expanded-directive-properties.ts @@ -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]), diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/component-metadata.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/component-metadata.component.ts index cab4f067f99..e6bc7d83641 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/component-metadata.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/component-metadata.component.ts @@ -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) {} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-header.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-header.component.ts index 4d8228cefdd..9f37283f2e7 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-header.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-header.component.ts @@ -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; } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts index 698a6e73d8a..6eaa91b2dd0 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts @@ -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(); @Output() inspect = new EventEmitter<{node: FlatNode; directivePosition: DirectivePosition}>(); } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-editor.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-editor.component.ts index 571d6096baa..77a222859f4 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-editor.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-editor.component.ts @@ -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(); readState = PropertyEditorState.Read; writeState = PropertyEditorState.Write; - valueToSubmit: EditorResult; + valueToSubmit!: EditorResult; currentPropertyState = this.readState; constructor(private _cd: ChangeDetectorRef, private _elementRef: ElementRef) {} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-preview.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-preview.component.ts index 8a0ab6e0acb..27349e1912a 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-preview.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-preview.component.ts @@ -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(); get isClickableProp(): boolean { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.ts index ce4160eb363..bbbd5615164 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.ts @@ -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(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body.component.ts index 0acfcb6dbd7..c3bad32a7d6 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body.component.ts @@ -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]; } } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-header.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-header.component.ts index 85d8ca5643c..06f2bd2c557 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-header.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-header.component.ts @@ -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(); // output that emits directive diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-tree.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-tree.component.ts index 5c4d0afd824..0c4383457a0 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-tree.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-tree.component.ts @@ -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; + @Input({required: true}) dataSource!: PropertyDataSource; + @Input({required: true}) treeControl!: FlatTreeControl; @Output() updateValue = new EventEmitter(); @Output() inspect = new EventEmitter(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts index f6391b7d2c1..6e725373d21 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts @@ -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(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-providers.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-providers.component.ts index c8463f6bdcb..b7f1078c7b1 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-providers.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-providers.component.ts @@ -40,7 +40,7 @@ import {Events, MessageBus, SerializedInjector, SerializedProviderRecord} from ' > None @for (type of providerTypes; track type) { - {{providerTypeToLabel[type]}} + {{$any(providerTypeToLabel)[type]}} } @@ -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]}} } @@ -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(''); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree-fns.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree-fns.spec.ts index 2081df8d801..9d5783c0a9e 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree-fns.spec.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree-fns.spec.ts @@ -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']); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree-fns.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree-fns.ts index 41fafb61cd4..6ac1e695896 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree-fns.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree-fns.ts @@ -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()}); } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree.component.ts index dbed2d01919..033e6c3fc93 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-tree.component.ts @@ -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; 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 = new Map(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/profiler.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/profiler.component.ts index 28939878256..462ea915255 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/profiler.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/profiler.component.ts @@ -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(); - 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, - public dialog: MatDialog) {} + private _fileApiService: FileApiService, + private _messageBus: MessageBus, + 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 { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/frame-selector.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/frame-selector.component.ts index 829dae109a9..aafe0d1e7b0 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/frame-selector.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/frame-selector.component.ts @@ -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) { 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 { @@ -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(); - 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; - private _graphDataSubscription: Subscription; - private _tabUpdateSubscription: Subscription; + private _graphData$!: Observable; + 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) { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/frame-merger.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/frame-merger.ts index fc04bb6464f..e74506efd9b 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/frame-merger.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/frame-merger.ts @@ -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]); }); }; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/record-formatter.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/record-formatter.spec.ts index a9f6c0c68b8..e81d726ec58 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/record-formatter.spec.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/record-formatter.spec.ts @@ -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); }); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/record-formatter.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/record-formatter.ts index 09a53a15050..40c198ba657 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/record-formatter.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/record-formatter/record-formatter.ts @@ -51,8 +51,8 @@ export abstract class RecordFormatter { 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; } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/BUILD.bazel index 7afd58d75fc..5f182eaccbe 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/BUILD.bazel @@ -57,6 +57,7 @@ ng_module( "//packages/animations", "//packages/common", "//packages/core", + "//packages/core/rxjs-interop", "@npm//@angular/material", "@npm//@types", "@npm//ngx-flamegraph", diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/bar-chart.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/bar-chart.component.ts index 2d840886b53..a9264d5f8cd 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/bar-chart.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/bar-chart.component.ts @@ -49,10 +49,10 @@ export class BarChartComponent { }); } } - @Input() color: string; + @Input({required: true}) color!: string; @Output() barClick = new EventEmitter(); - originalData: BargraphNode[]; + originalData!: BargraphNode[]; internalData: BarData[] = []; } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/bargraph-visualizer.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/bargraph-visualizer.component.ts index f6669291274..897e78e8011 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/bargraph-visualizer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/bargraph-visualizer.component.ts @@ -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(); 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 ?? []); } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/execution-details.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/execution-details.component.ts index 0be496540d1..d9d9beae89a 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/execution-details.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/execution-details.component.ts @@ -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[]; } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/flamegraph-visualizer.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/flamegraph-visualizer.component.ts index b88658ef3bb..4ba74223f0f 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/flamegraph-visualizer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/flamegraph-visualizer.component.ts @@ -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(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/profile-formatter.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/profile-formatter.ts index 4bd54c397a8..daedbf22c18 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/profile-formatter.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/profile-formatter.ts @@ -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), }); }); }); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/timeline-visualizer.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/timeline-visualizer.component.ts index 44ab5e43f27..11de53f2c12 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/timeline-visualizer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/timeline-visualizer.component.ts @@ -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; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/tree-map-visualizer.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/tree-map-visualizer.component.ts index 9fbad00e1f1..054fe6d9498 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/tree-map-visualizer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/recording-visualizer/tree-map-visualizer.component.ts @@ -34,13 +34,13 @@ export class TreeMapVisualizerComponent implements OnInit, OnDestroy { constructor(private _ngZone: NgZone) {} private resize$ = new Subject(); - 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; 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 { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/timeline-controls.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/timeline-controls.component.ts index 08e4e3d1eb8..f23e3570fd2 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/timeline-controls.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/timeline-controls.component.ts @@ -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(); @Output() exportProfile = new EventEmitter(); @Output() toggleChangeDetection = new EventEmitter(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/timeline.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/timeline.component.ts index d27785bb50f..e20b382d822 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/timeline.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/timeline/timeline.component.ts @@ -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([]); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.ts index 4fee025188c..d54a0248214 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/router-tree/router-tree.component.ts @@ -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) {} diff --git a/devtools/projects/ng-devtools/src/lib/devtools.component.ts b/devtools/projects/ng-devtools/src/lib/devtools.component.ts index c69f3c7c8ba..719999c30cd 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools.component.ts @@ -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'; diff --git a/devtools/projects/ng-devtools/src/lib/vendor/angular-split/lib/component/split.component.ts b/devtools/projects/ng-devtools/src/lib/vendor/angular-split/lib/component/split.component.ts index beae42f9d36..b03249caa0c 100644 --- a/devtools/projects/ng-devtools/src/lib/vendor/angular-split/lib/component/split.component.ts +++ b/devtools/projects/ng-devtools/src/lib/vendor/angular-split/lib/component/split.component.ts @@ -213,7 +213,7 @@ export class SplitComponent implements AfterViewInit, OnDestroy { @Output() gutterClick = new EventEmitter(false); @Output() gutterDblClick = new EventEmitter(false); - private transitionEndSubscriber: Subscriber; + private transitionEndSubscriber: Subscriber|null = null; @Output() get transitionEnd(): Observable { return new Observable((subscriber) => (this.transitionEndSubscriber = subscriber)) @@ -234,7 +234,7 @@ export class SplitComponent implements AfterViewInit, OnDestroy { public readonly displayedAreas: Array = []; private readonly hidedAreas: Array = []; - @ViewChildren('gutterEls') private gutterEls: QueryList; + @ViewChildren('gutterEls') private gutterEls!: QueryList; 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. diff --git a/devtools/projects/ng-devtools/src/lib/vendor/angular-split/lib/component/splitArea.directive.ts b/devtools/projects/ng-devtools/src/lib/vendor/angular-split/lib/component/splitArea.directive.ts index c809447eeb3..09e75804ac2 100644 --- a/devtools/projects/ng-devtools/src/lib/vendor/angular-split/lib/component/splitArea.directive.ts +++ b/devtools/projects/ng-devtools/src/lib/vendor/angular-split/lib/component/splitArea.directive.ts @@ -107,7 +107,7 @@ export class SplitAreaDirective implements OnInit, OnDestroy { //// - private transitionListener: Function; + private transitionListener!: Function; private readonly lockListeners: Array = []; constructor( diff --git a/devtools/projects/protocol/src/lib/priority-aware-message-bus.ts b/devtools/projects/protocol/src/lib/priority-aware-message-bus.ts index f8c61536ae8..7d01fe33a4b 100644 --- a/devtools/projects/protocol/src/lib/priority-aware-message-bus.ts +++ b/devtools/projects/protocol/src/lib/priority-aware-message-bus.ts @@ -57,18 +57,16 @@ export class PriorityAwareMessageBus extends MessageBus { } override on(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(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); }); } diff --git a/devtools/projects/shared-utils/src/lib/shared-utils.spec.ts b/devtools/projects/shared-utils/src/lib/shared-utils.spec.ts index 9951a4ca5c3..e747622e542 100644 --- a/devtools/projects/shared-utils/src/lib/shared-utils.spec.ts +++ b/devtools/projects/shared-utils/src/lib/shared-utils.spec.ts @@ -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(() => { diff --git a/devtools/projects/shell-browser/src/app/background.ts b/devtools/projects/shell-browser/src/app/background.ts index f9f2dc1f7ef..2730999a339 100644 --- a/devtools/projects/shell-browser/src/app/background.ts +++ b/devtools/projects/shell-browser/src/app/background.ts @@ -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; diff --git a/devtools/projects/shell-browser/src/app/chrome-application-environment.ts b/devtools/projects/shell-browser/src/app/chrome-application-environment.ts index 0941192bbc1..dd4708d24e0 100644 --- a/devtools/projects/shell-browser/src/app/chrome-application-environment.ts +++ b/devtools/projects/shell-browser/src/app/chrome-application-environment.ts @@ -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; } } diff --git a/devtools/projects/shell-browser/src/app/chrome-application-operations.ts b/devtools/projects/shell-browser/src/app/chrome-application-operations.ts index df40dd2cb5b..5b27aa43caa 100644 --- a/devtools/projects/shell-browser/src/app/chrome-application-operations.ts +++ b/devtools/projects/shell-browser/src/app/chrome-application-operations.ts @@ -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, diff --git a/devtools/projects/shell-browser/src/app/chrome-message-bus.ts b/devtools/projects/shell-browser/src/app/chrome-message-bus.ts index 1b296f1ff17..8858ec6a3da 100644 --- a/devtools/projects/shell-browser/src/app/chrome-message-bus.ts +++ b/devtools/projects/shell-browser/src/app/chrome-message-bus.ts @@ -42,10 +42,10 @@ export class ChromeMessageBus extends MessageBus { }; } - on(topic: E, cb: Events[E]): () => void { + override on(topic: E, cb: Events[E]): () => void { const listener = (msg: ChromeMessage): 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 { }; } - once(topic: E, cb: Events[E]): void { + override once(topic: E, cb: Events[E]): void { const listener = (msg: ChromeMessage) => { 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(topic: E, args?: Parameters): boolean { + override emit(topic: E, args?: Parameters): boolean { if (this._disconnected) { return false; } @@ -78,7 +78,7 @@ export class ChromeMessageBus extends MessageBus { return true; } - destroy(): void { + override destroy(): void { this._listeners.forEach((l) => window.removeEventListener('message', l)); this._listeners = []; } diff --git a/devtools/projects/shell-browser/src/app/chrome-window-extensions.ts b/devtools/projects/shell-browser/src/app/chrome-window-extensions.ts index 5c8d66b90ba..7f27d06e760 100644 --- a/devtools/projects/shell-browser/src/app/chrome-window-extensions.ts +++ b/devtools/projects/shell-browser/src/app/chrome-window-extensions.ts @@ -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 = (target, classImpl: T) => { +const extendWindowOperations = (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) { diff --git a/devtools/projects/shell-browser/src/app/content-script.ts b/devtools/projects/shell-browser/src/app/content-script.ts index ce6b263a30d..527a372e8e8 100644 --- a/devtools/projects/shell-browser/src/app/content-script.ts +++ b/devtools/projects/shell-browser/src/app/content-script.ts @@ -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; diff --git a/devtools/projects/shell-browser/src/app/same-page-message-bus.ts b/devtools/projects/shell-browser/src/app/same-page-message-bus.ts index 347eedfc4c4..f59a0a0dd90 100644 --- a/devtools/projects/shell-browser/src/app/same-page-message-bus.ts +++ b/devtools/projects/shell-browser/src/app/same-page-message-bus.ts @@ -32,13 +32,13 @@ export class SamePageMessageBus extends MessageBus { }; } - on(topic: E, cb: Events[E]): () => void { + override on(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 { }; } - once(topic: E, cb: Events[E]): void { + override once(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(topic: E, args?: Parameters): boolean { + override emit(topic: E, args?: Parameters): boolean { window.postMessage( { source: this._source, @@ -74,7 +74,7 @@ export class SamePageMessageBus extends MessageBus { return true; } - destroy(): void { + override destroy(): void { this._listeners.forEach((l) => window.removeEventListener('message', l)); this._listeners = []; } diff --git a/devtools/projects/shell-browser/src/app/zone-aware-chrome-message-bus.ts b/devtools/projects/shell-browser/src/app/zone-aware-chrome-message-bus.ts index 71b57d5d111..001325b1ac3 100644 --- a/devtools/projects/shell-browser/src/app/zone-aware-chrome-message-bus.ts +++ b/devtools/projects/shell-browser/src/app/zone-aware-chrome-message-bus.ts @@ -18,24 +18,24 @@ export class ZoneAwareChromeMessageBus extends MessageBus { this._bus = new ChromeMessageBus(port); } - on(topic: E, cb: Events[E]): void { - this._bus.on(topic, function(): void { - this._ngZone.run(() => cb.apply(null, arguments)); + override on(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(topic: E, cb: Events[E]): void { - this._bus.once(topic, function(): void { - this._ngZone.run(() => cb.apply(null, arguments)); + override once(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(topic: E, args?: Parameters): boolean { + override emit(topic: E, args?: Parameters): boolean { this._ngZone.run(() => this._bus.emit(topic, args)); return true; } - destroy(): void { + override destroy(): void { this._bus.destroy(); } } diff --git a/devtools/src/app/demo-app/demo-app.component.ts b/devtools/src/app/demo-app/demo-app.component.ts index 78a71c74337..3fd0ef9507b 100644 --- a/devtools/src/app/demo-app/demo-app.component.ts +++ b/devtools/src/app/demo-app/demo-app.component.ts @@ -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'; diff --git a/devtools/src/app/demo-app/heavy.component.ts b/devtools/src/app/demo-app/heavy.component.ts index b70999d4caa..37696ce8b54 100644 --- a/devtools/src/app/demo-app/heavy.component.ts +++ b/devtools/src/app/demo-app/heavy.component.ts @@ -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; } diff --git a/devtools/src/app/demo-app/sample-properties.component.ts b/devtools/src/app/demo-app/sample-properties.component.ts index 02d04e78468..92431f799a5 100644 --- a/devtools/src/app/demo-app/sample-properties.component.ts +++ b/devtools/src/app/demo-app/sample-properties.component.ts @@ -17,7 +17,7 @@ import {SampleService} from './sample.service'; styles: [''], }) export class SamplePropertiesComponent { - @ViewChild('elementReference') elementRef: ElementRef; + @ViewChild('elementReference') elementRef!: ElementRef; exampleService = inject(SampleService); diff --git a/devtools/src/app/demo-app/todo/app-todo.component.ts b/devtools/src/app/demo-app/todo/app-todo.component.ts index 1895557430f..d65486029ec 100644 --- a/devtools/src/app/demo-app/todo/app-todo.component.ts +++ b/devtools/src/app/demo-app/todo/app-todo.component.ts @@ -22,8 +22,8 @@ export class MyServiceA { viewProviders: [MyServiceA] }) export class AppTodoComponent { - name: string; - animal: string; + name!: string; + animal!: string; constructor(public dialog: MatDialog) {} diff --git a/devtools/src/app/demo-app/todo/home/todo.component.ts b/devtools/src/app/demo-app/todo/home/todo.component.ts index f53af99fb3d..7a4430081d4 100644 --- a/devtools/src/app/demo-app/todo/home/todo.component.ts +++ b/devtools/src/app/demo-app/todo/home/todo.component.ts @@ -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(); diff --git a/devtools/src/app/demo-app/todo/home/todos.component.ts b/devtools/src/app/demo-app/todo/home/todos.component.ts index 74e03c81a21..f72670b9108 100644 --- a/devtools/src/app/demo-app/todo/home/todos.component.ts +++ b/devtools/src/app/demo-app/todo/home/todos.component.ts @@ -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) {} diff --git a/devtools/src/app/demo-app/zippy.component.ts b/devtools/src/app/demo-app/zippy.component.ts index 4eeb6688d89..6875ef074d9 100644 --- a/devtools/src/app/demo-app/zippy.component.ts +++ b/devtools/src/app/demo-app/zippy.component.ts @@ -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; } diff --git a/devtools/src/app/devtools-app/devtools-app.component.ts b/devtools/src/app/devtools-app/devtools-app.component.ts index 64096e31cb3..58dde7bfdae 100644 --- a/devtools/src/app/devtools-app/devtools-app.component.ts +++ b/devtools/src/app/devtools-app/devtools-app.component.ts @@ -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; } diff --git a/devtools/src/demo-application-environment.ts b/devtools/src/demo-application-environment.ts index 1b9dcc4b212..470867b7917 100644 --- a/devtools/src/demo-application-environment.ts +++ b/devtools/src/demo-application-environment.ts @@ -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; } } diff --git a/devtools/src/demo-application-operations.ts b/devtools/src/demo-application-operations.ts index b3d284475b8..95c3abe1849 100644 --- a/devtools/src/demo-application-operations.ts +++ b/devtools/src/demo-application-operations.ts @@ -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; } diff --git a/devtools/src/iframe-message-bus.ts b/devtools/src/iframe-message-bus.ts index aa855b495d0..021875358b4 100644 --- a/devtools/src/iframe-message-bus.ts +++ b/devtools/src/iframe-message-bus.ts @@ -33,13 +33,13 @@ export class IFrameMessageBus extends MessageBus { }; } - on(topic: E, cb: Events[E]): () => void { + override on(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 { }; } - once(topic: E, cb: Events[E]): void { + override once(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(topic: E, args?: Parameters): boolean { + override emit(topic: E, args?: Parameters): boolean { this._docWindow().postMessage( { source: this._source, @@ -80,7 +80,7 @@ export class IFrameMessageBus extends MessageBus { return true; } - destroy(): void { + override destroy(): void { this._listeners.forEach((l) => window.removeEventListener('message', l)); this._listeners = []; } diff --git a/devtools/src/zone-unaware-iframe-message-bus.ts b/devtools/src/zone-unaware-iframe-message-bus.ts index d73ada38d92..59aeeb486fd 100644 --- a/devtools/src/zone-unaware-iframe-message-bus.ts +++ b/devtools/src/zone-unaware-iframe-message-bus.ts @@ -37,7 +37,7 @@ export class ZoneUnawareIFrameMessageBus extends MessageBus { return result; } - on(topic: E, cb: Events[E]): any { + override on(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 { return result; } - once(topic: E, cb: Events[E]): any { + override once(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 { // Need to be run in the zone because otherwise it won't be caught by the // listener in the extension. - emit(topic: E, args?: Parameters): boolean { + override emit(topic: E, args?: Parameters): boolean { return this._delegate.emit(topic, args); } - destroy(): void { + override destroy(): void { this._delegate.destroy(); } } diff --git a/devtools/tsconfig.json b/devtools/tsconfig.json index 25d457c936b..fae55a04b1e 100644 --- a/devtools/tsconfig.json +++ b/devtools/tsconfig.json @@ -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 } } diff --git a/package.json b/package.json index c3119bff416..faf8df0fd1f 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/yarn.lock b/yarn.lock index 815f8eaf703..9a492528173 100644 --- a/yarn.lock +++ b/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"