feat(ivy): implement TestBed (#25369)

PR Close #25369
This commit is contained in:
Victor Berchet
2018-08-06 14:09:38 -07:00
committed by Ben Lesh
parent 85106375ac
commit 14ac7ad6b4
37 changed files with 1543 additions and 363 deletions

View File

@ -15,6 +15,8 @@ export {
detectChanges as ɵdetectChanges,
renderComponent as ɵrenderComponent,
ComponentType as ɵComponentType,
ComponentFactory as ɵRender3ComponentFactory,
ComponentRef as ɵRender3ComponentRef,
DirectiveType as ɵDirectiveType,
RenderFlags as ɵRenderFlags,
directiveInject as ɵdirectiveInject,
@ -29,6 +31,7 @@ export {
InheritDefinitionFeature as ɵInheritDefinitionFeature,
NgOnChangesFeature as ɵNgOnChangesFeature,
NgModuleType as ɵNgModuleType,
NgModuleRef as ɵRender3NgModuleRef,
CssSelectorList as ɵCssSelectorList,
markDirty as ɵmarkDirty,
NgModuleFactory as ɵNgModuleFactory,
@ -95,6 +98,7 @@ export {
ld as ɵld,
Pp as ɵPp,
ComponentDef as ɵComponentDef,
ComponentDefInternal as ɵComponentDefInternal,
DirectiveDef as ɵDirectiveDef,
PipeDef as ɵPipeDef,
whenRendered as ɵwhenRendered,
@ -112,14 +116,38 @@ export {
iM as ɵiM,
I18nInstruction as ɵI18nInstruction,
I18nExpInstruction as ɵI18nExpInstruction,
WRAP_RENDERER_FACTORY2 as ɵWRAP_RENDERER_FACTORY2,
Render3DebugRendererFactory2 as ɵRender3DebugRendererFactory2,
} from './render3/index';
export {NgModuleDef as ɵNgModuleDef} from './metadata/ng_module';
export {
compileNgModuleDefs as ɵcompileNgModuleDefs,
patchComponentDefWithScope as ɵpatchComponentDefWithScope,
} from './render3/jit/module';
export {
compileComponent as ɵcompileComponent,
compileDirective as ɵcompileDirective,
} from './render3/jit/directive';
export {
compilePipe as ɵcompilePipe,
} from './render3/jit/pipe';
export {
NgModuleDef as ɵNgModuleDef,
NgModuleDefInternal as ɵNgModuleDefInternal,
NgModuleTransitiveScopes as ɵNgModuleTransitiveScopes,
} from './metadata/ng_module';
export {
sanitizeHtml as ɵsanitizeHtml,
sanitizeStyle as ɵsanitizeStyle,
sanitizeUrl as ɵsanitizeUrl,
sanitizeResourceUrl as ɵsanitizeResourceUrl,
} from './sanitization/sanitization';
export {
bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml,
bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle,
@ -127,4 +155,4 @@ export {
bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl,
bypassSanitizationTrustResourceUrl as ɵbypassSanitizationTrustResourceUrl,
} from './sanitization/bypass';
// clang-format on
// clang-format on

View File

@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {InjectionToken} from '../di/injection_token';
import {Injector, inject} from '../di/injector';
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
import {ElementRef} from '../linker/element_ref';
import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
import {RendererFactory2} from '../render/api';
import {Type} from '../type';
@ -21,7 +21,7 @@ import {LifecycleHooksFeature, createRootContext} from './component';
import {baseDirectiveCreate, createLNode, createLViewData, createTView, elementCreate, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderEmbeddedTemplate} from './instructions';
import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition';
import {LElementNode, TNode, TNodeType} from './interfaces/node';
import {RElement, domRendererFactory3} from './interfaces/renderer';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {RootViewRef, ViewRef} from './view_ref';
@ -55,8 +55,20 @@ export const ROOT_CONTEXT = new InjectionToken<RootContext>(
* A change detection scheduler token for {@link RootContext}. This token is the default value used
* for the default `RootContext` found in the {@link ROOT_CONTEXT} token.
*/
export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>(
'SCHEDULER_TOKEN', {providedIn: 'root', factory: () => requestAnimationFrame.bind(window)});
export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>('SCHEDULER_TOKEN', {
providedIn: 'root',
factory: () => {
const useRaf = typeof requestAnimationFrame !== 'undefined' && typeof window !== 'undefined';
return useRaf ? requestAnimationFrame.bind(window) : setTimeout;
},
});
/**
* A function used to wrap the `RendererFactory2`.
* Used in tests to change the `RendererFactory2` into a `DebugRendererFactory2`.
*/
export const WRAP_RENDERER_FACTORY2 =
new InjectionToken<(rf: RendererFactory2) => RendererFactory2>('WRAP_RENDERER_FACTORY2');
/**
* Render3 implementation of {@link viewEngine_ComponentFactory}.
@ -65,9 +77,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
selector: string;
componentType: Type<any>;
ngContentSelectors: string[];
get inputs(): {propName: string; templateName: string;}[] {
return toRefArray(this.componentDef.inputs);
}
get outputs(): {propName: string; templateName: string;}[] {
return toRefArray(this.componentDef.outputs);
}
@ -84,8 +98,15 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
ngModule?: viewEngine_NgModuleRef<any>|undefined): viewEngine_ComponentRef<T> {
const isInternalRootView = rootSelectorOrNode === undefined;
const rendererFactory =
ngModule ? ngModule.injector.get(RendererFactory2) : domRendererFactory3;
let rendererFactory: RendererFactory3;
if (ngModule) {
const wrapper = ngModule.injector.get(WRAP_RENDERER_FACTORY2, (v: RendererFactory2) => v);
rendererFactory = wrapper(ngModule.injector.get(RendererFactory2)) as RendererFactory3;
} else {
rendererFactory = domRendererFactory3;
}
const hostNode = isInternalRootView ?
elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef)) :
locateHostElement(rendererFactory, rootSelectorOrNode);
@ -116,11 +137,10 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
elementNode = hostElement(componentTag, hostNode, this.componentDef);
// Create directive instance with factory() and store at index 0 in directives array
rootContext.components.push(
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef) as T);
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef);
rootContext.components.push(component);
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
(elementNode.data as LViewData)[CONTEXT] = component;
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
// executed here?
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
@ -177,11 +197,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
*/
export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
destroyCbs: (() => void)[]|null = [];
location: ElementRef<any>;
location: viewEngine_ElementRef<any>;
injector: Injector;
instance: T;
hostView: ViewRef<T>;
changeDetectorRef: ChangeDetectorRef;
changeDetectorRef: ViewEngine_ChangeDetectorRef;
componentType: Type<T>;
constructor(
@ -192,7 +212,7 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
this.hostView = this.changeDetectorRef = new RootViewRef<T>(rootView);
this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView);
this.injector = injector;
this.location = new ElementRef(hostNode);
this.location = new viewEngine_ElementRef(hostNode);
this.componentType = componentType;
}
@ -205,4 +225,4 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
this.destroyCbs !.push(callback);
}
}
}

View File

@ -0,0 +1,115 @@
/**
* @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
*/
import {Injector} from '../di/injector';
import {Renderer2, RendererType2} from '../render/api';
import {DebugContext} from '../view';
import {DebugRenderer2, DebugRendererFactory2} from '../view/services';
import * as di from './di';
import {NG_HOST_SYMBOL, _getViewData} from './instructions';
import {LElementNode} from './interfaces/node';
import {CONTEXT, DIRECTIVES, LViewData, TVIEW} from './interfaces/view';
/**
* Adapts the DebugRendererFactory2 to create a DebugRenderer2 specific for IVY.
*
* The created DebugRenderer know how to create a Debug Context specific to IVY.
*/
export class Render3DebugRendererFactory2 extends DebugRendererFactory2 {
createRenderer(element: any, renderData: RendererType2|null): Renderer2 {
const renderer = super.createRenderer(element, renderData) as DebugRenderer2;
renderer.debugContextFactory = () => new Render3DebugContext(_getViewData());
return renderer;
}
}
/**
* Stores context information about view nodes.
*
* Used in tests to retrieve information those nodes.
*/
class Render3DebugContext implements DebugContext {
readonly nodeIndex: number|null;
constructor(private viewData: LViewData) {
// The LNode will be created next and appended to viewData
this.nodeIndex = viewData ? viewData.length : null;
}
get view(): any { return this.viewData; }
get injector(): Injector {
if (this.nodeIndex !== null) {
const lElementNode: LElementNode = this.view[this.nodeIndex];
const nodeInjector = lElementNode.nodeInjector;
if (nodeInjector) {
return new di.NodeInjector(nodeInjector);
}
}
return Injector.NULL;
}
get component(): any {
// TODO(vicb): why/when
if (this.nodeIndex === null) {
return null;
}
const tView = this.view[TVIEW];
const components: number[]|null = tView.components;
return (components && components.indexOf(this.nodeIndex) == -1) ?
null :
this.view[this.nodeIndex].data[CONTEXT];
}
// TODO(vicb): add view providers when supported
get providerTokens(): any[] {
const matchedDirectives: any[] = [];
// TODO(vicb): why/when
if (this.nodeIndex === null) {
return matchedDirectives;
}
const directives = this.view[DIRECTIVES];
if (directives) {
const currentNode = this.view[this.nodeIndex];
for (let dirIndex = 0; dirIndex < directives.length; dirIndex++) {
const directive = directives[dirIndex];
if (directive[NG_HOST_SYMBOL] === currentNode) {
matchedDirectives.push(directive.constructor);
}
}
}
return matchedDirectives;
}
get references(): {[key: string]: any} {
// TODO(vicb): implement retrieving references
throw new Error('Not implemented yet in ivy');
}
get context(): any {
if (this.nodeIndex === null) {
return null;
}
const lNode = this.view[this.nodeIndex];
return lNode.view[CONTEXT];
}
get componentRenderElement(): any { throw new Error('Not implemented in ivy'); }
get renderNode(): any { throw new Error('Not implemented in ivy'); }
// TODO(vicb): check previous implementation
logError(console: Console, ...values: any[]): void { console.error(...values); }
}

View File

@ -605,7 +605,7 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine.ViewContainer
return di.viewContainerRef;
}
class NodeInjector implements Injector {
export class NodeInjector implements Injector {
constructor(private _lInjector: LInjector) {}
get(token: any): any {

View File

@ -89,7 +89,9 @@ export function NgOnChangesFeature<T>(definition: DirectiveDefInternal<T>): void
}
if (setter) setter.call(this, value);
}
},
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode
});
}
}

View File

@ -13,13 +13,12 @@ import {NgOnChangesFeature} from './features/ng_onchanges_feature';
import {PublicFeature} from './features/public_feature';
import {ComponentDef, ComponentDefInternal, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveDefInternal, DirectiveType, PipeDef} from './interfaces/definition';
export {ComponentFactory, ComponentFactoryResolver, ComponentRef} from './component_ref';
export {ComponentFactory, ComponentFactoryResolver, ComponentRef, WRAP_RENDERER_FACTORY2} from './component_ref';
export {Render3DebugRendererFactory2} from './debug';
export {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, directiveInject, getFactoryOf, getInheritedFactory, injectAttribute, injectChangeDetectorRef, injectComponentFactoryResolver, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di';
export {RenderFlags} from './interfaces/definition';
export {CssSelectorList} from './interfaces/projection';
// Naming scheme:
// - Capital letters are for creating things: T(Text), E(Element), D(Directive), V(View),
// C(Container), L(Listener)

View File

@ -30,8 +30,6 @@ import {StylingContext, allocStylingContext, createStylingContextTemplate, rende
import {assertDataInRangeInternal, isDifferent, loadElementInternal, loadInternal, stringify} from './util';
import {ViewRef} from './view_ref';
/**
* Directive (D) sets a property on all component instances using this constant as a key and the
* component's host node (LElement) as the value. This is used in methods like detectChanges to
@ -126,16 +124,6 @@ export function getCurrentView(): OpaqueViewState {
return viewData as any as OpaqueViewState;
}
/**
* Internal function that returns the current LViewData instance.
*
* The getCurrentView() instruction should be used for anything public.
*/
export function _getViewData(): LViewData {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return viewData;
}
/**
* Restores `contextViewData` to the given OpaqueViewState instance.
*
@ -207,6 +195,16 @@ export function getCreationMode(): boolean {
*/
let viewData: LViewData;
/**
* Internal function that returns the current LViewData instance.
*
* The getCurrentView() instruction should be used for anything public.
*/
export function _getViewData(): LViewData {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return viewData;
}
/**
* The last viewData retrieved by nextContext().
* Allows building nextContext() and reference() calls.

View File

@ -10,13 +10,12 @@ import {ConstantPool, R3DirectiveMetadata, WrappedNodeExpr, compileComponentFrom
import {Component, Directive, HostBinding, HostListener, Input, Output} from '../../metadata/directives';
import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading';
import {ReflectionCapabilities} from '../../reflection/reflection_capabilities';
import {Type} from '../../type';
import {stringify} from '../../util';
import {angularCoreEnv} from './environment';
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from './fields';
import {patchComponentDefWithScope} from './module';
import {patchComponentDefWithScope, transitiveScopesFor} from './module';
import {getReflect, reflectDependencies} from './util';
type StringMap = {
@ -33,12 +32,12 @@ type StringMap = {
* until the global queue has been resolved with a call to `resolveComponentResources`.
*/
export function compileComponent(type: Type<any>, metadata: Component): void {
let def: any = null;
let ngComponentDef: any = null;
// Metadata may have resources which need to be resolved.
maybeQueueResolutionOfComponentResources(metadata);
Object.defineProperty(type, NG_COMPONENT_DEF, {
get: () => {
if (def === null) {
if (ngComponentDef === null) {
if (componentNeedsResolution(metadata)) {
const error = [`Component '${stringify(type)}' is not resolved:`];
if (metadata.templateUrl) {
@ -78,7 +77,7 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
constantPool, makeBindingParser());
const preStatements = [...constantPool.statements, ...res.statements];
def = jitExpression(
ngComponentDef = jitExpression(
res.expression, angularCoreEnv, `ng://${type.name}/ngComponentDef.js`, preStatements);
// If component compilation is async, then the @NgModule annotation which declares the
@ -86,11 +85,14 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
// allows the component to patch itself with directiveDefs from the module after it finishes
// compiling.
if (hasSelectorScope(type)) {
patchComponentDefWithScope(def, type.ngSelectorScope);
const scopes = transitiveScopesFor(type.ngSelectorScope);
patchComponentDefWithScope(ngComponentDef, scopes);
}
}
return def;
return ngComponentDef;
},
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
});
}
@ -107,23 +109,24 @@ function hasSelectorScope<T>(component: Type<T>): component is Type<T>&
* will resolve when compilation completes and the directive becomes usable.
*/
export function compileDirective(type: Type<any>, directive: Directive): void {
let def: any = null;
let ngDirectiveDef: any = null;
Object.defineProperty(type, NG_DIRECTIVE_DEF, {
get: () => {
if (def === null) {
if (ngDirectiveDef === null) {
const constantPool = new ConstantPool();
const sourceMapUrl = `ng://${type && type.name}/ngDirectiveDef.js`;
const res = compileR3Directive(
directiveMetadata(type, directive), constantPool, makeBindingParser());
const preStatements = [...constantPool.statements, ...res.statements];
def = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, preStatements);
ngDirectiveDef = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, preStatements);
}
return def;
return ngDirectiveDef;
},
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
});
}
export function extendsDirectlyFromObject(type: Type<any>): boolean {
return Object.getPrototypeOf(type.prototype) === Object.prototype;
}

View File

@ -18,15 +18,28 @@ import {reflectDependencies} from './util';
const EMPTY_ARRAY: Type<any>[] = [];
export function compileNgModule(type: Type<any>, ngModule: NgModule): void {
/**
* Compiles a module in JIT mode.
*
* This function automatically gets called when a class has a `@NgModule` decorator.
*/
export function compileNgModule(moduleType: Type<any>, ngModule: NgModule): void {
compileNgModuleDefs(moduleType, ngModule);
setScopeOnDeclaredComponents(moduleType, ngModule);
}
/**
* Compiles and adds the `ngModuleDef` and `ngInjectorDef` properties to the module class.
*/
export function compileNgModuleDefs(moduleType: Type<any>, ngModule: NgModule): void {
const declarations: Type<any>[] = flatten(ngModule.declarations || EMPTY_ARRAY);
let ngModuleDef: any = null;
Object.defineProperty(type, NG_MODULE_DEF, {
Object.defineProperty(moduleType, NG_MODULE_DEF, {
get: () => {
if (ngModuleDef === null) {
const meta: R3NgModuleMetadata = {
type: wrap(type),
type: wrap(moduleType),
bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY).map(wrap),
declarations: declarations.map(wrapReference),
imports: flatten(ngModule.imports || EMPTY_ARRAY)
@ -38,21 +51,23 @@ export function compileNgModule(type: Type<any>, ngModule: NgModule): void {
emitInline: true,
};
const res = compileR3NgModule(meta);
ngModuleDef =
jitExpression(res.expression, angularCoreEnv, `ng://${type.name}/ngModuleDef.js`, []);
ngModuleDef = jitExpression(
res.expression, angularCoreEnv, `ng://${moduleType.name}/ngModuleDef.js`, []);
}
return ngModuleDef;
},
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
});
let ngInjectorDef: any = null;
Object.defineProperty(type, NG_INJECTOR_DEF, {
Object.defineProperty(moduleType, NG_INJECTOR_DEF, {
get: () => {
if (ngInjectorDef === null) {
const meta: R3InjectorMetadata = {
name: type.name,
type: wrap(type),
deps: reflectDependencies(type),
name: moduleType.name,
type: wrap(moduleType),
deps: reflectDependencies(moduleType),
providers: new WrappedNodeExpr(ngModule.providers || EMPTY_ARRAY),
imports: new WrappedNodeExpr([
ngModule.imports || EMPTY_ARRAY,
@ -61,25 +76,36 @@ export function compileNgModule(type: Type<any>, ngModule: NgModule): void {
};
const res = compileInjector(meta);
ngInjectorDef = jitExpression(
res.expression, angularCoreEnv, `ng://${type.name}/ngInjectorDef.js`, res.statements);
res.expression, angularCoreEnv, `ng://${moduleType.name}/ngInjectorDef.js`,
res.statements);
}
return ngInjectorDef;
},
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
});
}
/**
* Some declared components may be compiled asynchronously, and thus may not have their
* ngComponentDef set yet. If this is the case, then a reference to the module is written into
* the `ngSelectorScope` property of the declared type.
*/
function setScopeOnDeclaredComponents(moduleType: Type<any>, ngModule: NgModule) {
const declarations: Type<any>[] = flatten(ngModule.declarations || EMPTY_ARRAY);
const transitiveScopes = transitiveScopesFor(moduleType);
declarations.forEach(declaration => {
// Some declared components may be compiled asynchronously, and thus may not have their
// ngComponentDef set yet. If this is the case, then a reference to the module is written into
// the `ngSelectorScope` property of the declared type.
if (declaration.hasOwnProperty(NG_COMPONENT_DEF)) {
// An `ngComponentDef` field exists - go ahead and patch the component directly.
patchComponentDefWithScope(
(declaration as Type<any>& {ngComponentDef: ComponentDefInternal<any>}).ngComponentDef,
type);
const component = declaration as Type<any>& {ngComponentDef: ComponentDefInternal<any>};
const componentDef = component.ngComponentDef;
patchComponentDefWithScope(componentDef, transitiveScopes);
} else if (
!declaration.hasOwnProperty(NG_DIRECTIVE_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) {
// Set `ngSelectorScope` for future reference when the component compilation finishes.
(declaration as Type<any>& {ngSelectorScope?: any}).ngSelectorScope = type;
(declaration as Type<any>& {ngSelectorScope?: any}).ngSelectorScope = moduleType;
}
});
}
@ -88,13 +114,13 @@ export function compileNgModule(type: Type<any>, ngModule: NgModule): void {
* Patch the definition of a component with directives and pipes from the compilation scope of
* a given module.
*/
export function patchComponentDefWithScope<C, M>(
componentDef: ComponentDefInternal<C>, module: Type<M>) {
componentDef.directiveDefs = () => Array.from(transitiveScopesFor(module).compilation.directives)
export function patchComponentDefWithScope<C>(
componentDef: ComponentDefInternal<C>, transitiveScopes: NgModuleTransitiveScopes) {
componentDef.directiveDefs = () => Array.from(transitiveScopes.compilation.directives)
.map(dir => dir.ngDirectiveDef || dir.ngComponentDef)
.filter(def => !!def);
componentDef.pipeDefs = () =>
Array.from(transitiveScopesFor(module).compilation.pipes).map(pipe => pipe.ngPipeDef);
Array.from(transitiveScopes.compilation.pipes).map(pipe => pipe.ngPipeDef);
}
/**
@ -104,7 +130,7 @@ export function patchComponentDefWithScope<C, M>(
* on modules with components that have not fully compiled yet, but the result should not be used
* until they have.
*/
function transitiveScopesFor<T>(moduleType: Type<T>): NgModuleTransitiveScopes {
export function transitiveScopesFor<T>(moduleType: Type<T>): NgModuleTransitiveScopes {
if (!isNgModule(moduleType)) {
throw new Error(`${moduleType.name} does not have an ngModuleDef`);
}

View File

@ -17,10 +17,10 @@ import {NG_PIPE_DEF} from './fields';
import {reflectDependencies} from './util';
export function compilePipe(type: Type<any>, meta: Pipe): void {
let def: any = null;
let ngPipeDef: any = null;
Object.defineProperty(type, NG_PIPE_DEF, {
get: () => {
if (def === null) {
if (ngPipeDef === null) {
const sourceMapUrl = `ng://${stringify(type)}/ngPipeDef.js`;
const name = type.name;
@ -32,9 +32,11 @@ export function compilePipe(type: Type<any>, meta: Pipe): void {
pure: meta.pure !== undefined ? meta.pure : true,
});
def = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
ngPipeDef = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
}
return def;
}
return ngPipeDef;
},
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
});
}

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
declare global {
const ngDevMode: null|NgDevModePerfCounters;
interface NgDevModePerfCounters {
@ -33,8 +32,6 @@ declare global {
}
}
declare let global: any;
// NOTE: The order here matters: Checking window, then global, then self is important.

View File

@ -663,8 +663,7 @@ export function getCurrentDebugContext(): DebugContext|null {
return _currentView ? new DebugContext_(_currentView, _currentNodeIndex) : null;
}
class DebugRendererFactory2 implements RendererFactory2 {
export class DebugRendererFactory2 implements RendererFactory2 {
constructor(private delegate: RendererFactory2) {}
createRenderer(element: any, renderData: RendererType2|null): Renderer2 {
@ -690,9 +689,21 @@ class DebugRendererFactory2 implements RendererFactory2 {
}
}
class DebugRenderer2 implements Renderer2 {
export class DebugRenderer2 implements Renderer2 {
readonly data: {[key: string]: any};
/**
* Factory function used to create a `DebugContext` when a node is created.
*
* The `DebugContext` allows to retrieve information about the nodes that are useful in tests.
*
* The factory is configurable so that the `DebugRenderer2` could instantiate either a View Engine
* or a Render context.
*/
debugContextFactory: () => DebugContext | null = getCurrentDebugContext;
private get debugContext() { return this.debugContextFactory(); }
constructor(private delegate: Renderer2) { this.data = this.delegate.data; }
destroyNode(node: any) {
@ -706,7 +717,7 @@ class DebugRenderer2 implements Renderer2 {
createElement(name: string, namespace?: string): any {
const el = this.delegate.createElement(name, namespace);
const debugCtx = getCurrentDebugContext();
const debugCtx = this.debugContext;
if (debugCtx) {
const debugEl = new DebugElement(el, null, debugCtx);
debugEl.name = name;
@ -717,7 +728,7 @@ class DebugRenderer2 implements Renderer2 {
createComment(value: string): any {
const comment = this.delegate.createComment(value);
const debugCtx = getCurrentDebugContext();
const debugCtx = this.debugContext;
if (debugCtx) {
indexDebugNode(new DebugNode(comment, null, debugCtx));
}
@ -726,7 +737,7 @@ class DebugRenderer2 implements Renderer2 {
createText(value: string): any {
const text = this.delegate.createText(value);
const debugCtx = getCurrentDebugContext();
const debugCtx = this.debugContext;
if (debugCtx) {
indexDebugNode(new DebugNode(text, null, debugCtx));
}
@ -764,7 +775,7 @@ class DebugRenderer2 implements Renderer2 {
selectRootElement(selectorOrNode: string|any): any {
const el = this.delegate.selectRootElement(selectorOrNode);
const debugCtx = getCurrentDebugContext();
const debugCtx = this.debugContext;
if (debugCtx) {
indexDebugNode(new DebugElement(el, null, debugCtx));
}