angular/projects/ng-devtools-backend/src/lib/lview-transform.ts

99 lines
2.8 KiB
TypeScript

import { ComponentTreeNode, ComponentInstanceType, DirectiveInstanceType } from './component-tree';
import { isCustomElement } from './utils';
import { getDirectiveName } from './highlighter';
const HEADER_OFFSET = 19;
const TYPE = 1;
const ELEMENT = 0;
const LVIEW_TVIEW = 1;
export const METADATA_PROPERTY_NAME = '__ngContext__';
const isLContainer = (value: any): boolean => {
return Array.isArray(value) && value[TYPE] === true;
};
const isLView = (value: any): boolean => {
return Array.isArray(value) && typeof value[TYPE] === 'object';
};
export const getLViewFromDirectiveOrElementInstance = (dir: any): null | {} => {
if (!dir) {
return null;
}
const context = dir[METADATA_PROPERTY_NAME];
if (!context) {
return null;
}
if (isLView(context)) {
return context;
}
return context.lView;
};
export const getDirectiveHostElement = (dir: any) => {
const ctx = dir[METADATA_PROPERTY_NAME];
if (ctx[0] !== null) {
return ctx[0];
}
const components = ctx[LVIEW_TVIEW].components;
if (!components || components.length !== 1) {
return false;
}
return ctx[components[0]][0];
};
const getNode = (lView: any, data: any, idx: number): ComponentTreeNode => {
const directives: DirectiveInstanceType[] = [];
let component: ComponentInstanceType | null = null;
const tNode = data[idx];
const node = lView[idx][ELEMENT] || lView[idx][ELEMENT];
const elementName = (node.tagName || node.nodeName).toLowerCase();
for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
const dir = lView[i];
const dirMeta = data[i];
if (dirMeta && dirMeta.template) {
component = {
name: elementName,
instance: dir,
isElement: isCustomElement(node),
};
} else if (dirMeta) {
directives.push({
name: getDirectiveName(dir),
instance: dir,
});
}
}
return {
element: elementName,
nativeElement: lView[idx][ELEMENT],
directives,
component,
children: [],
};
};
const extractNodes = (lViewOrLContainer: any, nodes: ComponentTreeNode[] = []): ComponentTreeNode[] => {
if (isLContainer(lViewOrLContainer)) {
for (let i = 9; i < lViewOrLContainer.length; i++) {
extractNodes(lViewOrLContainer[i], nodes);
}
return nodes;
}
const lView = lViewOrLContainer;
const tView = lView[LVIEW_TVIEW];
for (let i = HEADER_OFFSET; i < lView.length; i++) {
if (lView[i] && lView[i][ELEMENT] instanceof Node) {
const node = getNode(lView, tView.data, i);
// TODO(mgechev): verify if this won't make us skip projected content.
if (node.component || node.directives.length) {
nodes.push(node);
extractNodes(lView[i], node.children);
}
}
}
return nodes;
};
export const buildDirectiveTree = (lView: any) => extractNodes(lView);