feat(compiler): add TemplateCompiler
TemplateCompiler is the entry point to the new compiler Related to #3605 Closes #4220
This commit is contained in:
@ -14,7 +14,7 @@ import {
|
||||
ASTWithSource
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
import {DirectiveMetadata, TypeMetadata} from './api';
|
||||
import {NormalizedDirectiveMetadata, TypeMetadata} from './directive_metadata';
|
||||
import {
|
||||
TemplateAst,
|
||||
ElementAst,
|
||||
@ -203,5 +203,5 @@ function _collectNestedProtoViewsVariableNames(pvVisitors: ProtoViewVisitor[]):
|
||||
|
||||
|
||||
function _protoViewId(hostComponentType: TypeMetadata, pvIndex: number, viewType: string): string {
|
||||
return `${hostComponentType.typeName}_${viewType}_${pvIndex}`;
|
||||
return `${hostComponentType.name}_${viewType}_${pvIndex}`;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {TypeMetadata, SourceModule} from './api';
|
||||
import {TypeMetadata} from './directive_metadata';
|
||||
import {SourceExpression, moduleRef} from './source_module';
|
||||
import {
|
||||
ChangeDetectorJITGenerator
|
||||
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
||||
@ -15,20 +16,19 @@ import {
|
||||
|
||||
import {TemplateAst} from './template_ast';
|
||||
import {Codegen} from 'angular2/src/transform/template_compiler/change_detector_codegen';
|
||||
|
||||
var IS_DART = !isJsObject({});
|
||||
import {IS_DART} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||
const UTIL = "ChangeDetectionUtil";
|
||||
|
||||
const JS_CHANGE_DETECTOR_IMPORTS = CONST_EXPR([
|
||||
['angular2/src/core/change_detection/abstract_change_detector', 'acd'],
|
||||
['angular2/src/core/change_detection/change_detection_util', 'cdu']
|
||||
]);
|
||||
|
||||
const DART_CHANGE_DETECTOR_IMPORTS =
|
||||
CONST_EXPR([['angular2/src/core/change_detection/pregen_proto_change_detector', '_gen']]);
|
||||
var ABSTRACT_CHANGE_DETECTOR_MODULE =
|
||||
moduleRef('angular2/src/core/change_detection/abstract_change_detector');
|
||||
var UTIL_MODULE = moduleRef('angular2/src/core/change_detection/change_detection_util');
|
||||
var PREGEN_PROTO_CHANGE_DETECTOR_MODULE =
|
||||
moduleRef('angular2/src/core/change_detection/pregen_proto_change_detector');
|
||||
|
||||
@Injectable()
|
||||
export class ChangeDetectionCompiler {
|
||||
constructor(private _genConfig: ChangeDetectorGenConfig) {}
|
||||
|
||||
@ -52,10 +52,9 @@ export class ChangeDetectionCompiler {
|
||||
}
|
||||
|
||||
compileComponentCodeGen(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
|
||||
parsedTemplate: TemplateAst[]): SourceModule {
|
||||
parsedTemplate: TemplateAst[]): SourceExpression {
|
||||
var changeDetectorDefinitions =
|
||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
||||
var imports = IS_DART ? DART_CHANGE_DETECTOR_IMPORTS : JS_CHANGE_DETECTOR_IMPORTS;
|
||||
var factories = [];
|
||||
var sourceParts = changeDetectorDefinitions.map(definition => {
|
||||
var codegen: any;
|
||||
@ -63,19 +62,20 @@ export class ChangeDetectionCompiler {
|
||||
// suffix
|
||||
// and have the same API for calling them!
|
||||
if (IS_DART) {
|
||||
codegen = new Codegen();
|
||||
codegen = new Codegen(PREGEN_PROTO_CHANGE_DETECTOR_MODULE);
|
||||
var className = definition.id;
|
||||
codegen.generate(componentType.typeName, className, definition);
|
||||
codegen.generate(componentType.name, className, definition);
|
||||
factories.push(`(dispatcher) => new ${className}(dispatcher)`);
|
||||
return codegen.toString();
|
||||
} else {
|
||||
codegen = new ChangeDetectorJITGenerator(definition, `cdu.${UTIL}`,
|
||||
`acd.${ABSTRACT_CHANGE_DETECTOR}`);
|
||||
codegen = new ChangeDetectorJITGenerator(
|
||||
definition, `${UTIL_MODULE}${UTIL}`,
|
||||
`${ABSTRACT_CHANGE_DETECTOR_MODULE}${ABSTRACT_CHANGE_DETECTOR}`);
|
||||
factories.push(`function(dispatcher) { return new ${codegen.typeName}(dispatcher); }`);
|
||||
return codegen.generateSource();
|
||||
}
|
||||
});
|
||||
sourceParts.push(`var CHANGE_DETECTORS = [ ${factories.join(',')} ];`);
|
||||
return new SourceModule(componentType.typeUrl, sourceParts.join('\n'), imports);
|
||||
var expression = `[ ${factories.join(',')} ]`;
|
||||
return new SourceExpression(sourceParts, expression);
|
||||
}
|
||||
}
|
||||
|
@ -25,16 +25,19 @@ import {
|
||||
BoundDirectivePropertyAst,
|
||||
templateVisitAll
|
||||
} from './template_ast';
|
||||
import {SourceModule, DirectiveMetadata, TypeMetadata} from './api';
|
||||
import {TypeMetadata, NormalizedDirectiveMetadata} from './directive_metadata';
|
||||
import {SourceExpression, moduleRef} from './source_module';
|
||||
|
||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||
import {shimHostAttribute, shimContentAttribute} from './style_compiler';
|
||||
import {escapeSingleQuoteString} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
const TEMPLATE_COMMANDS_MODULE = 'angular2/src/core/compiler/template_commands';
|
||||
const TEMPLATE_COMMANDS_MODULE_ALIAS = 'tc';
|
||||
export var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands');
|
||||
|
||||
@Injectable()
|
||||
export class CommandCompiler {
|
||||
compileComponentRuntime(component: DirectiveMetadata, template: TemplateAst[],
|
||||
compileComponentRuntime(component: NormalizedDirectiveMetadata, template: TemplateAst[],
|
||||
componentTemplateFactory: Function): TemplateCmd[] {
|
||||
var visitor =
|
||||
new CommandBuilderVisitor(new RuntimeCommandFactory(componentTemplateFactory), component);
|
||||
@ -42,16 +45,13 @@ export class CommandCompiler {
|
||||
return visitor.result;
|
||||
}
|
||||
|
||||
compileComponentCodeGen(component: DirectiveMetadata, template: TemplateAst[],
|
||||
componentTemplateFactory: Function): SourceModule {
|
||||
var imports: string[][] = [[TEMPLATE_COMMANDS_MODULE, TEMPLATE_COMMANDS_MODULE_ALIAS]];
|
||||
var visitor = new CommandBuilderVisitor(
|
||||
new CodegenCommandFactory(componentTemplateFactory, TEMPLATE_COMMANDS_MODULE_ALIAS,
|
||||
imports),
|
||||
component);
|
||||
compileComponentCodeGen(component: NormalizedDirectiveMetadata, template: TemplateAst[],
|
||||
componentTemplateFactory: Function): SourceExpression {
|
||||
var visitor =
|
||||
new CommandBuilderVisitor(new CodegenCommandFactory(componentTemplateFactory), component);
|
||||
templateVisitAll(visitor, template);
|
||||
var source = `var COMMANDS = [${visitor.result.join(',')}];`;
|
||||
return new SourceModule(null, source, imports);
|
||||
var source = `[${visitor.result.join(',')}]`;
|
||||
return new SourceExpression([], source);
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,22 +59,22 @@ interface CommandFactory<R> {
|
||||
createText(value: string, isBound: boolean, ngContentIndex: number): R;
|
||||
createNgContent(ngContentIndex: number): R;
|
||||
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||
variableNameAndValues: string[], directives: TypeMetadata[], isBound: boolean,
|
||||
ngContentIndex: number): R;
|
||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||
isBound: boolean, ngContentIndex: number): R;
|
||||
createEndElement(): R;
|
||||
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||
variableNameAndValues: string[], directives: TypeMetadata[],
|
||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||
nativeShadow: boolean, ngContentIndex: number): R;
|
||||
createEndComponent(): R;
|
||||
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
||||
directives: TypeMetadata[], isMerged: boolean, ngContentIndex: number,
|
||||
children: R[]): R;
|
||||
directives: NormalizedDirectiveMetadata[], isMerged: boolean,
|
||||
ngContentIndex: number, children: R[]): R;
|
||||
}
|
||||
|
||||
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
||||
constructor(public componentTemplateFactory: Function) {}
|
||||
private _mapDirectives(directives: TypeMetadata[]): Type[] {
|
||||
return directives.map(directive => directive.type);
|
||||
private _mapDirectives(directives: NormalizedDirectiveMetadata[]): Type[] {
|
||||
return directives.map(directive => directive.type.runtime);
|
||||
}
|
||||
|
||||
createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd {
|
||||
@ -82,14 +82,14 @@ class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
||||
}
|
||||
createNgContent(ngContentIndex: number): TemplateCmd { return ngContent(ngContentIndex); }
|
||||
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||
variableNameAndValues: string[], directives: TypeMetadata[], isBound: boolean,
|
||||
ngContentIndex: number): TemplateCmd {
|
||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||
isBound: boolean, ngContentIndex: number): TemplateCmd {
|
||||
return beginElement(name, attrNameAndValues, eventNames, variableNameAndValues,
|
||||
this._mapDirectives(directives), isBound, ngContentIndex);
|
||||
}
|
||||
createEndElement(): TemplateCmd { return endElement(); }
|
||||
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||
variableNameAndValues: string[], directives: TypeMetadata[],
|
||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||
nativeShadow: boolean, ngContentIndex: number): TemplateCmd {
|
||||
return beginComponent(name, attrNameAndValues, eventNames, variableNameAndValues,
|
||||
this._mapDirectives(directives), nativeShadow, ngContentIndex,
|
||||
@ -97,8 +97,8 @@ class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
||||
}
|
||||
createEndComponent(): TemplateCmd { return endComponent(); }
|
||||
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
||||
directives: TypeMetadata[], isMerged: boolean, ngContentIndex: number,
|
||||
children: TemplateCmd[]): TemplateCmd {
|
||||
directives: NormalizedDirectiveMetadata[], isMerged: boolean,
|
||||
ngContentIndex: number, children: TemplateCmd[]): TemplateCmd {
|
||||
return embeddedTemplate(attrNameAndValues, variableNameAndValues,
|
||||
this._mapDirectives(directives), isMerged, ngContentIndex, children);
|
||||
}
|
||||
@ -109,42 +109,38 @@ function escapeStringArray(data: string[]): string {
|
||||
}
|
||||
|
||||
class CodegenCommandFactory implements CommandFactory<string> {
|
||||
constructor(public componentTemplateFactory: Function, public templateCommandsModuleAlias,
|
||||
public imports: string[][]) {}
|
||||
|
||||
private _escapeDirectives(directives: TypeMetadata[]): string[] {
|
||||
return directives.map(directiveType => {
|
||||
var importAlias = `dir${this.imports.length}`;
|
||||
this.imports.push([directiveType.typeUrl, importAlias]);
|
||||
return `${importAlias}.${directiveType.typeName}`;
|
||||
});
|
||||
}
|
||||
constructor(public componentTemplateFactory: Function) {}
|
||||
|
||||
createText(value: string, isBound: boolean, ngContentIndex: number): string {
|
||||
return `${this.templateCommandsModuleAlias}.text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`;
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`;
|
||||
}
|
||||
createNgContent(ngContentIndex: number): string {
|
||||
return `${this.templateCommandsModuleAlias}.ngContent(${ngContentIndex})`;
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}ngContent(${ngContentIndex})`;
|
||||
}
|
||||
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||
variableNameAndValues: string[], directives: TypeMetadata[], isBound: boolean,
|
||||
ngContentIndex: number): string {
|
||||
return `${this.templateCommandsModuleAlias}.beginElement(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${this._escapeDirectives(directives).join(',')}], ${isBound}, ${ngContentIndex})`;
|
||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||
isBound: boolean, ngContentIndex: number): string {
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isBound}, ${ngContentIndex})`;
|
||||
}
|
||||
createEndElement(): string { return `${this.templateCommandsModuleAlias}.endElement()`; }
|
||||
createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; }
|
||||
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||
variableNameAndValues: string[], directives: TypeMetadata[],
|
||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||
nativeShadow: boolean, ngContentIndex: number): string {
|
||||
return `${this.templateCommandsModuleAlias}.beginComponent(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${this._escapeDirectives(directives).join(',')}], ${nativeShadow}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0], this.imports)})`;
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${nativeShadow}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`;
|
||||
}
|
||||
createEndComponent(): string { return `${this.templateCommandsModuleAlias}.endComponent()`; }
|
||||
createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; }
|
||||
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
||||
directives: TypeMetadata[], isMerged: boolean, ngContentIndex: number,
|
||||
children: string[]): string {
|
||||
return `${this.templateCommandsModuleAlias}.embeddedTemplate(${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(variableNameAndValues)}, [${this._escapeDirectives(directives).join(',')}], ${isMerged}, ${ngContentIndex}, [${children.join(',')}])`;
|
||||
directives: NormalizedDirectiveMetadata[], isMerged: boolean,
|
||||
ngContentIndex: number, children: string[]): string {
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isMerged}, ${ngContentIndex}, [${children.join(',')}])`;
|
||||
}
|
||||
}
|
||||
|
||||
function _escapeDirectives(directives: NormalizedDirectiveMetadata[]): string[] {
|
||||
return directives.map(directiveType =>
|
||||
`${moduleRef(directiveType.type.moduleId)}${directiveType.type.name}`);
|
||||
}
|
||||
|
||||
function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[], context: any):
|
||||
any {
|
||||
templateVisitAll(visitor, asts, context);
|
||||
@ -154,18 +150,19 @@ function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[],
|
||||
class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
||||
result: R[] = [];
|
||||
transitiveNgContentCount: number = 0;
|
||||
constructor(public commandFactory: CommandFactory<R>, public component: DirectiveMetadata) {}
|
||||
constructor(public commandFactory: CommandFactory<R>,
|
||||
public component: NormalizedDirectiveMetadata) {}
|
||||
|
||||
private _readAttrNameAndValues(localComponent: DirectiveMetadata,
|
||||
private _readAttrNameAndValues(localComponent: NormalizedDirectiveMetadata,
|
||||
attrAsts: TemplateAst[]): string[] {
|
||||
var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []);
|
||||
if (isPresent(localComponent) &&
|
||||
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
|
||||
attrNameAndValues.push(shimHostAttribute(localComponent.type));
|
||||
attrNameAndValues.push(shimHostAttribute(localComponent.type.id));
|
||||
attrNameAndValues.push('');
|
||||
}
|
||||
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
|
||||
attrNameAndValues.push(shimContentAttribute(this.component.type));
|
||||
attrNameAndValues.push(shimContentAttribute(this.component.type.id));
|
||||
attrNameAndValues.push('');
|
||||
}
|
||||
return attrNameAndValues;
|
||||
@ -228,7 +225,7 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
||||
return null;
|
||||
}
|
||||
visitDirective(ast: DirectiveAst, directivesAndEventNames: any[][]): any {
|
||||
directivesAndEventNames[0].push(ast.directive.type);
|
||||
directivesAndEventNames[0].push(ast.directive);
|
||||
templateVisitAll(this, ast.hostEvents, directivesAndEventNames[1]);
|
||||
return null;
|
||||
}
|
||||
|
9
modules/angular2/src/compiler/compiler.ts
Normal file
9
modules/angular2/src/compiler/compiler.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export {TemplateCompiler} from './template_compiler';
|
||||
export {
|
||||
DirectiveMetadata,
|
||||
TypeMetadata,
|
||||
TemplateMetadata,
|
||||
ChangeDetectionMetadata,
|
||||
INormalizedDirectiveMetadata
|
||||
} from './directive_metadata';
|
||||
export {SourceModule, SourceWithImports} from './source_module';
|
@ -4,31 +4,31 @@ import {
|
||||
changeDetectionStrategyFromJson
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {ViewEncapsulation, viewEncapsulationFromJson} from 'angular2/src/core/render/api';
|
||||
import {CssSelector} from 'angular2/src/core/render/dom/compiler/selector';
|
||||
|
||||
export class TypeMetadata {
|
||||
id: number;
|
||||
type: Type;
|
||||
typeName: string;
|
||||
typeUrl: string;
|
||||
constructor({id, type, typeName, typeUrl}:
|
||||
{id?: number, type?: Type, typeName?: string, typeUrl?: string} = {}) {
|
||||
runtime: Type;
|
||||
name: string;
|
||||
moduleId: string;
|
||||
constructor({id, runtime, name, moduleId}:
|
||||
{id?: number, runtime?: Type, name?: string, moduleId?: string} = {}) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.typeName = typeName;
|
||||
this.typeUrl = typeUrl;
|
||||
this.runtime = runtime;
|
||||
this.name = name;
|
||||
this.moduleId = moduleId;
|
||||
}
|
||||
|
||||
static fromJson(data: StringMap<string, any>): TypeMetadata {
|
||||
return new TypeMetadata(
|
||||
{id: data['id'], type: data['type'], typeName: data['typeName'], typeUrl: data['typeUrl']});
|
||||
return new TypeMetadata({id: data['id'], name: data['name'], moduleId: data['moduleId']});
|
||||
}
|
||||
|
||||
toJson(): StringMap<string, any> {
|
||||
return {
|
||||
// Note: Runtime type can't be serialized...
|
||||
'id': this.id,
|
||||
'typeName': this.typeName,
|
||||
'typeUrl': this.typeUrl
|
||||
'name': this.name,
|
||||
'moduleId': this.moduleId
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -63,17 +63,17 @@ export class ChangeDetectionMetadata {
|
||||
callOnInit?: boolean
|
||||
} = {}) {
|
||||
this.changeDetection = changeDetection;
|
||||
this.properties = properties;
|
||||
this.events = events;
|
||||
this.hostListeners = hostListeners;
|
||||
this.hostProperties = hostProperties;
|
||||
this.callAfterContentInit = callAfterContentInit;
|
||||
this.callAfterContentChecked = callAfterContentChecked;
|
||||
this.callAfterViewInit = callAfterViewInit;
|
||||
this.callAfterViewChecked = callAfterViewChecked;
|
||||
this.callOnChanges = callOnChanges;
|
||||
this.callDoCheck = callDoCheck;
|
||||
this.callOnInit = callOnInit;
|
||||
this.properties = isPresent(properties) ? properties : [];
|
||||
this.events = isPresent(events) ? events : [];
|
||||
this.hostListeners = isPresent(hostListeners) ? hostListeners : {};
|
||||
this.hostProperties = isPresent(hostProperties) ? hostProperties : {};
|
||||
this.callAfterContentInit = normalizeBool(callAfterContentInit);
|
||||
this.callAfterContentChecked = normalizeBool(callAfterContentChecked);
|
||||
this.callAfterViewInit = normalizeBool(callAfterViewInit);
|
||||
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
|
||||
this.callOnChanges = normalizeBool(callOnChanges);
|
||||
this.callDoCheck = normalizeBool(callDoCheck);
|
||||
this.callOnInit = normalizeBool(callOnInit);
|
||||
}
|
||||
|
||||
static fromJson(data: StringMap<string, any>): ChangeDetectionMetadata {
|
||||
@ -115,27 +115,79 @@ export class ChangeDetectionMetadata {
|
||||
}
|
||||
|
||||
export class TemplateMetadata {
|
||||
encapsulation: ViewEncapsulation;
|
||||
template: string;
|
||||
templateUrl: string;
|
||||
styles: string[];
|
||||
styleUrls: string[];
|
||||
hostAttributes: StringMap<string, string>;
|
||||
constructor({encapsulation, template, templateUrl, styles, styleUrls, hostAttributes}: {
|
||||
encapsulation?: ViewEncapsulation,
|
||||
template?: string,
|
||||
templateUrl?: string,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
hostAttributes?: StringMap<string, string>
|
||||
} = {}) {
|
||||
this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.None;
|
||||
this.template = template;
|
||||
this.templateUrl = templateUrl;
|
||||
this.styles = isPresent(styles) ? styles : [];
|
||||
this.styleUrls = isPresent(styleUrls) ? styleUrls : [];
|
||||
this.hostAttributes = isPresent(hostAttributes) ? hostAttributes : {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class DirectiveMetadata {
|
||||
type: TypeMetadata;
|
||||
isComponent: boolean;
|
||||
dynamicLoadable: boolean;
|
||||
selector: string;
|
||||
changeDetection: ChangeDetectionMetadata;
|
||||
template: TemplateMetadata;
|
||||
constructor({type, isComponent, dynamicLoadable, selector, changeDetection, template}: {
|
||||
type?: TypeMetadata,
|
||||
isComponent?: boolean,
|
||||
dynamicLoadable?: boolean,
|
||||
selector?: string,
|
||||
changeDetection?: ChangeDetectionMetadata,
|
||||
template?: TemplateMetadata
|
||||
} = {}) {
|
||||
this.type = type;
|
||||
this.isComponent = normalizeBool(isComponent);
|
||||
this.dynamicLoadable = normalizeBool(dynamicLoadable);
|
||||
this.selector = selector;
|
||||
this.changeDetection = changeDetection;
|
||||
this.template = template;
|
||||
}
|
||||
}
|
||||
|
||||
export class NormalizedTemplateMetadata {
|
||||
encapsulation: ViewEncapsulation;
|
||||
template: string;
|
||||
styles: string[];
|
||||
styleAbsUrls: string[];
|
||||
ngContentSelectors: string[];
|
||||
constructor({encapsulation, template, styles, styleAbsUrls, ngContentSelectors}: {
|
||||
hostAttributes: StringMap<string, string>;
|
||||
constructor({encapsulation, template, styles, styleAbsUrls, ngContentSelectors, hostAttributes}: {
|
||||
encapsulation?: ViewEncapsulation,
|
||||
template?: string,
|
||||
styles?: string[],
|
||||
styleAbsUrls?: string[],
|
||||
ngContentSelectors?: string[]
|
||||
ngContentSelectors?: string[],
|
||||
hostAttributes?: StringMap<string, string>
|
||||
} = {}) {
|
||||
this.encapsulation = encapsulation;
|
||||
this.template = template;
|
||||
this.styles = styles;
|
||||
this.styleAbsUrls = styleAbsUrls;
|
||||
this.ngContentSelectors = ngContentSelectors;
|
||||
this.hostAttributes = hostAttributes;
|
||||
}
|
||||
|
||||
static fromJson(data: StringMap<string, any>): TemplateMetadata {
|
||||
return new TemplateMetadata({
|
||||
static fromJson(data: StringMap<string, any>): NormalizedTemplateMetadata {
|
||||
return new NormalizedTemplateMetadata({
|
||||
encapsulation: isPresent(data['encapsulation']) ?
|
||||
viewEncapsulationFromJson(data['encapsulation']) :
|
||||
data['encapsulation'],
|
||||
@ -143,6 +195,7 @@ export class TemplateMetadata {
|
||||
styles: data['styles'],
|
||||
styleAbsUrls: data['styleAbsUrls'],
|
||||
ngContentSelectors: data['ngContentSelectors'],
|
||||
hostAttributes: data['hostAttributes']
|
||||
});
|
||||
}
|
||||
|
||||
@ -154,54 +207,58 @@ export class TemplateMetadata {
|
||||
'styles': this.styles,
|
||||
'styleAbsUrls': this.styleAbsUrls,
|
||||
'ngContentSelectors': this.ngContentSelectors,
|
||||
'hostAttributes': this.hostAttributes
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface INormalizedDirectiveMetadata {}
|
||||
|
||||
export class DirectiveMetadata {
|
||||
export class NormalizedDirectiveMetadata implements INormalizedDirectiveMetadata {
|
||||
type: TypeMetadata;
|
||||
isComponent: boolean;
|
||||
dynamicLoadable: boolean;
|
||||
selector: string;
|
||||
hostAttributes: StringMap<string, string>;
|
||||
changeDetection: ChangeDetectionMetadata;
|
||||
template: TemplateMetadata;
|
||||
constructor({type, isComponent, selector, hostAttributes, changeDetection, template}: {
|
||||
template: NormalizedTemplateMetadata;
|
||||
constructor({type, isComponent, dynamicLoadable, selector, changeDetection, template}: {
|
||||
id?: number,
|
||||
type?: TypeMetadata,
|
||||
isComponent?: boolean,
|
||||
dynamicLoadable?: boolean,
|
||||
selector?: string,
|
||||
hostAttributes?: StringMap<string, string>,
|
||||
changeDetection?: ChangeDetectionMetadata,
|
||||
template?: TemplateMetadata
|
||||
template?: NormalizedTemplateMetadata
|
||||
} = {}) {
|
||||
this.type = type;
|
||||
this.isComponent = normalizeBool(isComponent);
|
||||
this.dynamicLoadable = normalizeBool(dynamicLoadable);
|
||||
this.selector = selector;
|
||||
this.hostAttributes = hostAttributes;
|
||||
this.changeDetection = changeDetection;
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
static fromJson(data: StringMap<string, any>): DirectiveMetadata {
|
||||
return new DirectiveMetadata({
|
||||
type: isPresent(data['type']) ? TypeMetadata.fromJson(data['type']) : data['type'],
|
||||
static fromJson(data: StringMap<string, any>): NormalizedDirectiveMetadata {
|
||||
return new NormalizedDirectiveMetadata({
|
||||
isComponent: data['isComponent'],
|
||||
dynamicLoadable: data['dynamicLoadable'],
|
||||
selector: data['selector'],
|
||||
hostAttributes: data['hostAttributes'],
|
||||
type: isPresent(data['type']) ? TypeMetadata.fromJson(data['type']) : data['type'],
|
||||
changeDetection: isPresent(data['changeDetection']) ?
|
||||
ChangeDetectionMetadata.fromJson(data['changeDetection']) :
|
||||
data['changeDetection'],
|
||||
template: isPresent(data['template']) ? TemplateMetadata.fromJson(data['template']) :
|
||||
data['template']
|
||||
template:
|
||||
isPresent(data['template']) ? NormalizedTemplateMetadata.fromJson(data['template']) :
|
||||
data['template']
|
||||
});
|
||||
}
|
||||
|
||||
toJson(): StringMap<string, any> {
|
||||
return {
|
||||
'type': isPresent(this.type) ? this.type.toJson() : this.type,
|
||||
'isComponent': this.isComponent,
|
||||
'dynamicLoadable': this.dynamicLoadable,
|
||||
'selector': this.selector,
|
||||
'hostAttributes': this.hostAttributes,
|
||||
'type': isPresent(this.type) ? this.type.toJson() : this.type,
|
||||
'changeDetection':
|
||||
isPresent(this.changeDetection) ? this.changeDetection.toJson() : this.changeDetection,
|
||||
'template': isPresent(this.template) ? this.template.toJson() : this.template
|
||||
@ -209,6 +266,39 @@ export class DirectiveMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
export class SourceModule {
|
||||
constructor(public moduleName: string, public source: string, public imports: string[][]) {}
|
||||
export function createHostComponentMeta(componentType: TypeMetadata, componentSelector: string):
|
||||
NormalizedDirectiveMetadata {
|
||||
var template = CssSelector.parse(componentSelector)[0].getMatchingElementTemplate();
|
||||
return new NormalizedDirectiveMetadata({
|
||||
type: new TypeMetadata({
|
||||
runtime: Object,
|
||||
id: (componentType.id * -1) - 1,
|
||||
name: `Host${componentType.name}`,
|
||||
moduleId: componentType.moduleId
|
||||
}),
|
||||
template: new NormalizedTemplateMetadata({
|
||||
template: template,
|
||||
styles: [],
|
||||
styleAbsUrls: [],
|
||||
hostAttributes: {},
|
||||
ngContentSelectors: []
|
||||
}),
|
||||
changeDetection: new ChangeDetectionMetadata({
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
properties: [],
|
||||
events: [],
|
||||
hostListeners: {},
|
||||
hostProperties: {},
|
||||
callAfterContentInit: false,
|
||||
callAfterContentChecked: false,
|
||||
callAfterViewInit: false,
|
||||
callAfterViewChecked: false,
|
||||
callOnChanges: false,
|
||||
callDoCheck: false,
|
||||
callOnInit: false
|
||||
}),
|
||||
isComponent: true,
|
||||
dynamicLoadable: false,
|
||||
selector: '*'
|
||||
});
|
||||
}
|
@ -17,9 +17,11 @@ import {
|
||||
} from './html_ast';
|
||||
|
||||
import {escapeDoubleQuoteString} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
const NG_NON_BINDABLE = 'ng-non-bindable';
|
||||
|
||||
@Injectable()
|
||||
export class HtmlParser {
|
||||
parse(template: string, sourceInfo: string): HtmlAst[] {
|
||||
var root = DOM.createTemplate(template);
|
||||
|
150
modules/angular2/src/compiler/runtime_metadata.ts
Normal file
150
modules/angular2/src/compiler/runtime_metadata.ts
Normal file
@ -0,0 +1,150 @@
|
||||
import {resolveForwardRef} from 'angular2/src/core/di';
|
||||
import {
|
||||
Type,
|
||||
isBlank,
|
||||
isPresent,
|
||||
isArray,
|
||||
stringify,
|
||||
RegExpWrapper
|
||||
} from 'angular2/src/core/facade/lang';
|
||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||
import {MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import * as cpl from './directive_metadata';
|
||||
import * as dirAnn from 'angular2/src/core/metadata/directives';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
||||
import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector';
|
||||
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
// group 1: "property" from "[property]"
|
||||
// group 2: "event" from "(event)"
|
||||
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
||||
|
||||
@Injectable()
|
||||
export class RuntimeMetadataResolver {
|
||||
private _directiveCounter = 0;
|
||||
private _cache: Map<Type, cpl.DirectiveMetadata> = new Map();
|
||||
|
||||
constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {}
|
||||
|
||||
getMetadata(directiveType: Type): cpl.DirectiveMetadata {
|
||||
var meta = this._cache.get(directiveType);
|
||||
if (isBlank(meta)) {
|
||||
var directiveAnnotation = this._directiveResolver.resolve(directiveType);
|
||||
var moduleId = calcModuleId(directiveType, directiveAnnotation);
|
||||
var templateMeta = null;
|
||||
var hostListeners = {};
|
||||
var hostProperties = {};
|
||||
var hostAttributes = {};
|
||||
var changeDetectionStrategy = null;
|
||||
var dynamicLoadable: boolean = false;
|
||||
|
||||
if (isPresent(directiveAnnotation.host)) {
|
||||
StringMapWrapper.forEach(directiveAnnotation.host, (value: string, key: string) => {
|
||||
var matches = RegExpWrapper.firstMatch(HOST_REG_EXP, key);
|
||||
if (isBlank(matches)) {
|
||||
hostAttributes[key] = value;
|
||||
} else if (isPresent(matches[1])) {
|
||||
hostProperties[matches[1]] = value;
|
||||
} else if (isPresent(matches[2])) {
|
||||
hostListeners[matches[2]] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (directiveAnnotation instanceof dirAnn.ComponentMetadata) {
|
||||
var compAnnotation = <dirAnn.ComponentMetadata>directiveAnnotation;
|
||||
var viewAnnotation = this._viewResolver.resolve(directiveType);
|
||||
templateMeta = new cpl.TemplateMetadata({
|
||||
encapsulation: viewAnnotation.encapsulation,
|
||||
template: viewAnnotation.template,
|
||||
templateUrl: viewAnnotation.templateUrl,
|
||||
styles: viewAnnotation.styles,
|
||||
styleUrls: viewAnnotation.styleUrls,
|
||||
hostAttributes: hostAttributes
|
||||
});
|
||||
changeDetectionStrategy = compAnnotation.changeDetection;
|
||||
dynamicLoadable = compAnnotation.dynamicLoadable;
|
||||
}
|
||||
meta = new cpl.DirectiveMetadata({
|
||||
selector: directiveAnnotation.selector,
|
||||
isComponent: isPresent(templateMeta),
|
||||
dynamicLoadable: dynamicLoadable,
|
||||
type: new cpl.TypeMetadata({
|
||||
id: this._directiveCounter++,
|
||||
name: stringify(directiveType),
|
||||
moduleId: moduleId,
|
||||
runtime: directiveType
|
||||
}),
|
||||
template: templateMeta,
|
||||
changeDetection: new cpl.ChangeDetectionMetadata({
|
||||
changeDetection: changeDetectionStrategy,
|
||||
properties: directiveAnnotation.properties,
|
||||
events: directiveAnnotation.events,
|
||||
hostListeners: hostListeners,
|
||||
hostProperties: hostProperties,
|
||||
callAfterContentInit: hasLifecycleHook(LifecycleHooks.AfterContentInit, directiveType),
|
||||
callAfterContentChecked:
|
||||
hasLifecycleHook(LifecycleHooks.AfterContentChecked, directiveType),
|
||||
callAfterViewInit: hasLifecycleHook(LifecycleHooks.AfterViewInit, directiveType),
|
||||
callAfterViewChecked: hasLifecycleHook(LifecycleHooks.AfterViewChecked, directiveType),
|
||||
callOnChanges: hasLifecycleHook(LifecycleHooks.OnChanges, directiveType),
|
||||
callDoCheck: hasLifecycleHook(LifecycleHooks.DoCheck, directiveType),
|
||||
callOnInit: hasLifecycleHook(LifecycleHooks.OnInit, directiveType),
|
||||
})
|
||||
});
|
||||
this._cache.set(directiveType, meta);
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
getViewDirectivesMetadata(component: Type): cpl.DirectiveMetadata[] {
|
||||
var view = this._viewResolver.resolve(component);
|
||||
var directives = flattenDirectives(view);
|
||||
for (var i = 0; i < directives.length; i++) {
|
||||
if (!isValidDirective(directives[i])) {
|
||||
throw new BaseException(
|
||||
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
|
||||
}
|
||||
}
|
||||
return removeDuplicatedDirectives(directives.map(type => this.getMetadata(type)));
|
||||
}
|
||||
}
|
||||
|
||||
function removeDuplicatedDirectives(directives: cpl.DirectiveMetadata[]): cpl.DirectiveMetadata[] {
|
||||
var directivesMap: Map<number, cpl.DirectiveMetadata> = new Map();
|
||||
directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.id, dirMeta); });
|
||||
return MapWrapper.values(directivesMap);
|
||||
}
|
||||
|
||||
function flattenDirectives(view: ViewMetadata): Type[] {
|
||||
if (isBlank(view.directives)) return [];
|
||||
var directives = [];
|
||||
flattenList(view.directives, directives);
|
||||
return directives;
|
||||
}
|
||||
|
||||
function flattenList(tree: any[], out: Array<Type | any[]>): void {
|
||||
for (var i = 0; i < tree.length; i++) {
|
||||
var item = resolveForwardRef(tree[i]);
|
||||
if (isArray(item)) {
|
||||
flattenList(item, out);
|
||||
} else {
|
||||
out.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isValidDirective(value: Type): boolean {
|
||||
return isPresent(value) && (value instanceof Type);
|
||||
}
|
||||
|
||||
function calcModuleId(type: Type, directiveAnnotation: dirAnn.DirectiveMetadata): string {
|
||||
if (isPresent(directiveAnnotation.moduleId)) {
|
||||
return directiveAnnotation.moduleId;
|
||||
} else {
|
||||
return reflector.moduleId(type);
|
||||
}
|
||||
}
|
39
modules/angular2/src/compiler/source_module.ts
Normal file
39
modules/angular2/src/compiler/source_module.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import {StringWrapper, isBlank} from 'angular2/src/core/facade/lang';
|
||||
|
||||
var MODULE_REGEXP = /#MODULE\[([^\]]*)\]/g;
|
||||
|
||||
export function moduleRef(moduleId): string {
|
||||
return `#MODULE[${moduleId}]`;
|
||||
}
|
||||
|
||||
export class SourceModule {
|
||||
constructor(public moduleId: string, public source: string) {}
|
||||
|
||||
getSourceWithImports(): SourceWithImports {
|
||||
var moduleAliases = {};
|
||||
var imports: string[][] = [];
|
||||
var newSource = StringWrapper.replaceAllMapped(this.source, MODULE_REGEXP, (match) => {
|
||||
var moduleId = match[1];
|
||||
var alias = moduleAliases[moduleId];
|
||||
if (isBlank(alias)) {
|
||||
if (moduleId == this.moduleId) {
|
||||
alias = '';
|
||||
} else {
|
||||
alias = `import${imports.length}`;
|
||||
imports.push([moduleId, alias]);
|
||||
}
|
||||
moduleAliases[moduleId] = alias;
|
||||
}
|
||||
return alias.length > 0 ? `${alias}.` : '';
|
||||
});
|
||||
return new SourceWithImports(newSource, imports);
|
||||
}
|
||||
}
|
||||
|
||||
export class SourceExpression {
|
||||
constructor(public declarations: string[], public expression: string) {}
|
||||
}
|
||||
|
||||
export class SourceWithImports {
|
||||
constructor(public source: string, public imports: string[][]) {}
|
||||
}
|
@ -1,26 +1,35 @@
|
||||
import {DirectiveMetadata, SourceModule, TypeMetadata} from './api';
|
||||
import {TypeMetadata, NormalizedDirectiveMetadata} from './directive_metadata';
|
||||
import {SourceModule, SourceExpression, moduleRef} from './source_module';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||
import {XHR} from 'angular2/src/core/render/xhr';
|
||||
import {StringWrapper, isJsObject, isBlank} from 'angular2/src/core/facade/lang';
|
||||
import {StringWrapper, isBlank} from 'angular2/src/core/facade/lang';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||
import {ShadowCss} from 'angular2/src/core/render/dom/compiler/shadow_css';
|
||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||
import {resolveStyleUrls} from './style_url_resolver';
|
||||
import {escapeSingleQuoteString} from './util';
|
||||
import {
|
||||
escapeSingleQuoteString,
|
||||
IS_DART,
|
||||
codeGenConcatArray,
|
||||
codeGenMapArray,
|
||||
codeGenReplaceAll,
|
||||
codeGenExportVariable
|
||||
} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
const COMPONENT_VARIABLE = '%COMP%';
|
||||
var COMPONENT_REGEX = /%COMP%/g;
|
||||
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
||||
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||
var IS_DART = !isJsObject({});
|
||||
|
||||
@Injectable()
|
||||
export class StyleCompiler {
|
||||
private _styleCache: Map<string, Promise<string[]>> = new Map<string, Promise<string[]>>();
|
||||
private _shadowCss: ShadowCss = new ShadowCss();
|
||||
|
||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
||||
|
||||
compileComponentRuntime(component: DirectiveMetadata): Promise<string[]> {
|
||||
compileComponentRuntime(component: NormalizedDirectiveMetadata): Promise<string[]> {
|
||||
var styles = component.template.styles;
|
||||
var styleAbsUrls = component.template.styleAbsUrls;
|
||||
return this._loadStyles(styles, styleAbsUrls,
|
||||
@ -29,7 +38,7 @@ export class StyleCompiler {
|
||||
`${component.type.id}`)));
|
||||
}
|
||||
|
||||
compileComponentCodeGen(component: DirectiveMetadata): SourceModule {
|
||||
compileComponentCodeGen(component: NormalizedDirectiveMetadata): SourceExpression {
|
||||
var shim = component.template.encapsulation === ViewEncapsulation.Emulated;
|
||||
var suffix;
|
||||
if (shim) {
|
||||
@ -39,16 +48,17 @@ export class StyleCompiler {
|
||||
} else {
|
||||
suffix = '';
|
||||
}
|
||||
return this._styleCodeGen(`$component.type.typeUrl}.styles`, component.template.styles,
|
||||
component.template.styleAbsUrls, shim, suffix);
|
||||
return this._styleCodeGen(component.template.styles, component.template.styleAbsUrls, shim,
|
||||
suffix);
|
||||
}
|
||||
|
||||
compileStylesheetCodeGen(moduleName: string, cssText: string): SourceModule[] {
|
||||
var styleWithImports = resolveStyleUrls(this._urlResolver, moduleName, cssText);
|
||||
compileStylesheetCodeGen(moduleId: string, cssText: string): SourceModule[] {
|
||||
var styleWithImports = resolveStyleUrls(this._urlResolver, moduleId, cssText);
|
||||
return [
|
||||
this._styleCodeGen(moduleName, [styleWithImports.style], styleWithImports.styleUrls, false,
|
||||
''),
|
||||
this._styleCodeGen(moduleName, [styleWithImports.style], styleWithImports.styleUrls, true, '')
|
||||
this._styleModule(moduleId, false, this._styleCodeGen([styleWithImports.style],
|
||||
styleWithImports.styleUrls, false, '')),
|
||||
this._styleModule(moduleId, true, this._styleCodeGen([styleWithImports.style],
|
||||
styleWithImports.styleUrls, true, ''))
|
||||
];
|
||||
}
|
||||
|
||||
@ -74,55 +84,41 @@ export class StyleCompiler {
|
||||
});
|
||||
}
|
||||
|
||||
private _styleCodeGen(moduleName: string, plainStyles: string[], absUrls: string[], shim: boolean,
|
||||
suffix: string): SourceModule {
|
||||
var imports: string[][] = [];
|
||||
var moduleSource = `var STYLES = (`;
|
||||
moduleSource +=
|
||||
private _styleCodeGen(plainStyles: string[], absUrls: string[], shim: boolean,
|
||||
suffix: string): SourceExpression {
|
||||
var expressionSource = `(`;
|
||||
expressionSource +=
|
||||
`[${plainStyles.map( plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)) ).join(',')}]`;
|
||||
for (var i = 0; i < absUrls.length; i++) {
|
||||
var url = absUrls[i];
|
||||
var moduleAlias = `import${i}`;
|
||||
imports.push([this._shimModuleName(url, shim), moduleAlias]);
|
||||
moduleSource += `${codeGenConcatArray(moduleAlias+'.STYLES')}`;
|
||||
var moduleId = this._shimModuleIdIfNeeded(absUrls[i], shim);
|
||||
expressionSource += codeGenConcatArray(`${moduleRef(moduleId)}STYLES`);
|
||||
}
|
||||
moduleSource += `)${suffix};`;
|
||||
return new SourceModule(this._shimModuleName(moduleName, shim), moduleSource, imports);
|
||||
expressionSource += `)${suffix}`;
|
||||
return new SourceExpression([], expressionSource);
|
||||
}
|
||||
|
||||
private _styleModule(moduleId: string, shim: boolean,
|
||||
expression: SourceExpression): SourceModule {
|
||||
var moduleSource = `
|
||||
${expression.declarations.join('\n')}
|
||||
${codeGenExportVariable('STYLES')}${expression.expression};
|
||||
`;
|
||||
return new SourceModule(this._shimModuleIdIfNeeded(moduleId, shim), moduleSource);
|
||||
}
|
||||
|
||||
private _shimIfNeeded(style: string, shim: boolean): string {
|
||||
return shim ? this._shadowCss.shimCssText(style, CONTENT_ATTR, HOST_ATTR) : style;
|
||||
}
|
||||
|
||||
private _shimModuleName(originalUrl: string, shim: boolean): string {
|
||||
return shim ? `${originalUrl}.shim` : originalUrl;
|
||||
private _shimModuleIdIfNeeded(moduleId: string, shim: boolean): string {
|
||||
return shim ? `${moduleId}.shim` : moduleId;
|
||||
}
|
||||
}
|
||||
|
||||
export function shimContentAttribute(component: TypeMetadata): string {
|
||||
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, `${component.id}`);
|
||||
export function shimContentAttribute(componentId: number): string {
|
||||
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, `${componentId}`);
|
||||
}
|
||||
|
||||
export function shimHostAttribute(component: TypeMetadata): string {
|
||||
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, `${component.id}`);
|
||||
export function shimHostAttribute(componentId: number): string {
|
||||
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, `${componentId}`);
|
||||
}
|
||||
|
||||
function codeGenConcatArray(expression: string): string {
|
||||
return `${IS_DART ? '..addAll' : '.concat'}(${expression})`;
|
||||
}
|
||||
|
||||
function codeGenMapArray(argNames: string[], callback: string): string {
|
||||
if (IS_DART) {
|
||||
return `.map( (${argNames.join(',')}) => ${callback} ).toList()`;
|
||||
} else {
|
||||
return `.map(function(${argNames.join(',')}) { return ${callback}; })`;
|
||||
}
|
||||
}
|
||||
|
||||
function codeGenReplaceAll(pattern: string, value: string): string {
|
||||
if (IS_DART) {
|
||||
return `.replaceAll('${pattern}', '${value}')`;
|
||||
} else {
|
||||
return `.replace(/${pattern}/g, '${value}')`;
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import {AST} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||
import {DirectiveMetadata} from './api';
|
||||
import {NormalizedDirectiveMetadata} from './directive_metadata';
|
||||
|
||||
export interface TemplateAst {
|
||||
sourceInfo: string;
|
||||
@ -69,7 +69,7 @@ export class ElementAst implements TemplateAst {
|
||||
this.directives.length > 0);
|
||||
}
|
||||
|
||||
getComponent(): DirectiveMetadata {
|
||||
getComponent(): NormalizedDirectiveMetadata {
|
||||
return this.directives.length > 0 && this.directives[0].directive.isComponent ?
|
||||
this.directives[0].directive :
|
||||
null;
|
||||
@ -94,7 +94,8 @@ export class BoundDirectivePropertyAst implements TemplateAst {
|
||||
}
|
||||
|
||||
export class DirectiveAst implements TemplateAst {
|
||||
constructor(public directive: DirectiveMetadata, public properties: BoundDirectivePropertyAst[],
|
||||
constructor(public directive: NormalizedDirectiveMetadata,
|
||||
public properties: BoundDirectivePropertyAst[],
|
||||
public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[],
|
||||
public sourceInfo: string) {}
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
|
205
modules/angular2/src/compiler/template_compiler.ts
Normal file
205
modules/angular2/src/compiler/template_compiler.ts
Normal file
@ -0,0 +1,205 @@
|
||||
import {Type, Json, isBlank, stringify} from 'angular2/src/core/facade/lang';
|
||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||
import {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||
import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands';
|
||||
import {
|
||||
createHostComponentMeta,
|
||||
DirectiveMetadata,
|
||||
INormalizedDirectiveMetadata,
|
||||
NormalizedDirectiveMetadata,
|
||||
TypeMetadata,
|
||||
ChangeDetectionMetadata,
|
||||
NormalizedTemplateMetadata
|
||||
} from './directive_metadata';
|
||||
import {TemplateAst} from './template_ast';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {SourceModule, moduleRef} from './source_module';
|
||||
import {ChangeDetectionCompiler} from './change_detector_compiler';
|
||||
import {StyleCompiler} from './style_compiler';
|
||||
import {CommandCompiler} from './command_compiler';
|
||||
import {TemplateParser} from './template_parser';
|
||||
import {TemplateNormalizer} from './template_normalizer';
|
||||
import {RuntimeMetadataResolver} from './runtime_metadata';
|
||||
|
||||
import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler';
|
||||
import {IS_DART, codeGenExportVariable, escapeSingleQuoteString, codeGenValueFn} from './util';
|
||||
|
||||
@Injectable()
|
||||
export class TemplateCompiler {
|
||||
private _compiledTemplateCache: Map<number, CompiledTemplate> = new Map();
|
||||
private _compiledTemplateDone: Map<number, Promise<CompiledTemplate>> = new Map();
|
||||
|
||||
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
|
||||
private _templateNormalizer: TemplateNormalizer,
|
||||
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
||||
private _commandCompiler: CommandCompiler,
|
||||
private _cdCompiler: ChangeDetectionCompiler) {}
|
||||
|
||||
normalizeDirective(directive: DirectiveMetadata): Promise<INormalizedDirectiveMetadata> {
|
||||
var normalizedTemplatePromise;
|
||||
if (directive.isComponent) {
|
||||
normalizedTemplatePromise =
|
||||
this._templateNormalizer.normalizeTemplate(directive.type, directive.template);
|
||||
} else {
|
||||
normalizedTemplatePromise = PromiseWrapper.resolve(null);
|
||||
}
|
||||
return normalizedTemplatePromise.then(
|
||||
(normalizedTemplate) => new NormalizedDirectiveMetadata({
|
||||
selector: directive.selector,
|
||||
dynamicLoadable: directive.dynamicLoadable,
|
||||
isComponent: directive.isComponent,
|
||||
type: directive.type,
|
||||
changeDetection: directive.changeDetection, template: normalizedTemplate
|
||||
}));
|
||||
}
|
||||
|
||||
serializeTemplateMetadata(metadata: INormalizedDirectiveMetadata): string {
|
||||
return Json.stringify((<NormalizedDirectiveMetadata>metadata).toJson());
|
||||
}
|
||||
|
||||
deserializeTemplateMetadata(data: string): INormalizedDirectiveMetadata {
|
||||
return NormalizedDirectiveMetadata.fromJson(Json.parse(data));
|
||||
}
|
||||
|
||||
compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> {
|
||||
var compMeta: DirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
||||
if (isBlank(compMeta) || !compMeta.isComponent || !compMeta.dynamicLoadable) {
|
||||
throw new BaseException(
|
||||
`Could not compile '${stringify(type)}' because it is not dynamically loadable.`);
|
||||
}
|
||||
var hostMeta: NormalizedDirectiveMetadata =
|
||||
createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||
this._compileComponentRuntime(hostMeta, [compMeta], new Set());
|
||||
return this._compiledTemplateDone.get(hostMeta.type.id);
|
||||
}
|
||||
|
||||
private _compileComponentRuntime(compMeta: NormalizedDirectiveMetadata,
|
||||
viewDirectives: DirectiveMetadata[],
|
||||
compilingComponentIds: Set<number>): CompiledTemplate {
|
||||
var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.id);
|
||||
var done = this._compiledTemplateDone.get(compMeta.type.id);
|
||||
if (isBlank(compiledTemplate)) {
|
||||
var styles;
|
||||
var changeDetectorFactories;
|
||||
var commands;
|
||||
compiledTemplate =
|
||||
new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactories, commands, styles]);
|
||||
this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate);
|
||||
compilingComponentIds.add(compMeta.type.id);
|
||||
done =
|
||||
PromiseWrapper.all([this._styleCompiler.compileComponentRuntime(compMeta)].concat(
|
||||
viewDirectives.map(dirMeta => this.normalizeDirective(dirMeta))))
|
||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||
var childPromises = [];
|
||||
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
||||
var parsedTemplate = this._templateParser.parse(
|
||||
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
||||
|
||||
changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||
compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate);
|
||||
styles = stylesAndNormalizedViewDirMetas[0];
|
||||
commands = this._compileCommandsRuntime(compMeta, parsedTemplate,
|
||||
compilingComponentIds, childPromises);
|
||||
return PromiseWrapper.all(childPromises);
|
||||
})
|
||||
.then((_) => {
|
||||
SetWrapper.delete(compilingComponentIds, compMeta.type.id);
|
||||
return compiledTemplate;
|
||||
});
|
||||
this._compiledTemplateDone.set(compMeta.type.id, done);
|
||||
}
|
||||
return compiledTemplate;
|
||||
}
|
||||
|
||||
private _compileCommandsRuntime(compMeta: NormalizedDirectiveMetadata,
|
||||
parsedTemplate: TemplateAst[], compilingComponentIds: Set<number>,
|
||||
childPromises: Promise<any>[]): TemplateCmd[] {
|
||||
return this._commandCompiler.compileComponentRuntime(
|
||||
compMeta, parsedTemplate, (childComponentDir: NormalizedDirectiveMetadata) => {
|
||||
var childViewDirectives: DirectiveMetadata[] =
|
||||
this._runtimeMetadataResolver.getViewDirectivesMetadata(
|
||||
childComponentDir.type.runtime);
|
||||
var childIsRecursive = SetWrapper.has(compilingComponentIds, childComponentDir.type.id);
|
||||
var childTemplate = this._compileComponentRuntime(childComponentDir, childViewDirectives,
|
||||
compilingComponentIds);
|
||||
if (!childIsRecursive) {
|
||||
// Only wait for a child if it is not a cycle
|
||||
childPromises.push(this._compiledTemplateDone.get(childComponentDir.type.id));
|
||||
}
|
||||
return childTemplate;
|
||||
});
|
||||
}
|
||||
|
||||
compileTemplatesCodeGen(moduleId: string,
|
||||
components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
||||
var declarations = [];
|
||||
var templateArguments = [];
|
||||
var componentMetas: NormalizedDirectiveMetadata[] = [];
|
||||
components.forEach(componentWithDirs => {
|
||||
var compMeta = <NormalizedDirectiveMetadata>componentWithDirs.component;
|
||||
componentMetas.push(compMeta);
|
||||
this._processTemplateCodeGen(compMeta,
|
||||
<NormalizedDirectiveMetadata[]>componentWithDirs.directives,
|
||||
declarations, templateArguments);
|
||||
if (compMeta.dynamicLoadable) {
|
||||
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||
componentMetas.push(hostMeta);
|
||||
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
|
||||
}
|
||||
});
|
||||
ListWrapper.forEachWithIndex(componentMetas, (compMeta: NormalizedDirectiveMetadata,
|
||||
index: number) => {
|
||||
var templateDataFn = codeGenValueFn([], `[${templateArguments[index].join(',')}]`);
|
||||
declarations.push(
|
||||
`${codeGenExportVariable(templateVariableName(compMeta.type))}new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${compMeta.type.id},${templateDataFn});`);
|
||||
});
|
||||
return new SourceModule(`${templateModuleName(moduleId)}`, declarations.join('\n'));
|
||||
}
|
||||
|
||||
compileStylesheetCodeGen(moduleId: string, cssText: string): SourceModule[] {
|
||||
return this._styleCompiler.compileStylesheetCodeGen(moduleId, cssText);
|
||||
}
|
||||
|
||||
private _processTemplateCodeGen(compMeta: NormalizedDirectiveMetadata,
|
||||
directives: NormalizedDirectiveMetadata[],
|
||||
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
||||
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta);
|
||||
var parsedTemplate =
|
||||
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
|
||||
var changeDetectorsExpr = this._cdCompiler.compileComponentCodeGen(
|
||||
compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate);
|
||||
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
|
||||
compMeta, parsedTemplate, codeGenComponentTemplateFactory);
|
||||
|
||||
addAll(styleExpr.declarations, targetDeclarations);
|
||||
addAll(changeDetectorsExpr.declarations, targetDeclarations);
|
||||
addAll(commandsExpr.declarations, targetDeclarations);
|
||||
|
||||
targetTemplateArguments.push(
|
||||
[changeDetectorsExpr.expression, commandsExpr.expression, styleExpr.expression]);
|
||||
}
|
||||
}
|
||||
|
||||
export class NormalizedComponentWithViewDirectives {
|
||||
constructor(public component: INormalizedDirectiveMetadata,
|
||||
public directives: INormalizedDirectiveMetadata[]) {}
|
||||
}
|
||||
|
||||
function templateVariableName(type: TypeMetadata): string {
|
||||
return `${type.name}Template`;
|
||||
}
|
||||
|
||||
function templateModuleName(moduleId: string): string {
|
||||
return `${moduleId}.template`;
|
||||
}
|
||||
|
||||
function addAll(source: any[], target: any[]) {
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
target.push(source[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function codeGenComponentTemplateFactory(nestedCompType: NormalizedDirectiveMetadata): string {
|
||||
return `${moduleRef(templateModuleName(nestedCompType.type.moduleId))}${templateVariableName(nestedCompType.type)}`;
|
||||
}
|
@ -1,11 +1,16 @@
|
||||
import {TypeMetadata, TemplateMetadata} from './api';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||
import {
|
||||
TypeMetadata,
|
||||
TemplateMetadata,
|
||||
NormalizedDirectiveMetadata,
|
||||
NormalizedTemplateMetadata
|
||||
} from './directive_metadata';
|
||||
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
||||
|
||||
import {XHR} from 'angular2/src/core/render/xhr';
|
||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||
import {resolveStyleUrls} from './style_url_resolver';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
import {
|
||||
HtmlAstVisitor,
|
||||
@ -25,43 +30,43 @@ const LINK_STYLE_HREF_ATTR = 'href';
|
||||
const LINK_STYLE_REL_VALUE = 'stylesheet';
|
||||
const STYLE_ELEMENT = 'style';
|
||||
|
||||
export class TemplateLoader {
|
||||
@Injectable()
|
||||
export class TemplateNormalizer {
|
||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver,
|
||||
private _domParser: HtmlParser) {}
|
||||
|
||||
loadTemplate(directiveType: TypeMetadata, encapsulation: ViewEncapsulation, template: string,
|
||||
templateUrl: string, styles: string[],
|
||||
styleUrls: string[]): Promise<TemplateMetadata> {
|
||||
if (isPresent(template)) {
|
||||
return PromiseWrapper.resolve(this.createTemplateFromString(
|
||||
directiveType, encapsulation, template, directiveType.typeUrl, styles, styleUrls));
|
||||
normalizeTemplate(directiveType: TypeMetadata,
|
||||
template: TemplateMetadata): Promise<NormalizedTemplateMetadata> {
|
||||
if (isPresent(template.template)) {
|
||||
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
|
||||
directiveType, template, template.template, directiveType.moduleId));
|
||||
} else {
|
||||
var sourceAbsUrl = this._urlResolver.resolve(directiveType.typeUrl, templateUrl);
|
||||
var sourceAbsUrl = this._urlResolver.resolve(directiveType.moduleId, template.templateUrl);
|
||||
return this._xhr.get(sourceAbsUrl)
|
||||
.then(templateContent =>
|
||||
this.createTemplateFromString(directiveType, encapsulation, templateContent,
|
||||
sourceAbsUrl, styles, styleUrls));
|
||||
.then(templateContent => this.normalizeLoadedTemplate(directiveType, template,
|
||||
templateContent, sourceAbsUrl));
|
||||
}
|
||||
}
|
||||
|
||||
createTemplateFromString(directiveType: TypeMetadata, encapsulation: ViewEncapsulation,
|
||||
template: string, templateSourceUrl: string, styles: string[],
|
||||
styleUrls: string[]): TemplateMetadata {
|
||||
var domNodes = this._domParser.parse(template, directiveType.typeName);
|
||||
normalizeLoadedTemplate(directiveType: TypeMetadata, templateMeta: TemplateMetadata,
|
||||
template: string, templateAbsUrl: string): NormalizedTemplateMetadata {
|
||||
var domNodes = this._domParser.parse(template, directiveType.name);
|
||||
var visitor = new TemplatePreparseVisitor();
|
||||
var remainingNodes = htmlVisitAll(visitor, domNodes);
|
||||
var allStyles = styles.concat(visitor.styles);
|
||||
var allStyleUrls = styleUrls.concat(visitor.styleUrls);
|
||||
var allResolvedStyles = allStyles.map(style => {
|
||||
var styleWithImports = resolveStyleUrls(this._urlResolver, templateSourceUrl, style);
|
||||
styleWithImports.styleUrls.forEach(styleUrl => allStyleUrls.push(styleUrl));
|
||||
return styleWithImports.style;
|
||||
});
|
||||
var allStyles = templateMeta.styles.concat(visitor.styles);
|
||||
|
||||
var allStyleAbsUrls =
|
||||
allStyleUrls.map(styleUrl => this._urlResolver.resolve(templateSourceUrl, styleUrl));
|
||||
return new TemplateMetadata({
|
||||
encapsulation: encapsulation,
|
||||
visitor.styleUrls.map(url => this._urlResolver.resolve(templateAbsUrl, url))
|
||||
.concat(templateMeta.styleUrls.map(
|
||||
url => this._urlResolver.resolve(directiveType.moduleId, url)));
|
||||
|
||||
var allResolvedStyles = allStyles.map(style => {
|
||||
var styleWithImports = resolveStyleUrls(this._urlResolver, templateAbsUrl, style);
|
||||
styleWithImports.styleUrls.forEach(styleUrl => allStyleAbsUrls.push(styleUrl));
|
||||
return styleWithImports.style;
|
||||
});
|
||||
return new NormalizedTemplateMetadata({
|
||||
encapsulation: templateMeta.encapsulation,
|
||||
template: this._domParser.unparse(remainingNodes),
|
||||
styles: allResolvedStyles,
|
||||
styleAbsUrls: allStyleAbsUrls,
|
@ -8,11 +8,13 @@ import {
|
||||
assertionsEnabled,
|
||||
isBlank
|
||||
} from 'angular2/src/core/facade/lang';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
|
||||
import {NormalizedDirectiveMetadata} from './directive_metadata';
|
||||
import {HtmlParser} from './html_parser';
|
||||
|
||||
import {DirectiveMetadata, TemplateMetadata} from './api';
|
||||
import {
|
||||
ElementAst,
|
||||
BoundElementPropertyAst,
|
||||
@ -68,12 +70,16 @@ const STYLE_PREFIX = 'style';
|
||||
|
||||
var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
|
||||
|
||||
@Injectable()
|
||||
export class TemplateParser {
|
||||
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry) {}
|
||||
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
||||
private _htmlParser: HtmlParser) {}
|
||||
|
||||
parse(domNodes: HtmlAst[], directives: DirectiveMetadata[]): TemplateAst[] {
|
||||
parse(template: string, directives: NormalizedDirectiveMetadata[],
|
||||
sourceInfo: string): TemplateAst[] {
|
||||
var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry);
|
||||
var result = htmlVisitAll(parseVisitor, domNodes, EMPTY_COMPONENT);
|
||||
var result =
|
||||
htmlVisitAll(parseVisitor, this._htmlParser.parse(template, sourceInfo), EMPTY_COMPONENT);
|
||||
if (parseVisitor.errors.length > 0) {
|
||||
var errorString = parseVisitor.errors.join('\n');
|
||||
throw new BaseException(`Template parse errors:\n${errorString}`);
|
||||
@ -85,7 +91,7 @@ export class TemplateParser {
|
||||
class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
selectorMatcher: SelectorMatcher;
|
||||
errors: string[] = [];
|
||||
constructor(directives: DirectiveMetadata[], private _exprParser: Parser,
|
||||
constructor(directives: NormalizedDirectiveMetadata[], private _exprParser: Parser,
|
||||
private _schemaRegistry: ElementSchemaRegistry) {
|
||||
this.selectorMatcher = new SelectorMatcher();
|
||||
directives.forEach(directive => {
|
||||
@ -371,31 +377,32 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
}
|
||||
|
||||
private _parseDirectives(selectorMatcher: SelectorMatcher,
|
||||
elementCssSelector: CssSelector): DirectiveMetadata[] {
|
||||
elementCssSelector: CssSelector): NormalizedDirectiveMetadata[] {
|
||||
var directives = [];
|
||||
selectorMatcher.match(elementCssSelector,
|
||||
(selector, directive) => { directives.push(directive); });
|
||||
// Need to sort the directives so that we get consistent results throughout,
|
||||
// as selectorMatcher uses Maps inside.
|
||||
// Also need to make components the first directive in the array
|
||||
ListWrapper.sort(directives, (dir1: DirectiveMetadata, dir2: DirectiveMetadata) => {
|
||||
var dir1Comp = dir1.isComponent;
|
||||
var dir2Comp = dir2.isComponent;
|
||||
if (dir1Comp && !dir2Comp) {
|
||||
return -1;
|
||||
} else if (!dir1Comp && dir2Comp) {
|
||||
return 1;
|
||||
} else {
|
||||
return StringWrapper.compare(dir1.type.typeName, dir2.type.typeName);
|
||||
}
|
||||
});
|
||||
ListWrapper.sort(directives,
|
||||
(dir1: NormalizedDirectiveMetadata, dir2: NormalizedDirectiveMetadata) => {
|
||||
var dir1Comp = dir1.isComponent;
|
||||
var dir2Comp = dir2.isComponent;
|
||||
if (dir1Comp && !dir2Comp) {
|
||||
return -1;
|
||||
} else if (!dir1Comp && dir2Comp) {
|
||||
return 1;
|
||||
} else {
|
||||
return StringWrapper.compare(dir1.type.name, dir2.type.name);
|
||||
}
|
||||
});
|
||||
return directives;
|
||||
}
|
||||
|
||||
private _createDirectiveAsts(elementName: string, directives: DirectiveMetadata[],
|
||||
private _createDirectiveAsts(elementName: string, directives: NormalizedDirectiveMetadata[],
|
||||
props: BoundElementOrDirectiveProperty[],
|
||||
sourceInfo: string): DirectiveAst[] {
|
||||
return directives.map((directive: DirectiveMetadata) => {
|
||||
return directives.map((directive: NormalizedDirectiveMetadata) => {
|
||||
var hostProperties: BoundElementPropertyAst[] = [];
|
||||
var hostEvents: BoundEventAst[] = [];
|
||||
var directiveProperties: BoundDirectivePropertyAst[] = [];
|
||||
@ -510,7 +517,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
|
||||
var componentTypeNames: string[] = [];
|
||||
directives.forEach(directive => {
|
||||
var typeName = directive.directive.type.typeName;
|
||||
var typeName = directive.directive.type.name;
|
||||
if (directive.directive.isComponent) {
|
||||
componentTypeNames.push(typeName);
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
import {StringWrapper, isBlank} from 'angular2/src/core/facade/lang';
|
||||
import {StringWrapper, isBlank, isJsObject} from 'angular2/src/core/facade/lang';
|
||||
|
||||
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||
var DASH_CASE_REGEXP = /-([a-z])/g;
|
||||
var SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n/g;
|
||||
var DOUBLE_QUOTE_ESCAPE_STRING_RE = /"|\\|\n/g;
|
||||
|
||||
export var IS_DART = !isJsObject({});
|
||||
|
||||
export function camelCaseToDashCase(input: string): string {
|
||||
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
|
||||
(m) => { return '-' + m[1].toLowerCase(); });
|
||||
@ -38,3 +40,35 @@ function escapeString(input: string, re: RegExp): string {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function codeGenExportVariable(name: string): string {
|
||||
return IS_DART ? `var ${name} = ` : `var ${name} = exports['${name}'] = `;
|
||||
}
|
||||
|
||||
export function codeGenConcatArray(expression: string): string {
|
||||
return `${IS_DART ? '..addAll' : '.concat'}(${expression})`;
|
||||
}
|
||||
|
||||
export function codeGenMapArray(argNames: string[], callback: string): string {
|
||||
if (IS_DART) {
|
||||
return `.map( (${argNames.join(',')}) => ${callback} ).toList()`;
|
||||
} else {
|
||||
return `.map(function(${argNames.join(',')}) { return ${callback}; })`;
|
||||
}
|
||||
}
|
||||
|
||||
export function codeGenReplaceAll(pattern: string, value: string): string {
|
||||
if (IS_DART) {
|
||||
return `.replaceAll('${pattern}', '${value}')`;
|
||||
} else {
|
||||
return `.replace(/${pattern}/g, '${value}')`;
|
||||
}
|
||||
}
|
||||
|
||||
export function codeGenValueFn(params: string[], value: string): string {
|
||||
if (IS_DART) {
|
||||
return `(${params.join(',')}) => ${value}`;
|
||||
} else {
|
||||
return `function(${params.join(',')}) { return ${value}; }`;
|
||||
}
|
||||
}
|
||||
|
@ -93,8 +93,11 @@ export class DirectiveResolver {
|
||||
properties: mergedProperties,
|
||||
events: mergedEvents,
|
||||
host: mergedHost,
|
||||
dynamicLoadable: dm.dynamicLoadable,
|
||||
compiledHostTemplate: dm.compiledHostTemplate,
|
||||
bindings: dm.bindings,
|
||||
exportAs: dm.exportAs,
|
||||
moduleId: dm.moduleId,
|
||||
compileChildren: dm.compileChildren,
|
||||
changeDetection: dm.changeDetection,
|
||||
viewBindings: dm.viewBindings
|
||||
@ -108,6 +111,7 @@ export class DirectiveResolver {
|
||||
host: mergedHost,
|
||||
bindings: dm.bindings,
|
||||
exportAs: dm.exportAs,
|
||||
moduleId: dm.moduleId,
|
||||
compileChildren: dm.compileChildren
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Type, CONST_EXPR, isPresent} from 'angular2/src/core/facade/lang';
|
||||
import {Type, CONST_EXPR, isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
||||
import {
|
||||
RenderTemplateCmd,
|
||||
RenderCommandVisitor,
|
||||
@ -10,7 +10,35 @@ import {
|
||||
} from 'angular2/src/core/render/render';
|
||||
|
||||
export class CompiledTemplate {
|
||||
constructor(public id: string, public commands: TemplateCmd[]) {}
|
||||
private _changeDetectorFactories: Function[] = null;
|
||||
private _styles: string[] = null;
|
||||
private _commands: TemplateCmd[] = null;
|
||||
// Note: paramGetter is a function so that we can have cycles between templates!
|
||||
constructor(public id: number, private _paramGetter: Function) {}
|
||||
|
||||
private _init() {
|
||||
if (isBlank(this._commands)) {
|
||||
var params = this._paramGetter();
|
||||
this._changeDetectorFactories = params[0];
|
||||
this._commands = params[1];
|
||||
this._styles = params[2];
|
||||
}
|
||||
}
|
||||
|
||||
get changeDetectorFactories(): Function[] {
|
||||
this._init();
|
||||
return this._changeDetectorFactories;
|
||||
}
|
||||
|
||||
get styles(): string[] {
|
||||
this._init();
|
||||
return this._styles;
|
||||
}
|
||||
|
||||
get commands(): TemplateCmd[] {
|
||||
this._init();
|
||||
return this._commands;
|
||||
}
|
||||
}
|
||||
|
||||
const EMPTY_ARR = CONST_EXPR([]);
|
||||
@ -73,14 +101,14 @@ export function endElement(): TemplateCmd {
|
||||
|
||||
export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
|
||||
isBound: boolean = true;
|
||||
templateId: string;
|
||||
templateId: number;
|
||||
component: Type;
|
||||
constructor(public name: string, public attrNameAndValues: string[], public eventNames: string[],
|
||||
public variableNameAndValues: string[], public directives: Type[],
|
||||
public nativeShadow: boolean, public ngContentIndex: number,
|
||||
public template: CompiledTemplate) {
|
||||
this.component = directives[0];
|
||||
this.templateId = isPresent(template) ? template.id : null;
|
||||
this.templateId = template.id;
|
||||
}
|
||||
visit(visitor: CommandVisitor, context: any): any {
|
||||
return visitor.visitBeginComponent(this, context);
|
||||
|
@ -10,7 +10,7 @@ import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
|
||||
@Injectable()
|
||||
export class ViewResolver {
|
||||
_cache: Map<Type, /*node*/ any> = new Map();
|
||||
_cache: Map<Type, ViewMetadata> = new Map();
|
||||
|
||||
resolve(component: Type): ViewMetadata {
|
||||
var view = this._cache.get(component);
|
||||
|
@ -1,14 +1,14 @@
|
||||
library angular2.src.core.metadata;
|
||||
|
||||
import "package:angular2/src/core/facade/collection.dart" show List;
|
||||
import 'package:angular2/src/core/facade/collection.dart' show List;
|
||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
import "./metadata/di.dart";
|
||||
import "./metadata/directives.dart";
|
||||
import "./metadata/view.dart";
|
||||
import './metadata/di.dart';
|
||||
import './metadata/directives.dart';
|
||||
import './metadata/view.dart';
|
||||
|
||||
export "./metadata/di.dart";
|
||||
export "./metadata/directives.dart";
|
||||
export "./metadata/view.dart";
|
||||
export './metadata/di.dart';
|
||||
export './metadata/directives.dart';
|
||||
export './metadata/view.dart';
|
||||
|
||||
/**
|
||||
* See: [DirectiveMetadata] for docs.
|
||||
@ -16,7 +16,7 @@ export "./metadata/view.dart";
|
||||
class Directive extends DirectiveMetadata {
|
||||
const Directive({String selector, List<String> properties,
|
||||
List<String> events, Map<String, String> host,
|
||||
List bindings, String exportAs,
|
||||
List bindings, String exportAs, String moduleId,
|
||||
bool compileChildren: true})
|
||||
: super(
|
||||
selector: selector,
|
||||
@ -25,6 +25,7 @@ class Directive extends DirectiveMetadata {
|
||||
host: host,
|
||||
bindings: bindings,
|
||||
exportAs: exportAs,
|
||||
moduleId: moduleId,
|
||||
compileChildren: compileChildren);
|
||||
}
|
||||
|
||||
@ -33,16 +34,18 @@ class Directive extends DirectiveMetadata {
|
||||
*/
|
||||
class Component extends ComponentMetadata {
|
||||
const Component({String selector, List<String> properties,
|
||||
List<String> events, Map<String, String> host,
|
||||
List bindings, String exportAs,
|
||||
List<String> events, Map<String, String> host, bool dynamicLoadable,
|
||||
List bindings, String exportAs, String moduleId,
|
||||
bool compileChildren, List viewBindings, ChangeDetectionStrategy changeDetection})
|
||||
: super(
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
host: host,
|
||||
dynamicLoadable: dynamicLoadable,
|
||||
bindings: bindings,
|
||||
exportAs: exportAs,
|
||||
moduleId: moduleId,
|
||||
compileChildren: compileChildren,
|
||||
viewBindings: viewBindings,
|
||||
changeDetection: changeDetection);
|
||||
|
@ -139,11 +139,11 @@ export interface ViewDecorator extends TypeDecorator {
|
||||
export interface DirectiveFactory {
|
||||
(obj: {
|
||||
selector?: string, properties?: string[], events?: string[], host?: StringMap<string, string>,
|
||||
bindings?: any[], exportAs?: string, compileChildren?: boolean;
|
||||
bindings?: any[], exportAs?: string, moduleId?: string, compileChildren?: boolean;
|
||||
}): DirectiveDecorator;
|
||||
new (obj: {
|
||||
selector?: string, properties?: string[], events?: string[], host?: StringMap<string, string>,
|
||||
bindings?: any[], exportAs?: string, compileChildren?: boolean;
|
||||
bindings?: any[], exportAs?: string, moduleId?: string, compileChildren?: boolean;
|
||||
}): DirectiveMetadata;
|
||||
}
|
||||
|
||||
@ -196,8 +196,10 @@ export interface ComponentFactory {
|
||||
properties?: string[],
|
||||
events?: string[],
|
||||
host?: StringMap<string, string>,
|
||||
dynamicLoadable?: boolean,
|
||||
bindings?: any[],
|
||||
exportAs?: string,
|
||||
moduleId?: string,
|
||||
compileChildren?: boolean,
|
||||
viewBindings?: any[],
|
||||
changeDetection?: ChangeDetectionStrategy,
|
||||
@ -207,8 +209,10 @@ export interface ComponentFactory {
|
||||
properties?: string[],
|
||||
events?: string[],
|
||||
host?: StringMap<string, string>,
|
||||
dynamicLoadable?: boolean,
|
||||
bindings?: any[],
|
||||
exportAs?: string,
|
||||
moduleId?: string,
|
||||
compileChildren?: boolean,
|
||||
viewBindings?: any[],
|
||||
changeDetection?: ChangeDetectionStrategy,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {isPresent, CONST, CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||
import {isPresent, CONST, CONST_EXPR, Type} from 'angular2/src/core/facade/lang';
|
||||
import {InjectableMetadata} from 'angular2/src/core/di/metadata';
|
||||
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
|
||||
|
||||
@ -699,8 +699,29 @@ export class DirectiveMetadata extends InjectableMetadata {
|
||||
*/
|
||||
exportAs: string;
|
||||
|
||||
/**
|
||||
* The module id of the module that contains the directive.
|
||||
* Needed to be able to resolve relative urls for templates and styles.
|
||||
* In Dart, this can be determined automatically and does not need to be set.
|
||||
* In CommonJS, this can always be set to `module.id`.
|
||||
*
|
||||
* ## Simple Example
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'someDir',
|
||||
* moduleId: module.id
|
||||
* })
|
||||
* class SomeDir {
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
moduleId: string;
|
||||
|
||||
constructor({
|
||||
selector, properties, events, host, bindings, exportAs, compileChildren = true,
|
||||
selector, properties, events, host, bindings, exportAs, moduleId,
|
||||
compileChildren = true,
|
||||
}: {
|
||||
selector?: string,
|
||||
properties?: string[],
|
||||
@ -708,6 +729,7 @@ export class DirectiveMetadata extends InjectableMetadata {
|
||||
host?: StringMap<string, string>,
|
||||
bindings?: any[],
|
||||
exportAs?: string,
|
||||
moduleId?: string,
|
||||
compileChildren?: boolean,
|
||||
} = {}) {
|
||||
super();
|
||||
@ -716,6 +738,7 @@ export class DirectiveMetadata extends InjectableMetadata {
|
||||
this.events = events;
|
||||
this.host = host;
|
||||
this.exportAs = exportAs;
|
||||
this.moduleId = moduleId;
|
||||
this.compileChildren = compileChildren;
|
||||
this.bindings = bindings;
|
||||
}
|
||||
@ -764,6 +787,34 @@ export class DirectiveMetadata extends InjectableMetadata {
|
||||
*/
|
||||
@CONST()
|
||||
export class ComponentMetadata extends DirectiveMetadata {
|
||||
/**
|
||||
* Declare that this component can be programatically loaded.
|
||||
* Every component that is used in bootstrap, routing, ... has to be
|
||||
* annotated with this.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'root',
|
||||
* dynamicLoadable: true
|
||||
* })
|
||||
* @View({
|
||||
* template: 'hello world!'
|
||||
* })
|
||||
* class RootComponent {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
dynamicLoadable: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* Used by build tools to store the compiled template.
|
||||
* Not intended to be used by a user.
|
||||
*/
|
||||
compiledHostTemplate: /* CompiledTemplate */ any;
|
||||
|
||||
/**
|
||||
* Defines the used change detection strategy.
|
||||
*
|
||||
@ -817,14 +868,18 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||
*/
|
||||
viewBindings: any[];
|
||||
|
||||
constructor({selector, properties, events, host, exportAs, bindings, viewBindings,
|
||||
changeDetection = ChangeDetectionStrategy.Default, compileChildren = true}: {
|
||||
constructor({selector, properties, events, host, dynamicLoadable, compiledHostTemplate, exportAs,
|
||||
moduleId, bindings, viewBindings, changeDetection = ChangeDetectionStrategy.Default,
|
||||
compileChildren = true}: {
|
||||
selector?: string,
|
||||
properties?: string[],
|
||||
events?: string[],
|
||||
host?: StringMap<string, string>,
|
||||
dynamicLoadable?: boolean,
|
||||
compiledHostTemplate?: any,
|
||||
bindings?: any[],
|
||||
exportAs?: string,
|
||||
moduleId?: string,
|
||||
compileChildren?: boolean,
|
||||
viewBindings?: any[],
|
||||
changeDetection?: ChangeDetectionStrategy,
|
||||
@ -835,12 +890,15 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||
events: events,
|
||||
host: host,
|
||||
exportAs: exportAs,
|
||||
moduleId: moduleId,
|
||||
bindings: bindings,
|
||||
compileChildren: compileChildren
|
||||
});
|
||||
|
||||
this.changeDetection = changeDetection;
|
||||
this.viewBindings = viewBindings;
|
||||
this.dynamicLoadable = dynamicLoadable;
|
||||
this.compiledHostTemplate = compiledHostTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,5 +11,8 @@ export interface PlatformReflectionCapabilities {
|
||||
getter(name: string): GetterFn;
|
||||
setter(name: string): SetterFn;
|
||||
method(name: string): MethodFn;
|
||||
// TODO(tbosch): remove this method after the new compiler is done
|
||||
// (and ComponentUrlMapper as well).
|
||||
importUri(type: Type): string;
|
||||
moduleId(type: Type): string;
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ class NoReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
}
|
||||
|
||||
String importUri(Type type) => './';
|
||||
|
||||
String moduleId(Type type) => null;
|
||||
}
|
||||
|
||||
final Reflector reflector = new Reflector(new NoReflectionCapabilities());
|
||||
|
@ -5,6 +5,8 @@ import 'types.dart';
|
||||
import 'dart:mirrors';
|
||||
import 'platform_reflection_capabilities.dart';
|
||||
|
||||
var DOT_REGEX = new RegExp('\\.');
|
||||
|
||||
class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
ReflectionCapabilities([metadataReader]) {}
|
||||
|
||||
@ -315,4 +317,8 @@ class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
String importUri(Type type) {
|
||||
return '${(reflectClass(type).owner as LibraryMirror).uri}';
|
||||
}
|
||||
|
||||
String moduleId(Type type) {
|
||||
return '${MirrorSystem.getName((reflectClass(type).owner as LibraryMirror).qualifiedName).replaceAll(DOT_REGEX, "/")}';
|
||||
}
|
||||
}
|
||||
|
@ -168,4 +168,6 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
|
||||
// There is not a concept of import uri in Js, but this is useful in developing Dart applications.
|
||||
importUri(type: Type): string { return './'; }
|
||||
|
||||
moduleId(type: Type): string { return null; }
|
||||
}
|
||||
|
@ -156,6 +156,8 @@ export class Reflector {
|
||||
_containsReflectionInfo(typeOrFunc) { return this._injectableInfo.has(typeOrFunc); }
|
||||
|
||||
importUri(type: Type): string { return this.reflectionCapabilities.importUri(type); }
|
||||
|
||||
moduleId(type: Type): string { return this.reflectionCapabilities.moduleId(type); }
|
||||
}
|
||||
|
||||
function _mergeMaps(target: Map<any, any>, config: StringMap<string, Function>): void {
|
||||
|
@ -409,7 +409,7 @@ export interface RenderBeginElementCmd extends RenderBeginCmd {
|
||||
|
||||
export interface RenderBeginComponentCmd extends RenderBeginElementCmd {
|
||||
nativeShadow: boolean;
|
||||
templateId: string;
|
||||
templateId: number;
|
||||
}
|
||||
|
||||
export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
|
||||
|
@ -29,8 +29,11 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
||||
properties: dm.properties,
|
||||
events: dm.events,
|
||||
host: dm.host,
|
||||
dynamicLoadable: dm.dynamicLoadable,
|
||||
compiledHostTemplate: dm.compiledHostTemplate,
|
||||
bindings: bindings,
|
||||
exportAs: dm.exportAs,
|
||||
moduleId: dm.moduleId,
|
||||
compileChildren: dm.compileChildren,
|
||||
changeDetection: dm.changeDetection,
|
||||
viewBindings: viewBindings
|
||||
@ -44,6 +47,7 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
||||
host: dm.host,
|
||||
bindings: bindings,
|
||||
exportAs: dm.exportAs,
|
||||
moduleId: dm.moduleId,
|
||||
compileChildren: dm.compileChildren
|
||||
});
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
// TODO(tbosch): Move the corresponding code into angular2/src/compiler once
|
||||
// the new compiler is done.
|
||||
export class Codegen {
|
||||
constructor(moduleAlias: string) {}
|
||||
generate(typeName: string, changeDetectorTypeName: string, def: ChangeDetectorDefinition): void {
|
||||
throw "Not implemented in JS";
|
||||
}
|
||||
|
Reference in New Issue
Block a user