parent
8c51c276c7
commit
86d9612230
|
@ -19,7 +19,7 @@ import {NgModuleCompiler} from '../ng_module_compiler';
|
|||
import {OutputEmitter} from '../output/abstract_emitter';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ParseError} from '../parse_util';
|
||||
import {compileComponent as compileIvyComponent} from '../render3/r3_view_compiler';
|
||||
import {compileComponent as compileIvyComponent, compileDirective as compileIvyDirective} from '../render3/r3_view_compiler';
|
||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||
import {SummaryResolver} from '../summary_resolver';
|
||||
import {TemplateAst} from '../template_parser/template_ast';
|
||||
|
@ -325,7 +325,7 @@ export class AotCompiler {
|
|||
|
||||
const context = this._createOutputContext(fileName);
|
||||
|
||||
// Process all components
|
||||
// Process all components and directives
|
||||
directives.forEach(directiveType => {
|
||||
const directiveMetadata = this._metadataResolver.getDirectiveMetadata(directiveType);
|
||||
if (directiveMetadata.isComponent) {
|
||||
|
@ -337,6 +337,8 @@ export class AotCompiler {
|
|||
const {template: parsedTemplate} =
|
||||
this._parseTemplate(directiveMetadata, module, module.transitiveModule.directives);
|
||||
compileIvyComponent(context, directiveMetadata, parsedTemplate, this._reflector);
|
||||
} else {
|
||||
compileIvyDirective(context, directiveMetadata, this._reflector);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
import * as o from './output/output_ast';
|
||||
import {OutputContext, error} from './util';
|
||||
|
||||
const CONSTANT_PREFIX = '_c';
|
||||
|
||||
export const enum DefinitionKind {Injector, Directive, Component}
|
||||
|
||||
/**
|
||||
|
@ -48,29 +50,34 @@ export class ConstantPool {
|
|||
private literals = new Map<string, FixupExpression>();
|
||||
private injectorDefinitions = new Map<any, FixupExpression>();
|
||||
private directiveDefinitions = new Map<any, FixupExpression>();
|
||||
private componentDefintions = new Map<any, FixupExpression>();
|
||||
private componentDefinitions = new Map<any, FixupExpression>();
|
||||
|
||||
private nextNameIndex = 0;
|
||||
|
||||
getConstLiteral(literal: o.Expression): o.Expression {
|
||||
getConstLiteral(literal: o.Expression, forceShared?: boolean): o.Expression {
|
||||
const key = this.keyOf(literal);
|
||||
let fixup = this.literals.get(key);
|
||||
let newValue = false;
|
||||
if (!fixup) {
|
||||
fixup = new FixupExpression(literal);
|
||||
this.literals.set(key, fixup);
|
||||
} else if (!fixup.shared) {
|
||||
newValue = true;
|
||||
}
|
||||
|
||||
if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
|
||||
// Replace the expression with a variable
|
||||
const name = this.freshName();
|
||||
this.statements.push(
|
||||
o.variable(name).set(literal).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
|
||||
fixup.fixup(o.variable(name));
|
||||
}
|
||||
|
||||
return fixup;
|
||||
}
|
||||
|
||||
getDefinition(type: any, kind: DefinitionKind, ctx: OutputContext): o.Expression {
|
||||
const declarations = kind == DefinitionKind.Component ?
|
||||
this.componentDefintions :
|
||||
this.componentDefinitions :
|
||||
kind == DefinitionKind.Directive ? this.directiveDefinitions : this.injectorDefinitions;
|
||||
let fixup = declarations.get(type);
|
||||
if (!fixup) {
|
||||
|
@ -97,7 +104,7 @@ export class ConstantPool {
|
|||
*/
|
||||
uniqueName(prefix: string): string { return `${prefix}${this.nextNameIndex++}`; }
|
||||
|
||||
private freshName(): string { return this.uniqueName(`_$`); }
|
||||
private freshName(): string { return this.uniqueName(CONSTANT_PREFIX); }
|
||||
|
||||
private keyOf(expression: o.Expression) {
|
||||
return expression.visitExpression(new KeyVisitor(), null);
|
||||
|
@ -105,15 +112,22 @@ export class ConstantPool {
|
|||
}
|
||||
|
||||
class KeyVisitor implements o.ExpressionVisitor {
|
||||
visitLiteralExpr(ast: o.LiteralExpr): string { return `${ast.value}`; }
|
||||
visitLiteralExpr(ast: o.LiteralExpr): string {
|
||||
return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`;
|
||||
}
|
||||
visitLiteralArrayExpr(ast: o.LiteralArrayExpr): string {
|
||||
return ast.entries.map(entry => entry.visitExpression(this, null)).join(',');
|
||||
return `[${ast.entries.map(entry => entry.visitExpression(this, null)).join(',')}]`;
|
||||
}
|
||||
|
||||
visitLiteralMapExpr(ast: o.LiteralMapExpr): string {
|
||||
const entries =
|
||||
ast.entries.map(entry => `${entry.key}:${entry.value.visitExpression(this, null)}`);
|
||||
return `{${entries.join(',')}`;
|
||||
const mapEntry = (entry: o.LiteralMapEntry) =>
|
||||
`${entry.key}:${entry.value.visitExpression(this, null)}`;
|
||||
return `{${ast.entries.map(mapEntry).join(',')}`;
|
||||
}
|
||||
|
||||
visitExternalExpr(ast: o.ExternalExpr): string {
|
||||
return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` :
|
||||
`EX:${ast.value.runtime.name}`;
|
||||
}
|
||||
|
||||
visitReadVarExpr = invalid;
|
||||
|
@ -123,7 +137,6 @@ class KeyVisitor implements o.ExpressionVisitor {
|
|||
visitInvokeMethodExpr = invalid;
|
||||
visitInvokeFunctionExpr = invalid;
|
||||
visitInstantiateExpr = invalid;
|
||||
visitExternalExpr = invalid;
|
||||
visitConditionalExpr = invalid;
|
||||
visitNotExpr = invalid;
|
||||
visitAssertNotNullExpr = invalid;
|
||||
|
|
|
@ -40,9 +40,9 @@ export type ReferenceFilter = (reference: o.ExternalReference) => boolean;
|
|||
export class TypeScriptEmitter implements OutputEmitter {
|
||||
emitStatementsAndContext(
|
||||
genFilePath: string, stmts: o.Statement[], preamble: string = '',
|
||||
emitSourceMaps: boolean = true,
|
||||
referenceFilter?: ReferenceFilter): {sourceText: string, context: EmitterVisitorContext} {
|
||||
const converter = new _TsEmitterVisitor(referenceFilter);
|
||||
emitSourceMaps: boolean = true, referenceFilter?: ReferenceFilter,
|
||||
importFilter?: ReferenceFilter): {sourceText: string, context: EmitterVisitorContext} {
|
||||
const converter = new _TsEmitterVisitor(referenceFilter, importFilter);
|
||||
|
||||
const ctx = EmitterVisitorContext.createRoot();
|
||||
|
||||
|
@ -83,7 +83,9 @@ export class TypeScriptEmitter implements OutputEmitter {
|
|||
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
||||
private typeExpression = 0;
|
||||
|
||||
constructor(private referenceFilter?: ReferenceFilter) { super(false); }
|
||||
constructor(private referenceFilter?: ReferenceFilter, private importFilter?: ReferenceFilter) {
|
||||
super(false);
|
||||
}
|
||||
|
||||
importsWithPrefixes = new Map<string, string>();
|
||||
reexports = new Map<string, {name: string, as: string}[]>();
|
||||
|
@ -390,7 +392,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
|||
ctx.print(null, '(null as any)');
|
||||
return;
|
||||
}
|
||||
if (moduleName) {
|
||||
if (moduleName && (!this.importFilter || !this.importFilter(value))) {
|
||||
let prefix = this.importsWithPrefixes.get(moduleName);
|
||||
if (prefix == null) {
|
||||
prefix = `i${this.importsWithPrefixes.size}`;
|
||||
|
|
|
@ -40,6 +40,10 @@ export class Identifiers {
|
|||
|
||||
static containerEnd: o.ExternalReference = {name: 'ɵc', moduleName: CORE};
|
||||
|
||||
static containerRefreshStart: o.ExternalReference = {name: 'ɵcR', moduleName: CORE};
|
||||
|
||||
static containerRefreshEnd: o.ExternalReference = {name: 'ɵcr', moduleName: CORE};
|
||||
|
||||
static directiveCreate: o.ExternalReference = {name: 'ɵD', moduleName: CORE};
|
||||
|
||||
static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE};
|
||||
|
@ -61,6 +65,8 @@ export class Identifiers {
|
|||
static bind9: o.ExternalReference = {name: 'ɵb9', moduleName: CORE};
|
||||
static bindV: o.ExternalReference = {name: 'ɵbV', moduleName: CORE};
|
||||
|
||||
static memory: o.ExternalReference = {name: 'ɵm', moduleName: CORE};
|
||||
|
||||
static refreshComponent: o.ExternalReference = {name: 'ɵr', moduleName: CORE};
|
||||
|
||||
static directiveLifeCycle: o.ExternalReference = {name: 'ɵl', moduleName: CORE};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompileDirectiveMetadata, CompilePipeSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
|
||||
import {CompileDirectiveMetadata, CompilePipeSummary, CompileTokenMetadata, CompileTypeMetadata, flatten, identifierName, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
|
||||
import {CompileReflector} from '../compile_reflector';
|
||||
import {BindingForm, BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
||||
import {ConstantPool, DefinitionKind} from '../constant_pool';
|
||||
|
@ -21,6 +21,7 @@ import {OutputContext, error} from '../util';
|
|||
import {Identifiers as R3} from './r3_identifiers';
|
||||
|
||||
|
||||
|
||||
/** Name of the context parameter passed into a template function */
|
||||
const CONTEXT_NAME = 'ctx';
|
||||
|
||||
|
@ -30,15 +31,40 @@ const CREATION_MODE_FLAG = 'cm';
|
|||
/** Name of the temporary to use during data binding */
|
||||
const TEMPORARY_NAME = '_t';
|
||||
|
||||
/** The prefix reference variables */
|
||||
const REFERENCE_PREFIX = '_r';
|
||||
|
||||
export function compileDirective(
|
||||
outputCtx: OutputContext, directive: CompileDirectiveMetadata, reflector: CompileReflector) {
|
||||
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
||||
|
||||
// e.g. `factory: () => new MyApp(injectElementRef())`
|
||||
const templateFactory = createFactory(directive.type, outputCtx, reflector);
|
||||
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
||||
|
||||
const className = identifierName(directive.type) !;
|
||||
className || error(`Cannot resolver the name of ${directive.type}`);
|
||||
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
/* name */ className,
|
||||
/* parent */ null,
|
||||
/* fields */[new o.ClassField(
|
||||
/* name */ 'ngDirectiveDef',
|
||||
/* type */ o.INFERRED_TYPE,
|
||||
/* modifiers */[o.StmtModifier.Static],
|
||||
/* initializer */ o.importExpr(R3.defineDirective).callFn([o.literalMap(
|
||||
definitionMapValues)]))],
|
||||
/* getters */[],
|
||||
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
||||
/* methods */[]));
|
||||
}
|
||||
|
||||
export function compileComponent(
|
||||
outputCtx: OutputContext, component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
reflector: CompileReflector) {
|
||||
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
||||
|
||||
// e.g. `type: MyApp`
|
||||
definitionMapValues.push(
|
||||
{key: 'type', value: outputCtx.importExpr(component.type.reference), quoted: false});
|
||||
|
||||
// e.g. `tag: 'my-app'
|
||||
// This is optional and only included if the first selector of a component has element.
|
||||
const selector = component.selector && CssSelector.parse(component.selector);
|
||||
|
@ -54,24 +80,27 @@ export function compileComponent(
|
|||
if (selectorAttributes.length) {
|
||||
definitionMapValues.push({
|
||||
key: 'attrs',
|
||||
value: outputCtx.constantPool.getConstLiteral(o.literalArr(selectorAttributes.map(
|
||||
value => value != null ? o.literal(value) : o.literal(undefined)))),
|
||||
value: outputCtx.constantPool.getConstLiteral(
|
||||
o.literalArr(selectorAttributes.map(
|
||||
value => value != null ? o.literal(value) : o.literal(undefined))),
|
||||
/* forceShared */ true),
|
||||
quoted: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// e.g. `template: function(_ctx, _cm) {...}`
|
||||
const templateFunctionExpression =
|
||||
new TemplateDefinitionBuilder(outputCtx, outputCtx.constantPool, CONTEXT_NAME)
|
||||
.buildTemplateFunction(template);
|
||||
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});
|
||||
|
||||
|
||||
// e.g. `factory: () => new MyApp(injectElementRef())`
|
||||
const templateFactory = createFactory(component.type, outputCtx, reflector);
|
||||
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
||||
|
||||
// e.g. `template: function(_ctx, _cm) {...}`
|
||||
const templateFunctionExpression =
|
||||
new TemplateDefinitionBuilder(
|
||||
outputCtx, outputCtx.constantPool, CONTEXT_NAME, ROOT_SCOPE.nestedScope())
|
||||
.buildTemplateFunction(template);
|
||||
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});
|
||||
|
||||
|
||||
const className = identifierName(component.type) !;
|
||||
className || error(`Cannot resolver the name of ${component.type}`);
|
||||
|
||||
|
@ -136,9 +165,49 @@ function interpolate(args: o.Expression[]): o.Expression {
|
|||
return o.importExpr(R3.bindV).callFn(args);
|
||||
}
|
||||
|
||||
class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
class BindingScope {
|
||||
private map = new Map<string, o.Expression>();
|
||||
private referenceNameIndex = 0;
|
||||
|
||||
constructor(private parent: BindingScope|null) {}
|
||||
|
||||
get(name: string): o.Expression|null {
|
||||
let current: BindingScope|null = this;
|
||||
while (current) {
|
||||
const value = current.map.get(name);
|
||||
if (value != null) {
|
||||
// Cache the value locally.
|
||||
this.map.set(name, value);
|
||||
return value;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set(name: string, variableName: string): BindingScope {
|
||||
!this.map.has(name) ||
|
||||
error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
|
||||
this.map.set(name, o.variable(variableName));
|
||||
return this;
|
||||
}
|
||||
|
||||
nestedScope(): BindingScope { return new BindingScope(this); }
|
||||
|
||||
freshReferenceName(): string {
|
||||
let current: BindingScope|null = this;
|
||||
// Find the top scope as it maintains the global reference count
|
||||
while (current.parent) current = current.parent;
|
||||
return `${REFERENCE_PREFIX}${current.referenceNameIndex++}`;
|
||||
}
|
||||
}
|
||||
|
||||
const ROOT_SCOPE = new BindingScope(null).set('$event', '$event');
|
||||
|
||||
class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
private _dataIndex = 0;
|
||||
private _bindingContext = 0;
|
||||
private _referenceIndex = 0;
|
||||
private _temporaryAllocated = false;
|
||||
private _prefix: o.Statement[] = [];
|
||||
private _creationMode: o.Statement[] = [];
|
||||
|
@ -151,7 +220,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
|
||||
constructor(
|
||||
private outputCtx: OutputContext, private constantPool: ConstantPool,
|
||||
private contextParameter: string, private level = 0) {}
|
||||
private contextParameter: string, private bindingScope: BindingScope, private level = 0) {}
|
||||
|
||||
buildTemplateFunction(asts: TemplateAst[]): o.FunctionExpr {
|
||||
templateVisitAll(this, asts);
|
||||
|
@ -173,7 +242,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
// Host mode (i.e. Comp.h(...))
|
||||
...this._hostMode,
|
||||
|
||||
// Refesh mode (i.e. Comp.r(...))
|
||||
// Refresh mode (i.e. Comp.r(...))
|
||||
...this._refreshMode,
|
||||
|
||||
// Nested templates (i.e. function CompTemplate() {})
|
||||
|
@ -182,31 +251,91 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
o.INFERRED_TYPE);
|
||||
}
|
||||
|
||||
getLocal(name: string): o.Expression|null { return this.bindingScope.get(name); }
|
||||
|
||||
// TODO(chuckj): Implement ng-content
|
||||
visitNgContent = unknown;
|
||||
|
||||
private _computeDirectivesArray(directives: DirectiveAst[]) {
|
||||
const directiveIndexMap = new Map<any, number>();
|
||||
const directiveExpressions: o.Expression[] =
|
||||
directives.filter(directive => !directive.directive.isComponent).map(directive => {
|
||||
directiveIndexMap.set(directive.directive.type.reference, this.allocateDataSlot());
|
||||
return this.typeReference(directive.directive.type.reference);
|
||||
});
|
||||
return {
|
||||
directivesArray: directiveExpressions.length ?
|
||||
this.constantPool.getConstLiteral(
|
||||
o.literalArr(directiveExpressions), /* forceShared */ true) :
|
||||
o.literal(null),
|
||||
directiveIndexMap
|
||||
};
|
||||
}
|
||||
|
||||
visitElement(ast: ElementAst) {
|
||||
let bindingCount = 0;
|
||||
const elementIndex = this.allocateNode();
|
||||
const elementIndex = this.allocateDataSlot();
|
||||
let componentIndex: number|undefined = undefined;
|
||||
const referenceDataSlots = new Map<string, number>();
|
||||
|
||||
// Element creation mode
|
||||
const component = findComponent(ast.directives);
|
||||
const nullNode = o.literal(null, o.INFERRED_TYPE);
|
||||
const parameters: o.Expression[] = [o.literal(elementIndex)];
|
||||
|
||||
// Add component type or element tag
|
||||
if (component) {
|
||||
parameters.push(this.typeReference(component.directive.type.reference));
|
||||
componentIndex = this.allocateDataSlot();
|
||||
} else {
|
||||
parameters.push(o.literal(ast.name));
|
||||
}
|
||||
|
||||
// Add attributes array
|
||||
const attributes: o.Expression[] = [];
|
||||
for (let attr of ast.attrs) {
|
||||
attributes.push(o.literal(attr.name), o.literal(attr.value));
|
||||
}
|
||||
parameters.push(
|
||||
attributes.length > 0 ?
|
||||
this.constantPool.getConstLiteral(o.literalArr(attributes), /* forceShared */ true) :
|
||||
nullNode);
|
||||
|
||||
if (attributes.length !== 0) {
|
||||
parameters.push(this.constantPool.getConstLiteral(o.literalArr(attributes)));
|
||||
// Add directives array
|
||||
const {directivesArray, directiveIndexMap} = this._computeDirectivesArray(ast.directives);
|
||||
parameters.push(directiveIndexMap.size > 0 ? directivesArray : nullNode);
|
||||
|
||||
if (component && componentIndex != null) {
|
||||
// Record the data slot for the component
|
||||
directiveIndexMap.set(component.directive.type.reference, componentIndex);
|
||||
}
|
||||
|
||||
// Add references array
|
||||
if (ast.references && ast.references.length > 0) {
|
||||
const references =
|
||||
flatten(ast.references.map(reference => {
|
||||
const slot = this.allocateDataSlot();
|
||||
referenceDataSlots.set(reference.name, slot);
|
||||
// Generate the update temporary.
|
||||
const variableName = this.bindingScope.freshReferenceName();
|
||||
this._bindingMode.push(o.variable(variableName, o.INFERRED_TYPE)
|
||||
.set(o.importExpr(R3.memory).callFn([o.literal(slot)]))
|
||||
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
|
||||
this.bindingScope.set(reference.name, variableName);
|
||||
return [reference.name, reference.originalValue];
|
||||
})).map(value => o.literal(value));
|
||||
parameters.push(
|
||||
this.constantPool.getConstLiteral(o.literalArr(references), /* forceShared */ true));
|
||||
} else {
|
||||
parameters.push(nullNode);
|
||||
}
|
||||
|
||||
// Remove trailing null nodes as they are implied.
|
||||
while (parameters[parameters.length - 1] === nullNode) {
|
||||
parameters.pop();
|
||||
}
|
||||
|
||||
// Generate the instruction create element instruction
|
||||
this.instruction(this._creationMode, ast.sourceSpan, R3.createElement, ...parameters);
|
||||
|
||||
const implicit = o.variable(this.contextParameter);
|
||||
|
@ -216,9 +345,9 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
if (input.isAnimation) {
|
||||
this.unsupported('animations');
|
||||
}
|
||||
// TODO(chuckj): Builtins transform?
|
||||
// TODO(chuckj): Built-in transform?
|
||||
const convertedBinding = convertPropertyBinding(
|
||||
null, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this._bindingMode.push(...convertedBinding.stmts);
|
||||
const parameters =
|
||||
[o.literal(elementIndex), o.literal(input.name), convertedBinding.currValExpr];
|
||||
|
@ -234,19 +363,20 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
}
|
||||
|
||||
// Generate directives input bindings
|
||||
this._visitDirectives(ast.directives, implicit, elementIndex);
|
||||
this._visitDirectives(ast.directives, implicit, elementIndex, directiveIndexMap);
|
||||
|
||||
// Traverse element child nodes
|
||||
templateVisitAll(this, ast.children);
|
||||
|
||||
|
||||
// Finish element construction mode.
|
||||
this.instruction(this._creationMode, ast.endSourceSpan || ast.sourceSpan, R3.elementEnd);
|
||||
}
|
||||
|
||||
private _visitDirectives(directives: DirectiveAst[], implicit: o.Expression, nodeIndex: number) {
|
||||
private _visitDirectives(
|
||||
directives: DirectiveAst[], implicit: o.Expression, nodeIndex: number,
|
||||
directiveIndexMap: Map<any, number>) {
|
||||
for (let directive of directives) {
|
||||
const directiveIndex = this.allocateDirective();
|
||||
const directiveIndex = directiveIndexMap.get(directive.directive.type.reference);
|
||||
|
||||
// Creation mode
|
||||
// e.g. D(0, TodoComponentDef.n(), TodoComponentDef);
|
||||
|
@ -258,28 +388,16 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
// node is referenced multiple times to know that it must generate the reference into a
|
||||
// temporary.
|
||||
|
||||
this.instruction(
|
||||
this._creationMode, directive.sourceSpan, R3.directiveCreate, o.literal(directiveIndex),
|
||||
this.definitionOf(directiveType, kind)
|
||||
.callMethod(R3.NEW_METHOD, [], directive.sourceSpan),
|
||||
this.definitionOf(directiveType, kind));
|
||||
|
||||
// Bindings
|
||||
for (const input of directive.inputs) {
|
||||
const convertedBinding = convertPropertyBinding(
|
||||
null, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this._bindingMode.push(...convertedBinding.stmts);
|
||||
this.instruction(
|
||||
this._bindingMode, directive.sourceSpan, R3.elementProperty,
|
||||
o.literal(input.templateName), o.literal(nodeIndex), convertedBinding.currValExpr);
|
||||
}
|
||||
|
||||
// e.g. TodoComponentDef.h(0, 0);
|
||||
this._hostMode.push(
|
||||
this.definitionOf(directiveType, kind)
|
||||
.callMethod(R3.HOST_BINDING_METHOD, [o.literal(directiveIndex), o.literal(nodeIndex)])
|
||||
.toStmt());
|
||||
|
||||
// e.g. TodoComponentDef.r(0, 0);
|
||||
this._refreshMode.push(
|
||||
this.definitionOf(directiveType, kind)
|
||||
|
@ -289,32 +407,35 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst) {
|
||||
const templateIndex = this.allocateNode();
|
||||
const templateIndex = this.allocateDataSlot();
|
||||
|
||||
const templateName = `C${templateIndex}Template`;
|
||||
const templateContext = `ctx${this.level}`;
|
||||
|
||||
// TODO(chuckj): attrs?
|
||||
const {directivesArray, directiveIndexMap} = this._computeDirectivesArray(ast.directives);
|
||||
|
||||
// e.g. C(1, C1Template)
|
||||
this.instruction(
|
||||
this._creationMode, ast.sourceSpan, R3.containerCreate, o.literal(templateIndex),
|
||||
o.variable(templateName));
|
||||
directivesArray, o.variable(templateName));
|
||||
|
||||
// Generate directies
|
||||
// e.g. Cr(1)
|
||||
this.instruction(
|
||||
this._refreshMode, ast.sourceSpan, R3.containerRefreshStart, o.literal(templateIndex));
|
||||
|
||||
// Generate directives
|
||||
this._visitDirectives(
|
||||
ast.directives, o.variable(this.contextParameter),
|
||||
// TODO(chuckj): This should be the element index of the element that contained the template
|
||||
templateIndex);
|
||||
ast.directives, o.variable(this.contextParameter), templateIndex, directiveIndexMap);
|
||||
|
||||
// e.g. cr();
|
||||
this.instruction(this._refreshMode, ast.sourceSpan, R3.containerRefreshEnd);
|
||||
|
||||
// Create the template function
|
||||
const templateVisitor = new TemplateDefinitionBuilder(
|
||||
this.outputCtx, this.constantPool, templateContext, this.level + 1);
|
||||
this.outputCtx, this.constantPool, templateContext, this.bindingScope.nestedScope(),
|
||||
this.level + 1);
|
||||
const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children);
|
||||
this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||
|
||||
// Terminate the definition
|
||||
this.instruction(this._creationMode, ast.sourceSpan, R3.containerEnd);
|
||||
}
|
||||
|
||||
// These should be handled in the template or element directly.
|
||||
|
@ -325,7 +446,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
readonly visitAttr = invalid;
|
||||
|
||||
visitBoundText(ast: BoundTextAst) {
|
||||
const nodeIndex = this.allocateNode();
|
||||
const nodeIndex = this.allocateDataSlot();
|
||||
|
||||
// Creation mode
|
||||
this.instruction(this._creationMode, ast.sourceSpan, R3.text, o.literal(nodeIndex));
|
||||
|
@ -333,20 +454,21 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
// Refresh mode
|
||||
this.instruction(
|
||||
this._refreshMode, ast.sourceSpan, R3.textCreateBound, o.literal(nodeIndex),
|
||||
this.bind(o.variable(this.contextParameter), ast.value, ast.sourceSpan));
|
||||
this.bind(o.variable(CONTEXT_NAME), ast.value, ast.sourceSpan));
|
||||
}
|
||||
|
||||
visitText(ast: TextAst) {
|
||||
// Text is defined in creation mode only.
|
||||
this.instruction(this._creationMode, ast.sourceSpan, R3.text, o.literal(ast.value));
|
||||
this.instruction(
|
||||
this._creationMode, ast.sourceSpan, R3.text, o.literal(this.allocateDataSlot()),
|
||||
o.literal(ast.value));
|
||||
}
|
||||
|
||||
// These should be handled in the template or element directly
|
||||
readonly visitDirective = invalid;
|
||||
readonly visitDirectiveProperty = invalid;
|
||||
|
||||
private allocateDirective() { return this._dataIndex++; }
|
||||
private allocateNode() { return this._dataIndex++; }
|
||||
private allocateDataSlot() { return this._dataIndex++; }
|
||||
private bindingContext() { return `${this._bindingContext++}`; }
|
||||
|
||||
private instruction(
|
||||
|
@ -373,13 +495,13 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
|||
|
||||
private convertPropertyBinding(implicit: o.Expression, value: AST): o.Expression {
|
||||
const convertedPropertyBinding = convertPropertyBinding(
|
||||
null, implicit, value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this, implicit, value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this._refreshMode.push(...convertedPropertyBinding.stmts);
|
||||
return convertedPropertyBinding.currValExpr;
|
||||
}
|
||||
|
||||
private bind(implicit: o.Expression, value: AST, sourceSpan: ParseSourceSpan): o.Expression {
|
||||
return o.importExpr(R3.bind).callFn([this.convertPropertyBinding(implicit, value)]);
|
||||
return this.convertPropertyBinding(implicit, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,8 +110,8 @@ export class BoundEventAst implements TemplateAst {
|
|||
*/
|
||||
export class ReferenceAst implements TemplateAst {
|
||||
constructor(
|
||||
public name: string, public value: CompileTokenMetadata, public sourceSpan: ParseSourceSpan) {
|
||||
}
|
||||
public name: string, public value: CompileTokenMetadata, public originalValue: string,
|
||||
public sourceSpan: ParseSourceSpan) {}
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
return visitor.visitReference(this, context);
|
||||
}
|
||||
|
|
|
@ -574,7 +574,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
||||
(elOrDirRef.isReferenceToDirective(directive))) {
|
||||
targetReferences.push(new ReferenceAst(
|
||||
elOrDirRef.name, createTokenForReference(directive.type.reference),
|
||||
elOrDirRef.name, createTokenForReference(directive.type.reference), elOrDirRef.value,
|
||||
elOrDirRef.sourceSpan));
|
||||
matchedReferences.add(elOrDirRef.name);
|
||||
}
|
||||
|
@ -598,7 +598,8 @@ class TemplateParseVisitor implements html.Visitor {
|
|||
if (isTemplateElement) {
|
||||
refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef);
|
||||
}
|
||||
targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.sourceSpan));
|
||||
targetReferences.push(
|
||||
new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.value, elOrDirRef.sourceSpan));
|
||||
}
|
||||
});
|
||||
return directiveAsts;
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AotCompilerHost, AotCompilerOptions, AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, Lexer, NgModuleResolver, Parser, PipeResolver, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, TypeScriptEmitter, analyzeNgModules, createAotUrlResolver} from '@angular/compiler';
|
||||
import {AotCompilerHost, AotCompilerOptions, AotSummaryResolver, CompileDirectiveMetadata, CompileMetadataResolver, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, Lexer, NgModuleResolver, Parser, PipeResolver, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, TypeScriptEmitter, analyzeNgModules, createAotUrlResolver} from '@angular/compiler';
|
||||
import {ViewEncapsulation} from '@angular/core';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ConstantPool} from '../../src/constant_pool';
|
||||
import * as o from '../../src/output/output_ast';
|
||||
import {compileComponent} from '../../src/render3/r3_view_compiler';
|
||||
import {compileComponent, compileDirective} from '../../src/render3/r3_view_compiler';
|
||||
import {OutputContext} from '../../src/util';
|
||||
import {MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, arrayToMockDir, settings, setup, toMockFileArray} from '../aot/test_util';
|
||||
|
||||
|
@ -99,8 +99,327 @@ describe('r3_view_compiler', () => {
|
|||
const result = compile(files, angularFiles);
|
||||
expect(result.source).toContain('@angular/core');
|
||||
});
|
||||
|
||||
/* These tests are codified version of the tests in compiler_canonical_spec.ts. Every
|
||||
* test in compiler_canonical_spec.ts should have a corresponding test here.
|
||||
*/
|
||||
describe('compiler conformance', () => {
|
||||
describe('elements', () => {
|
||||
it('should translate DOM structure', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: \`<div class="my-app" title="Hello">Hello <b>World</b>!</div>\`
|
||||
})
|
||||
export class MyComponent {}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
// The factory should look like this:
|
||||
const factory = 'factory: () => { return new MyComponent(); }';
|
||||
|
||||
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||
const template = `
|
||||
template: (ctx: IDENT, cm: IDENT) => {
|
||||
if (cm) {
|
||||
IDENT.ɵE(0, 'div', IDENT);
|
||||
IDENT.ɵT(1, 'Hello ');
|
||||
IDENT.ɵE(2, 'b');
|
||||
IDENT.ɵT(3, 'World');
|
||||
IDENT.ɵe();
|
||||
IDENT.ɵT(4, '!');
|
||||
IDENT.ɵe();
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// The compiler should also emit a const array like this:
|
||||
const constants = `const IDENT = ['class', 'my-app', 'title', 'Hello'];`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
|
||||
expectEmit(result.source, factory, 'Incorrect factory');
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
expectEmit(result.source, constants, 'Incorrect shared constants');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('components & directives', () => {
|
||||
it('should instantiate directives', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, NgModule} from '@angular/core';
|
||||
|
||||
@Component({selector: 'child', template: 'child-view'})
|
||||
export class ChildComponent {}
|
||||
|
||||
@Directive({selector: '[some-directive]'})
|
||||
export class SomeDirective {}
|
||||
|
||||
@Component({selector: 'my-component', template: '<child some-directive></child>!'})
|
||||
export class MyComponent {}
|
||||
|
||||
@NgModule({declarations: [ChildComponent, SomeDirective, MyComponent]})
|
||||
export class MyModule{}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
// ChildComponent definition should be:
|
||||
const ChildComponentDefinition = `
|
||||
static ngComponentDef = IDENT.ɵdefineComponent({
|
||||
tag: 'child',
|
||||
factory: () => { return new ChildComponent(); },
|
||||
template: (ctx: IDENT, cm: IDENT) => {
|
||||
if (cm) {
|
||||
IDENT.ɵT(0, 'child-view');
|
||||
}
|
||||
}
|
||||
});`;
|
||||
|
||||
// SomeDirective definition should be:
|
||||
const SomeDirectiveDefinition = `
|
||||
static ngDirectiveDef = IDENT.ɵdefineDirective({
|
||||
factory: () => {return new SomeDirective(); }
|
||||
});
|
||||
`;
|
||||
|
||||
// MyComponent definition should be:
|
||||
const MyComponentDefinition = `
|
||||
static ngComponentDef = IDENT.ɵdefineComponent({
|
||||
tag: 'my-component',
|
||||
factory: () => { return new MyComponent(); },
|
||||
template: (ctx: IDENT, cm: IDENT) => {
|
||||
if (cm) {
|
||||
IDENT.ɵE(0, ChildComponent, IDENT, IDENT);
|
||||
IDENT.ɵe();
|
||||
IDENT.ɵT(3, '!');
|
||||
}
|
||||
ChildComponent.ngComponentDef.r(1, 0);
|
||||
SomeDirective.ngDirectiveDef.r(2, 0);
|
||||
}
|
||||
});
|
||||
`;
|
||||
|
||||
// The following constants should be emitted as well.
|
||||
const AttributesConstant = `const IDENT = ['some-directive', ''];`;
|
||||
|
||||
const DirectivesConstant = `const IDENT = [SomeDirective];`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
const source = result.source;
|
||||
|
||||
expectEmit(source, ChildComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
|
||||
expectEmit(source, SomeDirectiveDefinition, 'Incorrect SomeDirective.ngDirectiveDef');
|
||||
expectEmit(source, MyComponentDefinition, 'Incorrect MyComponentDefinition.ngComponentDef');
|
||||
expectEmit(source, AttributesConstant, 'Incorrect shared attributes constant');
|
||||
expectEmit(source, DirectivesConstant, 'Incorrect share directives constant');
|
||||
});
|
||||
|
||||
it('should support structural directives', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
|
||||
|
||||
@Directive({selector: '[if]'})
|
||||
export class IfDirective {
|
||||
constructor(template: TemplateRef<any>) { }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '<ul #foo><li *if>{{salutation}} {{foo}}</li></ul>'
|
||||
})
|
||||
export class MyComponent {
|
||||
salutation = 'Hello';
|
||||
}
|
||||
|
||||
@NgModule({declarations: [IfDirective, MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const IfDirectiveDefinition = `
|
||||
static ngDirectiveDef = IDENT.ɵdefineDirective({
|
||||
factory: () => { return new IfDirective(IDENT.ɵinjectTemplateRef()); }
|
||||
});`;
|
||||
const MyComponentDefinition = `
|
||||
static ngComponentDef = IDENT.ɵdefineComponent({
|
||||
tag: 'my-component',
|
||||
factory: () => { return new MyComponent(); },
|
||||
template: (ctx: IDENT, cm: IDENT) => {
|
||||
if (cm) {
|
||||
IDENT.ɵE(0, 'ul', null, null, IDENT);
|
||||
IDENT.ɵC(2, IDENT, C2Template);
|
||||
IDENT.ɵe();
|
||||
}
|
||||
const IDENT = IDENT.ɵm(1);
|
||||
IDENT.ɵcR(2);
|
||||
IfDirective.ngDirectiveDef.r(3,2);
|
||||
IDENT.ɵcr();
|
||||
|
||||
function C2Template(ctx0: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
IDENT.ɵE(0, 'li');
|
||||
IDENT.ɵT(1);
|
||||
IDENT.ɵe();
|
||||
}
|
||||
IDENT.ɵt(1, IDENT.ɵb2('', ctx.salutation, ' ', IDENT, ''));
|
||||
}
|
||||
}
|
||||
});`;
|
||||
const locals = `const IDENT = ['foo', ''];`;
|
||||
const directives = `const IDENT = [IfDirective];`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
const source = result.source;
|
||||
|
||||
expectEmit(source, IfDirectiveDefinition, 'Incorrect IfDirective.ngDirectiveDef');
|
||||
expectEmit(source, MyComponentDefinition, 'Incorrect MyComponent.ngComponentDef');
|
||||
expectEmit(source, locals, 'Incorrect share locals constant');
|
||||
expectEmit(source, directives, 'Incorrect shared directive constant');
|
||||
});
|
||||
|
||||
it('local reference', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({selector: 'my-component', template: '<input #user>Hello {{user.value}}!'})
|
||||
export class MyComponent {}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const MyComponentDefinition = `
|
||||
static ngComponentDef = IDENT.ɵdefineComponent({
|
||||
tag: 'my-component',
|
||||
factory: () => { return new MyComponent(); },
|
||||
template: (ctx: IDENT, cm: IDENT) => {
|
||||
if (cm) {
|
||||
IDENT.ɵE(0, 'input', null, null, IDENT);
|
||||
IDENT.ɵe();
|
||||
IDENT.ɵT(2);
|
||||
}
|
||||
const IDENT = IDENT.ɵm(1);
|
||||
IDENT.ɵt(2, IDENT.ɵb1('Hello ', IDENT.value, '!'));
|
||||
}
|
||||
});
|
||||
`;
|
||||
|
||||
const locals = `
|
||||
const IDENT = ['user', ''];
|
||||
`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
const source = result.source;
|
||||
|
||||
expectEmit(source, MyComponentDefinition, 'Incorrect MyComponent.ngComponentDef');
|
||||
expectEmit(source, locals, 'Incorrect locals constant definition');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/;
|
||||
const OPERATOR =
|
||||
/!|%|\*|\/|\^|\&|\&\&\|\||\|\||\(|\)|\{|\}|\[|\]|:|;|\.|<|<=|>|>=|=|==|===|!=|!==|=>|\+|\+\+|-|--|@|,|\.|\.\.\./;
|
||||
const STRING = /\'[^'\n]*\'|"[^'\n]*"|`[^`]*`/;
|
||||
const NUMBER = /[0-9]+/;
|
||||
const TOKEN = new RegExp(
|
||||
`^((${IDENTIFIER.source})|(${OPERATOR.source})|(${STRING.source})|${NUMBER.source})`);
|
||||
const WHITESPACE = /^\s+/;
|
||||
|
||||
type Piece = string | RegExp;
|
||||
|
||||
const IDENT = /[A-Za-z$_][A-Za-z0-9$_]*/;
|
||||
|
||||
function tokenize(text: string): Piece[] {
|
||||
function matches(exp: RegExp): string|false {
|
||||
const m = text.match(exp);
|
||||
if (!m) return false;
|
||||
text = text.substr(m[0].length);
|
||||
return m[0];
|
||||
}
|
||||
function next(): string {
|
||||
const result = matches(TOKEN);
|
||||
if (!result) {
|
||||
throw Error(`Invalid test, no token found for '${text.substr(0, 30)}...'`);
|
||||
}
|
||||
matches(WHITESPACE);
|
||||
return result;
|
||||
}
|
||||
|
||||
const pieces: Piece[] = [];
|
||||
matches(WHITESPACE);
|
||||
while (text) {
|
||||
const token = next();
|
||||
if (token === 'IDENT') {
|
||||
pieces.push(IDENT);
|
||||
} else {
|
||||
pieces.push(token);
|
||||
}
|
||||
}
|
||||
return pieces;
|
||||
}
|
||||
|
||||
const contextWidth = 100;
|
||||
function expectEmit(source: string, emitted: string, description: string) {
|
||||
const pieces = tokenize(emitted);
|
||||
const expr = r(...pieces);
|
||||
if (!expr.test(source)) {
|
||||
let last: number = 0;
|
||||
for (let i = 1; i < pieces.length; i++) {
|
||||
let t = r(...pieces.slice(0, i));
|
||||
let m = source.match(t);
|
||||
let expected = pieces[i - 1] == IDENT ? '<IDENT>' : pieces[i - 1];
|
||||
if (!m) {
|
||||
const contextPieceWidth = contextWidth / 2;
|
||||
fail(
|
||||
`${description}: Expected to find ${expected} '${source.substr(0,last)}[<---HERE]${source.substr(last)}'`);
|
||||
return;
|
||||
} else {
|
||||
last = (m.index || 0) + m[0].length;
|
||||
}
|
||||
}
|
||||
fail(
|
||||
'Test helper failure: Expected expression failed but the reporting logic could not find where it failed');
|
||||
}
|
||||
}
|
||||
|
||||
const IDENT_LIKE = /^[a-z][A-Z]/;
|
||||
const SPECIAL_RE_CHAR = /\/|\(|\)|\||\*|\+|\[|\]|\{|\}/g;
|
||||
function r(...pieces: (string | RegExp)[]): RegExp {
|
||||
let results: string[] = [];
|
||||
let first = true;
|
||||
for (const piece of pieces) {
|
||||
if (!first)
|
||||
results.push(`\\s${typeof piece === 'string' && IDENT_LIKE.test(piece) ? '+' : '*'}`);
|
||||
first = false;
|
||||
if (typeof piece === 'string') {
|
||||
results.push(piece.replace(SPECIAL_RE_CHAR, s => '\\' + s));
|
||||
} else {
|
||||
results.push('(' + piece.source + ')');
|
||||
}
|
||||
}
|
||||
return new RegExp(results.join(''));
|
||||
}
|
||||
|
||||
function compile(
|
||||
data: MockDirectory, angularFiles: MockData, options: AotCompilerOptions = {},
|
||||
errorCollector: (error: any, fileName?: string) => void = error => { throw error; }) {
|
||||
|
@ -156,7 +475,7 @@ function compile(
|
|||
|
||||
const directives = Array.from(analyzedModules.ngModuleByPipeOrDirective.keys());
|
||||
|
||||
const fakeOuputContext: OutputContext = {
|
||||
const fakeOutputContext: OutputContext = {
|
||||
genFilePath: 'fakeFactory.ts',
|
||||
statements: [],
|
||||
importExpr(symbol: StaticSymbol, typeParams: o.Type[]) {
|
||||
|
@ -182,7 +501,10 @@ function compile(
|
|||
|
||||
// Compile the directives.
|
||||
for (const directive of directives) {
|
||||
const module = analyzedModules.ngModuleByPipeOrDirective.get(directive) !;
|
||||
const module = analyzedModules.ngModuleByPipeOrDirective.get(directive);
|
||||
if (!module || !module.type.reference.filePath.startsWith('/app')) {
|
||||
continue;
|
||||
}
|
||||
if (resolver.isDirective(directive)) {
|
||||
const metadata = resolver.getDirectiveMetadata(directive);
|
||||
if (metadata.isComponent) {
|
||||
|
@ -196,17 +518,24 @@ function compile(
|
|||
const parsedTemplate = templateParser.parse(
|
||||
metadata, htmlAst, directives, pipes, module.schemas, fakeUrl, false);
|
||||
|
||||
compileComponent(fakeOuputContext, metadata, parsedTemplate.template, staticReflector);
|
||||
compileComponent(fakeOutputContext, metadata, parsedTemplate.template, staticReflector);
|
||||
} else {
|
||||
compileDirective(fakeOutputContext, metadata, staticReflector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fakeOuputContext.statements.unshift(...fakeOuputContext.constantPool.statements);
|
||||
fakeOutputContext.statements.unshift(...fakeOutputContext.constantPool.statements);
|
||||
|
||||
const emitter = new TypeScriptEmitter();
|
||||
|
||||
const result = emitter.emitStatementsAndContext(
|
||||
fakeOuputContext.genFilePath, fakeOuputContext.statements, '', false);
|
||||
const moduleName = compilerHost.fileNameToModuleName(
|
||||
fakeOutputContext.genFilePath, fakeOutputContext.genFilePath);
|
||||
|
||||
return {source: result.sourceText, outputContext: fakeOuputContext};
|
||||
const result = emitter.emitStatementsAndContext(
|
||||
fakeOutputContext.genFilePath, fakeOutputContext.statements, '', false,
|
||||
/* referenceFilter */ undefined,
|
||||
/* importFilter */ e => e.moduleName != null && e.moduleName.startsWith('/app'));
|
||||
|
||||
return {source: result.sourceText, outputContext: fakeOutputContext};
|
||||
}
|
|
@ -407,7 +407,7 @@ class ArrayConsole implements Console {
|
|||
expectVisitedNode(
|
||||
new class extends
|
||||
NullVisitor{visitReference(ast: ReferenceAst, context: any): any{return ast;}},
|
||||
new ReferenceAst('foo', null !, null !));
|
||||
new ReferenceAst('foo', null !, null !, null !));
|
||||
});
|
||||
|
||||
it('should visit VariableAst', () => {
|
||||
|
@ -474,7 +474,7 @@ class ArrayConsole implements Console {
|
|||
new NgContentAst(0, 0, null !),
|
||||
new EmbeddedTemplateAst([], [], [], [], [], [], false, [], [], 0, null !),
|
||||
new ElementAst('foo', [], [], [], [], [], [], false, [], [], 0, null !, null !),
|
||||
new ReferenceAst('foo', null !, null !), new VariableAst('foo', 'bar', null !),
|
||||
new ReferenceAst('foo', null !, 'bar', null !), new VariableAst('foo', 'bar', null !),
|
||||
new BoundEventAst('foo', 'bar', 'goo', null !, null !),
|
||||
new BoundElementPropertyAst('foo', null !, null !, null !, 'bar', null !),
|
||||
new AttrAst('foo', 'bar', null !), new BoundTextAst(null !, 0, null !),
|
||||
|
|
Loading…
Reference in New Issue