2016-06-23 09:47:54 -07:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2016-04-28 17:50:03 -07:00
|
|
|
import {Injector} from '../di';
|
2018-11-21 21:14:06 -08:00
|
|
|
import {DirectiveDef} from '../render3';
|
|
|
|
import {assertDomNode} from '../render3/assert';
|
|
|
|
import {getComponent, getInjector, getLocalRefs, loadContext} from '../render3/discovery_utils';
|
|
|
|
import {TNode, TNodeFlags} from '../render3/interfaces/node';
|
|
|
|
import {TVIEW} from '../render3/interfaces/view';
|
2017-03-14 09:16:15 -07:00
|
|
|
import {DebugContext} from '../view/index';
|
2016-01-13 21:35:21 -08:00
|
|
|
|
2017-09-22 19:51:03 +02:00
|
|
|
export class EventListener {
|
|
|
|
constructor(public name: string, public callback: Function) {}
|
|
|
|
}
|
2016-01-13 21:35:21 -08:00
|
|
|
|
2016-05-25 15:00:05 -07:00
|
|
|
/**
|
2018-10-19 12:12:20 +01:00
|
|
|
* @publicApi
|
2016-05-25 15:00:05 -07:00
|
|
|
*/
|
2018-11-21 21:14:06 -08:00
|
|
|
export interface DebugNode {
|
|
|
|
readonly listeners: EventListener[];
|
|
|
|
readonly parent: DebugElement|null;
|
|
|
|
readonly nativeNode: any;
|
|
|
|
readonly injector: Injector;
|
|
|
|
readonly componentInstance: any;
|
|
|
|
readonly context: any;
|
|
|
|
readonly references: {[key: string]: any};
|
|
|
|
readonly providerTokens: any[];
|
|
|
|
}
|
|
|
|
export class DebugNode__PRE_R3__ {
|
|
|
|
readonly listeners: EventListener[] = [];
|
|
|
|
readonly parent: DebugElement|null = null;
|
|
|
|
readonly nativeNode: any;
|
|
|
|
private readonly _debugContext: DebugContext;
|
2016-01-13 21:35:21 -08:00
|
|
|
|
2018-11-21 21:14:06 -08:00
|
|
|
constructor(nativeNode: any, parent: DebugNode|null, _debugContext: DebugContext) {
|
|
|
|
this._debugContext = _debugContext;
|
|
|
|
this.nativeNode = nativeNode;
|
|
|
|
if (parent && parent instanceof DebugElement__PRE_R3__) {
|
2016-01-13 21:35:21 -08:00
|
|
|
parent.addChild(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-29 09:34:45 -07:00
|
|
|
get injector(): Injector { return this._debugContext.injector; }
|
2016-01-06 14:13:44 -08:00
|
|
|
|
2017-03-29 09:34:45 -07:00
|
|
|
get componentInstance(): any { return this._debugContext.component; }
|
2016-01-06 14:13:44 -08:00
|
|
|
|
2017-03-29 09:34:45 -07:00
|
|
|
get context(): any { return this._debugContext.context; }
|
2016-04-28 14:00:31 -07:00
|
|
|
|
2017-03-29 09:34:45 -07:00
|
|
|
get references(): {[key: string]: any} { return this._debugContext.references; }
|
2016-01-13 21:35:21 -08:00
|
|
|
|
2017-03-29 09:34:45 -07:00
|
|
|
get providerTokens(): any[] { return this._debugContext.providerTokens; }
|
2016-01-13 21:35:21 -08:00
|
|
|
}
|
|
|
|
|
2016-05-25 15:00:05 -07:00
|
|
|
/**
|
2018-10-19 12:12:20 +01:00
|
|
|
* @publicApi
|
2016-05-25 15:00:05 -07:00
|
|
|
*/
|
2018-11-21 21:14:06 -08:00
|
|
|
export interface DebugElement extends DebugNode {
|
|
|
|
readonly name: string;
|
|
|
|
readonly properties: {[key: string]: any};
|
|
|
|
readonly attributes: {[key: string]: string | null};
|
|
|
|
readonly classes: {[key: string]: boolean};
|
|
|
|
readonly styles: {[key: string]: string | null};
|
|
|
|
readonly childNodes: DebugNode[];
|
|
|
|
readonly nativeElement: any;
|
|
|
|
readonly children: DebugElement[];
|
|
|
|
|
|
|
|
query(predicate: Predicate<DebugElement>): DebugElement;
|
|
|
|
queryAll(predicate: Predicate<DebugElement>): DebugElement[];
|
|
|
|
queryAllNodes(predicate: Predicate<DebugNode>): DebugNode[];
|
|
|
|
triggerEventHandler(eventName: string, eventObj: any): void;
|
|
|
|
}
|
|
|
|
export class DebugElement__PRE_R3__ extends DebugNode__PRE_R3__ implements DebugElement {
|
|
|
|
readonly name !: string;
|
|
|
|
readonly properties: {[key: string]: any} = {};
|
|
|
|
readonly attributes: {[key: string]: string | null} = {};
|
|
|
|
readonly classes: {[key: string]: boolean} = {};
|
|
|
|
readonly styles: {[key: string]: string | null} = {};
|
|
|
|
readonly childNodes: DebugNode[] = [];
|
|
|
|
readonly nativeElement: any;
|
2016-01-13 21:35:21 -08:00
|
|
|
|
2017-03-14 09:16:15 -07:00
|
|
|
constructor(nativeNode: any, parent: any, _debugContext: DebugContext) {
|
|
|
|
super(nativeNode, parent, _debugContext);
|
2016-01-13 21:35:21 -08:00
|
|
|
this.nativeElement = nativeNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
addChild(child: DebugNode) {
|
2016-11-03 16:58:27 -07:00
|
|
|
if (child) {
|
2016-01-13 21:35:21 -08:00
|
|
|
this.childNodes.push(child);
|
2018-11-21 21:14:06 -08:00
|
|
|
(child as{parent: DebugNode}).parent = this;
|
2016-01-13 21:35:21 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
removeChild(child: DebugNode) {
|
2016-11-03 16:58:27 -07:00
|
|
|
const childIndex = this.childNodes.indexOf(child);
|
2016-01-13 21:35:21 -08:00
|
|
|
if (childIndex !== -1) {
|
2018-11-21 21:14:06 -08:00
|
|
|
(child as{parent: DebugNode | null}).parent = null;
|
2016-01-13 21:35:21 -08:00
|
|
|
this.childNodes.splice(childIndex, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
insertChildrenAfter(child: DebugNode, newChildren: DebugNode[]) {
|
2016-11-03 16:58:27 -07:00
|
|
|
const siblingIndex = this.childNodes.indexOf(child);
|
2016-01-13 21:35:21 -08:00
|
|
|
if (siblingIndex !== -1) {
|
2017-02-14 21:03:18 -08:00
|
|
|
this.childNodes.splice(siblingIndex + 1, 0, ...newChildren);
|
|
|
|
newChildren.forEach(c => {
|
|
|
|
if (c.parent) {
|
2018-11-21 21:14:06 -08:00
|
|
|
(c.parent as DebugElement__PRE_R3__).removeChild(c);
|
2016-01-13 21:35:21 -08:00
|
|
|
}
|
2018-11-21 21:14:06 -08:00
|
|
|
(child as{parent: DebugNode}).parent = this;
|
2017-02-14 21:03:18 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
insertBefore(refChild: DebugNode, newChild: DebugNode): void {
|
|
|
|
const refIndex = this.childNodes.indexOf(refChild);
|
|
|
|
if (refIndex === -1) {
|
|
|
|
this.addChild(newChild);
|
|
|
|
} else {
|
|
|
|
if (newChild.parent) {
|
2018-11-21 21:14:06 -08:00
|
|
|
(newChild.parent as DebugElement__PRE_R3__).removeChild(newChild);
|
2016-01-13 21:35:21 -08:00
|
|
|
}
|
2018-11-21 21:14:06 -08:00
|
|
|
(newChild as{parent: DebugNode}).parent = this;
|
2017-02-14 21:03:18 -08:00
|
|
|
this.childNodes.splice(refIndex, 0, newChild);
|
2016-01-13 21:35:21 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
query(predicate: Predicate<DebugElement>): DebugElement {
|
2016-11-03 16:58:27 -07:00
|
|
|
const results = this.queryAll(predicate);
|
|
|
|
return results[0] || null;
|
2016-01-13 21:35:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
queryAll(predicate: Predicate<DebugElement>): DebugElement[] {
|
2016-11-03 16:58:27 -07:00
|
|
|
const matches: DebugElement[] = [];
|
2016-01-13 21:35:21 -08:00
|
|
|
_queryElementChildren(this, predicate, matches);
|
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
|
|
|
|
queryAllNodes(predicate: Predicate<DebugNode>): DebugNode[] {
|
2016-11-03 16:58:27 -07:00
|
|
|
const matches: DebugNode[] = [];
|
2016-01-13 21:35:21 -08:00
|
|
|
_queryNodeChildren(this, predicate, matches);
|
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
|
|
|
|
get children(): DebugElement[] {
|
2018-11-21 21:14:06 -08:00
|
|
|
return this
|
|
|
|
.childNodes //
|
|
|
|
.filter((node) => node instanceof DebugElement__PRE_R3__) as DebugElement[];
|
2016-01-13 21:35:21 -08:00
|
|
|
}
|
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
triggerEventHandler(eventName: string, eventObj: any) {
|
2016-01-13 21:35:21 -08:00
|
|
|
this.listeners.forEach((listener) => {
|
|
|
|
if (listener.name == eventName) {
|
|
|
|
listener.callback(eventObj);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-25 15:00:05 -07:00
|
|
|
/**
|
2018-10-19 12:12:20 +01:00
|
|
|
* @publicApi
|
2016-05-25 15:00:05 -07:00
|
|
|
*/
|
2016-01-13 21:35:21 -08:00
|
|
|
export function asNativeElements(debugEls: DebugElement[]): any {
|
|
|
|
return debugEls.map((el) => el.nativeElement);
|
|
|
|
}
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
function _queryElementChildren(
|
|
|
|
element: DebugElement, predicate: Predicate<DebugElement>, matches: DebugElement[]) {
|
2016-01-13 21:35:21 -08:00
|
|
|
element.childNodes.forEach(node => {
|
2018-11-21 21:14:06 -08:00
|
|
|
if (node instanceof DebugElement__PRE_R3__) {
|
2016-01-13 21:35:21 -08:00
|
|
|
if (predicate(node)) {
|
|
|
|
matches.push(node);
|
|
|
|
}
|
|
|
|
_queryElementChildren(node, predicate, matches);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
function _queryNodeChildren(
|
|
|
|
parentNode: DebugNode, predicate: Predicate<DebugNode>, matches: DebugNode[]) {
|
2018-11-21 21:14:06 -08:00
|
|
|
if (parentNode instanceof DebugElement__PRE_R3__) {
|
2016-01-13 21:35:21 -08:00
|
|
|
parentNode.childNodes.forEach(node => {
|
|
|
|
if (predicate(node)) {
|
|
|
|
matches.push(node);
|
|
|
|
}
|
2018-11-21 21:14:06 -08:00
|
|
|
if (node instanceof DebugElement__PRE_R3__) {
|
2016-01-13 21:35:21 -08:00
|
|
|
_queryNodeChildren(node, predicate, matches);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 21:14:06 -08:00
|
|
|
function notImplemented(): Error {
|
|
|
|
throw new Error('Missing proper ivy implementation.');
|
|
|
|
}
|
|
|
|
|
|
|
|
class DebugNode__POST_R3__ implements DebugNode {
|
|
|
|
readonly nativeNode: Node;
|
|
|
|
|
|
|
|
constructor(nativeNode: Node) { this.nativeNode = nativeNode; }
|
|
|
|
|
|
|
|
get parent(): DebugElement|null {
|
|
|
|
const parent = this.nativeNode.parentNode as HTMLElement;
|
|
|
|
return parent ? new DebugElement__POST_R3__(parent) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
get injector(): Injector { return getInjector(this.nativeNode); }
|
|
|
|
|
|
|
|
get componentInstance(): any {
|
|
|
|
const nativeElement = this.nativeNode;
|
|
|
|
return nativeElement && getComponent(nativeElement as HTMLElement);
|
|
|
|
}
|
|
|
|
get context(): any {
|
|
|
|
// https://angular-team.atlassian.net/browse/FW-719
|
|
|
|
throw notImplemented();
|
|
|
|
}
|
|
|
|
|
|
|
|
get listeners(): EventListener[] {
|
|
|
|
// TODO: add real implementation;
|
|
|
|
// https://angular-team.atlassian.net/browse/FW-719
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
get references(): {[key: string]: any;} { return getLocalRefs(this.nativeNode); }
|
|
|
|
|
|
|
|
get providerTokens(): any[] {
|
|
|
|
// TODO move to discoverable utils
|
|
|
|
const context = loadContext(this.nativeNode as HTMLElement, false) !;
|
|
|
|
if (!context) return [];
|
|
|
|
const lView = context.lViewData;
|
|
|
|
const tView = lView[TVIEW];
|
|
|
|
const tNode = tView.data[context.nodeIndex] as TNode;
|
|
|
|
const providerTokens: any[] = [];
|
|
|
|
const nodeFlags = tNode.flags;
|
|
|
|
const startIndex = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
|
|
|
|
const directiveCount = nodeFlags & TNodeFlags.DirectiveCountMask;
|
|
|
|
const endIndex = startIndex + directiveCount;
|
|
|
|
for (let i = startIndex; i < endIndex; i++) {
|
|
|
|
let value = tView.data[i];
|
|
|
|
if (isDirectiveDefHack(value)) {
|
|
|
|
// The fact that we sometimes store Type and sometimes DirectiveDef in this location is a
|
|
|
|
// design flaw. We should always store same type so that we can be monomorphic. The issue
|
|
|
|
// is that for Components/Directives we store the def instead the type. The correct behavior
|
|
|
|
// is that we should always be storing injectable type in this location.
|
|
|
|
value = value.type;
|
|
|
|
}
|
|
|
|
providerTokens.push(value);
|
|
|
|
}
|
|
|
|
return providerTokens;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugElement {
|
|
|
|
constructor(nativeNode: Element) {
|
|
|
|
ngDevMode && assertDomNode(nativeNode);
|
|
|
|
super(nativeNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
get nativeElement(): Element|null {
|
|
|
|
return this.nativeNode.nodeType == Node.ELEMENT_NODE ? this.nativeNode as Element : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
get name(): string { return (this.nativeElement as HTMLElement).nodeName; }
|
|
|
|
|
|
|
|
get properties(): {[key: string]: any;} {
|
|
|
|
const context = loadContext(this.nativeNode) !;
|
|
|
|
const lView = context.lViewData;
|
|
|
|
const tView = lView[TVIEW];
|
|
|
|
const tNode = tView.data[context.nodeIndex] as TNode;
|
|
|
|
const properties = {};
|
|
|
|
// TODO: https://angular-team.atlassian.net/browse/FW-681
|
|
|
|
// Missing implementation here...
|
|
|
|
return properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
get attributes(): {[key: string]: string | null;} {
|
|
|
|
// https://angular-team.atlassian.net/browse/FW-719
|
|
|
|
throw notImplemented();
|
|
|
|
}
|
|
|
|
|
|
|
|
get classes(): {[key: string]: boolean;} {
|
|
|
|
// https://angular-team.atlassian.net/browse/FW-719
|
|
|
|
throw notImplemented();
|
|
|
|
}
|
|
|
|
|
|
|
|
get styles(): {[key: string]: string | null;} {
|
|
|
|
// https://angular-team.atlassian.net/browse/FW-719
|
|
|
|
throw notImplemented();
|
|
|
|
}
|
|
|
|
|
|
|
|
get childNodes(): DebugNode[] {
|
|
|
|
const childNodes = this.nativeNode.childNodes;
|
|
|
|
const children: DebugNode[] = [];
|
|
|
|
for (let i = 0; i < childNodes.length; i++) {
|
|
|
|
const element = childNodes[i];
|
|
|
|
children.push(getDebugNode__POST_R3__(element));
|
|
|
|
}
|
|
|
|
return children;
|
|
|
|
}
|
|
|
|
|
|
|
|
get children(): DebugElement[] {
|
|
|
|
const nativeElement = this.nativeElement;
|
|
|
|
if (!nativeElement) return [];
|
|
|
|
const childNodes = nativeElement.children;
|
|
|
|
const children: DebugElement[] = [];
|
|
|
|
for (let i = 0; i < childNodes.length; i++) {
|
|
|
|
const element = childNodes[i];
|
|
|
|
children.push(getDebugNode__POST_R3__(element));
|
|
|
|
}
|
|
|
|
return children;
|
|
|
|
}
|
|
|
|
|
|
|
|
query(predicate: Predicate<DebugElement>): DebugElement {
|
|
|
|
const results = this.queryAll(predicate);
|
|
|
|
return results[0] || null;
|
|
|
|
}
|
|
|
|
|
|
|
|
queryAll(predicate: Predicate<DebugElement>): DebugElement[] {
|
|
|
|
const matches: DebugElement[] = [];
|
|
|
|
_queryNodeChildrenR3(this, predicate, matches, true);
|
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
|
|
|
|
queryAllNodes(predicate: Predicate<DebugNode>): DebugNode[] {
|
|
|
|
const matches: DebugNode[] = [];
|
|
|
|
_queryNodeChildrenR3(this, predicate, matches, false);
|
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
|
|
|
|
triggerEventHandler(eventName: string, eventObj: any): void {
|
|
|
|
// This is a hack implementation. The correct implementation would bypass the DOM and `TNode`
|
|
|
|
// information to invoke the listeners directly.
|
|
|
|
// https://angular-team.atlassian.net/browse/FW-719
|
|
|
|
const event = document.createEvent('MouseEvent');
|
|
|
|
event.initEvent(eventName, true, true);
|
|
|
|
(this.nativeElement as HTMLElement).dispatchEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function should not exist because it is megamorphic and only mostly correct.
|
|
|
|
*
|
|
|
|
* See call site for more info.
|
|
|
|
*/
|
|
|
|
function isDirectiveDefHack(obj: any): obj is DirectiveDef<any> {
|
|
|
|
return obj.type !== undefined && obj.template !== undefined && obj.declaredInputs !== undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _queryNodeChildrenR3(
|
|
|
|
parentNode: DebugNode, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
|
|
|
elementsOnly: boolean) {
|
|
|
|
if (parentNode instanceof DebugElement__POST_R3__) {
|
|
|
|
parentNode.childNodes.forEach(node => {
|
|
|
|
if (predicate(node)) {
|
|
|
|
matches.push(node);
|
|
|
|
}
|
|
|
|
if (node instanceof DebugElement__POST_R3__) {
|
|
|
|
if (elementsOnly ? node.nativeElement : true) {
|
|
|
|
_queryNodeChildrenR3(node, predicate, matches, elementsOnly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-13 21:35:21 -08:00
|
|
|
// Need to keep the nodes in a global Map so that multiple angular apps are supported.
|
2016-11-03 16:58:27 -07:00
|
|
|
const _nativeNodeToDebugNode = new Map<any, DebugNode>();
|
2016-01-13 21:35:21 -08:00
|
|
|
|
2018-11-21 21:14:06 -08:00
|
|
|
function getDebugNode__PRE_R3__(nativeNode: any): DebugNode|null {
|
|
|
|
return _nativeNodeToDebugNode.get(nativeNode) || null;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getDebugNode__POST_R3__(nativeNode: Element): DebugElement__POST_R3__;
|
|
|
|
export function getDebugNode__POST_R3__(nativeNode: Node): DebugNode__POST_R3__;
|
|
|
|
export function getDebugNode__POST_R3__(nativeNode: null): null;
|
|
|
|
export function getDebugNode__POST_R3__(nativeNode: any): DebugNode|null {
|
|
|
|
if (nativeNode instanceof Node) {
|
|
|
|
return nativeNode.nodeType == Node.ELEMENT_NODE ?
|
|
|
|
new DebugElement__POST_R3__(nativeNode as Element) :
|
|
|
|
new DebugNode__POST_R3__(nativeNode);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2016-05-25 15:00:05 -07:00
|
|
|
/**
|
2018-10-19 12:12:20 +01:00
|
|
|
* @publicApi
|
2016-05-25 15:00:05 -07:00
|
|
|
*/
|
2018-11-21 21:14:06 -08:00
|
|
|
export const getDebugNode: (nativeNode: any) => DebugNode | null = getDebugNode__PRE_R3__;
|
2016-01-13 21:35:21 -08:00
|
|
|
|
|
|
|
export function getAllDebugNodes(): DebugNode[] {
|
2016-11-03 16:58:27 -07:00
|
|
|
return Array.from(_nativeNodeToDebugNode.values());
|
2016-01-13 21:35:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
export function indexDebugNode(node: DebugNode) {
|
|
|
|
_nativeNodeToDebugNode.set(node.nativeNode, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function removeDebugNodeFromIndex(node: DebugNode) {
|
|
|
|
_nativeNodeToDebugNode.delete(node.nativeNode);
|
|
|
|
}
|
2017-03-01 14:10:59 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A boolean-valued function over a value, possibly including context information
|
|
|
|
* regarding that value's position in an array.
|
|
|
|
*
|
2018-10-19 12:12:20 +01:00
|
|
|
* @publicApi
|
2017-03-01 14:10:59 -08:00
|
|
|
*/
|
2017-03-29 09:34:45 -07:00
|
|
|
export interface Predicate<T> { (value: T): boolean; }
|
2018-11-21 21:14:06 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @publicApi
|
|
|
|
*/
|
|
|
|
export const DebugNode: {new (...args: any[]): DebugNode} = DebugNode__PRE_R3__ as any;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @publicApi
|
|
|
|
*/
|
|
|
|
export const DebugElement: {new (...args: any[]): DebugElement} = DebugElement__PRE_R3__ as any;
|