refactor(ivy): assertion (#22189)
Encourage the use of message to explain the assertion PR Close #22189
This commit is contained in:
parent
363498b6b4
commit
0b683123d2
|
@ -10,58 +10,48 @@
|
||||||
// about state in an instruction are correct before implementing any logic.
|
// about state in an instruction are correct before implementing any logic.
|
||||||
// They are meant only to be called in dev mode as sanity checks.
|
// They are meant only to be called in dev mode as sanity checks.
|
||||||
|
|
||||||
/**
|
export function assertNumber(actual: any, msg: string) {
|
||||||
* Stringifies values such that strings are wrapped in explicit quotation marks and
|
if (typeof actual != 'number') {
|
||||||
* other types are stringified normally. Used in error messages (e.g. assertThrow)
|
throwError(msg);
|
||||||
* to make it clear that certain values are of the string type when comparing.
|
|
||||||
*
|
|
||||||
* e.g. `expected "3" to be 3` is easier to understand than `expected 3 to be 3`.
|
|
||||||
*
|
|
||||||
* @param value The value to be stringified
|
|
||||||
* @returns The stringified value
|
|
||||||
*/
|
|
||||||
function stringifyValueForError(value: any): string {
|
|
||||||
if (value && value.native && value.native.outerHTML) {
|
|
||||||
return value.native.outerHTML;
|
|
||||||
}
|
}
|
||||||
return typeof value === 'string' ? `"${value}"` : value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertNumber(actual: any, name: string) {
|
export function assertEqual<T>(actual: T, expected: T, msg: string) {
|
||||||
(typeof actual != 'number') && assertThrow(actual, 'number', name, 'typeof ==');
|
if (actual != expected) {
|
||||||
|
throwError(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertEqual<T>(
|
export function assertNotEqual<T>(actual: T, expected: T, msg: string) {
|
||||||
actual: T, expected: T, name: string, serializer?: ((v: T) => string)) {
|
if (actual == expected) {
|
||||||
(actual != expected) && assertThrow(actual, expected, name, '==', serializer);
|
throwError(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertLessThan<T>(actual: T, expected: T, name: string) {
|
export function assertSame<T>(actual: T, expected: T, msg: string) {
|
||||||
(actual >= expected) && assertThrow(actual, expected, name, '<');
|
if (actual !== expected) {
|
||||||
|
throwError(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertNotNull<T>(actual: T, name: string) {
|
export function assertLessThan<T>(actual: T, expected: T, msg: string) {
|
||||||
assertNotEqual(actual, null, name);
|
if (actual >= expected) {
|
||||||
|
throwError(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertNotEqual<T>(actual: T, expected: T, name: string) {
|
export function assertNull<T>(actual: T, msg: string) {
|
||||||
(actual == expected) && assertThrow(actual, expected, name, '!=');
|
if (actual != null) {
|
||||||
|
throwError(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function assertNotNull<T>(actual: T, msg: string) {
|
||||||
* Throws an error with a message constructed from the arguments.
|
if (actual == null) {
|
||||||
*
|
throwError(msg);
|
||||||
* @param actual The actual value (e.g. 3)
|
}
|
||||||
* @param expected The expected value (e.g. 5)
|
}
|
||||||
* @param name The name of the value being checked (e.g. attrs.length)
|
|
||||||
* @param operator The comparison operator (e.g. <, >, ==)
|
function throwError(msg: string): never {
|
||||||
* @param serializer Function that maps a value to its display value
|
throw new Error(`ASSERTION ERROR: ${msg}`);
|
||||||
*/
|
|
||||||
export function assertThrow<T>(
|
|
||||||
actual: T, expected: T, name: string, operator: string,
|
|
||||||
serializer: ((v: T) => string) = stringifyValueForError): never {
|
|
||||||
const error =
|
|
||||||
`ASSERT: expected ${name} ${operator} ${serializer(expected)} but was ${serializer(actual)}!`;
|
|
||||||
debugger; // leave `debugger` here to aid in debugging.
|
|
||||||
throw new Error(error);
|
|
||||||
}
|
}
|
|
@ -189,12 +189,12 @@ export function renderComponent<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function detectChanges<T>(component: T) {
|
export function detectChanges<T>(component: T) {
|
||||||
ngDevMode && assertNotNull(component, 'component');
|
ngDevMode && assertNotNull(component, 'detectChanges should be called with a component');
|
||||||
const hostNode = (component as any)[NG_HOST_SYMBOL] as LElementNode;
|
const hostNode = (component as any)[NG_HOST_SYMBOL] as LElementNode;
|
||||||
if (ngDevMode && !hostNode) {
|
if (ngDevMode && !hostNode) {
|
||||||
createError('Not a directive instance', component);
|
createError('Not a directive instance', component);
|
||||||
}
|
}
|
||||||
ngDevMode && assertNotNull(hostNode.data, 'hostNode.data');
|
ngDevMode && assertNotNull(hostNode.data, 'Component host node should be attached to an LView');
|
||||||
renderComponentOrTemplate(hostNode, hostNode.view, component);
|
renderComponentOrTemplate(hostNode, hostNode.view, component);
|
||||||
isDirty = false;
|
isDirty = false;
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ export function detectChanges<T>(component: T) {
|
||||||
let isDirty = false;
|
let isDirty = false;
|
||||||
export function markDirty<T>(
|
export function markDirty<T>(
|
||||||
component: T, scheduler: (fn: () => void) => void = requestAnimationFrame) {
|
component: T, scheduler: (fn: () => void) => void = requestAnimationFrame) {
|
||||||
ngDevMode && assertNotNull(component, 'component');
|
ngDevMode && assertNotNull(component, 'markDirty should be called with a component');
|
||||||
if (!isDirty) {
|
if (!isDirty) {
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
scheduler(() => detectChanges(component));
|
scheduler(() => detectChanges(component));
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import './ng_dev_mode';
|
import './ng_dev_mode';
|
||||||
|
|
||||||
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull} from './assert';
|
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull, assertNull, assertSame} from './assert';
|
||||||
import {LContainer, TContainer} from './interfaces/container';
|
import {LContainer, TContainer} from './interfaces/container';
|
||||||
import {CssSelector, LProjection} from './interfaces/projection';
|
import {CssSelector, LProjection} from './interfaces/projection';
|
||||||
import {LQueries} from './interfaces/query';
|
import {LQueries} from './interfaces/query';
|
||||||
|
@ -233,7 +233,7 @@ export function createLNode(
|
||||||
if ((type & LNodeFlags.ViewOrElement) === LNodeFlags.ViewOrElement && isState) {
|
if ((type & LNodeFlags.ViewOrElement) === LNodeFlags.ViewOrElement && isState) {
|
||||||
// Bit of a hack to bust through the readonly because there is a circular dep between
|
// Bit of a hack to bust through the readonly because there is a circular dep between
|
||||||
// LView and LNode.
|
// LView and LNode.
|
||||||
ngDevMode && assertEqual((state as LView).node, null, 'lView.node');
|
ngDevMode && assertNull((state as LView).node, 'LView.node should not have been initialized');
|
||||||
(state as LView as{node: LNode}).node = node;
|
(state as LView as{node: LNode}).node = node;
|
||||||
}
|
}
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
|
@ -254,13 +254,17 @@ export function createLNode(
|
||||||
if (previousOrParentNode.view === currentView ||
|
if (previousOrParentNode.view === currentView ||
|
||||||
(previousOrParentNode.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.View) {
|
(previousOrParentNode.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.View) {
|
||||||
// We are in the same view, which means we are adding content node to the parent View.
|
// We are in the same view, which means we are adding content node to the parent View.
|
||||||
ngDevMode && assertEqual(previousOrParentNode.child, null, 'previousNode.child');
|
ngDevMode && assertNull(
|
||||||
|
previousOrParentNode.child,
|
||||||
|
`previousOrParentNode's child should not have been set.`);
|
||||||
previousOrParentNode.child = node;
|
previousOrParentNode.child = node;
|
||||||
} else {
|
} else {
|
||||||
// We are adding component view, so we don't link parent node child to this node.
|
// We are adding component view, so we don't link parent node child to this node.
|
||||||
}
|
}
|
||||||
} else if (previousOrParentNode) {
|
} else if (previousOrParentNode) {
|
||||||
ngDevMode && assertEqual(previousOrParentNode.next, null, 'previousNode.next');
|
ngDevMode && assertNull(
|
||||||
|
previousOrParentNode.next,
|
||||||
|
`previousOrParentNode's next property should not have been set.`);
|
||||||
previousOrParentNode.next = node;
|
previousOrParentNode.next = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,7 +304,7 @@ export function renderTemplate<T>(
|
||||||
-1, providedRendererFactory.createRenderer(null, null), getOrCreateTView(template)));
|
-1, providedRendererFactory.createRenderer(null, null), getOrCreateTView(template)));
|
||||||
}
|
}
|
||||||
const hostView = host.data !;
|
const hostView = host.data !;
|
||||||
ngDevMode && assertNotEqual(hostView, null, 'hostView');
|
ngDevMode && assertNotNull(hostView, 'Host node should have an LView defined in host.data.');
|
||||||
renderComponentOrTemplate(host, hostView, context, template);
|
renderComponentOrTemplate(host, hostView, context, template);
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
@ -381,7 +385,8 @@ export function elementStart(
|
||||||
const node = data[index] !;
|
const node = data[index] !;
|
||||||
native = node && (node as LElementNode).native;
|
native = node && (node as LElementNode).native;
|
||||||
} else {
|
} else {
|
||||||
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
|
ngDevMode &&
|
||||||
|
assertNull(currentView.bindingStartIndex, 'elements should be created before any bindings');
|
||||||
const isHostElement = typeof nameOrComponentType !== 'string';
|
const isHostElement = typeof nameOrComponentType !== 'string';
|
||||||
// MEGAMORPHIC: `ngComponentDef` is a megamorphic property access here.
|
// MEGAMORPHIC: `ngComponentDef` is a megamorphic property access here.
|
||||||
// This is OK, since we will refactor this code and store the result in `TView.data`
|
// This is OK, since we will refactor this code and store the result in `TView.data`
|
||||||
|
@ -504,7 +509,7 @@ export function createTView(): TView {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpAttributes(native: RElement, attrs: string[]): void {
|
function setUpAttributes(native: RElement, attrs: string[]): void {
|
||||||
ngDevMode && assertEqual(attrs.length % 2, 0, 'attrs.length % 2');
|
ngDevMode && assertEqual(attrs.length % 2, 0, 'each attribute should have a key and a value');
|
||||||
|
|
||||||
const isProc = isProceduralRenderer(renderer);
|
const isProc = isProceduralRenderer(renderer);
|
||||||
for (let i = 0; i < attrs.length; i += 2) {
|
for (let i = 0; i < attrs.length; i += 2) {
|
||||||
|
@ -809,7 +814,8 @@ export function elementStyle<T>(
|
||||||
* If value is not provided than the actual creation of the text node is delayed.
|
* If value is not provided than the actual creation of the text node is delayed.
|
||||||
*/
|
*/
|
||||||
export function text(index: number, value?: any): void {
|
export function text(index: number, value?: any): void {
|
||||||
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
|
ngDevMode &&
|
||||||
|
assertNull(currentView.bindingStartIndex, 'text nodes should be created before bindings');
|
||||||
const textNode = value != null ?
|
const textNode = value != null ?
|
||||||
(isProceduralRenderer(renderer) ? renderer.createText(stringify(value)) :
|
(isProceduralRenderer(renderer) ? renderer.createText(stringify(value)) :
|
||||||
renderer.createTextNode(stringify(value))) :
|
renderer.createTextNode(stringify(value))) :
|
||||||
|
@ -865,7 +871,8 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
|
||||||
export function directiveCreate<T>(
|
export function directiveCreate<T>(
|
||||||
index: number, directive: T, directiveDef: DirectiveDef<T>, queryName?: string | null): T {
|
index: number, directive: T, directiveDef: DirectiveDef<T>, queryName?: string | null): T {
|
||||||
let instance;
|
let instance;
|
||||||
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
|
ngDevMode &&
|
||||||
|
assertNull(currentView.bindingStartIndex, 'directives should be created before any bindings');
|
||||||
ngDevMode && assertPreviousIsParent();
|
ngDevMode && assertPreviousIsParent();
|
||||||
let flags = previousOrParentNode !.flags;
|
let flags = previousOrParentNode !.flags;
|
||||||
let size = flags & LNodeFlags.SIZE_MASK;
|
let size = flags & LNodeFlags.SIZE_MASK;
|
||||||
|
@ -984,10 +991,12 @@ function generateInitialInputs(
|
||||||
export function container(
|
export function container(
|
||||||
index: number, directiveTypes?: DirectiveType<any>[], template?: ComponentTemplate<any>,
|
index: number, directiveTypes?: DirectiveType<any>[], template?: ComponentTemplate<any>,
|
||||||
tagName?: string, attrs?: string[], localRefs?: string[] | null): void {
|
tagName?: string, attrs?: string[], localRefs?: string[] | null): void {
|
||||||
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
|
ngDevMode &&
|
||||||
|
assertNull(
|
||||||
|
currentView.bindingStartIndex, 'container nodes should be created before any bindings');
|
||||||
|
|
||||||
const currentParent = isParent ? previousOrParentNode : previousOrParentNode.parent !;
|
const currentParent = isParent ? previousOrParentNode : previousOrParentNode.parent !;
|
||||||
ngDevMode && assertNotEqual(currentParent, null, 'currentParent');
|
ngDevMode && assertNotNull(currentParent, 'containers should have a parent');
|
||||||
|
|
||||||
const lContainer = <LContainer>{
|
const lContainer = <LContainer>{
|
||||||
views: [],
|
views: [],
|
||||||
|
@ -1037,9 +1046,9 @@ export function containerRefreshStart(index: number): void {
|
||||||
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Container);
|
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Container);
|
||||||
isParent = true;
|
isParent = true;
|
||||||
(previousOrParentNode as LContainerNode).data.nextIndex = 0;
|
(previousOrParentNode as LContainerNode).data.nextIndex = 0;
|
||||||
ngDevMode && assertEqual(
|
ngDevMode && assertSame(
|
||||||
(previousOrParentNode as LContainerNode).native === undefined, true,
|
(previousOrParentNode as LContainerNode).native, undefined,
|
||||||
'previousOrParentNode.native === undefined');
|
`the container's native element should not have been set yet.`);
|
||||||
|
|
||||||
// We need to execute init hooks here so ngOnInit hooks are called in top level views
|
// We need to execute init hooks here so ngOnInit hooks are called in top level views
|
||||||
// before they are called in embedded views (for backwards compatibility).
|
// before they are called in embedded views (for backwards compatibility).
|
||||||
|
@ -1181,11 +1190,11 @@ export function componentRefresh<T>(directiveIndex: number, elementIndex: number
|
||||||
ngDevMode && assertDataInRange(elementIndex);
|
ngDevMode && assertDataInRange(elementIndex);
|
||||||
const element = data ![elementIndex] as LElementNode;
|
const element = data ![elementIndex] as LElementNode;
|
||||||
ngDevMode && assertNodeType(element, LNodeFlags.Element);
|
ngDevMode && assertNodeType(element, LNodeFlags.Element);
|
||||||
ngDevMode && assertNotEqual(element.data, null, 'isComponent');
|
ngDevMode &&
|
||||||
|
assertNotNull(element.data, `Component's host node should have an LView attached.`);
|
||||||
ngDevMode && assertDataInRange(directiveIndex);
|
ngDevMode && assertDataInRange(directiveIndex);
|
||||||
const directive = getDirectiveInstance<T>(data[directiveIndex]);
|
const directive = getDirectiveInstance<T>(data[directiveIndex]);
|
||||||
const hostView = element.data !;
|
const hostView = element.data !;
|
||||||
ngDevMode && assertNotEqual(hostView, null, 'hostView');
|
|
||||||
const oldView = enterView(hostView, element);
|
const oldView = enterView(hostView, element);
|
||||||
try {
|
try {
|
||||||
template(directive, creationMode);
|
template(directive, creationMode);
|
||||||
|
@ -1243,9 +1252,9 @@ function appendToProjectionNode(
|
||||||
projectionNode: LProjectionNode,
|
projectionNode: LProjectionNode,
|
||||||
appendedFirst: LElementNode | LTextNode | LContainerNode | null,
|
appendedFirst: LElementNode | LTextNode | LContainerNode | null,
|
||||||
appendedLast: LElementNode | LTextNode | LContainerNode | null) {
|
appendedLast: LElementNode | LTextNode | LContainerNode | null) {
|
||||||
// appendedFirst can be null if and only if appendedLast is also null
|
ngDevMode && assertEqual(
|
||||||
ngDevMode &&
|
!!appendedFirst, !!appendedLast,
|
||||||
assertEqual(!appendedFirst === !appendedLast, true, '!appendedFirst === !appendedLast');
|
'appendedFirst can be null if and only if appendedLast is also null');
|
||||||
if (!appendedLast) {
|
if (!appendedLast) {
|
||||||
// nothing to append
|
// nothing to append
|
||||||
return;
|
return;
|
||||||
|
@ -1760,18 +1769,18 @@ export function getDirectiveInstance<T>(instanceOrArray: T | [T]): T {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertPreviousIsParent() {
|
export function assertPreviousIsParent() {
|
||||||
assertEqual(isParent, true, 'isParent');
|
assertEqual(isParent, true, 'previousOrParentNode should be a parent');
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertHasParent() {
|
function assertHasParent() {
|
||||||
assertNotEqual(previousOrParentNode.parent, null, 'isParent');
|
assertNotNull(previousOrParentNode.parent, 'previousOrParentNode should have a parent');
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertDataInRange(index: number, arr?: any[]) {
|
function assertDataInRange(index: number, arr?: any[]) {
|
||||||
if (arr == null) arr = data;
|
if (arr == null) arr = data;
|
||||||
assertLessThan(index, arr ? arr.length : 0, 'data.length');
|
assertLessThan(index, arr ? arr.length : 0, 'index expected to be a valid data index');
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertDataNext(index: number) {
|
function assertDataNext(index: number) {
|
||||||
assertEqual(data.length, index, 'data.length not in sequence');
|
assertEqual(data.length, index, 'index expected to be at the end of data');
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,30 +6,25 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {assertEqual, assertNotEqual} from './assert';
|
import {assertEqual, assertNotNull} from './assert';
|
||||||
import {LNode, LNodeFlags} from './interfaces/node';
|
import {LNode, LNodeFlags} from './interfaces/node';
|
||||||
|
|
||||||
export function assertNodeType(node: LNode, type: LNodeFlags) {
|
export function assertNodeType(node: LNode, type: LNodeFlags) {
|
||||||
assertNotEqual(node, null, 'node');
|
assertNotNull(node, 'should be called with a node');
|
||||||
assertEqual(node.flags & LNodeFlags.TYPE_MASK, type, 'Node.type', typeSerializer);
|
assertEqual(node.flags & LNodeFlags.TYPE_MASK, type, `should be a ${typeName(type)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertNodeOfPossibleTypes(node: LNode, ...types: LNodeFlags[]) {
|
export function assertNodeOfPossibleTypes(node: LNode, ...types: LNodeFlags[]) {
|
||||||
assertNotEqual(node, null, 'node');
|
assertNotNull(node, 'should be called with a node');
|
||||||
const nodeType = (node.flags & LNodeFlags.TYPE_MASK);
|
const nodeType = node.flags & LNodeFlags.TYPE_MASK;
|
||||||
for (let i = 0; i < types.length; i++) {
|
const found = types.some(type => nodeType === type);
|
||||||
if (nodeType === types[i]) {
|
assertEqual(found, true, `Should be one of ${types.map(typeName).join(', ')}`);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error(
|
|
||||||
`Expected node of possible types: ${types.map(typeSerializer).join(', ')} but got ${typeSerializer(nodeType)}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function typeSerializer(type: LNodeFlags): string {
|
function typeName(type: LNodeFlags): string {
|
||||||
if (type == LNodeFlags.Projection) return 'Projection';
|
if (type == LNodeFlags.Projection) return 'Projection';
|
||||||
if (type == LNodeFlags.Container) return 'Container';
|
if (type == LNodeFlags.Container) return 'Container';
|
||||||
if (type == LNodeFlags.View) return 'View';
|
if (type == LNodeFlags.View) return 'View';
|
||||||
if (type == LNodeFlags.Element) return 'Element';
|
if (type == LNodeFlags.Element) return 'Element';
|
||||||
return '??? ' + type + ' ???';
|
return '<unknown>';
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ function isCssClassMatching(nodeClassAttrVal: string, cssClassToMatch: string):
|
||||||
*/
|
*/
|
||||||
export function isNodeMatchingSimpleSelector(tNode: TNode, selector: SimpleCssSelector): boolean {
|
export function isNodeMatchingSimpleSelector(tNode: TNode, selector: SimpleCssSelector): boolean {
|
||||||
const noOfSelectorParts = selector.length;
|
const noOfSelectorParts = selector.length;
|
||||||
ngDevMode && assertNotNull(selector[0], 'selector[0]');
|
ngDevMode && assertNotNull(selector[0], 'the selector should have a tag name');
|
||||||
const tagNameInSelector = selector[0];
|
const tagNameInSelector = selector[0];
|
||||||
|
|
||||||
// check tag tame
|
// check tag tame
|
||||||
|
|
|
@ -245,7 +245,7 @@ function add(query: LQuery<any>| null, node: LNode) {
|
||||||
if (directiveIdx !== null) {
|
if (directiveIdx !== null) {
|
||||||
// a node is matching a predicate - determine what to read
|
// a node is matching a predicate - determine what to read
|
||||||
// note that queries using name selector must specify read strategy
|
// note that queries using name selector must specify read strategy
|
||||||
ngDevMode && assertNotNull(predicate.read, 'predicate.read');
|
ngDevMode && assertNotNull(predicate.read, 'the node should have a predicate');
|
||||||
const result = readFromNodeInjector(nodeInjector, node, predicate.read !, directiveIdx);
|
const result = readFromNodeInjector(nodeInjector, node, predicate.read !, directiveIdx);
|
||||||
if (result !== null) {
|
if (result !== null) {
|
||||||
addMatch(query, result);
|
addMatch(query, result);
|
||||||
|
|
Loading…
Reference in New Issue