feat(ivy): JIT renders the TODO app (#24138)
This commit builds out enough of the JIT compiler to render //packages/core/test/bundling/todo, and allows the tests to run in JIT mode. To play with the app, run: bazel run --define=compile=jit //packages/core/test/bundling/todo:prodserver PR Close #24138
This commit is contained in:
parent
24e5c5b425
commit
646b42a113
|
@ -518,8 +518,8 @@ ng_module = rule(
|
|||
|
||||
|
||||
# TODO(alxhub): this rule causes legacy ngc to produce Ivy outputs from global analysis information.
|
||||
# It to facilitate testing of the Ivy runtime until ngtsc is mature enough to be used instead, and
|
||||
# should be removed once ngtsc is capable of fulfilling the same requirements.
|
||||
# It exists to facilitate testing of the Ivy runtime until ngtsc is mature enough to be used
|
||||
# instead, and should be removed once ngtsc is capable of fulfilling the same requirements.
|
||||
internal_global_ng_module = rule(
|
||||
implementation = _ng_module_impl,
|
||||
attrs = dict(NG_MODULE_RULE_ATTRS, **{
|
||||
|
|
|
@ -81,9 +81,9 @@ export {getParseErrors, isSyntaxError, syntaxError, Version} from './util';
|
|||
export {SourceMap} from './output/source_map';
|
||||
export * from './injectable_compiler_2';
|
||||
export * from './render3/view/api';
|
||||
export {jitPatchDefinition} from './render3/r3_jit';
|
||||
export {jitExpression} from './render3/r3_jit';
|
||||
export {R3DependencyMetadata, R3FactoryMetadata, R3ResolvedDependencyType} from './render3/r3_factory';
|
||||
export {compileNgModule, R3NgModuleMetadata} from './render3/r3_module_compiler';
|
||||
export {makeBindingParser, parseTemplate} from './render3/view/template';
|
||||
export {compileComponent, compileDirective} from './render3/view/compiler';
|
||||
export {compileComponentFromMetadata, compileDirectiveFromMetadata} from './render3/view/compiler';
|
||||
// This file only reexports content of the `src` folder. Keep it that way.
|
|
@ -49,7 +49,7 @@ export function replaceNgsp(value: string): string {
|
|||
* whitespace removal. The default option for whitespace removal will be revisited in Angular 6
|
||||
* and might be changed to "on" by default.
|
||||
*/
|
||||
class WhitespaceVisitor implements html.Visitor {
|
||||
export class WhitespaceVisitor implements html.Visitor {
|
||||
visitElement(element: html.Element, context: any): any {
|
||||
if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
|
||||
// don't descent into elements where we need to preserve whitespaces
|
||||
|
|
|
@ -50,19 +50,17 @@ class R3JitReflector implements CompileReflector {
|
|||
}
|
||||
|
||||
/**
|
||||
* JIT compiles an expression and monkey-patches the result of executing the expression onto a given
|
||||
* type.
|
||||
* JIT compiles an expression and returns the result of executing that expression.
|
||||
*
|
||||
* @param type the type which will receive the monkey-patched result
|
||||
* @param field name of the field on the type to monkey-patch
|
||||
* @param def the definition which will be compiled and executed to get the value to patch
|
||||
* @param context an object map of @angular/core symbol names to symbols which will be available in
|
||||
* the context of the compiled expression
|
||||
* @param sourceUrl a URL to use for the source map of the compiled expression
|
||||
* @param constantPool an optional `ConstantPool` which contains constants used in the expression
|
||||
*/
|
||||
export function jitPatchDefinition(
|
||||
type: any, field: string, def: o.Expression, context: {[key: string]: any},
|
||||
constantPool?: ConstantPool): void {
|
||||
export function jitExpression(
|
||||
def: o.Expression, context: {[key: string]: any}, sourceUrl: string,
|
||||
constantPool?: ConstantPool): any {
|
||||
// The ConstantPool may contain Statements which declare variables used in the final expression.
|
||||
// Therefore, its statements need to precede the actual JIT operation. The final statement is a
|
||||
// declaration of $def which is set to the expression being compiled.
|
||||
|
@ -71,8 +69,6 @@ export function jitPatchDefinition(
|
|||
new o.DeclareVarStmt('$def', def, undefined, [o.StmtModifier.Exported]),
|
||||
];
|
||||
|
||||
// Monkey patch the field on the given type with the result of compilation.
|
||||
// TODO(alxhub): consider a better source url.
|
||||
type[field] = jitStatements(
|
||||
`ng://${type && type.name}/${field}`, statements, new R3JitReflector(context), false)['$def'];
|
||||
const res = jitStatements(sourceUrl, statements, new R3JitReflector(context), false);
|
||||
return res['$def'];
|
||||
}
|
||||
|
|
|
@ -66,6 +66,17 @@ export interface R3DirectiveMetadata {
|
|||
properties: {[key: string]: string};
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about usage of specific lifecycle events which require special treatment in the
|
||||
* code generator.
|
||||
*/
|
||||
lifecycle: {
|
||||
/**
|
||||
* Whether the directive uses NgOnChanges.
|
||||
*/
|
||||
usesOnChanges: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* A mapping of input field names to the property names.
|
||||
*/
|
||||
|
@ -101,17 +112,6 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
|||
ngContentSelectors: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about usage of specific lifecycle events which require special treatment in the
|
||||
* code generator.
|
||||
*/
|
||||
lifecycle: {
|
||||
/**
|
||||
* Whether the component uses NgOnChanges.
|
||||
*/
|
||||
usesOnChanges: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about the view queries made by the component.
|
||||
*/
|
||||
|
|
|
@ -65,13 +65,22 @@ function baseDirectiveFields(
|
|||
// e.g 'outputs: {a: 'a'}`
|
||||
definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
|
||||
|
||||
// e.g. `features: [NgOnChangesFeature(MyComponent)]`
|
||||
const features: o.Expression[] = [];
|
||||
if (meta.lifecycle.usesOnChanges) {
|
||||
features.push(o.importExpr(R3.NgOnChangesFeature, null, null).callFn([meta.type]));
|
||||
}
|
||||
if (features.length) {
|
||||
definitionMap.set('features', o.literalArr(features));
|
||||
}
|
||||
|
||||
return definitionMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a directive for the render3 runtime as defined by the `R3DirectiveMetadata`.
|
||||
*/
|
||||
export function compileDirective(
|
||||
export function compileDirectiveFromMetadata(
|
||||
meta: R3DirectiveMetadata, constantPool: ConstantPool,
|
||||
bindingParser: BindingParser): R3DirectiveDef {
|
||||
const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
||||
|
@ -84,7 +93,7 @@ export function compileDirective(
|
|||
/**
|
||||
* Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
|
||||
*/
|
||||
export function compileComponent(
|
||||
export function compileComponentFromMetadata(
|
||||
meta: R3ComponentMetadata, constantPool: ConstantPool,
|
||||
bindingParser: BindingParser): R3ComponentDef {
|
||||
const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
||||
|
@ -143,15 +152,6 @@ export function compileComponent(
|
|||
definitionMap.set('pipes', o.literalArr(Array.from(pipesUsed)));
|
||||
}
|
||||
|
||||
// e.g. `features: [NgOnChangesFeature(MyComponent)]`
|
||||
const features: o.Expression[] = [];
|
||||
if (meta.lifecycle.usesOnChanges) {
|
||||
features.push(o.importExpr(R3.NgOnChangesFeature, null, null).callFn([meta.type]));
|
||||
}
|
||||
if (features.length) {
|
||||
definitionMap.set('features', o.literalArr(features));
|
||||
}
|
||||
|
||||
const expression = o.importExpr(R3.defineComponent).callFn([definitionMap.toLiteralMap()]);
|
||||
const type =
|
||||
new o.ExpressionType(o.importExpr(R3.ComponentDef, [new o.ExpressionType(meta.type)]));
|
||||
|
@ -175,7 +175,7 @@ export function compileDirectiveFromRender2(
|
|||
const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Directive);
|
||||
|
||||
const meta = directiveMetadataFromGlobalMetadata(directive, outputCtx, reflector);
|
||||
const res = compileDirective(meta, outputCtx.constantPool, bindingParser);
|
||||
const res = compileDirectiveFromMetadata(meta, outputCtx.constantPool, bindingParser);
|
||||
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
|
@ -211,15 +211,11 @@ export function compileComponentFromRender2(
|
|||
hasNgContent: render3Ast.hasNgContent,
|
||||
ngContentSelectors: render3Ast.ngContentSelectors,
|
||||
},
|
||||
lifecycle: {
|
||||
usesOnChanges:
|
||||
component.type.lifecycleHooks.some(lifecycle => lifecycle == LifecycleHooks.OnChanges),
|
||||
},
|
||||
directives: typeMapToExpressionMap(directiveTypeBySel, outputCtx),
|
||||
pipes: typeMapToExpressionMap(pipeTypeByName, outputCtx),
|
||||
viewQueries: queriesFromGlobalMetadata(component.viewQueries, outputCtx),
|
||||
};
|
||||
const res = compileComponent(meta, outputCtx.constantPool, bindingParser);
|
||||
const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser);
|
||||
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
|
@ -251,6 +247,10 @@ function directiveMetadataFromGlobalMetadata(
|
|||
listeners: summary.hostListeners,
|
||||
properties: summary.hostProperties,
|
||||
},
|
||||
lifecycle: {
|
||||
usesOnChanges:
|
||||
directive.type.lifecycleHooks.some(lifecycle => lifecycle == LifecycleHooks.OnChanges),
|
||||
},
|
||||
inputs: directive.inputs,
|
||||
outputs: directive.outputs,
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@ import {Lexer} from '../../expression_parser/lexer';
|
|||
import {Parser} from '../../expression_parser/parser';
|
||||
import * as html from '../../ml_parser/ast';
|
||||
import {HtmlParser} from '../../ml_parser/html_parser';
|
||||
import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseError, ParseSourceSpan} from '../../parse_util';
|
||||
|
@ -777,16 +778,24 @@ function interpolate(args: o.Expression[]): o.Expression {
|
|||
* @param template text of the template to parse
|
||||
* @param templateUrl URL to use for source mapping of the parsed template
|
||||
*/
|
||||
export function parseTemplate(template: string, templateUrl: string):
|
||||
export function parseTemplate(
|
||||
template: string, templateUrl: string, options: {preserveWhitespace?: boolean} = {}):
|
||||
{errors?: ParseError[], nodes: t.Node[], hasNgContent: boolean, ngContentSelectors: string[]} {
|
||||
const bindingParser = makeBindingParser();
|
||||
const htmlParser = new HtmlParser();
|
||||
const parseResult = htmlParser.parse(template, templateUrl);
|
||||
|
||||
if (parseResult.errors && parseResult.errors.length > 0) {
|
||||
return {errors: parseResult.errors, nodes: [], hasNgContent: false, ngContentSelectors: []};
|
||||
}
|
||||
|
||||
let rootNodes: html.Node[] = parseResult.rootNodes;
|
||||
if (!options.preserveWhitespace) {
|
||||
rootNodes = html.visitAll(new WhitespaceVisitor(), rootNodes);
|
||||
}
|
||||
|
||||
const {nodes, hasNgContent, ngContentSelectors, errors} =
|
||||
htmlAstToRender3Ast(parseResult.rootNodes, bindingParser);
|
||||
htmlAstToRender3Ast(rootNodes, bindingParser);
|
||||
if (errors && errors.length > 0) {
|
||||
return {errors, nodes: [], hasNgContent: false, ngContentSelectors: []};
|
||||
}
|
||||
|
|
|
@ -1078,8 +1078,8 @@ describe('compiler compliance', () => {
|
|||
selectors: [['lifecycle-comp']],
|
||||
factory: function LifecycleComp_Factory() { return new LifecycleComp(); },
|
||||
inputs: {nameMin: 'name'},
|
||||
template: function LifecycleComp_Template(rf: IDENT, ctx: IDENT) {},
|
||||
features: [$r3$.ɵNgOnChangesFeature(LifecycleComp)]
|
||||
features: [$r3$.ɵNgOnChangesFeature(LifecycleComp)],
|
||||
template: function LifecycleComp_Template(rf: IDENT, ctx: IDENT) {}
|
||||
});`;
|
||||
|
||||
const SimpleLayoutDefinition = `
|
||||
|
|
|
@ -8,5 +8,6 @@
|
|||
|
||||
export const ivyEnabled = false;
|
||||
export const R3_COMPILE_COMPONENT: ((type: any, meta: any) => void)|null = null;
|
||||
export const R3_COMPILE_DIRECTIVE: ((type: any, meta: any) => void)|null = null;
|
||||
export const R3_COMPILE_INJECTABLE: ((type: any, meta: any) => void)|null = null;
|
||||
export const R3_COMPILE_NGMODULE: ((type: any, meta: any) => void)|null = null;
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {compileComponentDecorator} from './render3/jit/directive';
|
||||
import {compileComponentDecorator, compileDirective} from './render3/jit/directive';
|
||||
import {compileInjectable} from './render3/jit/injectable';
|
||||
import {compileNgModule} from './render3/jit/module';
|
||||
|
||||
export const ivyEnabled = true;
|
||||
export const R3_COMPILE_COMPONENT = compileComponentDecorator;
|
||||
export const R3_COMPILE_DIRECTIVE = compileDirective;
|
||||
export const R3_COMPILE_INJECTABLE = compileInjectable;
|
||||
export const R3_COMPILE_NGMODULE = compileNgModule;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {ChangeDetectionStrategy} from '../change_detection/constants';
|
||||
import {Provider} from '../di';
|
||||
import {R3_COMPILE_COMPONENT} from '../ivy_switch';
|
||||
import {R3_COMPILE_COMPONENT, R3_COMPILE_DIRECTIVE} from '../ivy_switch';
|
||||
import {Type} from '../type';
|
||||
import {TypeDecorator, makeDecorator, makePropDecorator} from '../util/decorators';
|
||||
import {ViewEncapsulation} from './view';
|
||||
|
@ -400,8 +400,9 @@ export interface Directive {
|
|||
*
|
||||
* @Annotation
|
||||
*/
|
||||
export const Directive: DirectiveDecorator =
|
||||
makeDecorator('Directive', (dir: Directive = {}) => dir);
|
||||
export const Directive: DirectiveDecorator = makeDecorator(
|
||||
'Directive', (dir: Directive = {}) => dir, undefined, undefined,
|
||||
(type: Type<any>, meta: Directive) => (R3_COMPILE_DIRECTIVE || (() => {}))(type, meta));
|
||||
|
||||
/**
|
||||
* Type of the Component decorator / constructor function.
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {compileComponent as compileIvyComponent, parseTemplate, ConstantPool, makeBindingParser, WrappedNodeExpr, jitPatchDefinition,} from '@angular/compiler';
|
||||
import {ConstantPool, R3DirectiveMetadata, WrappedNodeExpr, compileComponentFromMetadata as compileIvyComponent, compileDirectiveFromMetadata as compileIvyDirective, jitExpression, makeBindingParser, parseTemplate} from '@angular/compiler';
|
||||
|
||||
import {Component} from '../../metadata/directives';
|
||||
import {Component, Directive, HostBinding, Input, Output} from '../../metadata/directives';
|
||||
import {ReflectionCapabilities} from '../../reflection/reflection_capabilities';
|
||||
import {Type} from '../../type';
|
||||
|
||||
import {angularCoreEnv} from './environment';
|
||||
import {reflectDependencies} from './util';
|
||||
import {getReflect, reflectDependencies} from './util';
|
||||
|
||||
let _pendingPromises: Promise<void>[] = [];
|
||||
|
||||
|
@ -31,45 +31,65 @@ export function compileComponent(type: Type<any>, metadata: Component): Promise<
|
|||
if (!metadata.template) {
|
||||
throw new Error('templateUrl not yet supported');
|
||||
}
|
||||
const templateStr = metadata.template;
|
||||
|
||||
// Parse the template and check for errors.
|
||||
const template = parseTemplate(metadata.template !, `ng://${type.name}/template.html`);
|
||||
if (template.errors !== undefined) {
|
||||
const errors = template.errors.map(err => err.toString()).join(', ');
|
||||
throw new Error(`Errors during JIT compilation of template for ${type.name}: ${errors}`);
|
||||
}
|
||||
let def: any = null;
|
||||
Object.defineProperty(type, 'ngComponentDef', {
|
||||
get: () => {
|
||||
if (def === null) {
|
||||
// The ConstantPool is a requirement of the JIT'er.
|
||||
const constantPool = new ConstantPool();
|
||||
|
||||
// The ConstantPool is a requirement of the JIT'er.
|
||||
const constantPool = new ConstantPool();
|
||||
// Parse the template and check for errors.
|
||||
const template = parseTemplate(templateStr, `ng://${type.name}/template.html`);
|
||||
if (template.errors !== undefined) {
|
||||
const errors = template.errors.map(err => err.toString()).join(', ');
|
||||
throw new Error(`Errors during JIT compilation of template for ${type.name}: ${errors}`);
|
||||
}
|
||||
|
||||
// Compile the component metadata, including template, into an expression.
|
||||
// TODO(alxhub): implement inputs, outputs, queries, etc.
|
||||
const res = compileIvyComponent(
|
||||
{
|
||||
name: type.name,
|
||||
type: new WrappedNodeExpr(type),
|
||||
selector: metadata.selector !, template,
|
||||
deps: reflectDependencies(type),
|
||||
directives: new Map(),
|
||||
pipes: new Map(),
|
||||
host: {
|
||||
attributes: {},
|
||||
listeners: {},
|
||||
properties: {},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
lifecycle: {
|
||||
usesOnChanges: false,
|
||||
},
|
||||
queries: [],
|
||||
typeSourceSpan: null !,
|
||||
viewQueries: [],
|
||||
},
|
||||
constantPool, makeBindingParser());
|
||||
// Compile the component metadata, including template, into an expression.
|
||||
// TODO(alxhub): implement inputs, outputs, queries, etc.
|
||||
const res = compileIvyComponent(
|
||||
{
|
||||
...directiveMetadata(type, metadata),
|
||||
template,
|
||||
directives: new Map(),
|
||||
pipes: new Map(),
|
||||
viewQueries: [],
|
||||
},
|
||||
constantPool, makeBindingParser());
|
||||
|
||||
// Patch the generated expression as ngComponentDef on the type.
|
||||
jitPatchDefinition(type, 'ngComponentDef', res.expression, angularCoreEnv, constantPool);
|
||||
def = jitExpression(
|
||||
res.expression, angularCoreEnv, `ng://${type.name}/ngComponentDef.js`, constantPool);
|
||||
}
|
||||
return def;
|
||||
},
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile an Angular directive according to its decorator metadata, and patch the resulting
|
||||
* ngDirectiveDef onto the component type.
|
||||
*
|
||||
* In the event that compilation is not immediate, `compileDirective` will return a `Promise` which
|
||||
* will resolve when compilation completes and the directive becomes usable.
|
||||
*/
|
||||
export function compileDirective(type: Type<any>, directive: Directive): Promise<void>|null {
|
||||
let def: any = null;
|
||||
Object.defineProperty(type, 'ngDirectiveDef', {
|
||||
get: () => {
|
||||
if (def === null) {
|
||||
const constantPool = new ConstantPool();
|
||||
const sourceMapUrl = `ng://${type && type.name}/ngDirectiveDef.js`;
|
||||
const res = compileIvyDirective(
|
||||
directiveMetadata(type, directive), constantPool, makeBindingParser());
|
||||
def = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool);
|
||||
}
|
||||
return def;
|
||||
},
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -95,3 +115,51 @@ export function awaitCurrentlyCompilingComponents(): Promise<void> {
|
|||
_pendingPromises = [];
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the `R3DirectiveMetadata` for a particular directive (either a `Directive` or a
|
||||
* `Component`).
|
||||
*/
|
||||
function directiveMetadata(type: Type<any>, metadata: Directive): R3DirectiveMetadata {
|
||||
// Reflect inputs and outputs.
|
||||
const props = getReflect().propMetadata(type);
|
||||
const inputs: {[key: string]: string} = {};
|
||||
const outputs: {[key: string]: string} = {};
|
||||
|
||||
for (let field in props) {
|
||||
props[field].forEach(ann => {
|
||||
if (isInput(ann)) {
|
||||
inputs[field] = ann.bindingPropertyName || field;
|
||||
} else if (isOutput(ann)) {
|
||||
outputs[field] = ann.bindingPropertyName || field;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
name: type.name,
|
||||
type: new WrappedNodeExpr(type),
|
||||
selector: metadata.selector !,
|
||||
deps: reflectDependencies(type),
|
||||
host: {
|
||||
attributes: {},
|
||||
listeners: {},
|
||||
properties: {},
|
||||
},
|
||||
inputs,
|
||||
outputs,
|
||||
queries: [],
|
||||
lifecycle: {
|
||||
usesOnChanges: type.prototype.ngOnChanges !== undefined,
|
||||
},
|
||||
typeSourceSpan: null !,
|
||||
};
|
||||
}
|
||||
|
||||
function isInput(value: any): value is Input {
|
||||
return value.ngMetadataName === 'Input';
|
||||
}
|
||||
|
||||
function isOutput(value: any): value is Output {
|
||||
return value.ngMetadataName === 'Output';
|
||||
}
|
||||
|
|
|
@ -17,15 +17,37 @@ import * as r3 from '../index';
|
|||
*
|
||||
* This should be kept up to date with the public exports of @angular/core.
|
||||
*/
|
||||
export const angularCoreEnv = {
|
||||
export const angularCoreEnv: {[name: string]: Function} = {
|
||||
'ɵdefineComponent': r3.defineComponent,
|
||||
'ɵdefineDirective': r3.defineDirective,
|
||||
'defineInjectable': defineInjectable,
|
||||
'ɵdefineNgModule': defineNgModule,
|
||||
'ɵdirectiveInject': r3.directiveInject,
|
||||
'inject': inject,
|
||||
'ɵinjectAttribute': r3.injectAttribute,
|
||||
'ɵinjectChangeDetectorRef': r3.injectChangeDetectorRef,
|
||||
'ɵinjectElementRef': r3.injectElementRef,
|
||||
'ɵinjectTemplateRef': r3.injectTemplateRef,
|
||||
'ɵinjectViewContainerRef': r3.injectViewContainerRef,
|
||||
'ɵNgOnChangesFeature': r3.NgOnChangesFeature,
|
||||
'ɵa': r3.a,
|
||||
'ɵb': r3.b,
|
||||
'ɵC': r3.C,
|
||||
'ɵcR': r3.cR,
|
||||
'ɵcr': r3.cr,
|
||||
'ɵd': r3.d,
|
||||
'ɵE': r3.E,
|
||||
'ɵe': r3.e,
|
||||
'ɵf0': r3.f0,
|
||||
'ɵf1': r3.f1,
|
||||
'ɵf2': r3.f2,
|
||||
'ɵf3': r3.f3,
|
||||
'ɵf4': r3.f4,
|
||||
'ɵf5': r3.f5,
|
||||
'ɵf6': r3.f6,
|
||||
'ɵf7': r3.f7,
|
||||
'ɵf8': r3.f8,
|
||||
'ɵfV': r3.fV,
|
||||
'ɵi1': r3.i1,
|
||||
'ɵi2': r3.i2,
|
||||
'ɵi3': r3.i3,
|
||||
|
@ -34,6 +56,23 @@ export const angularCoreEnv = {
|
|||
'ɵi6': r3.i6,
|
||||
'ɵi7': r3.i7,
|
||||
'ɵi8': r3.i8,
|
||||
'ɵk': r3.k,
|
||||
'ɵkn': r3.kn,
|
||||
'ɵL': r3.L,
|
||||
'ɵld': r3.ld,
|
||||
'ɵp': r3.p,
|
||||
'ɵpb1': r3.pb1,
|
||||
'ɵpb2': r3.pb2,
|
||||
'ɵpb3': r3.pb3,
|
||||
'ɵpb4': r3.pb4,
|
||||
'ɵpbV': r3.pbV,
|
||||
'ɵQ': r3.Q,
|
||||
'ɵqR': r3.qR,
|
||||
'ɵs': r3.s,
|
||||
'ɵsn': r3.sn,
|
||||
'ɵst': r3.st,
|
||||
'ɵT': r3.T,
|
||||
'ɵt': r3.t,
|
||||
'ɵV': r3.V,
|
||||
'ɵv': r3.v,
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Expression, LiteralExpr, R3DependencyMetadata, WrappedNodeExpr, compileInjectable as compileIvyInjectable, jitPatchDefinition} from '@angular/compiler';
|
||||
import {Expression, LiteralExpr, R3DependencyMetadata, WrappedNodeExpr, compileInjectable as compileIvyInjectable, jitExpression} from '@angular/compiler';
|
||||
|
||||
import {Injectable} from '../../di/injectable';
|
||||
import {ClassSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from '../../di/provider';
|
||||
|
@ -27,60 +27,69 @@ export function compileInjectable(type: Type<any>, meta?: Injectable): void {
|
|||
return;
|
||||
}
|
||||
|
||||
// Check whether the injectable metadata includes a provider specification.
|
||||
const hasAProvider = isUseClassProvider(meta) || isUseFactoryProvider(meta) ||
|
||||
isUseValueProvider(meta) || isUseExistingProvider(meta);
|
||||
let def: any = null;
|
||||
Object.defineProperty(type, 'ngInjectableDef', {
|
||||
get: () => {
|
||||
if (def === null) {
|
||||
// Check whether the injectable metadata includes a provider specification.
|
||||
const hasAProvider = isUseClassProvider(meta) || isUseFactoryProvider(meta) ||
|
||||
isUseValueProvider(meta) || isUseExistingProvider(meta);
|
||||
|
||||
let deps: R3DependencyMetadata[]|undefined = undefined;
|
||||
if (!hasAProvider || (isUseClassProvider(meta) && type === meta.useClass)) {
|
||||
deps = reflectDependencies(type);
|
||||
} else if (isUseClassProvider(meta)) {
|
||||
deps = meta.deps && convertDependencies(meta.deps);
|
||||
} else if (isUseFactoryProvider(meta)) {
|
||||
deps = meta.deps && convertDependencies(meta.deps) || [];
|
||||
}
|
||||
let deps: R3DependencyMetadata[]|undefined = undefined;
|
||||
if (!hasAProvider || (isUseClassProvider(meta) && type === meta.useClass)) {
|
||||
deps = reflectDependencies(type);
|
||||
} else if (isUseClassProvider(meta)) {
|
||||
deps = meta.deps && convertDependencies(meta.deps);
|
||||
} else if (isUseFactoryProvider(meta)) {
|
||||
deps = meta.deps && convertDependencies(meta.deps) || [];
|
||||
}
|
||||
|
||||
// Decide which flavor of factory to generate, based on the provider specified.
|
||||
// Only one of the use* fields should be set.
|
||||
let useClass: Expression|undefined = undefined;
|
||||
let useFactory: Expression|undefined = undefined;
|
||||
let useValue: Expression|undefined = undefined;
|
||||
let useExisting: Expression|undefined = undefined;
|
||||
// Decide which flavor of factory to generate, based on the provider specified.
|
||||
// Only one of the use* fields should be set.
|
||||
let useClass: Expression|undefined = undefined;
|
||||
let useFactory: Expression|undefined = undefined;
|
||||
let useValue: Expression|undefined = undefined;
|
||||
let useExisting: Expression|undefined = undefined;
|
||||
|
||||
if (!hasAProvider) {
|
||||
// In the case the user specifies a type provider, treat it as {provide: X, useClass: X}.
|
||||
// The deps will have been reflected above, causing the factory to create the class by calling
|
||||
// its constructor with injected deps.
|
||||
useClass = new WrappedNodeExpr(type);
|
||||
} else if (isUseClassProvider(meta)) {
|
||||
// The user explicitly specified useClass, and may or may not have provided deps.
|
||||
useClass = new WrappedNodeExpr(meta.useClass);
|
||||
} else if (isUseValueProvider(meta)) {
|
||||
// The user explicitly specified useValue.
|
||||
useValue = new WrappedNodeExpr(meta.useValue);
|
||||
} else if (isUseFactoryProvider(meta)) {
|
||||
// The user explicitly specified useFactory.
|
||||
useFactory = new WrappedNodeExpr(meta.useFactory);
|
||||
} else if (isUseExistingProvider(meta)) {
|
||||
// The user explicitly specified useExisting.
|
||||
useExisting = new WrappedNodeExpr(meta.useExisting);
|
||||
} else {
|
||||
// Can't happen - either hasAProvider will be false, or one of the providers will be set.
|
||||
throw new Error(`Unreachable state.`);
|
||||
}
|
||||
if (!hasAProvider) {
|
||||
// In the case the user specifies a type provider, treat it as {provide: X, useClass: X}.
|
||||
// The deps will have been reflected above, causing the factory to create the class by
|
||||
// calling
|
||||
// its constructor with injected deps.
|
||||
useClass = new WrappedNodeExpr(type);
|
||||
} else if (isUseClassProvider(meta)) {
|
||||
// The user explicitly specified useClass, and may or may not have provided deps.
|
||||
useClass = new WrappedNodeExpr(meta.useClass);
|
||||
} else if (isUseValueProvider(meta)) {
|
||||
// The user explicitly specified useValue.
|
||||
useValue = new WrappedNodeExpr(meta.useValue);
|
||||
} else if (isUseFactoryProvider(meta)) {
|
||||
// The user explicitly specified useFactory.
|
||||
useFactory = new WrappedNodeExpr(meta.useFactory);
|
||||
} else if (isUseExistingProvider(meta)) {
|
||||
// The user explicitly specified useExisting.
|
||||
useExisting = new WrappedNodeExpr(meta.useExisting);
|
||||
} else {
|
||||
// Can't happen - either hasAProvider will be false, or one of the providers will be set.
|
||||
throw new Error(`Unreachable state.`);
|
||||
}
|
||||
|
||||
const {expression} = compileIvyInjectable({
|
||||
name: type.name,
|
||||
type: new WrappedNodeExpr(type),
|
||||
providedIn: computeProvidedIn(meta.providedIn),
|
||||
useClass,
|
||||
useFactory,
|
||||
useValue,
|
||||
useExisting,
|
||||
deps,
|
||||
const {expression} = compileIvyInjectable({
|
||||
name: type.name,
|
||||
type: new WrappedNodeExpr(type),
|
||||
providedIn: computeProvidedIn(meta.providedIn),
|
||||
useClass,
|
||||
useFactory,
|
||||
useValue,
|
||||
useExisting,
|
||||
deps,
|
||||
});
|
||||
|
||||
def = jitExpression(expression, angularCoreEnv, `ng://${type.name}/ngInjectableDef.js`);
|
||||
}
|
||||
return def;
|
||||
},
|
||||
});
|
||||
|
||||
jitPatchDefinition(type, 'ngInjectableDef', expression, angularCoreEnv);
|
||||
}
|
||||
|
||||
function computeProvidedIn(providedIn: Type<any>| string | null | undefined): Expression {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Expression, R3NgModuleMetadata, WrappedNodeExpr, compileNgModule as compileIvyNgModule, jitPatchDefinition} from '@angular/compiler';
|
||||
import {Expression, R3NgModuleMetadata, WrappedNodeExpr, compileNgModule as compileIvyNgModule, jitExpression} from '@angular/compiler';
|
||||
|
||||
import {ModuleWithProviders, NgModule, NgModuleDef} from '../../metadata/ng_module';
|
||||
import {Type} from '../../type';
|
||||
|
@ -33,17 +33,6 @@ export function compileNgModule(type: Type<any>, ngModule: NgModule): void {
|
|||
directives: [] as any[],
|
||||
pipes: [] as any[],
|
||||
};
|
||||
flatten(ngModule.declarations || EMPTY_ARRAY).forEach(decl => {
|
||||
if (decl.ngPipeDef) {
|
||||
transitiveCompileScope.pipes.push(decl);
|
||||
} else if (decl.ngComponentDef) {
|
||||
transitiveCompileScope.directives.push(decl);
|
||||
patchComponentWithScope(decl, type as any);
|
||||
} else {
|
||||
transitiveCompileScope.directives.push(decl);
|
||||
decl.ngSelectorScope = type;
|
||||
}
|
||||
});
|
||||
|
||||
function addExportsFrom(module: Type<any>& {ngModuleDef: NgModuleDef<any>}): void {
|
||||
module.ngModuleDef.exports.forEach((exp: any) => {
|
||||
|
@ -60,16 +49,49 @@ export function compileNgModule(type: Type<any>, ngModule: NgModule): void {
|
|||
flatten([(ngModule.imports || EMPTY_ARRAY), (ngModule.exports || EMPTY_ARRAY)])
|
||||
.filter(importExport => isNgModule(importExport))
|
||||
.forEach(mod => addExportsFrom(mod));
|
||||
jitPatchDefinition(type, 'ngModuleDef', res.expression, angularCoreEnv);
|
||||
((type as any).ngModuleDef as NgModuleDef<any>).transitiveCompileScope = transitiveCompileScope;
|
||||
|
||||
flatten(ngModule.declarations || EMPTY_ARRAY).forEach(decl => {
|
||||
if (decl.ngPipeDef) {
|
||||
transitiveCompileScope.pipes.push(decl);
|
||||
} else if (decl.ngComponentDef) {
|
||||
transitiveCompileScope.directives.push(decl);
|
||||
patchComponentWithScope(decl, type as any);
|
||||
} else {
|
||||
transitiveCompileScope.directives.push(decl);
|
||||
decl.ngSelectorScope = type;
|
||||
}
|
||||
});
|
||||
|
||||
let def: any = null;
|
||||
Object.defineProperty(type, 'ngModuleDef', {
|
||||
get: () => {
|
||||
if (def === null) {
|
||||
const meta: R3NgModuleMetadata = {
|
||||
type: wrap(type),
|
||||
bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY).map(wrap),
|
||||
declarations: flatten(ngModule.declarations || EMPTY_ARRAY).map(wrap),
|
||||
imports:
|
||||
flatten(ngModule.imports || EMPTY_ARRAY).map(expandModuleWithProviders).map(wrap),
|
||||
exports:
|
||||
flatten(ngModule.exports || EMPTY_ARRAY).map(expandModuleWithProviders).map(wrap),
|
||||
emitInline: true,
|
||||
};
|
||||
const res = compileIvyNgModule(meta);
|
||||
def = jitExpression(res.expression, angularCoreEnv, `ng://${type.name}/ngModuleDef.js`);
|
||||
def.transitiveCompileScope = transitiveCompileScope;
|
||||
}
|
||||
return def;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function patchComponentWithScope<C, M>(
|
||||
component: Type<C>& {ngComponentDef: ComponentDef<C>},
|
||||
module: Type<M>& {ngModuleDef: NgModuleDef<M>}) {
|
||||
component.ngComponentDef.directiveDefs = () =>
|
||||
module.ngModuleDef.transitiveCompileScope !.directives.map(
|
||||
dir => dir.ngDirectiveDef || dir.ngComponentDef);
|
||||
module.ngModuleDef.transitiveCompileScope !.directives
|
||||
.map(dir => dir.ngDirectiveDef || dir.ngComponentDef)
|
||||
.filter(def => !!def);
|
||||
component.ngComponentDef.pipeDefs = () =>
|
||||
module.ngModuleDef.transitiveCompileScope !.pipes.map(pipe => pipe.ngPipeDef);
|
||||
}
|
||||
|
|
|
@ -19,9 +19,12 @@ import {Type} from '../../type';
|
|||
|
||||
let _reflect: ReflectionCapabilities|null = null;
|
||||
|
||||
export function getReflect(): ReflectionCapabilities {
|
||||
return (_reflect = _reflect || new ReflectionCapabilities());
|
||||
}
|
||||
|
||||
export function reflectDependencies(type: Type<any>): R3DependencyMetadata[] {
|
||||
_reflect = _reflect || new ReflectionCapabilities();
|
||||
return convertDependencies(_reflect.parameters(type));
|
||||
return convertDependencies(getReflect().parameters(type));
|
||||
}
|
||||
|
||||
export function convertDependencies(deps: any[]): R3DependencyMetadata[] {
|
||||
|
|
|
@ -20,7 +20,10 @@ const __window = typeof window !== 'undefined' && window;
|
|||
const __self = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
|
||||
self instanceof WorkerGlobalScope && self;
|
||||
const __global = typeof global !== 'undefined' && global;
|
||||
const _global: {[name: string]: any} = __window || __global || __self;
|
||||
|
||||
// Check __global first, because in Node tests both __global and __window may be defined and _global
|
||||
// should be __global in that case.
|
||||
const _global: {[name: string]: any} = __global || __window || __self;
|
||||
|
||||
const promise: Promise<any> = Promise.resolve(0);
|
||||
/**
|
||||
|
|
|
@ -44,7 +44,7 @@ jasmine_node_test(
|
|||
data = [
|
||||
":bundle",
|
||||
":bundle.js",
|
||||
":bundle.min.js",
|
||||
":bundle.min.js.br",
|
||||
":bundle.min_debug.js",
|
||||
],
|
||||
tags = [
|
||||
|
|
|
@ -2072,9 +2072,6 @@
|
|||
{
|
||||
"name": "__extends$7"
|
||||
},
|
||||
{
|
||||
"name": "__global"
|
||||
},
|
||||
{
|
||||
"name": "__read"
|
||||
},
|
||||
|
|
|
@ -27,6 +27,7 @@ ng_rollup_bundle(
|
|||
entry_point = "packages/core/test/bundling/todo/index.js",
|
||||
deps = [
|
||||
":todo",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
],
|
||||
)
|
||||
|
@ -50,6 +51,7 @@ jasmine_node_test(
|
|||
":bundle.min.js",
|
||||
":bundle.min_debug.js",
|
||||
],
|
||||
tags = ["ivy-jit"],
|
||||
deps = [":test_lib"],
|
||||
)
|
||||
|
||||
|
|
|
@ -119,9 +119,6 @@
|
|||
{
|
||||
"name": "__extends"
|
||||
},
|
||||
{
|
||||
"name": "__global"
|
||||
},
|
||||
{
|
||||
"name": "__read"
|
||||
},
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import 'reflect-metadata';
|
||||
|
||||
import {CommonModule, NgForOf, NgIf} from '@angular/common';
|
||||
import {Component, Injectable, IterableDiffers, NgModule, defineInjector, ɵNgOnChangesFeature as NgOnChangesFeature, ɵdefineDirective as defineDirective, ɵdirectiveInject as directiveInject, ɵinjectTemplateRef as injectTemplateRef, ɵinjectViewContainerRef as injectViewContainerRef, ɵrenderComponent as renderComponent} from '@angular/core';
|
||||
|
||||
|
@ -145,35 +147,39 @@ export class ToDoAppComponent {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(misko): This hack is here because common is not compiled with Ivy flag turned on.
|
||||
(CommonModule as any).ngInjectorDef = defineInjector({factory: () => new CommonModule});
|
||||
// In JIT mode the @Directive decorators in //packages/common will compile the Ivy fields. When
|
||||
// running under --define=compile=legacy, //packages/common is not compiled with Ivy fields, so they
|
||||
// must be monkey-patched on.
|
||||
if (!(NgIf as any).ngDirectiveDef) {
|
||||
// TODO(misko): This hack is here because common is not compiled with Ivy flag turned on.
|
||||
(CommonModule as any).ngInjectorDef = defineInjector({factory: () => new CommonModule});
|
||||
|
||||
// TODO(misko): This hack is here because common is not compiled with Ivy flag turned on.
|
||||
(NgForOf as any).ngDirectiveDef = defineDirective({
|
||||
type: NgForOf,
|
||||
selectors: [['', 'ngFor', '', 'ngForOf', '']],
|
||||
factory: () => new NgForOf(
|
||||
injectViewContainerRef(), injectTemplateRef(), directiveInject(IterableDiffers)),
|
||||
features: [NgOnChangesFeature({
|
||||
ngForOf: 'ngForOf',
|
||||
ngForTrackBy: 'ngForTrackBy',
|
||||
ngForTemplate: 'ngForTemplate',
|
||||
})],
|
||||
inputs: {
|
||||
ngForOf: 'ngForOf',
|
||||
ngForTrackBy: 'ngForTrackBy',
|
||||
ngForTemplate: 'ngForTemplate',
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(misko): This hack is here because common is not compiled with Ivy flag turned on.
|
||||
(NgIf as any).ngDirectiveDef = defineDirective({
|
||||
type: NgIf,
|
||||
selectors: [['', 'ngIf', '']],
|
||||
factory: () => new NgIf(injectViewContainerRef(), injectTemplateRef()),
|
||||
inputs: {ngIf: 'ngIf', ngIfThen: 'ngIfThen', ngIfElse: 'ngIfElse'}
|
||||
});
|
||||
// TODO(misko): This hack is here because common is not compiled with Ivy flag turned on.
|
||||
(NgForOf as any).ngDirectiveDef = defineDirective({
|
||||
type: NgForOf,
|
||||
selectors: [['', 'ngFor', '', 'ngForOf', '']],
|
||||
factory: () => new NgForOf(
|
||||
injectViewContainerRef(), injectTemplateRef(), directiveInject(IterableDiffers)),
|
||||
features: [NgOnChangesFeature({
|
||||
ngForOf: 'ngForOf',
|
||||
ngForTrackBy: 'ngForTrackBy',
|
||||
ngForTemplate: 'ngForTemplate',
|
||||
})],
|
||||
inputs: {
|
||||
ngForOf: 'ngForOf',
|
||||
ngForTrackBy: 'ngForTrackBy',
|
||||
ngForTemplate: 'ngForTemplate',
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(misko): This hack is here because common is not compiled with Ivy flag turned on.
|
||||
(NgIf as any).ngDirectiveDef = defineDirective({
|
||||
type: NgIf,
|
||||
selectors: [['', 'ngIf', '']],
|
||||
factory: () => new NgIf(injectViewContainerRef(), injectTemplateRef()),
|
||||
inputs: {ngIf: 'ngIf', ngIfThen: 'ngIfThen', ngIfElse: 'ngIfElse'}
|
||||
});
|
||||
}
|
||||
|
||||
@NgModule({declarations: [ToDoAppComponent, ToDoAppComponent], imports: [CommonModule]})
|
||||
export class ToDoAppModule {
|
||||
|
|
Loading…
Reference in New Issue