feat(compiler): support sync runtime compile
Adds new abstraction `Compiler` with methods `compileComponentAsync` and `compileComponentSync`. This is in preparation of deprecating `ComponentResolver`. `compileComponentSync` is able to compile components synchronously given all components either have an inline template or they have been compiled before. Also changes `TestComponentBuilder.createSync` to take a `Type` and use the new `compileComponentSync` method. Also supports overriding the component metadata even if the component has already been compiled. Also fixes #7084 in a better way. BREAKING CHANGE: `TestComponentBuilder.createSync` now takes a component type and throws if not all templates are either inlined are compiled before via `createAsync`. Closes #9594
This commit is contained in:
parent
24eb8389d2
commit
bf598d6b8b
@ -101,44 +101,23 @@ export class CodeGenerator {
|
|||||||
return path.join(this.options.genDir, path.relative(root, filePath));
|
return path.join(this.options.genDir, path.relative(root, filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(tbosch): add a cache for shared css files
|
|
||||||
// TODO(tbosch): detect cycles!
|
|
||||||
private generateStylesheet(filepath: string, shim: boolean): Promise<any> {
|
|
||||||
return this.compiler.loadAndCompileStylesheet(filepath, shim, '.ts')
|
|
||||||
.then((sourceWithImports) => {
|
|
||||||
const emitPath = this.calculateEmitPath(sourceWithImports.source.moduleUrl);
|
|
||||||
// TODO(alexeagle): should include the sourceFile to the WriteFileCallback
|
|
||||||
this.host.writeFile(emitPath, PREAMBLE + sourceWithImports.source.source, false);
|
|
||||||
return Promise.all(
|
|
||||||
sourceWithImports.importedUrls.map(url => this.generateStylesheet(url, shim)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
codegen(): Promise<any> {
|
codegen(): Promise<any> {
|
||||||
let stylesheetPromises: Promise<any>[] = [];
|
|
||||||
const generateOneFile = (absSourcePath: string) =>
|
const generateOneFile = (absSourcePath: string) =>
|
||||||
Promise.all(this.readComponents(absSourcePath))
|
Promise.all(this.readComponents(absSourcePath))
|
||||||
.then((metadatas: compiler.CompileDirectiveMetadata[]) => {
|
.then((metadatas: compiler.CompileDirectiveMetadata[]) => {
|
||||||
if (!metadatas || !metadatas.length) {
|
if (!metadatas || !metadatas.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
metadatas.forEach((metadata) => {
|
|
||||||
let stylesheetPaths = metadata && metadata.template && metadata.template.styleUrls;
|
|
||||||
if (stylesheetPaths) {
|
|
||||||
stylesheetPaths.forEach((path) => {
|
|
||||||
stylesheetPromises.push(this.generateStylesheet(
|
|
||||||
path, metadata.template.encapsulation === ViewEncapsulation.Emulated));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return this.generateSource(metadatas);
|
return this.generateSource(metadatas);
|
||||||
})
|
})
|
||||||
.then(generated => {
|
.then(generatedModules => {
|
||||||
if (generated) {
|
if (generatedModules) {
|
||||||
const sourceFile = this.program.getSourceFile(absSourcePath);
|
generatedModules.forEach((generatedModule) => {
|
||||||
const emitPath = this.calculateEmitPath(generated.moduleUrl);
|
const sourceFile = this.program.getSourceFile(absSourcePath);
|
||||||
this.host.writeFile(
|
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
||||||
emitPath, PREAMBLE + generated.source, false, () => {}, [sourceFile]);
|
this.host.writeFile(
|
||||||
|
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((e) => { console.error(e.stack); });
|
.catch((e) => { console.error(e.stack); });
|
||||||
@ -146,7 +125,7 @@ export class CodeGenerator {
|
|||||||
.map(sf => sf.fileName)
|
.map(sf => sf.fileName)
|
||||||
.filter(f => !GENERATED_FILES.test(f))
|
.filter(f => !GENERATED_FILES.test(f))
|
||||||
.map(generateOneFile);
|
.map(generateOneFile);
|
||||||
return Promise.all(stylesheetPromises.concat(compPromises));
|
return Promise.all(compPromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(
|
static create(
|
||||||
@ -173,7 +152,7 @@ export class CodeGenerator {
|
|||||||
/*console*/ null, []);
|
/*console*/ null, []);
|
||||||
const offlineCompiler = new compiler.OfflineCompiler(
|
const offlineCompiler = new compiler.OfflineCompiler(
|
||||||
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
||||||
new TypeScriptEmitter(reflectorHost), xhr);
|
new TypeScriptEmitter(reflectorHost));
|
||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
new compiler.ViewResolver(staticReflector), config, staticReflector);
|
new compiler.ViewResolver(staticReflector), config, staticReflector);
|
||||||
|
@ -162,7 +162,7 @@ class Extractor {
|
|||||||
/*console*/ null, []);
|
/*console*/ null, []);
|
||||||
const offlineCompiler = new compiler.OfflineCompiler(
|
const offlineCompiler = new compiler.OfflineCompiler(
|
||||||
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
||||||
new TypeScriptEmitter(reflectorHost), xhr);
|
new TypeScriptEmitter(reflectorHost));
|
||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
new compiler.ViewResolver(staticReflector), config, staticReflector);
|
new compiler.ViewResolver(staticReflector), config, staticReflector);
|
||||||
|
@ -600,6 +600,31 @@ export class CompileQueryMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata about a stylesheet
|
||||||
|
*/
|
||||||
|
export class CompileStylesheetMetadata {
|
||||||
|
moduleUrl: string;
|
||||||
|
styles: string[];
|
||||||
|
styleUrls: string[];
|
||||||
|
constructor(
|
||||||
|
{moduleUrl, styles,
|
||||||
|
styleUrls}: {moduleUrl?: string, styles?: string[], styleUrls?: string[]} = {}) {
|
||||||
|
this.moduleUrl = moduleUrl;
|
||||||
|
this.styles = _normalizeArray(styles);
|
||||||
|
this.styleUrls = _normalizeArray(styleUrls);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(data: {[key: string]: any}): CompileStylesheetMetadata {
|
||||||
|
return new CompileStylesheetMetadata(
|
||||||
|
{moduleUrl: data['moduleUrl'], styles: data['styles'], styleUrls: data['styleUrls']});
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson(): {[key: string]: any} {
|
||||||
|
return {'moduleUrl': this.moduleUrl, 'styles': this.styles, 'styleUrls': this.styleUrls};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata regarding compilation of a template.
|
* Metadata regarding compilation of a template.
|
||||||
*/
|
*/
|
||||||
@ -609,17 +634,19 @@ export class CompileTemplateMetadata {
|
|||||||
templateUrl: string;
|
templateUrl: string;
|
||||||
styles: string[];
|
styles: string[];
|
||||||
styleUrls: string[];
|
styleUrls: string[];
|
||||||
|
externalStylesheets: CompileStylesheetMetadata[];
|
||||||
animations: CompileAnimationEntryMetadata[];
|
animations: CompileAnimationEntryMetadata[];
|
||||||
ngContentSelectors: string[];
|
ngContentSelectors: string[];
|
||||||
interpolation: [string, string];
|
interpolation: [string, string];
|
||||||
constructor(
|
constructor(
|
||||||
{encapsulation, template, templateUrl, styles, styleUrls, animations, ngContentSelectors,
|
{encapsulation, template, templateUrl, styles, styleUrls, externalStylesheets, animations,
|
||||||
interpolation}: {
|
ngContentSelectors, interpolation}: {
|
||||||
encapsulation?: ViewEncapsulation,
|
encapsulation?: ViewEncapsulation,
|
||||||
template?: string,
|
template?: string,
|
||||||
templateUrl?: string,
|
templateUrl?: string,
|
||||||
styles?: string[],
|
styles?: string[],
|
||||||
styleUrls?: string[],
|
styleUrls?: string[],
|
||||||
|
externalStylesheets?: CompileStylesheetMetadata[],
|
||||||
ngContentSelectors?: string[],
|
ngContentSelectors?: string[],
|
||||||
animations?: CompileAnimationEntryMetadata[],
|
animations?: CompileAnimationEntryMetadata[],
|
||||||
interpolation?: [string, string]
|
interpolation?: [string, string]
|
||||||
@ -627,8 +654,9 @@ export class CompileTemplateMetadata {
|
|||||||
this.encapsulation = encapsulation;
|
this.encapsulation = encapsulation;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
this.templateUrl = templateUrl;
|
this.templateUrl = templateUrl;
|
||||||
this.styles = isPresent(styles) ? styles : [];
|
this.styles = _normalizeArray(styles);
|
||||||
this.styleUrls = isPresent(styleUrls) ? styleUrls : [];
|
this.styleUrls = _normalizeArray(styleUrls);
|
||||||
|
this.externalStylesheets = _normalizeArray(externalStylesheets);
|
||||||
this.animations = isPresent(animations) ? ListWrapper.flatten(animations) : [];
|
this.animations = isPresent(animations) ? ListWrapper.flatten(animations) : [];
|
||||||
this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : [];
|
this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : [];
|
||||||
if (isPresent(interpolation) && interpolation.length != 2) {
|
if (isPresent(interpolation) && interpolation.length != 2) {
|
||||||
@ -648,6 +676,8 @@ export class CompileTemplateMetadata {
|
|||||||
templateUrl: data['templateUrl'],
|
templateUrl: data['templateUrl'],
|
||||||
styles: data['styles'],
|
styles: data['styles'],
|
||||||
styleUrls: data['styleUrls'],
|
styleUrls: data['styleUrls'],
|
||||||
|
externalStylesheets:
|
||||||
|
_arrayFromJson(data['externalStylesheets'], CompileStylesheetMetadata.fromJson),
|
||||||
animations: animations,
|
animations: animations,
|
||||||
ngContentSelectors: data['ngContentSelectors'],
|
ngContentSelectors: data['ngContentSelectors'],
|
||||||
interpolation: data['interpolation']
|
interpolation: data['interpolation']
|
||||||
@ -662,6 +692,7 @@ export class CompileTemplateMetadata {
|
|||||||
'templateUrl': this.templateUrl,
|
'templateUrl': this.templateUrl,
|
||||||
'styles': this.styles,
|
'styles': this.styles,
|
||||||
'styleUrls': this.styleUrls,
|
'styleUrls': this.styleUrls,
|
||||||
|
'externalStylesheets': _objToJson(this.externalStylesheets),
|
||||||
'animations': _objToJson(this.animations),
|
'animations': _objToJson(this.animations),
|
||||||
'ngContentSelectors': this.ngContentSelectors,
|
'ngContentSelectors': this.ngContentSelectors,
|
||||||
'interpolation': this.interpolation
|
'interpolation': this.interpolation
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ComponentResolver, Type} from '@angular/core';
|
import {Compiler, ComponentResolver, Type} from '@angular/core';
|
||||||
|
|
||||||
export * from './template_ast';
|
export * from './template_ast';
|
||||||
export {TEMPLATE_TRANSFORMS} from './template_parser';
|
export {TEMPLATE_TRANSFORMS} from './template_parser';
|
||||||
@ -49,6 +49,7 @@ export const COMPILER_PROVIDERS: Array<any|Type|{[k: string]: any}|any[]> =
|
|||||||
/*@ts2dart_Provider*/ {provide: CompilerConfig, useValue: new CompilerConfig()},
|
/*@ts2dart_Provider*/ {provide: CompilerConfig, useValue: new CompilerConfig()},
|
||||||
RuntimeCompiler,
|
RuntimeCompiler,
|
||||||
/*@ts2dart_Provider*/ {provide: ComponentResolver, useExisting: RuntimeCompiler},
|
/*@ts2dart_Provider*/ {provide: ComponentResolver, useExisting: RuntimeCompiler},
|
||||||
|
/*@ts2dart_Provider*/ {provide: Compiler, useExisting: RuntimeCompiler},
|
||||||
DomElementSchemaRegistry,
|
DomElementSchemaRegistry,
|
||||||
/*@ts2dart_Provider*/ {provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
|
/*@ts2dart_Provider*/ {provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
|
||||||
UrlResolver, ViewResolver, DirectiveResolver, PipeResolver
|
UrlResolver, ViewResolver, DirectiveResolver, PipeResolver
|
||||||
|
@ -12,65 +12,92 @@ import {PromiseWrapper} from '../src/facade/async';
|
|||||||
import {BaseException} from '../src/facade/exceptions';
|
import {BaseException} from '../src/facade/exceptions';
|
||||||
import {isBlank, isPresent} from '../src/facade/lang';
|
import {isBlank, isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
import {CompileTypeMetadata, CompileDirectiveMetadata, CompileTemplateMetadata,} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
|
||||||
import {XHR} from './xhr';
|
|
||||||
import {UrlResolver} from './url_resolver';
|
|
||||||
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
|
|
||||||
|
|
||||||
import {HtmlAstVisitor, HtmlElementAst, HtmlTextAst, HtmlAttrAst, HtmlCommentAst, HtmlExpansionAst, HtmlExpansionCaseAst, htmlVisitAll} from './html_ast';
|
|
||||||
import {HtmlParser} from './html_parser';
|
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
|
import {HtmlAstVisitor, HtmlAttrAst, HtmlCommentAst, HtmlElementAst, HtmlExpansionAst, HtmlExpansionCaseAst, HtmlTextAst, htmlVisitAll} from './html_ast';
|
||||||
|
import {HtmlParser} from './html_parser';
|
||||||
|
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
|
||||||
|
import {PreparsedElementType, preparseElement} from './template_preparser';
|
||||||
|
import {UrlResolver} from './url_resolver';
|
||||||
|
import {XHR} from './xhr';
|
||||||
|
|
||||||
import {preparseElement, PreparsedElementType} from './template_preparser';
|
export class NormalizeDirectiveResult {
|
||||||
|
constructor(
|
||||||
|
public syncResult: CompileDirectiveMetadata,
|
||||||
|
public asyncResult: Promise<CompileDirectiveMetadata>) {}
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DirectiveNormalizer {
|
export class DirectiveNormalizer {
|
||||||
|
private _xhrCache = new Map<string, Promise<string>>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _xhr: XHR, private _urlResolver: UrlResolver, private _htmlParser: HtmlParser,
|
private _xhr: XHR, private _urlResolver: UrlResolver, private _htmlParser: HtmlParser,
|
||||||
private _config: CompilerConfig) {}
|
private _config: CompilerConfig) {}
|
||||||
|
|
||||||
normalizeDirective(directive: CompileDirectiveMetadata): Promise<CompileDirectiveMetadata> {
|
clearCache() { this._xhrCache.clear(); }
|
||||||
if (!directive.isComponent) {
|
|
||||||
// For non components there is nothing to be normalized yet.
|
clearCacheFor(normalizedDirective: CompileDirectiveMetadata) {
|
||||||
return PromiseWrapper.resolve(directive);
|
if (!normalizedDirective.isComponent) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return this.normalizeTemplate(directive.type, directive.template)
|
this._xhrCache.delete(normalizedDirective.template.templateUrl);
|
||||||
.then((normalizedTemplate: CompileTemplateMetadata) => new CompileDirectiveMetadata({
|
normalizedDirective.template.externalStylesheets.forEach(
|
||||||
type: directive.type,
|
(stylesheet) => { this._xhrCache.delete(stylesheet.moduleUrl); });
|
||||||
isComponent: directive.isComponent,
|
|
||||||
selector: directive.selector,
|
|
||||||
exportAs: directive.exportAs,
|
|
||||||
changeDetection: directive.changeDetection,
|
|
||||||
inputs: directive.inputs,
|
|
||||||
outputs: directive.outputs,
|
|
||||||
hostListeners: directive.hostListeners,
|
|
||||||
hostProperties: directive.hostProperties,
|
|
||||||
hostAttributes: directive.hostAttributes,
|
|
||||||
lifecycleHooks: directive.lifecycleHooks,
|
|
||||||
providers: directive.providers,
|
|
||||||
viewProviders: directive.viewProviders,
|
|
||||||
queries: directive.queries,
|
|
||||||
viewQueries: directive.viewQueries,
|
|
||||||
precompile: directive.precompile,
|
|
||||||
template: normalizedTemplate
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeTemplate(directiveType: CompileTypeMetadata, template: CompileTemplateMetadata):
|
private _fetch(url: string): Promise<string> {
|
||||||
Promise<CompileTemplateMetadata> {
|
var result = this._xhrCache.get(url);
|
||||||
if (isPresent(template.template)) {
|
if (!result) {
|
||||||
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
|
result = this._xhr.get(url);
|
||||||
directiveType, template, template.template, directiveType.moduleUrl));
|
this._xhrCache.set(url, result);
|
||||||
} else if (isPresent(template.templateUrl)) {
|
|
||||||
var sourceAbsUrl = this._urlResolver.resolve(directiveType.moduleUrl, template.templateUrl);
|
|
||||||
return this._xhr.get(sourceAbsUrl)
|
|
||||||
.then(
|
|
||||||
templateContent => this.normalizeLoadedTemplate(
|
|
||||||
directiveType, template, templateContent, sourceAbsUrl));
|
|
||||||
} else {
|
|
||||||
throw new BaseException(`No template specified for component ${directiveType.name}`);
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeDirective(directive: CompileDirectiveMetadata): NormalizeDirectiveResult {
|
||||||
|
if (!directive.isComponent) {
|
||||||
|
// For non components there is nothing to be normalized yet.
|
||||||
|
return new NormalizeDirectiveResult(directive, Promise.resolve(directive));
|
||||||
|
}
|
||||||
|
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
||||||
|
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
||||||
|
if (isPresent(directive.template.template)) {
|
||||||
|
normalizedTemplateSync = this.normalizeTemplateSync(directive.type, directive.template);
|
||||||
|
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync);
|
||||||
|
} else if (directive.template.templateUrl) {
|
||||||
|
normalizedTemplateAsync = this.normalizeTemplateAsync(directive.type, directive.template);
|
||||||
|
} else {
|
||||||
|
throw new BaseException(`No template specified for component ${directive.type.name}`);
|
||||||
|
}
|
||||||
|
if (normalizedTemplateSync && normalizedTemplateSync.styleUrls.length === 0) {
|
||||||
|
// sync case
|
||||||
|
let normalizedDirective = _cloneDirectiveWithTemplate(directive, normalizedTemplateSync);
|
||||||
|
return new NormalizeDirectiveResult(
|
||||||
|
normalizedDirective, Promise.resolve(normalizedDirective));
|
||||||
|
} else {
|
||||||
|
// async case
|
||||||
|
return new NormalizeDirectiveResult(
|
||||||
|
null,
|
||||||
|
normalizedTemplateAsync
|
||||||
|
.then((normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate))
|
||||||
|
.then(
|
||||||
|
(normalizedTemplate) =>
|
||||||
|
_cloneDirectiveWithTemplate(directive, normalizedTemplate)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeTemplateSync(directiveType: CompileTypeMetadata, template: CompileTemplateMetadata):
|
||||||
|
CompileTemplateMetadata {
|
||||||
|
return this.normalizeLoadedTemplate(
|
||||||
|
directiveType, template, template.template, directiveType.moduleUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeTemplateAsync(directiveType: CompileTypeMetadata, template: CompileTemplateMetadata):
|
||||||
|
Promise<CompileTemplateMetadata> {
|
||||||
|
let templateUrl = this._urlResolver.resolve(directiveType.moduleUrl, template.templateUrl);
|
||||||
|
return this._fetch(templateUrl)
|
||||||
|
.then((value) => this.normalizeLoadedTemplate(directiveType, template, value, templateUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeLoadedTemplate(
|
normalizeLoadedTemplate(
|
||||||
@ -81,42 +108,87 @@ export class DirectiveNormalizer {
|
|||||||
var errorString = rootNodesAndErrors.errors.join('\n');
|
var errorString = rootNodesAndErrors.errors.join('\n');
|
||||||
throw new BaseException(`Template parse errors:\n${errorString}`);
|
throw new BaseException(`Template parse errors:\n${errorString}`);
|
||||||
}
|
}
|
||||||
|
var templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
|
||||||
|
styles: templateMeta.styles,
|
||||||
|
styleUrls: templateMeta.styleUrls,
|
||||||
|
moduleUrl: directiveType.moduleUrl
|
||||||
|
}));
|
||||||
|
|
||||||
var visitor = new TemplatePreparseVisitor();
|
var visitor = new TemplatePreparseVisitor();
|
||||||
htmlVisitAll(visitor, rootNodesAndErrors.rootNodes);
|
htmlVisitAll(visitor, rootNodesAndErrors.rootNodes);
|
||||||
var allStyles = templateMeta.styles.concat(visitor.styles);
|
var templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
|
||||||
|
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
||||||
|
|
||||||
var allStyleAbsUrls =
|
var allStyles = templateMetadataStyles.styles.concat(templateStyles.styles);
|
||||||
visitor.styleUrls.filter(isStyleUrlResolvable)
|
var allStyleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls);
|
||||||
.map(url => this._urlResolver.resolve(templateAbsUrl, url))
|
|
||||||
.concat(templateMeta.styleUrls.filter(isStyleUrlResolvable)
|
|
||||||
.map(url => this._urlResolver.resolve(directiveType.moduleUrl, url)));
|
|
||||||
|
|
||||||
var allResolvedStyles = allStyles.map(style => {
|
|
||||||
var styleWithImports = extractStyleUrls(this._urlResolver, templateAbsUrl, style);
|
|
||||||
styleWithImports.styleUrls.forEach(styleUrl => allStyleAbsUrls.push(styleUrl));
|
|
||||||
return styleWithImports.style;
|
|
||||||
});
|
|
||||||
|
|
||||||
var encapsulation = templateMeta.encapsulation;
|
var encapsulation = templateMeta.encapsulation;
|
||||||
if (isBlank(encapsulation)) {
|
if (isBlank(encapsulation)) {
|
||||||
encapsulation = this._config.defaultEncapsulation;
|
encapsulation = this._config.defaultEncapsulation;
|
||||||
}
|
}
|
||||||
if (encapsulation === ViewEncapsulation.Emulated && allResolvedStyles.length === 0 &&
|
if (encapsulation === ViewEncapsulation.Emulated && allStyles.length === 0 &&
|
||||||
allStyleAbsUrls.length === 0) {
|
allStyleUrls.length === 0) {
|
||||||
encapsulation = ViewEncapsulation.None;
|
encapsulation = ViewEncapsulation.None;
|
||||||
}
|
}
|
||||||
return new CompileTemplateMetadata({
|
return new CompileTemplateMetadata({
|
||||||
encapsulation: encapsulation,
|
encapsulation: encapsulation,
|
||||||
template: template,
|
template: template,
|
||||||
templateUrl: templateAbsUrl,
|
templateUrl: templateAbsUrl,
|
||||||
styles: allResolvedStyles,
|
styles: allStyles,
|
||||||
styleUrls: allStyleAbsUrls,
|
styleUrls: allStyleUrls,
|
||||||
|
externalStylesheets: templateMeta.externalStylesheets,
|
||||||
ngContentSelectors: visitor.ngContentSelectors,
|
ngContentSelectors: visitor.ngContentSelectors,
|
||||||
animations: templateMeta.animations,
|
animations: templateMeta.animations,
|
||||||
interpolation: templateMeta.interpolation
|
interpolation: templateMeta.interpolation
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
normalizeExternalStylesheets(templateMeta: CompileTemplateMetadata):
|
||||||
|
Promise<CompileTemplateMetadata> {
|
||||||
|
return this._loadMissingExternalStylesheets(templateMeta.styleUrls)
|
||||||
|
.then((externalStylesheets) => new CompileTemplateMetadata({
|
||||||
|
encapsulation: templateMeta.encapsulation,
|
||||||
|
template: templateMeta.template,
|
||||||
|
templateUrl: templateMeta.templateUrl,
|
||||||
|
styles: templateMeta.styles,
|
||||||
|
styleUrls: templateMeta.styleUrls,
|
||||||
|
externalStylesheets: externalStylesheets,
|
||||||
|
ngContentSelectors: templateMeta.ngContentSelectors,
|
||||||
|
animations: templateMeta.animations,
|
||||||
|
interpolation: templateMeta.interpolation
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadMissingExternalStylesheets(
|
||||||
|
styleUrls: string[],
|
||||||
|
loadedStylesheets:
|
||||||
|
Map<string, CompileStylesheetMetadata> = new Map<string, CompileStylesheetMetadata>()):
|
||||||
|
Promise<CompileStylesheetMetadata[]> {
|
||||||
|
return Promise
|
||||||
|
.all(styleUrls.filter((styleUrl) => !loadedStylesheets.has(styleUrl))
|
||||||
|
.map(styleUrl => this._fetch(styleUrl).then((loadedStyle) => {
|
||||||
|
var stylesheet = this.normalizeStylesheet(
|
||||||
|
new CompileStylesheetMetadata({styles: [loadedStyle], moduleUrl: styleUrl}));
|
||||||
|
loadedStylesheets.set(styleUrl, stylesheet);
|
||||||
|
return this._loadMissingExternalStylesheets(
|
||||||
|
stylesheet.styleUrls, loadedStylesheets);
|
||||||
|
})))
|
||||||
|
.then((_) => Array.from(loadedStylesheets.values()));
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeStylesheet(stylesheet: CompileStylesheetMetadata): CompileStylesheetMetadata {
|
||||||
|
var allStyleUrls = stylesheet.styleUrls.filter(isStyleUrlResolvable)
|
||||||
|
.map(url => this._urlResolver.resolve(stylesheet.moduleUrl, url));
|
||||||
|
|
||||||
|
var allStyles = stylesheet.styles.map(style => {
|
||||||
|
var styleWithImports = extractStyleUrls(this._urlResolver, stylesheet.moduleUrl, style);
|
||||||
|
allStyleUrls.push(...styleWithImports.styleUrls);
|
||||||
|
return styleWithImports.style;
|
||||||
|
});
|
||||||
|
|
||||||
|
return new CompileStylesheetMetadata(
|
||||||
|
{styles: allStyles, styleUrls: allStyleUrls, moduleUrl: stylesheet.moduleUrl});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TemplatePreparseVisitor implements HtmlAstVisitor {
|
class TemplatePreparseVisitor implements HtmlAstVisitor {
|
||||||
@ -166,3 +238,27 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
|
|||||||
|
|
||||||
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return null; }
|
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _cloneDirectiveWithTemplate(
|
||||||
|
directive: CompileDirectiveMetadata,
|
||||||
|
template: CompileTemplateMetadata): CompileDirectiveMetadata {
|
||||||
|
return new CompileDirectiveMetadata({
|
||||||
|
type: directive.type,
|
||||||
|
isComponent: directive.isComponent,
|
||||||
|
selector: directive.selector,
|
||||||
|
exportAs: directive.exportAs,
|
||||||
|
changeDetection: directive.changeDetection,
|
||||||
|
inputs: directive.inputs,
|
||||||
|
outputs: directive.outputs,
|
||||||
|
hostListeners: directive.hostListeners,
|
||||||
|
hostProperties: directive.hostProperties,
|
||||||
|
hostAttributes: directive.hostAttributes,
|
||||||
|
lifecycleHooks: directive.lifecycleHooks,
|
||||||
|
providers: directive.providers,
|
||||||
|
viewProviders: directive.viewProviders,
|
||||||
|
queries: directive.queries,
|
||||||
|
viewQueries: directive.viewQueries,
|
||||||
|
precompile: directive.precompile,
|
||||||
|
template: template
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -49,6 +49,16 @@ export class CompileMetadataResolver {
|
|||||||
return sanitizeIdentifier(identifier);
|
return sanitizeIdentifier(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearCacheFor(compType: Type) {
|
||||||
|
this._directiveCache.delete(compType);
|
||||||
|
this._pipeCache.delete(compType);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache() {
|
||||||
|
this._directiveCache.clear();
|
||||||
|
this._pipeCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
||||||
var defs = entry.definitions.map(def => this.getAnimationStateMetadata(def));
|
var defs = entry.definitions.map(def => this.getAnimationStateMetadata(def));
|
||||||
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
||||||
@ -101,7 +111,6 @@ export class CompileMetadataResolver {
|
|||||||
var moduleUrl = staticTypeModuleUrl(directiveType);
|
var moduleUrl = staticTypeModuleUrl(directiveType);
|
||||||
var precompileTypes: cpl.CompileTypeMetadata[] = [];
|
var precompileTypes: cpl.CompileTypeMetadata[] = [];
|
||||||
if (dirMeta instanceof ComponentMetadata) {
|
if (dirMeta instanceof ComponentMetadata) {
|
||||||
assertArrayOfStrings('styles', dirMeta.styles);
|
|
||||||
var cmpMeta = <ComponentMetadata>dirMeta;
|
var cmpMeta = <ComponentMetadata>dirMeta;
|
||||||
var viewMeta = this._viewResolver.resolve(directiveType);
|
var viewMeta = this._viewResolver.resolve(directiveType);
|
||||||
assertArrayOfStrings('styles', viewMeta.styles);
|
assertArrayOfStrings('styles', viewMeta.styles);
|
||||||
@ -109,6 +118,8 @@ export class CompileMetadataResolver {
|
|||||||
var animations = isPresent(viewMeta.animations) ?
|
var animations = isPresent(viewMeta.animations) ?
|
||||||
viewMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
|
viewMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
|
||||||
null;
|
null;
|
||||||
|
assertArrayOfStrings('styles', viewMeta.styles);
|
||||||
|
assertArrayOfStrings('styleUrls', viewMeta.styleUrls);
|
||||||
|
|
||||||
templateMeta = new cpl.CompileTemplateMetadata({
|
templateMeta = new cpl.CompileTemplateMetadata({
|
||||||
encapsulation: viewMeta.encapsulation,
|
encapsulation: viewMeta.encapsulation,
|
||||||
|
@ -14,7 +14,7 @@ import {ListWrapper} from './facade/collection';
|
|||||||
import {BaseException} from './facade/exceptions';
|
import {BaseException} from './facade/exceptions';
|
||||||
import {OutputEmitter} from './output/abstract_emitter';
|
import {OutputEmitter} from './output/abstract_emitter';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
import {StyleCompiler, StylesCompileResult} from './style_compiler';
|
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
|
||||||
import {TemplateParser} from './template_parser';
|
import {TemplateParser} from './template_parser';
|
||||||
import {assetUrl} from './util';
|
import {assetUrl} from './util';
|
||||||
import {ComponentFactoryDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
|
import {ComponentFactoryDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
|
||||||
@ -44,29 +44,38 @@ export class OfflineCompiler {
|
|||||||
constructor(
|
constructor(
|
||||||
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
private _outputEmitter: OutputEmitter, private _xhr: XHR) {}
|
private _outputEmitter: OutputEmitter) {}
|
||||||
|
|
||||||
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
|
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
|
||||||
Promise<CompileDirectiveMetadata> {
|
Promise<CompileDirectiveMetadata> {
|
||||||
return this._directiveNormalizer.normalizeDirective(directive);
|
return this._directiveNormalizer.normalizeDirective(directive).asyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
compileTemplates(components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
compileTemplates(components: NormalizedComponentWithViewDirectives[]): SourceModule[] {
|
||||||
if (components.length === 0) {
|
if (components.length === 0) {
|
||||||
throw new BaseException('No components given');
|
throw new BaseException('No components given');
|
||||||
}
|
}
|
||||||
var statements: o.DeclareVarStmt[] = [];
|
var statements: o.DeclareVarStmt[] = [];
|
||||||
var exportedVars: string[] = [];
|
var exportedVars: string[] = [];
|
||||||
var moduleUrl = _ngfactoryModuleUrl(components[0].component.type);
|
var moduleUrl = _ngfactoryModuleUrl(components[0].component.type);
|
||||||
|
var outputSourceModules: SourceModule[] = [];
|
||||||
components.forEach(componentWithDirs => {
|
components.forEach(componentWithDirs => {
|
||||||
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
||||||
_assertComponent(compMeta);
|
_assertComponent(compMeta);
|
||||||
|
var fileSuffix = _splitLastSuffix(compMeta.type.moduleUrl)[1];
|
||||||
|
var stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
||||||
|
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
||||||
|
outputSourceModules.push(this._codgenStyles(compiledStyleSheet, fileSuffix));
|
||||||
|
});
|
||||||
|
|
||||||
var compViewFactoryVar = this._compileComponent(
|
var compViewFactoryVar = this._compileComponent(
|
||||||
compMeta, componentWithDirs.directives, componentWithDirs.pipes, statements);
|
compMeta, componentWithDirs.directives, componentWithDirs.pipes,
|
||||||
|
stylesCompileResults.componentStylesheet, fileSuffix, statements);
|
||||||
exportedVars.push(compViewFactoryVar);
|
exportedVars.push(compViewFactoryVar);
|
||||||
|
|
||||||
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
var hostViewFactoryVar = this._compileComponent(hostMeta, [compMeta], [], statements);
|
var hostViewFactoryVar =
|
||||||
|
this._compileComponent(hostMeta, [compMeta], [], null, fileSuffix, statements);
|
||||||
var compFactoryVar = _componentFactoryName(compMeta.type);
|
var compFactoryVar = _componentFactoryName(compMeta.type);
|
||||||
statements.push(
|
statements.push(
|
||||||
o.variable(compFactoryVar)
|
o.variable(compFactoryVar)
|
||||||
@ -82,43 +91,32 @@ export class OfflineCompiler {
|
|||||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
exportedVars.push(compFactoryVar);
|
exportedVars.push(compFactoryVar);
|
||||||
});
|
});
|
||||||
return this._codegenSourceModule(moduleUrl, statements, exportedVars);
|
outputSourceModules.unshift(this._codegenSourceModule(moduleUrl, statements, exportedVars));
|
||||||
}
|
return outputSourceModules;
|
||||||
|
|
||||||
loadAndCompileStylesheet(stylesheetUrl: string, shim: boolean, suffix: string):
|
|
||||||
Promise<StyleSheetSourceWithImports> {
|
|
||||||
return this._xhr.get(stylesheetUrl).then((cssText) => {
|
|
||||||
var compileResult = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, shim);
|
|
||||||
var importedUrls: string[] = [];
|
|
||||||
compileResult.dependencies.forEach((dep) => {
|
|
||||||
importedUrls.push(dep.moduleUrl);
|
|
||||||
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.moduleUrl, dep.isShimmed, suffix);
|
|
||||||
});
|
|
||||||
return new StyleSheetSourceWithImports(
|
|
||||||
this._codgenStyles(stylesheetUrl, shim, suffix, compileResult), importedUrls);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileComponent(
|
private _compileComponent(
|
||||||
compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||||
pipes: CompilePipeMetadata[], targetStatements: o.Statement[]): string {
|
pipes: CompilePipeMetadata[], componentStyles: CompiledStylesheet, fileSuffix: string,
|
||||||
var styleResult = this._styleCompiler.compileComponent(compMeta);
|
targetStatements: o.Statement[]): string {
|
||||||
var parsedTemplate = this._templateParser.parse(
|
var parsedTemplate = this._templateParser.parse(
|
||||||
compMeta, compMeta.template.template, directives, pipes, compMeta.type.name);
|
compMeta, compMeta.template.template, directives, pipes, compMeta.type.name);
|
||||||
var viewResult = this._viewCompiler.compileComponent(
|
var stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||||
compMeta, parsedTemplate, o.variable(styleResult.stylesVar), pipes);
|
var viewResult =
|
||||||
ListWrapper.addAll(
|
this._viewCompiler.compileComponent(compMeta, parsedTemplate, stylesExpr, pipes);
|
||||||
targetStatements, _resolveStyleStatements(compMeta.type.moduleUrl, styleResult));
|
if (componentStyles) {
|
||||||
|
ListWrapper.addAll(targetStatements, _resolveStyleStatements(componentStyles, fileSuffix));
|
||||||
|
}
|
||||||
ListWrapper.addAll(targetStatements, _resolveViewStatements(viewResult));
|
ListWrapper.addAll(targetStatements, _resolveViewStatements(viewResult));
|
||||||
return viewResult.viewFactoryVar;
|
return viewResult.viewFactoryVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _codgenStyles(
|
private _codgenStyles(stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
|
||||||
inputUrl: string, shim: boolean, suffix: string,
|
_resolveStyleStatements(stylesCompileResult, fileSuffix);
|
||||||
stylesCompileResult: StylesCompileResult): SourceModule {
|
|
||||||
return this._codegenSourceModule(
|
return this._codegenSourceModule(
|
||||||
_stylesModuleUrl(inputUrl, shim, suffix), stylesCompileResult.statements,
|
_stylesModuleUrl(
|
||||||
[stylesCompileResult.stylesVar]);
|
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
|
||||||
|
stylesCompileResult.statements, [stylesCompileResult.stylesVar]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _codegenSourceModule(
|
private _codegenSourceModule(
|
||||||
@ -131,7 +129,7 @@ export class OfflineCompiler {
|
|||||||
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
|
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
|
||||||
compileResult.dependencies.forEach((dep) => {
|
compileResult.dependencies.forEach((dep) => {
|
||||||
if (dep instanceof ViewFactoryDependency) {
|
if (dep instanceof ViewFactoryDependency) {
|
||||||
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.type);
|
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp);
|
||||||
} else if (dep instanceof ComponentFactoryDependency) {
|
} else if (dep instanceof ComponentFactoryDependency) {
|
||||||
dep.placeholder.name = _componentFactoryName(dep.comp);
|
dep.placeholder.name = _componentFactoryName(dep.comp);
|
||||||
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp);
|
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp);
|
||||||
@ -142,17 +140,15 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
|
|||||||
|
|
||||||
|
|
||||||
function _resolveStyleStatements(
|
function _resolveStyleStatements(
|
||||||
containingModuleUrl: string, compileResult: StylesCompileResult): o.Statement[] {
|
compileResult: CompiledStylesheet, fileSuffix: string): o.Statement[] {
|
||||||
var containingSuffix = _splitSuffix(containingModuleUrl)[1];
|
|
||||||
compileResult.dependencies.forEach((dep) => {
|
compileResult.dependencies.forEach((dep) => {
|
||||||
dep.valuePlaceholder.moduleUrl =
|
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.moduleUrl, dep.isShimmed, fileSuffix);
|
||||||
_stylesModuleUrl(dep.moduleUrl, dep.isShimmed, containingSuffix);
|
|
||||||
});
|
});
|
||||||
return compileResult.statements;
|
return compileResult.statements;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _ngfactoryModuleUrl(comp: CompileIdentifierMetadata): string {
|
function _ngfactoryModuleUrl(comp: CompileIdentifierMetadata): string {
|
||||||
var urlWithSuffix = _splitSuffix(comp.moduleUrl);
|
var urlWithSuffix = _splitLastSuffix(comp.moduleUrl);
|
||||||
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +166,7 @@ function _assertComponent(meta: CompileDirectiveMetadata) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _splitSuffix(path: string): string[] {
|
function _splitLastSuffix(path: string): string[] {
|
||||||
let lastDot = path.lastIndexOf('.');
|
let lastDot = path.lastIndexOf('.');
|
||||||
if (lastDot !== -1) {
|
if (lastDot !== -1) {
|
||||||
return [path.substring(0, lastDot), path.substring(lastDot)];
|
return [path.substring(0, lastDot), path.substring(lastDot)];
|
||||||
|
@ -6,26 +6,25 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ComponentFactory, ComponentResolver, Injectable} from '@angular/core';
|
import {Compiler, ComponentFactory, ComponentResolver, Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {BaseException} from '../src/facade/exceptions';
|
import {BaseException} from '../src/facade/exceptions';
|
||||||
import {IS_DART, Type, isBlank, isString} from '../src/facade/lang';
|
import {ConcreteType, IS_DART, Type, isBlank, isString, stringify} from '../src/facade/lang';
|
||||||
|
|
||||||
import {ListWrapper,} from '../src/facade/collection';
|
import {ListWrapper,} from '../src/facade/collection';
|
||||||
import {PromiseWrapper} from '../src/facade/async';
|
import {PromiseWrapper} from '../src/facade/async';
|
||||||
import {createHostComponentMeta, CompileDirectiveMetadata, CompilePipeMetadata, CompileIdentifierMetadata} from './compile_metadata';
|
import {createHostComponentMeta, CompileDirectiveMetadata, CompilePipeMetadata, CompileIdentifierMetadata} from './compile_metadata';
|
||||||
import {TemplateAst,} from './template_ast';
|
import {TemplateAst,} from './template_ast';
|
||||||
import {StyleCompiler, StylesCompileDependency, StylesCompileResult} from './style_compiler';
|
import {StyleCompiler, StylesCompileDependency, CompiledStylesheet} from './style_compiler';
|
||||||
import {ViewCompiler, ViewFactoryDependency, ComponentFactoryDependency} from './view_compiler/view_compiler';
|
import {ViewCompiler, ViewCompileResult, ViewFactoryDependency, ComponentFactoryDependency} from './view_compiler/view_compiler';
|
||||||
import {TemplateParser} from './template_parser';
|
import {TemplateParser} from './template_parser';
|
||||||
import {DirectiveNormalizer} from './directive_normalizer';
|
import {DirectiveNormalizer, NormalizeDirectiveResult} from './directive_normalizer';
|
||||||
import {CompileMetadataResolver} from './metadata_resolver';
|
import {CompileMetadataResolver} from './metadata_resolver';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import * as ir from './output/output_ast';
|
import * as ir from './output/output_ast';
|
||||||
import {jitStatements} from './output/output_jit';
|
import {jitStatements} from './output/output_jit';
|
||||||
import {interpretStatements} from './output/output_interpreter';
|
import {interpretStatements} from './output/output_interpreter';
|
||||||
import {InterpretiveAppViewInstanceFactory} from './output/interpretive_view';
|
import {InterpretiveAppViewInstanceFactory} from './output/interpretive_view';
|
||||||
import {XHR} from './xhr';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An internal module of the Angular compiler that begins with component types,
|
* An internal module of the Angular compiler that begins with component types,
|
||||||
@ -33,15 +32,14 @@ import {XHR} from './xhr';
|
|||||||
* ready for linking into an application.
|
* ready for linking into an application.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RuntimeCompiler implements ComponentResolver {
|
export class RuntimeCompiler implements ComponentResolver, Compiler {
|
||||||
private _styleCache: Map<string, Promise<string>> = new Map<string, Promise<string>>();
|
|
||||||
private _hostCacheKeys = new Map<Type, any>();
|
|
||||||
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
|
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
|
||||||
|
private _compiledHostTemplateCache = new Map<any, CompiledTemplate>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _metadataResolver: CompileMetadataResolver,
|
private _metadataResolver: CompileMetadataResolver,
|
||||||
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, private _xhr: XHR,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
private _genConfig: CompilerConfig) {}
|
private _genConfig: CompilerConfig) {}
|
||||||
|
|
||||||
resolveComponent(component: Type|string): Promise<ComponentFactory<any>> {
|
resolveComponent(component: Type|string): Promise<ComponentFactory<any>> {
|
||||||
@ -49,175 +47,215 @@ export class RuntimeCompiler implements ComponentResolver {
|
|||||||
return PromiseWrapper.reject(
|
return PromiseWrapper.reject(
|
||||||
new BaseException(`Cannot resolve component using '${component}'.`), null);
|
new BaseException(`Cannot resolve component using '${component}'.`), null);
|
||||||
}
|
}
|
||||||
return this._loadAndCompileHostComponent(<Type>component).done;
|
return this.compileComponentAsync(<ConcreteType<any>>component);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileComponentAsync<T>(compType: ConcreteType<T>): Promise<ComponentFactory<T>> {
|
||||||
|
var templates = this._getTransitiveCompiledTemplates(compType, true);
|
||||||
|
var loadingPromises: Promise<any>[] = [];
|
||||||
|
templates.forEach((template) => {
|
||||||
|
if (template.loading) {
|
||||||
|
loadingPromises.push(template.loading);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Promise.all(loadingPromises).then(() => {
|
||||||
|
templates.forEach((template) => { this._compileTemplate(template); });
|
||||||
|
return this._getCompiledHostTemplate(compType).proxyComponentFactory;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compileComponentSync<T>(compType: ConcreteType<T>): ComponentFactory<T> {
|
||||||
|
var templates = this._getTransitiveCompiledTemplates(compType, true);
|
||||||
|
templates.forEach((template) => {
|
||||||
|
if (template.loading) {
|
||||||
|
throw new BaseException(
|
||||||
|
`Can't compile synchronously as ${template.compType.name} is still being loaded!`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
templates.forEach((template) => { this._compileTemplate(template); });
|
||||||
|
return this._getCompiledHostTemplate(compType).proxyComponentFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCacheFor(compType: Type) {
|
||||||
|
this._metadataResolver.clearCacheFor(compType);
|
||||||
|
this._compiledHostTemplateCache.delete(compType);
|
||||||
|
var compiledTemplate = this._compiledTemplateCache.get(compType);
|
||||||
|
if (compiledTemplate) {
|
||||||
|
this._templateNormalizer.clearCacheFor(compiledTemplate.normalizedCompMeta);
|
||||||
|
this._compiledTemplateCache.delete(compType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache(): void {
|
clearCache(): void {
|
||||||
this._styleCache.clear();
|
this._metadataResolver.clearCache();
|
||||||
this._compiledTemplateCache.clear();
|
this._compiledTemplateCache.clear();
|
||||||
this._hostCacheKeys.clear();
|
this._compiledHostTemplateCache.clear();
|
||||||
|
this._templateNormalizer.clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadAndCompileHostComponent(componentType: Type): CompileHostTemplate {
|
private _getCompiledHostTemplate(type: Type): CompiledTemplate {
|
||||||
var compMeta: CompileDirectiveMetadata =
|
var compiledTemplate = this._compiledHostTemplateCache.get(type);
|
||||||
this._metadataResolver.getDirectiveMetadata(componentType);
|
|
||||||
var hostCacheKey = this._hostCacheKeys.get(compMeta.type.runtime);
|
|
||||||
if (isBlank(hostCacheKey)) {
|
|
||||||
hostCacheKey = new Object();
|
|
||||||
this._hostCacheKeys.set(compMeta.type.runtime, hostCacheKey);
|
|
||||||
assertComponent(compMeta);
|
|
||||||
var hostMeta: CompileDirectiveMetadata =
|
|
||||||
createHostComponentMeta(compMeta.type, compMeta.selector);
|
|
||||||
|
|
||||||
this._loadAndCompileComponent(hostCacheKey, hostMeta, [compMeta], [], []);
|
|
||||||
}
|
|
||||||
var compTemplate = this._compiledTemplateCache.get(hostCacheKey);
|
|
||||||
return new CompileHostTemplate(compTemplate, compMeta);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _loadAndCompileComponent(
|
|
||||||
cacheKey: any, compMeta: CompileDirectiveMetadata, viewDirectives: CompileDirectiveMetadata[],
|
|
||||||
pipes: CompilePipeMetadata[], compilingComponentsPath: any[]): CompiledTemplate {
|
|
||||||
var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
|
|
||||||
if (isBlank(compiledTemplate)) {
|
if (isBlank(compiledTemplate)) {
|
||||||
let done =
|
var compMeta = this._metadataResolver.getDirectiveMetadata(type);
|
||||||
PromiseWrapper
|
assertComponent(compMeta);
|
||||||
.all([<any>this._compileComponentStyles(compMeta)].concat(viewDirectives.map(
|
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
dirMeta => this._templateNormalizer.normalizeDirective(dirMeta))))
|
compiledTemplate = new CompiledTemplate(
|
||||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
true, compMeta.selector, compMeta.type, [], [type], [], [],
|
||||||
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
this._templateNormalizer.normalizeDirective(hostMeta));
|
||||||
var styles = stylesAndNormalizedViewDirMetas[0];
|
this._compiledHostTemplateCache.set(type, compiledTemplate);
|
||||||
var parsedTemplate = this._templateParser.parse(
|
|
||||||
compMeta, compMeta.template.template, normalizedViewDirMetas, pipes,
|
|
||||||
compMeta.type.name);
|
|
||||||
|
|
||||||
var childPromises: Promise<any>[] = [];
|
|
||||||
compiledTemplate.init(this._compileComponent(
|
|
||||||
compMeta, parsedTemplate, styles, pipes, compilingComponentsPath,
|
|
||||||
childPromises));
|
|
||||||
return PromiseWrapper.all(childPromises).then((_) => { return compiledTemplate; });
|
|
||||||
});
|
|
||||||
compiledTemplate = new CompiledTemplate(done);
|
|
||||||
this._compiledTemplateCache.set(cacheKey, compiledTemplate);
|
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileComponent(
|
private _getCompiledTemplate(type: Type): CompiledTemplate {
|
||||||
compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[], styles: string[],
|
var compiledTemplate = this._compiledTemplateCache.get(type);
|
||||||
pipes: CompilePipeMetadata[], compilingComponentsPath: any[],
|
if (isBlank(compiledTemplate)) {
|
||||||
childPromises: Promise<any>[]): Function {
|
var compMeta = this._metadataResolver.getDirectiveMetadata(type);
|
||||||
var compileResult = this._viewCompiler.compileComponent(
|
assertComponent(compMeta);
|
||||||
compMeta, parsedTemplate,
|
var viewDirectives: CompileDirectiveMetadata[] = [];
|
||||||
new ir.ExternalExpr(new CompileIdentifierMetadata({runtime: styles})), pipes);
|
var viewComponentTypes: Type[] = [];
|
||||||
compileResult.dependencies.forEach((dep) => {
|
this._metadataResolver.getViewDirectivesMetadata(type).forEach(dirOrComp => {
|
||||||
if (dep instanceof ViewFactoryDependency) {
|
if (dirOrComp.isComponent) {
|
||||||
let childCompilingComponentsPath = ListWrapper.clone(compilingComponentsPath);
|
viewComponentTypes.push(dirOrComp.type.runtime);
|
||||||
let childCacheKey = dep.comp.type.runtime;
|
} else {
|
||||||
let childViewDirectives: CompileDirectiveMetadata[] =
|
viewDirectives.push(dirOrComp);
|
||||||
this._metadataResolver.getViewDirectivesMetadata(dep.comp.type.runtime);
|
|
||||||
let childViewPipes: CompilePipeMetadata[] =
|
|
||||||
this._metadataResolver.getViewPipesMetadata(dep.comp.type.runtime);
|
|
||||||
let childIsRecursive = childCompilingComponentsPath.indexOf(childCacheKey) > -1;
|
|
||||||
childCompilingComponentsPath.push(childCacheKey);
|
|
||||||
|
|
||||||
let childComp = this._loadAndCompileComponent(
|
|
||||||
dep.comp.type.runtime, dep.comp, childViewDirectives, childViewPipes,
|
|
||||||
childCompilingComponentsPath);
|
|
||||||
dep.placeholder.runtime = childComp.proxyViewFactory;
|
|
||||||
dep.placeholder.name = `viewFactory_${dep.comp.type.name}`;
|
|
||||||
if (!childIsRecursive) {
|
|
||||||
// Only wait for a child if it is not a cycle
|
|
||||||
childPromises.push(childComp.done);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
var precompileComponentTypes = compMeta.precompile.map((typeMeta) => typeMeta.runtime);
|
||||||
|
var pipes = this._metadataResolver.getViewPipesMetadata(type);
|
||||||
|
compiledTemplate = new CompiledTemplate(
|
||||||
|
false, compMeta.selector, compMeta.type, viewDirectives, viewComponentTypes,
|
||||||
|
precompileComponentTypes, pipes, this._templateNormalizer.normalizeDirective(compMeta));
|
||||||
|
this._compiledTemplateCache.set(type, compiledTemplate);
|
||||||
|
}
|
||||||
|
return compiledTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getTransitiveCompiledTemplates(
|
||||||
|
compType: Type, isHost: boolean,
|
||||||
|
target: Set<CompiledTemplate> = new Set<CompiledTemplate>()): Set<CompiledTemplate> {
|
||||||
|
var template =
|
||||||
|
isHost ? this._getCompiledHostTemplate(compType) : this._getCompiledTemplate(compType);
|
||||||
|
if (!target.has(template)) {
|
||||||
|
target.add(template);
|
||||||
|
template.viewComponentTypes.forEach(
|
||||||
|
(compType) => { this._getTransitiveCompiledTemplates(compType, false, target); });
|
||||||
|
template.precompileHostComponentTypes.forEach(
|
||||||
|
(compType) => { this._getTransitiveCompiledTemplates(compType, true, target); });
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileTemplate(template: CompiledTemplate) {
|
||||||
|
if (template.isCompiled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var compMeta = template.normalizedCompMeta;
|
||||||
|
var externalStylesheetsByModuleUrl = new Map<string, CompiledStylesheet>();
|
||||||
|
var stylesCompileResult = this._styleCompiler.compileComponent(compMeta);
|
||||||
|
stylesCompileResult.externalStylesheets.forEach(
|
||||||
|
(r) => { externalStylesheetsByModuleUrl.set(r.meta.moduleUrl, r); });
|
||||||
|
this._resolveStylesCompileResult(
|
||||||
|
stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl);
|
||||||
|
var viewCompMetas = template.viewComponentTypes.map(
|
||||||
|
(compType) => this._getCompiledTemplate(compType).normalizedCompMeta);
|
||||||
|
var parsedTemplate = this._templateParser.parse(
|
||||||
|
compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas),
|
||||||
|
template.viewPipes, compMeta.type.name);
|
||||||
|
var compileResult = this._viewCompiler.compileComponent(
|
||||||
|
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
|
||||||
|
template.viewPipes);
|
||||||
|
var depTemplates = compileResult.dependencies.map((dep) => {
|
||||||
|
let depTemplate: CompiledTemplate;
|
||||||
|
if (dep instanceof ViewFactoryDependency) {
|
||||||
|
depTemplate = this._getCompiledTemplate(dep.comp.runtime);
|
||||||
|
dep.placeholder.runtime = depTemplate.proxyViewFactory;
|
||||||
|
dep.placeholder.name = `viewFactory_${dep.comp.name}`;
|
||||||
} else if (dep instanceof ComponentFactoryDependency) {
|
} else if (dep instanceof ComponentFactoryDependency) {
|
||||||
let childComp = this._loadAndCompileHostComponent(dep.comp.runtime);
|
depTemplate = this._getCompiledHostTemplate(dep.comp.runtime);
|
||||||
dep.placeholder.runtime = childComp.componentFactory;
|
dep.placeholder.runtime = depTemplate.proxyComponentFactory;
|
||||||
dep.placeholder.name = `compFactory_${dep.comp.name}`;
|
dep.placeholder.name = `compFactory_${dep.comp.name}`;
|
||||||
childPromises.push(childComp.done);
|
|
||||||
}
|
}
|
||||||
|
return depTemplate;
|
||||||
});
|
});
|
||||||
|
var statements =
|
||||||
|
stylesCompileResult.componentStylesheet.statements.concat(compileResult.statements);
|
||||||
var factory: any;
|
var factory: any;
|
||||||
if (IS_DART || !this._genConfig.useJit) {
|
if (IS_DART || !this._genConfig.useJit) {
|
||||||
factory = interpretStatements(
|
factory = interpretStatements(
|
||||||
compileResult.statements, compileResult.viewFactoryVar,
|
statements, compileResult.viewFactoryVar, new InterpretiveAppViewInstanceFactory());
|
||||||
new InterpretiveAppViewInstanceFactory());
|
|
||||||
} else {
|
} else {
|
||||||
factory = jitStatements(
|
factory = jitStatements(
|
||||||
`${compMeta.type.name}.template.js`, compileResult.statements,
|
`${template.compType.name}.template.js`, statements, compileResult.viewFactoryVar);
|
||||||
compileResult.viewFactoryVar);
|
|
||||||
}
|
}
|
||||||
return factory;
|
template.compiled(factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileComponentStyles(compMeta: CompileDirectiveMetadata): Promise<string[]> {
|
private _resolveStylesCompileResult(
|
||||||
var compileResult = this._styleCompiler.compileComponent(compMeta);
|
result: CompiledStylesheet, externalStylesheetsByModuleUrl: Map<string, CompiledStylesheet>) {
|
||||||
return this._resolveStylesCompileResult(compMeta.type.name, compileResult);
|
result.dependencies.forEach((dep, i) => {
|
||||||
|
var nestedCompileResult = externalStylesheetsByModuleUrl.get(dep.moduleUrl);
|
||||||
|
var nestedStylesArr = this._resolveAndEvalStylesCompileResult(
|
||||||
|
nestedCompileResult, externalStylesheetsByModuleUrl);
|
||||||
|
dep.valuePlaceholder.runtime = nestedStylesArr;
|
||||||
|
dep.valuePlaceholder.name = `importedStyles${i}`;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resolveStylesCompileResult(sourceUrl: string, result: StylesCompileResult):
|
private _resolveAndEvalStylesCompileResult(
|
||||||
Promise<string[]> {
|
result: CompiledStylesheet,
|
||||||
var promises = result.dependencies.map((dep) => this._loadStylesheetDep(dep));
|
externalStylesheetsByModuleUrl: Map<string, CompiledStylesheet>): string[] {
|
||||||
return PromiseWrapper.all(promises)
|
this._resolveStylesCompileResult(result, externalStylesheetsByModuleUrl);
|
||||||
.then((cssTexts) => {
|
if (IS_DART || !this._genConfig.useJit) {
|
||||||
var nestedCompileResultPromises: Promise<string[]>[] = [];
|
return interpretStatements(
|
||||||
for (var i = 0; i < result.dependencies.length; i++) {
|
result.statements, result.stylesVar, new InterpretiveAppViewInstanceFactory());
|
||||||
var dep = result.dependencies[i];
|
} else {
|
||||||
var cssText = cssTexts[i];
|
return jitStatements(`${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar);
|
||||||
var nestedCompileResult =
|
|
||||||
this._styleCompiler.compileStylesheet(dep.moduleUrl, cssText, dep.isShimmed);
|
|
||||||
nestedCompileResultPromises.push(
|
|
||||||
this._resolveStylesCompileResult(dep.moduleUrl, nestedCompileResult));
|
|
||||||
}
|
|
||||||
return PromiseWrapper.all(nestedCompileResultPromises);
|
|
||||||
})
|
|
||||||
.then((nestedStylesArr) => {
|
|
||||||
for (var i = 0; i < result.dependencies.length; i++) {
|
|
||||||
var dep = result.dependencies[i];
|
|
||||||
dep.valuePlaceholder.runtime = nestedStylesArr[i];
|
|
||||||
dep.valuePlaceholder.name = `importedStyles${i}`;
|
|
||||||
}
|
|
||||||
if (IS_DART || !this._genConfig.useJit) {
|
|
||||||
return interpretStatements(
|
|
||||||
result.statements, result.stylesVar, new InterpretiveAppViewInstanceFactory());
|
|
||||||
} else {
|
|
||||||
return jitStatements(`${sourceUrl}.css.js`, result.statements, result.stylesVar);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _loadStylesheetDep(dep: StylesCompileDependency): Promise<string> {
|
|
||||||
var cacheKey = `${dep.moduleUrl}${dep.isShimmed ? '.shim' : ''}`;
|
|
||||||
var cssTextPromise = this._styleCache.get(cacheKey);
|
|
||||||
if (isBlank(cssTextPromise)) {
|
|
||||||
cssTextPromise = this._xhr.get(dep.moduleUrl);
|
|
||||||
this._styleCache.set(cacheKey, cssTextPromise);
|
|
||||||
}
|
}
|
||||||
return cssTextPromise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CompileHostTemplate {
|
|
||||||
componentFactory: ComponentFactory<any>;
|
|
||||||
done: Promise<ComponentFactory<any>>;
|
|
||||||
constructor(_template: CompiledTemplate, compMeta: CompileDirectiveMetadata) {
|
|
||||||
this.componentFactory = new ComponentFactory<any>(
|
|
||||||
compMeta.selector, _template.proxyViewFactory, compMeta.type.runtime);
|
|
||||||
this.done = _template.done.then((_) => this.componentFactory);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CompiledTemplate {
|
class CompiledTemplate {
|
||||||
private _viewFactory: Function = null;
|
private _viewFactory: Function = null;
|
||||||
proxyViewFactory: Function;
|
proxyViewFactory: Function;
|
||||||
constructor(public done: Promise<CompiledTemplate>) {
|
proxyComponentFactory: ComponentFactory<any>;
|
||||||
this.proxyViewFactory =
|
loading: Promise<any> = null;
|
||||||
(viewUtils: any /** TODO #9100 */, childInjector: any /** TODO #9100 */,
|
private _normalizedCompMeta: CompileDirectiveMetadata = null;
|
||||||
contextEl: any /** TODO #9100 */) =>
|
isCompiled = false;
|
||||||
this._viewFactory(viewUtils, childInjector, contextEl);
|
isCompiledWithDeps = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
|
||||||
|
public viewDirectives: CompileDirectiveMetadata[], public viewComponentTypes: Type[],
|
||||||
|
public precompileHostComponentTypes: Type[], public viewPipes: CompilePipeMetadata[],
|
||||||
|
private _normalizeResult: NormalizeDirectiveResult) {
|
||||||
|
this.proxyViewFactory = (...args: any[]) => this._viewFactory.apply(null, args);
|
||||||
|
this.proxyComponentFactory = isHost ?
|
||||||
|
new ComponentFactory<any>(selector, this.proxyViewFactory, compType.runtime) :
|
||||||
|
null;
|
||||||
|
if (_normalizeResult.syncResult) {
|
||||||
|
this._normalizedCompMeta = _normalizeResult.syncResult;
|
||||||
|
} else {
|
||||||
|
this.loading = _normalizeResult.asyncResult.then((normalizedCompMeta) => {
|
||||||
|
this._normalizedCompMeta = normalizedCompMeta;
|
||||||
|
this.loading = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(viewFactory: Function) { this._viewFactory = viewFactory; }
|
get normalizedCompMeta(): CompileDirectiveMetadata {
|
||||||
|
if (this.loading) {
|
||||||
|
throw new BaseException(`Template is still loading for ${this.compType.name}!`);
|
||||||
|
}
|
||||||
|
return this._normalizedCompMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
compiled(viewFactory: Function) {
|
||||||
|
this._viewFactory = viewFactory;
|
||||||
|
this.isCompiled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
depsCompiled() { this.isCompiledWithDeps = true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertComponent(meta: CompileDirectiveMetadata) {
|
function assertComponent(meta: CompileDirectiveMetadata) {
|
||||||
|
@ -10,7 +10,7 @@ import {Injectable, ViewEncapsulation} from '@angular/core';
|
|||||||
|
|
||||||
import {isPresent} from '../src/facade/lang';
|
import {isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileStylesheetMetadata} from './compile_metadata';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
import {ShadowCss} from './shadow_css';
|
import {ShadowCss} from './shadow_css';
|
||||||
import {extractStyleUrls} from './style_url_resolver';
|
import {extractStyleUrls} from './style_url_resolver';
|
||||||
@ -27,9 +27,16 @@ export class StylesCompileDependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class StylesCompileResult {
|
export class StylesCompileResult {
|
||||||
|
constructor(
|
||||||
|
public componentStylesheet: CompiledStylesheet,
|
||||||
|
public externalStylesheets: CompiledStylesheet[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CompiledStylesheet {
|
||||||
constructor(
|
constructor(
|
||||||
public statements: o.Statement[], public stylesVar: string,
|
public statements: o.Statement[], public stylesVar: string,
|
||||||
public dependencies: StylesCompileDependency[]) {}
|
public dependencies: StylesCompileDependency[], public isShimmed: boolean,
|
||||||
|
public meta: CompileStylesheetMetadata) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -40,35 +47,41 @@ export class StyleCompiler {
|
|||||||
|
|
||||||
compileComponent(comp: CompileDirectiveMetadata): StylesCompileResult {
|
compileComponent(comp: CompileDirectiveMetadata): StylesCompileResult {
|
||||||
var shim = comp.template.encapsulation === ViewEncapsulation.Emulated;
|
var shim = comp.template.encapsulation === ViewEncapsulation.Emulated;
|
||||||
return this._compileStyles(
|
var externalStylesheets: CompiledStylesheet[] = [];
|
||||||
getStylesVarName(comp), comp.template.styles, comp.template.styleUrls, shim);
|
var componentStylesheet: CompiledStylesheet = this._compileStyles(
|
||||||
}
|
comp, new CompileStylesheetMetadata({
|
||||||
|
styles: comp.template.styles,
|
||||||
compileStylesheet(stylesheetUrl: string, cssText: string, isShimmed: boolean):
|
styleUrls: comp.template.styleUrls,
|
||||||
StylesCompileResult {
|
moduleUrl: comp.type.moduleUrl
|
||||||
var styleWithImports = extractStyleUrls(this._urlResolver, stylesheetUrl, cssText);
|
}),
|
||||||
return this._compileStyles(
|
true);
|
||||||
getStylesVarName(null), [styleWithImports.style], styleWithImports.styleUrls, isShimmed);
|
comp.template.externalStylesheets.forEach((stylesheetMeta) => {
|
||||||
|
var compiledStylesheet = this._compileStyles(comp, stylesheetMeta, false);
|
||||||
|
externalStylesheets.push(compiledStylesheet);
|
||||||
|
});
|
||||||
|
return new StylesCompileResult(componentStylesheet, externalStylesheets);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileStyles(
|
private _compileStyles(
|
||||||
stylesVar: string, plainStyles: string[], absUrls: string[],
|
comp: CompileDirectiveMetadata, stylesheet: CompileStylesheetMetadata,
|
||||||
shim: boolean): StylesCompileResult {
|
isComponentStylesheet: boolean): CompiledStylesheet {
|
||||||
|
var shim = comp.template.encapsulation === ViewEncapsulation.Emulated;
|
||||||
var styleExpressions =
|
var styleExpressions =
|
||||||
plainStyles.map(plainStyle => o.literal(this._shimIfNeeded(plainStyle, shim)));
|
stylesheet.styles.map(plainStyle => o.literal(this._shimIfNeeded(plainStyle, shim)));
|
||||||
var dependencies: StylesCompileDependency[] = [];
|
var dependencies: StylesCompileDependency[] = [];
|
||||||
for (var i = 0; i < absUrls.length; i++) {
|
for (var i = 0; i < stylesheet.styleUrls.length; i++) {
|
||||||
var identifier = new CompileIdentifierMetadata({name: getStylesVarName(null)});
|
var identifier = new CompileIdentifierMetadata({name: getStylesVarName(null)});
|
||||||
dependencies.push(new StylesCompileDependency(absUrls[i], shim, identifier));
|
dependencies.push(new StylesCompileDependency(stylesheet.styleUrls[i], shim, identifier));
|
||||||
styleExpressions.push(new o.ExternalExpr(identifier));
|
styleExpressions.push(new o.ExternalExpr(identifier));
|
||||||
}
|
}
|
||||||
// styles variable contains plain strings and arrays of other styles arrays (recursive),
|
// styles variable contains plain strings and arrays of other styles arrays (recursive),
|
||||||
// so we set its type to dynamic.
|
// so we set its type to dynamic.
|
||||||
|
var stylesVar = getStylesVarName(isComponentStylesheet ? comp : null);
|
||||||
var stmt = o.variable(stylesVar)
|
var stmt = o.variable(stylesVar)
|
||||||
.set(o.literalArr(
|
.set(o.literalArr(
|
||||||
styleExpressions, new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])))
|
styleExpressions, new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])))
|
||||||
.toDeclStmt(null, [o.StmtModifier.Final]);
|
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||||
return new StylesCompileResult([stmt], stylesVar, dependencies);
|
return new CompiledStylesheet([stmt], stylesVar, dependencies, shim, stylesheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _shimIfNeeded(style: string, shim: boolean): string {
|
private _shimIfNeeded(style: string, shim: boolean): string {
|
||||||
@ -78,7 +91,7 @@ export class StyleCompiler {
|
|||||||
|
|
||||||
function getStylesVarName(component: CompileDirectiveMetadata): string {
|
function getStylesVarName(component: CompileDirectiveMetadata): string {
|
||||||
var result = `styles`;
|
var result = `styles`;
|
||||||
if (isPresent(component)) {
|
if (component) {
|
||||||
result += `_${component.type.name}`;
|
result += `_${component.type.name}`;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -32,7 +32,7 @@ var rootSelectorVar = o.variable('rootSelector');
|
|||||||
|
|
||||||
export class ViewFactoryDependency {
|
export class ViewFactoryDependency {
|
||||||
constructor(
|
constructor(
|
||||||
public comp: CompileDirectiveMetadata, public placeholder: CompileIdentifierMetadata) {}
|
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ComponentFactoryDependency {
|
export class ComponentFactoryDependency {
|
||||||
@ -208,7 +208,8 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
if (isPresent(component)) {
|
if (isPresent(component)) {
|
||||||
let nestedComponentIdentifier =
|
let nestedComponentIdentifier =
|
||||||
new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)});
|
new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)});
|
||||||
this.targetDependencies.push(new ViewFactoryDependency(component, nestedComponentIdentifier));
|
this.targetDependencies.push(
|
||||||
|
new ViewFactoryDependency(component.type, nestedComponentIdentifier));
|
||||||
let precompileComponentIdentifiers =
|
let precompileComponentIdentifiers =
|
||||||
component.precompile.map((precompileComp: CompileIdentifierMetadata) => {
|
component.precompile.map((precompileComp: CompileIdentifierMetadata) => {
|
||||||
var id = new CompileIdentifierMetadata({name: precompileComp.name});
|
var id = new CompileIdentifierMetadata({name: precompileComp.name});
|
||||||
|
@ -19,24 +19,9 @@ import {Map} from '../src/facade/collection';
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ViewResolver {
|
export class ViewResolver {
|
||||||
/** @internal */
|
|
||||||
_cache = new Map<Type, ViewMetadata>();
|
|
||||||
|
|
||||||
constructor(private _reflector: ReflectorReader = reflector) {}
|
constructor(private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
resolve(component: Type): ViewMetadata {
|
resolve(component: Type): ViewMetadata {
|
||||||
var view = this._cache.get(component);
|
|
||||||
|
|
||||||
if (isBlank(view)) {
|
|
||||||
view = this._resolve(component);
|
|
||||||
this._cache.set(component, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_resolve(component: Type): ViewMetadata {
|
|
||||||
var compMeta: ComponentMetadata;
|
var compMeta: ComponentMetadata;
|
||||||
|
|
||||||
this._reflector.annotations(component).forEach(m => {
|
this._reflector.annotations(component).forEach(m => {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata';
|
import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata';
|
||||||
import {CompilerConfig} from '@angular/compiler/src/config';
|
import {CompilerConfig} from '@angular/compiler/src/config';
|
||||||
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
|
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
|
||||||
import {XHR} from '@angular/compiler/src/xhr';
|
import {XHR} from '@angular/compiler/src/xhr';
|
||||||
@ -15,6 +15,7 @@ import {ViewEncapsulation} from '@angular/core/src/metadata/view';
|
|||||||
import {beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
|
import {SpyXHR} from './spies';
|
||||||
import {TEST_PROVIDERS} from './test_bindings';
|
import {TEST_PROVIDERS} from './test_bindings';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -30,176 +31,230 @@ export function main() {
|
|||||||
new CompileTypeMetadata({moduleUrl: 'http://some/module/a.js', name: 'SomeComp'});
|
new CompileTypeMetadata({moduleUrl: 'http://some/module/a.js', name: 'SomeComp'});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('loadTemplate', () => {
|
describe('normalizeDirective', () => {
|
||||||
describe('inline template', () => {
|
|
||||||
it('should store the template',
|
|
||||||
inject(
|
|
||||||
[AsyncTestCompleter, DirectiveNormalizer],
|
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer) => {
|
|
||||||
normalizer
|
|
||||||
.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
|
||||||
encapsulation: null,
|
|
||||||
template: 'a',
|
|
||||||
templateUrl: null,
|
|
||||||
styles: [],
|
|
||||||
styleUrls: ['test.css']
|
|
||||||
}))
|
|
||||||
.then((template: CompileTemplateMetadata) => {
|
|
||||||
expect(template.template).toEqual('a');
|
|
||||||
expect(template.templateUrl).toEqual('package:some/module/a.js');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should resolve styles on the annotation against the moduleUrl',
|
|
||||||
inject(
|
|
||||||
[AsyncTestCompleter, DirectiveNormalizer],
|
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer) => {
|
|
||||||
normalizer
|
|
||||||
.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
|
||||||
encapsulation: null,
|
|
||||||
template: '',
|
|
||||||
templateUrl: null,
|
|
||||||
styles: [],
|
|
||||||
styleUrls: ['test.css']
|
|
||||||
}))
|
|
||||||
.then((template: CompileTemplateMetadata) => {
|
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should resolve styles in the template against the moduleUrl',
|
|
||||||
inject(
|
|
||||||
[AsyncTestCompleter, DirectiveNormalizer],
|
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer) => {
|
|
||||||
normalizer
|
|
||||||
.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
|
||||||
encapsulation: null,
|
|
||||||
template: '<style>@import test.css</style>',
|
|
||||||
templateUrl: null,
|
|
||||||
styles: [],
|
|
||||||
styleUrls: []
|
|
||||||
}))
|
|
||||||
.then((template: CompileTemplateMetadata) => {
|
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should use ViewEncapsulation.Emulated by default',
|
|
||||||
inject(
|
|
||||||
[AsyncTestCompleter, DirectiveNormalizer],
|
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer) => {
|
|
||||||
normalizer
|
|
||||||
.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
|
||||||
encapsulation: null,
|
|
||||||
template: '',
|
|
||||||
templateUrl: null,
|
|
||||||
styles: [],
|
|
||||||
styleUrls: ['test.css']
|
|
||||||
}))
|
|
||||||
.then((template: CompileTemplateMetadata) => {
|
|
||||||
expect(template.encapsulation).toEqual(ViewEncapsulation.Emulated);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should use default encapsulation provided by CompilerConfig',
|
|
||||||
inject(
|
|
||||||
[AsyncTestCompleter, CompilerConfig, DirectiveNormalizer],
|
|
||||||
(async: AsyncTestCompleter, config: CompilerConfig,
|
|
||||||
normalizer: DirectiveNormalizer) => {
|
|
||||||
config.defaultEncapsulation = ViewEncapsulation.None;
|
|
||||||
normalizer
|
|
||||||
.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
|
||||||
encapsulation: null,
|
|
||||||
template: '',
|
|
||||||
templateUrl: null,
|
|
||||||
styles: [],
|
|
||||||
styleUrls: ['test.css']
|
|
||||||
}))
|
|
||||||
.then((template: CompileTemplateMetadata) => {
|
|
||||||
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('templateUrl', () => {
|
|
||||||
|
|
||||||
it('should load a template from a url that is resolved against moduleUrl',
|
|
||||||
inject(
|
|
||||||
[AsyncTestCompleter, DirectiveNormalizer, XHR],
|
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
|
|
||||||
xhr.expect('package:some/module/sometplurl.html', 'a');
|
|
||||||
normalizer
|
|
||||||
.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
|
||||||
encapsulation: null,
|
|
||||||
template: null,
|
|
||||||
templateUrl: 'sometplurl.html',
|
|
||||||
styles: [],
|
|
||||||
styleUrls: ['test.css']
|
|
||||||
}))
|
|
||||||
.then((template: CompileTemplateMetadata) => {
|
|
||||||
expect(template.template).toEqual('a');
|
|
||||||
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should resolve styles on the annotation against the moduleUrl',
|
|
||||||
inject(
|
|
||||||
[AsyncTestCompleter, DirectiveNormalizer, XHR],
|
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
|
|
||||||
xhr.expect('package:some/module/tpl/sometplurl.html', '');
|
|
||||||
normalizer
|
|
||||||
.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
|
||||||
encapsulation: null,
|
|
||||||
template: null,
|
|
||||||
templateUrl: 'tpl/sometplurl.html',
|
|
||||||
styles: [],
|
|
||||||
styleUrls: ['test.css']
|
|
||||||
}))
|
|
||||||
.then((template: CompileTemplateMetadata) => {
|
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should resolve styles in the template against the templateUrl',
|
|
||||||
inject(
|
|
||||||
[AsyncTestCompleter, DirectiveNormalizer, XHR],
|
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
|
|
||||||
xhr.expect(
|
|
||||||
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
|
|
||||||
normalizer
|
|
||||||
.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
|
||||||
encapsulation: null,
|
|
||||||
template: null,
|
|
||||||
templateUrl: 'tpl/sometplurl.html',
|
|
||||||
styles: [],
|
|
||||||
styleUrls: []
|
|
||||||
}))
|
|
||||||
.then((template: CompileTemplateMetadata) => {
|
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw if no template was specified',
|
it('should throw if no template was specified',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
expect(
|
expect(() => normalizer.normalizeDirective(new CompileDirectiveMetadata({
|
||||||
() => normalizer.normalizeTemplate(
|
type: dirType,
|
||||||
dirType,
|
isComponent: true,
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []})))
|
template:
|
||||||
.toThrowError('No template specified for component SomeComp');
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []})
|
||||||
|
}))).toThrowError('No template specified for component SomeComp');
|
||||||
}));
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('normalizeTemplateSync', () => {
|
||||||
|
it('should store the template',
|
||||||
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
|
let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: 'a',
|
||||||
|
templateUrl: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
}))
|
||||||
|
expect(template.template).toEqual('a');
|
||||||
|
expect(template.templateUrl).toEqual('package:some/module/a.js');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve styles on the annotation against the moduleUrl',
|
||||||
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
|
let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: '',
|
||||||
|
templateUrl: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
}))
|
||||||
|
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve styles in the template against the moduleUrl',
|
||||||
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
|
let template =
|
||||||
|
normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: '<style>@import test.css</style>',
|
||||||
|
templateUrl: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
}))
|
||||||
|
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should use ViewEncapsulation.Emulated by default',
|
||||||
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
|
let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: '',
|
||||||
|
templateUrl: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
}))
|
||||||
|
expect(template.encapsulation).toEqual(ViewEncapsulation.Emulated);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should use default encapsulation provided by CompilerConfig',
|
||||||
|
inject(
|
||||||
|
[CompilerConfig, DirectiveNormalizer],
|
||||||
|
(config: CompilerConfig, normalizer: DirectiveNormalizer) => {
|
||||||
|
config.defaultEncapsulation = ViewEncapsulation.None;
|
||||||
|
let template =
|
||||||
|
normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: '',
|
||||||
|
templateUrl: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
}))
|
||||||
|
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('templateUrl', () => {
|
||||||
|
|
||||||
|
it('should load a template from a url that is resolved against moduleUrl',
|
||||||
|
inject(
|
||||||
|
[AsyncTestCompleter, DirectiveNormalizer, XHR],
|
||||||
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
|
||||||
|
xhr.expect('package:some/module/sometplurl.html', 'a');
|
||||||
|
normalizer
|
||||||
|
.normalizeTemplateAsync(dirType, new CompileTemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: null,
|
||||||
|
templateUrl: 'sometplurl.html',
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
}))
|
||||||
|
.then((template: CompileTemplateMetadata) => {
|
||||||
|
expect(template.template).toEqual('a');
|
||||||
|
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve styles on the annotation against the moduleUrl',
|
||||||
|
inject(
|
||||||
|
[AsyncTestCompleter, DirectiveNormalizer, XHR],
|
||||||
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
|
||||||
|
xhr.expect('package:some/module/tpl/sometplurl.html', '');
|
||||||
|
normalizer
|
||||||
|
.normalizeTemplateAsync(dirType, new CompileTemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: null,
|
||||||
|
templateUrl: 'tpl/sometplurl.html',
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
}))
|
||||||
|
.then((template: CompileTemplateMetadata) => {
|
||||||
|
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve styles in the template against the templateUrl',
|
||||||
|
inject(
|
||||||
|
[AsyncTestCompleter, DirectiveNormalizer, XHR],
|
||||||
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
|
||||||
|
xhr.expect(
|
||||||
|
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
|
||||||
|
normalizer
|
||||||
|
.normalizeTemplateAsync(dirType, new CompileTemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: null,
|
||||||
|
templateUrl: 'tpl/sometplurl.html',
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
}))
|
||||||
|
.then((template: CompileTemplateMetadata) => {
|
||||||
|
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('normalizeExternalStylesheets', () => {
|
||||||
|
|
||||||
|
beforeEachProviders(() => [{provide: XHR, useClass: SpyXHR}]);
|
||||||
|
|
||||||
|
it('should load an external stylesheet',
|
||||||
|
inject(
|
||||||
|
[AsyncTestCompleter, DirectiveNormalizer, XHR],
|
||||||
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: SpyXHR) => {
|
||||||
|
programXhrSpy(xhr, {'package:some/module/test.css': 'a'});
|
||||||
|
normalizer
|
||||||
|
.normalizeExternalStylesheets(new CompileTemplateMetadata({
|
||||||
|
template: '',
|
||||||
|
templateUrl: '',
|
||||||
|
styleUrls: ['package:some/module/test.css']
|
||||||
|
}))
|
||||||
|
.then((template: CompileTemplateMetadata) => {
|
||||||
|
expect(template.externalStylesheets.length).toBe(1);
|
||||||
|
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
|
||||||
|
moduleUrl: 'package:some/module/test.css',
|
||||||
|
styles: ['a'],
|
||||||
|
styleUrls: []
|
||||||
|
}));
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should load stylesheets referenced by external stylesheets',
|
||||||
|
inject(
|
||||||
|
[AsyncTestCompleter, DirectiveNormalizer, XHR],
|
||||||
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: SpyXHR) => {
|
||||||
|
programXhrSpy(xhr, {
|
||||||
|
'package:some/module/test.css': 'a@import "test2.css"',
|
||||||
|
'package:some/module/test2.css': 'b'
|
||||||
|
});
|
||||||
|
normalizer
|
||||||
|
.normalizeExternalStylesheets(new CompileTemplateMetadata({
|
||||||
|
template: '',
|
||||||
|
templateUrl: '',
|
||||||
|
styleUrls: ['package:some/module/test.css']
|
||||||
|
}))
|
||||||
|
.then((template: CompileTemplateMetadata) => {
|
||||||
|
expect(template.externalStylesheets.length).toBe(2);
|
||||||
|
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
|
||||||
|
moduleUrl: 'package:some/module/test.css',
|
||||||
|
styles: ['a'],
|
||||||
|
styleUrls: ['package:some/module/test2.css']
|
||||||
|
}));
|
||||||
|
expect(template.externalStylesheets[1]).toEqual(new CompileStylesheetMetadata({
|
||||||
|
moduleUrl: 'package:some/module/test2.css',
|
||||||
|
styles: ['b'],
|
||||||
|
styleUrls: []
|
||||||
|
}));
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('caching', () => {
|
||||||
|
it('should work for templateUrl',
|
||||||
|
inject(
|
||||||
|
[AsyncTestCompleter, DirectiveNormalizer, XHR],
|
||||||
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
|
||||||
|
xhr.expect('package:some/module/cmp.html', 'a');
|
||||||
|
var templateMeta = new CompileTemplateMetadata({
|
||||||
|
templateUrl: 'cmp.html',
|
||||||
|
});
|
||||||
|
Promise
|
||||||
|
.all([
|
||||||
|
normalizer.normalizeTemplateAsync(dirType, templateMeta),
|
||||||
|
normalizer.normalizeTemplateAsync(dirType, templateMeta)
|
||||||
|
])
|
||||||
|
.then((templates: CompileTemplateMetadata[]) => {
|
||||||
|
expect(templates[0].template).toEqual('a');
|
||||||
|
expect(templates[1].template).toEqual('a');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -369,3 +424,14 @@ export function main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function programXhrSpy(spy: SpyXHR, results: {[key: string]: string}) {
|
||||||
|
spy.spy('get').andCallFake((url: string): Promise<any> => {
|
||||||
|
var result = results[url];
|
||||||
|
if (result) {
|
||||||
|
return Promise.resolve(result);
|
||||||
|
} else {
|
||||||
|
return Promise.reject(`Unknown mock url ${url}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -20,11 +20,11 @@ import {TemplateParser} from '@angular/compiler/src/template_parser';
|
|||||||
import {createOfflineCompileUrlResolver} from '@angular/compiler/src/url_resolver';
|
import {createOfflineCompileUrlResolver} from '@angular/compiler/src/url_resolver';
|
||||||
import {MODULE_SUFFIX} from '@angular/compiler/src/util';
|
import {MODULE_SUFFIX} from '@angular/compiler/src/util';
|
||||||
import {ViewCompiler} from '@angular/compiler/src/view_compiler/view_compiler';
|
import {ViewCompiler} from '@angular/compiler/src/view_compiler/view_compiler';
|
||||||
|
import {XHR} from '@angular/compiler/src/xhr';
|
||||||
|
|
||||||
import {Console} from '../core_private';
|
import {Console} from '../core_private';
|
||||||
import {IS_DART, isPresent, print} from '../src/facade/lang';
|
import {IS_DART, isPresent, print} from '../src/facade/lang';
|
||||||
import {MockSchemaRegistry} from '../testing/schema_registry_mock';
|
import {MockSchemaRegistry} from '../testing/schema_registry_mock';
|
||||||
import {MockXHR} from '../testing/xhr_mock';
|
|
||||||
|
|
||||||
|
|
||||||
export class CompA { user: string; }
|
export class CompA { user: string; }
|
||||||
@ -44,9 +44,8 @@ export var compAMetadata = CompileDirectiveMetadata.create({
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
function _createOfflineCompiler(xhr: MockXHR, emitter: OutputEmitter): OfflineCompiler {
|
function _createOfflineCompiler(xhr: XHR, emitter: OutputEmitter): OfflineCompiler {
|
||||||
var urlResolver = createOfflineCompileUrlResolver();
|
var urlResolver = createOfflineCompileUrlResolver();
|
||||||
xhr.when(`${THIS_MODULE_PATH}/offline_compiler_compa.html`, 'Hello World {{user}}!');
|
|
||||||
var htmlParser = new HtmlParser();
|
var htmlParser = new HtmlParser();
|
||||||
var config = new CompilerConfig({genDebugInfo: true, useJit: true});
|
var config = new CompilerConfig({genDebugInfo: true, useJit: true});
|
||||||
var normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
|
var normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
|
||||||
@ -54,24 +53,25 @@ function _createOfflineCompiler(xhr: MockXHR, emitter: OutputEmitter): OfflineCo
|
|||||||
normalizer,
|
normalizer,
|
||||||
new TemplateParser(
|
new TemplateParser(
|
||||||
new Parser(new Lexer()), new MockSchemaRegistry({}, {}), htmlParser, new Console(), []),
|
new Parser(new Lexer()), new MockSchemaRegistry({}, {}), htmlParser, new Console(), []),
|
||||||
new StyleCompiler(urlResolver), new ViewCompiler(config), emitter, xhr);
|
new StyleCompiler(urlResolver), new ViewCompiler(config), emitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compileComp(
|
export function compileComp(
|
||||||
emitter: OutputEmitter, comp: CompileDirectiveMetadata): Promise<string> {
|
emitter: OutputEmitter, comp: CompileDirectiveMetadata): Promise<string> {
|
||||||
var xhr = new MockXHR();
|
var xhr = new MockXhr();
|
||||||
|
xhr.setResult(`${THIS_MODULE_PATH}/offline_compiler_compa.html`, '');
|
||||||
|
xhr.setResult(`${THIS_MODULE_PATH}/offline_compiler_compa.css`, '');
|
||||||
var compiler = _createOfflineCompiler(xhr, emitter);
|
var compiler = _createOfflineCompiler(xhr, emitter);
|
||||||
var result = compiler.normalizeDirectiveMetadata(comp).then((normComp) => {
|
var result = compiler.normalizeDirectiveMetadata(comp).then((normComp) => {
|
||||||
return compiler.compileTemplates([new NormalizedComponentWithViewDirectives(normComp, [], [])])
|
return compiler
|
||||||
|
.compileTemplates([new NormalizedComponentWithViewDirectives(normComp, [], [])])[0]
|
||||||
.source;
|
.source;
|
||||||
});
|
});
|
||||||
xhr.flush();
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SimpleJsImportGenerator implements ImportGenerator {
|
export class SimpleJsImportGenerator implements ImportGenerator {
|
||||||
getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
|
getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
|
||||||
// var moduleAssetUrl = ImportGenerator.parseAssetUrl(moduleUrlStr);
|
|
||||||
var importedAssetUrl = ImportGenerator.parseAssetUrl(importedUrlStr);
|
var importedAssetUrl = ImportGenerator.parseAssetUrl(importedUrlStr);
|
||||||
if (isPresent(importedAssetUrl)) {
|
if (isPresent(importedAssetUrl)) {
|
||||||
return `${importedAssetUrl.packageName}/${importedAssetUrl.modulePath}`;
|
return `${importedAssetUrl.packageName}/${importedAssetUrl.modulePath}`;
|
||||||
@ -80,3 +80,15 @@ export class SimpleJsImportGenerator implements ImportGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MockXhr implements XHR {
|
||||||
|
results: {[key: string]: string} = {};
|
||||||
|
setResult(url: string, content: string) { this.results[url] = content; }
|
||||||
|
|
||||||
|
get(url: string): Promise<string> {
|
||||||
|
if (url in this.results) {
|
||||||
|
return Promise.resolve(this.results[url]);
|
||||||
|
}
|
||||||
|
return Promise.reject<any>(`Unknown url ${url}`);
|
||||||
|
}
|
||||||
|
}
|
108
modules/@angular/compiler/test/runtime_compiler_spec.ts
Normal file
108
modules/@angular/compiler/test/runtime_compiler_spec.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal';
|
||||||
|
import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector} from '@angular/core';
|
||||||
|
import {ConcreteType, stringify} from '../src/facade/lang';
|
||||||
|
import {fakeAsync, tick, TestComponentBuilder, ComponentFixture} from '@angular/core/testing';
|
||||||
|
import {XHR, ViewResolver} from '@angular/compiler';
|
||||||
|
import {MockViewResolver} from '@angular/compiler/testing';
|
||||||
|
|
||||||
|
import {SpyXHR} from './spies';
|
||||||
|
|
||||||
|
@Component({selector: 'child-cmp', template: 'childComp'})
|
||||||
|
class ChildComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'some-cmp', template: 'someComp'})
|
||||||
|
class SomeComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'some-cmp', templateUrl: './someTpl'})
|
||||||
|
class SomeCompWithUrlTemplate {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('RuntimeCompiler', () => {
|
||||||
|
let compiler: Compiler;
|
||||||
|
let xhr: SpyXHR;
|
||||||
|
let tcb: TestComponentBuilder;
|
||||||
|
let viewResolver: MockViewResolver;
|
||||||
|
let injector: Injector;
|
||||||
|
|
||||||
|
beforeEachProviders(() => [{provide: XHR, useValue: new SpyXHR()}]);
|
||||||
|
|
||||||
|
beforeEach(inject(
|
||||||
|
[Compiler, TestComponentBuilder, XHR, ViewResolver, Injector],
|
||||||
|
(_compiler: Compiler, _tcb: TestComponentBuilder, _xhr: SpyXHR,
|
||||||
|
_viewResolver: MockViewResolver, _injector: Injector) => {
|
||||||
|
compiler = _compiler;
|
||||||
|
tcb = _tcb;
|
||||||
|
xhr = _xhr;
|
||||||
|
viewResolver = _viewResolver;
|
||||||
|
injector = _injector;
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('clearCacheFor', () => {
|
||||||
|
it('should support changing the content of a template referenced via templateUrl',
|
||||||
|
fakeAsync(() => {
|
||||||
|
xhr.spy('get').andCallFake(() => Promise.resolve('init'));
|
||||||
|
let compFixture =
|
||||||
|
tcb.overrideView(SomeComp, new ViewMetadata({templateUrl: '/myComp.html'}))
|
||||||
|
.createFakeAsync(SomeComp);
|
||||||
|
expect(compFixture.nativeElement).toHaveText('init');
|
||||||
|
|
||||||
|
xhr.spy('get').andCallFake(() => Promise.resolve('new content'));
|
||||||
|
// Note: overrideView is calling .clearCacheFor...
|
||||||
|
compFixture = tcb.overrideView(SomeComp, new ViewMetadata({templateUrl: '/myComp.html'}))
|
||||||
|
.createFakeAsync(SomeComp);
|
||||||
|
expect(compFixture.nativeElement).toHaveText('new content');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should support overwriting inline templates', () => {
|
||||||
|
let componentFixture = tcb.createSync(SomeComp);
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('someComp');
|
||||||
|
|
||||||
|
componentFixture = tcb.overrideTemplate(SomeComp, 'test').createSync(SomeComp);
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update existing compilation results', () => {
|
||||||
|
viewResolver.setView(
|
||||||
|
SomeComp,
|
||||||
|
new ViewMetadata({template: '<child-cmp></child-cmp>', directives: [ChildComp]}));
|
||||||
|
viewResolver.setInlineTemplate(ChildComp, 'oldChild');
|
||||||
|
let compFactory = compiler.compileComponentSync(SomeComp);
|
||||||
|
viewResolver.setInlineTemplate(ChildComp, 'newChild');
|
||||||
|
compiler.compileComponentSync(SomeComp);
|
||||||
|
let compRef = compFactory.create(injector);
|
||||||
|
expect(compRef.location.nativeElement).toHaveText('oldChild');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('compileComponentSync', () => {
|
||||||
|
it('should throw when using a templateUrl that has not been compiled before', () => {
|
||||||
|
xhr.spy('get').andCallFake(() => Promise.resolve(''));
|
||||||
|
expect(() => tcb.createSync(SomeCompWithUrlTemplate))
|
||||||
|
.toThrowError(
|
||||||
|
`Can't compile synchronously as ${stringify(SomeCompWithUrlTemplate)} is still being loaded!`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when using a templateUrl in a nested component that has not been compiled before',
|
||||||
|
() => {
|
||||||
|
xhr.spy('get').andCallFake(() => Promise.resolve(''));
|
||||||
|
let localTcb =
|
||||||
|
tcb.overrideView(SomeComp, new ViewMetadata({template: '', directives: [ChildComp]}))
|
||||||
|
.overrideView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'}));
|
||||||
|
expect(() => localTcb.createSync(SomeComp))
|
||||||
|
.toThrowError(
|
||||||
|
`Can't compile synchronously as ${stringify(ChildComp)} is still being loaded!`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to use templateUrl components that have been loaded before',
|
||||||
|
fakeAsync(() => {
|
||||||
|
xhr.spy('get').andCallFake(() => Promise.resolve('hello'));
|
||||||
|
tcb.createFakeAsync(SomeCompWithUrlTemplate);
|
||||||
|
let compFixture = tcb.createSync(SomeCompWithUrlTemplate);
|
||||||
|
expect(compFixture.nativeElement).toHaveText('hello');
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal';
|
import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal';
|
||||||
import {TestComponentBuilder, ComponentFixtureAutoDetect, ComponentFixtureNoNgZone} from '@angular/compiler/testing';
|
import {TestComponentBuilder, ComponentFixtureAutoDetect, ComponentFixtureNoNgZone} from '@angular/compiler/testing';
|
||||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||||
import {Injectable, Component, Input, ViewMetadata, ComponentResolver} from '@angular/core';
|
import {Injectable, Component, Input, ViewMetadata} from '@angular/core';
|
||||||
import {NgIf} from '@angular/common';
|
import {NgIf} from '@angular/common';
|
||||||
import {TimerWrapper} from '../src/facade/async';
|
import {TimerWrapper} from '../src/facade/async';
|
||||||
import {IS_DART} from '../src/facade/lang';
|
import {IS_DART} from '../src/facade/lang';
|
||||||
@ -320,6 +320,15 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should create components synchronously',
|
||||||
|
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
|
|
||||||
|
let componentFixture =
|
||||||
|
tcb.overrideTemplate(MockChildComp, '<span>Mock</span>').createSync(MockChildComp);
|
||||||
|
componentFixture.detectChanges();
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('Mock');
|
||||||
|
}));
|
||||||
|
|
||||||
if (!IS_DART) {
|
if (!IS_DART) {
|
||||||
describe('ComponentFixture', () => {
|
describe('ComponentFixture', () => {
|
||||||
it('should auto detect changes if autoDetectChanges is called',
|
it('should auto detect changes if autoDetectChanges is called',
|
||||||
@ -604,27 +613,6 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createSync', () => {
|
|
||||||
it('should create components',
|
|
||||||
inject(
|
|
||||||
[ComponentResolver, TestComponentBuilder, AsyncTestCompleter],
|
|
||||||
(cr: ComponentResolver, tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
|
||||||
cr.resolveComponent(MyIfComp).then((cmpFactory) => {
|
|
||||||
let componentFixture = tcb.createSync(cmpFactory);
|
|
||||||
|
|
||||||
componentFixture.detectChanges();
|
|
||||||
expect(componentFixture.nativeElement).toHaveText('MyIf()');
|
|
||||||
|
|
||||||
componentFixture.componentInstance.showMore = true;
|
|
||||||
componentFixture.detectChanges();
|
|
||||||
expect(componentFixture.nativeElement).toHaveText('MyIf(More)');
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -6,18 +6,19 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {beforeEach, ddescribe, describe, expect, iit, it,} from '@angular/core/testing/testing_internal';
|
import {beforeEach, ddescribe, describe, expect, iit, it, inject,} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {stringify} from '../src/facade/lang';
|
import {stringify} from '../src/facade/lang';
|
||||||
import {MockViewResolver} from '../testing';
|
import {MockViewResolver} from '../testing';
|
||||||
import {Component, ViewMetadata} from '@angular/core';
|
import {Component, ViewMetadata, Injector} from '@angular/core';
|
||||||
import {isBlank} from '../src/facade/lang';
|
import {isBlank} from '../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('MockViewResolver', () => {
|
describe('MockViewResolver', () => {
|
||||||
var viewResolver: MockViewResolver;
|
var viewResolver: MockViewResolver;
|
||||||
|
|
||||||
beforeEach(() => { viewResolver = new MockViewResolver(); });
|
beforeEach(inject(
|
||||||
|
[Injector], (injector: Injector) => { viewResolver = new MockViewResolver(injector); }));
|
||||||
|
|
||||||
describe('View overriding', () => {
|
describe('View overriding', () => {
|
||||||
it('should fallback to the default ViewResolver when templates are not overridden', () => {
|
it('should fallback to the default ViewResolver when templates are not overridden', () => {
|
||||||
@ -33,13 +34,12 @@ export function main() {
|
|||||||
expect(isBlank(view.directives)).toBe(true);
|
expect(isBlank(view.directives)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow overriding a view after it has been resolved', () => {
|
it('should allow overriding a view after it has been resolved', () => {
|
||||||
viewResolver.resolve(SomeComponent);
|
viewResolver.resolve(SomeComponent);
|
||||||
expect(() => {
|
viewResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'}));
|
||||||
viewResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'}));
|
var view = viewResolver.resolve(SomeComponent);
|
||||||
})
|
expect(view.template).toEqual('overridden template');
|
||||||
.toThrowError(
|
expect(isBlank(view.directives)).toBe(true);
|
||||||
`The component ${stringify(SomeComponent)} has already been compiled, its configuration can not be changed`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -58,11 +58,11 @@ export function main() {
|
|||||||
expect(view.template).toEqual('overridden template x 2');
|
expect(view.template).toEqual('overridden template x 2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow overriding a view after it has been resolved', () => {
|
it('should allow overriding a view after it has been resolved', () => {
|
||||||
viewResolver.resolve(SomeComponent);
|
viewResolver.resolve(SomeComponent);
|
||||||
expect(() => { viewResolver.setInlineTemplate(SomeComponent, 'overridden template'); })
|
viewResolver.setInlineTemplate(SomeComponent, 'overridden template');
|
||||||
.toThrowError(
|
var view = viewResolver.resolve(SomeComponent);
|
||||||
`The component ${stringify(SomeComponent)} has already been compiled, its configuration can not be changed`);
|
expect(view.template).toEqual('overridden template');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -90,13 +90,12 @@ export function main() {
|
|||||||
`Overriden directive ${stringify(SomeOtherDirective)} not found in the template of ${stringify(SomeComponent)}`);
|
`Overriden directive ${stringify(SomeOtherDirective)} not found in the template of ${stringify(SomeComponent)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow overriding a directive after its view has been resolved', () => {
|
it('should allow overriding a directive after its view has been resolved', () => {
|
||||||
viewResolver.resolve(SomeComponent);
|
viewResolver.resolve(SomeComponent);
|
||||||
expect(() => {
|
viewResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
|
||||||
viewResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
|
var view = viewResolver.resolve(SomeComponent);
|
||||||
})
|
expect(view.directives.length).toEqual(1);
|
||||||
.toThrowError(
|
expect(view.directives[0]).toBe(SomeOtherDirective);
|
||||||
`The component ${stringify(SomeComponent)} has already been compiled, its configuration can not be changed`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,13 +6,14 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ComponentMetadata, DirectiveMetadata, Injectable} from '@angular/core';
|
import {Compiler, ComponentMetadata, DirectiveMetadata, Injectable, Injector} from '@angular/core';
|
||||||
|
|
||||||
import {DirectiveResolver} from '../src/directive_resolver';
|
import {DirectiveResolver} from '../src/directive_resolver';
|
||||||
import {Map} from '../src/facade/collection';
|
import {Map} from '../src/facade/collection';
|
||||||
import {Type, isPresent} from '../src/facade/lang';
|
import {Type, isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of {@link DirectiveResolver} that allows overriding
|
* An implementation of {@link DirectiveResolver} that allows overriding
|
||||||
* various properties of directives.
|
* various properties of directives.
|
||||||
@ -22,6 +23,10 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
|||||||
private _providerOverrides = new Map<Type, any[]>();
|
private _providerOverrides = new Map<Type, any[]>();
|
||||||
private viewProviderOverrides = new Map<Type, any[]>();
|
private viewProviderOverrides = new Map<Type, any[]>();
|
||||||
|
|
||||||
|
constructor(private _injector: Injector) { super(); }
|
||||||
|
|
||||||
|
private get _compiler(): Compiler { return this._injector.get(Compiler); }
|
||||||
|
|
||||||
resolve(type: Type): DirectiveMetadata {
|
resolve(type: Type): DirectiveMetadata {
|
||||||
var dm = super.resolve(type);
|
var dm = super.resolve(type);
|
||||||
|
|
||||||
@ -69,9 +74,11 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
|||||||
|
|
||||||
setProvidersOverride(type: Type, providers: any[]): void {
|
setProvidersOverride(type: Type, providers: any[]): void {
|
||||||
this._providerOverrides.set(type, providers);
|
this._providerOverrides.set(type, providers);
|
||||||
|
this._compiler.clearCacheFor(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
setViewProvidersOverride(type: Type, viewProviders: any[]): void {
|
setViewProvidersOverride(type: Type, viewProviders: any[]): void {
|
||||||
this.viewProviderOverrides.set(type, viewProviders);
|
this.viewProviderOverrides.set(type, viewProviders);
|
||||||
|
this._compiler.clearCacheFor(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,39 +6,36 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AnimationEntryMetadata, ComponentFactory, ComponentResolver, Injectable, Injector, NgZone, ViewMetadata} from '@angular/core';
|
import {AnimationEntryMetadata, Compiler, ComponentFactory, Injectable, Injector, NgZone, ViewMetadata} from '@angular/core';
|
||||||
import {ComponentFixture, ComponentFixtureNoNgZone, TestComponentBuilder} from '@angular/core/testing';
|
import {ComponentFixture, ComponentFixtureNoNgZone, TestComponentBuilder} from '@angular/core/testing';
|
||||||
|
|
||||||
import {DirectiveResolver, ViewResolver} from '../index';
|
import {DirectiveResolver, ViewResolver} from '../index';
|
||||||
import {MapWrapper} from '../src/facade/collection';
|
import {MapWrapper} from '../src/facade/collection';
|
||||||
import {IS_DART, Type, isPresent} from '../src/facade/lang';
|
import {ConcreteType, IS_DART, Type, isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Import TestComponentRenderer from @angular/core/testing
|
* @deprecated Import TestComponentRenderer from @angular/core/testing
|
||||||
*/
|
*/
|
||||||
export {TestComponentRenderer} from '@angular/core/testing';
|
export {TestComponentRenderer} from '@angular/core/testing';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Import TestComponentBuilder from @angular/core/testing
|
* @deprecated Import TestComponentBuilder from @angular/core/testing
|
||||||
*/
|
*/
|
||||||
export {TestComponentBuilder} from '@angular/core/testing';
|
export {TestComponentBuilder} from '@angular/core/testing';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Import ComponentFixture from @angular/core/testing
|
* @deprecated Import ComponentFixture from @angular/core/testing
|
||||||
*/
|
*/
|
||||||
export {ComponentFixture} from '@angular/core/testing';
|
export {ComponentFixture} from '@angular/core/testing';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Import ComponentFixtureNoNgZone from @angular/core/testing
|
* @deprecated Import ComponentFixtureNoNgZone from @angular/core/testing
|
||||||
*/
|
*/
|
||||||
export {ComponentFixtureNoNgZone} from '@angular/core/testing';
|
export {ComponentFixtureNoNgZone} from '@angular/core/testing';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Import ComponentFixtureAutoDetect from @angular/core/testing
|
* @deprecated Import ComponentFixtureAutoDetect from @angular/core/testing
|
||||||
*/
|
*/
|
||||||
export {ComponentFixtureAutoDetect} from '@angular/core/testing';
|
export {ComponentFixtureAutoDetect} from '@angular/core/testing';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A TestComponentBuilder that allows overriding based on the compiler.
|
* A TestComponentBuilder that allows overriding based on the compiler.
|
||||||
*/
|
*/
|
||||||
@ -114,32 +111,31 @@ export class OverridingTestComponentBuilder extends TestComponentBuilder {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
createAsync(rootComponentType: Type): Promise<ComponentFixture<any>> {
|
createAsync<T>(rootComponentType: ConcreteType<T>): Promise<ComponentFixture<T>> {
|
||||||
let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false);
|
this._applyMetadataOverrides();
|
||||||
let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null);
|
return super.createAsync(rootComponentType);
|
||||||
|
}
|
||||||
|
|
||||||
let initComponent = () => {
|
createSync<T>(rootComponentType: ConcreteType<T>): ComponentFixture<T> {
|
||||||
let mockDirectiveResolver = this._injector.get(DirectiveResolver);
|
this._applyMetadataOverrides();
|
||||||
let mockViewResolver = this._injector.get(ViewResolver);
|
return super.createSync(rootComponentType);
|
||||||
this._viewOverrides.forEach((view, type) => mockViewResolver.setView(type, view));
|
}
|
||||||
this._templateOverrides.forEach(
|
|
||||||
(template, type) => mockViewResolver.setInlineTemplate(type, template));
|
|
||||||
this._animationOverrides.forEach(
|
|
||||||
(animationsEntry, type) => mockViewResolver.setAnimations(type, animationsEntry));
|
|
||||||
this._directiveOverrides.forEach((overrides, component) => {
|
|
||||||
overrides.forEach(
|
|
||||||
(to, from) => { mockViewResolver.overrideViewDirective(component, from, to); });
|
|
||||||
});
|
|
||||||
this._bindingsOverrides.forEach(
|
|
||||||
(bindings, type) => mockDirectiveResolver.setProvidersOverride(type, bindings));
|
|
||||||
this._viewBindingsOverrides.forEach(
|
|
||||||
(bindings, type) => mockDirectiveResolver.setViewProvidersOverride(type, bindings));
|
|
||||||
|
|
||||||
let promise: Promise<ComponentFactory<any>> =
|
private _applyMetadataOverrides() {
|
||||||
this._injector.get(ComponentResolver).resolveComponent(rootComponentType);
|
let mockDirectiveResolver = this._injector.get(DirectiveResolver);
|
||||||
return promise.then(componentFactory => this.createFromFactory(ngZone, componentFactory));
|
let mockViewResolver = this._injector.get(ViewResolver);
|
||||||
};
|
this._viewOverrides.forEach((view, type) => { mockViewResolver.setView(type, view); });
|
||||||
|
this._templateOverrides.forEach(
|
||||||
return ngZone == null ? initComponent() : ngZone.run(initComponent);
|
(template, type) => mockViewResolver.setInlineTemplate(type, template));
|
||||||
|
this._animationOverrides.forEach(
|
||||||
|
(animationsEntry, type) => mockViewResolver.setAnimations(type, animationsEntry));
|
||||||
|
this._directiveOverrides.forEach((overrides, component) => {
|
||||||
|
overrides.forEach(
|
||||||
|
(to, from) => { mockViewResolver.overrideViewDirective(component, from, to); });
|
||||||
|
});
|
||||||
|
this._bindingsOverrides.forEach(
|
||||||
|
(bindings, type) => mockDirectiveResolver.setProvidersOverride(type, bindings));
|
||||||
|
this._viewBindingsOverrides.forEach(
|
||||||
|
(bindings, type) => mockDirectiveResolver.setViewProvidersOverride(type, bindings));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AnimationEntryMetadata, BaseException, Injectable, Type, ViewMetadata, resolveForwardRef} from '@angular/core';
|
import {AnimationEntryMetadata, BaseException, Compiler, Injectable, Injector, Type, ViewMetadata, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {ViewResolver} from '../index';
|
import {ViewResolver} from '../index';
|
||||||
import {Map} from '../src/facade/collection';
|
import {Map} from '../src/facade/collection';
|
||||||
@ -21,38 +21,38 @@ export class MockViewResolver extends ViewResolver {
|
|||||||
/** @internal */
|
/** @internal */
|
||||||
_animations = new Map<Type, AnimationEntryMetadata[]>();
|
_animations = new Map<Type, AnimationEntryMetadata[]>();
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_viewCache = new Map<Type, ViewMetadata>();
|
|
||||||
/** @internal */
|
|
||||||
_directiveOverrides = new Map<Type, Map<Type, Type>>();
|
_directiveOverrides = new Map<Type, Map<Type, Type>>();
|
||||||
|
|
||||||
constructor() { super(); }
|
constructor(private _injector: Injector) { super(); }
|
||||||
|
|
||||||
|
private get _compiler(): Compiler { return this._injector.get(Compiler); }
|
||||||
|
|
||||||
|
private _clearCacheFor(component: Type) { this._compiler.clearCacheFor(component); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides the {@link ViewMetadata} for a component.
|
* Overrides the {@link ViewMetadata} for a component.
|
||||||
*/
|
*/
|
||||||
setView(component: Type, view: ViewMetadata): void {
|
setView(component: Type, view: ViewMetadata): void {
|
||||||
this._checkOverrideable(component);
|
|
||||||
this._views.set(component, view);
|
this._views.set(component, view);
|
||||||
|
this._clearCacheFor(component);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Overrides the inline template for a component - other configuration remains unchanged.
|
* Overrides the inline template for a component - other configuration remains unchanged.
|
||||||
*/
|
*/
|
||||||
setInlineTemplate(component: Type, template: string): void {
|
setInlineTemplate(component: Type, template: string): void {
|
||||||
this._checkOverrideable(component);
|
|
||||||
this._inlineTemplates.set(component, template);
|
this._inlineTemplates.set(component, template);
|
||||||
|
this._clearCacheFor(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAnimations(component: Type, animations: AnimationEntryMetadata[]): void {
|
setAnimations(component: Type, animations: AnimationEntryMetadata[]): void {
|
||||||
this._checkOverrideable(component);
|
|
||||||
this._animations.set(component, animations);
|
this._animations.set(component, animations);
|
||||||
|
this._clearCacheFor(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides a directive from the component {@link ViewMetadata}.
|
* Overrides a directive from the component {@link ViewMetadata}.
|
||||||
*/
|
*/
|
||||||
overrideViewDirective(component: Type, from: Type, to: Type): void {
|
overrideViewDirective(component: Type, from: Type, to: Type): void {
|
||||||
this._checkOverrideable(component);
|
|
||||||
|
|
||||||
var overrides = this._directiveOverrides.get(component);
|
var overrides = this._directiveOverrides.get(component);
|
||||||
|
|
||||||
if (isBlank(overrides)) {
|
if (isBlank(overrides)) {
|
||||||
@ -61,6 +61,7 @@ export class MockViewResolver extends ViewResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
overrides.set(from, to);
|
overrides.set(from, to);
|
||||||
|
this._clearCacheFor(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,10 +73,7 @@ export class MockViewResolver extends ViewResolver {
|
|||||||
* - Override the @View definition, see `setInlineTemplate`.
|
* - Override the @View definition, see `setInlineTemplate`.
|
||||||
*/
|
*/
|
||||||
resolve(component: Type): ViewMetadata {
|
resolve(component: Type): ViewMetadata {
|
||||||
var view = this._viewCache.get(component);
|
var view = this._views.get(component);
|
||||||
if (isPresent(view)) return view;
|
|
||||||
|
|
||||||
view = this._views.get(component);
|
|
||||||
if (isBlank(view)) {
|
if (isBlank(view)) {
|
||||||
view = super.resolve(component);
|
view = super.resolve(component);
|
||||||
}
|
}
|
||||||
@ -123,26 +121,8 @@ export class MockViewResolver extends ViewResolver {
|
|||||||
interpolation: view.interpolation
|
interpolation: view.interpolation
|
||||||
});
|
});
|
||||||
|
|
||||||
this._viewCache.set(component, view);
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*
|
|
||||||
* Once a component has been compiled, the AppProtoView is stored in the compiler cache.
|
|
||||||
*
|
|
||||||
* Then it should not be possible to override the component configuration after the component
|
|
||||||
* has been compiled.
|
|
||||||
*/
|
|
||||||
_checkOverrideable(component: Type): void {
|
|
||||||
var cached = this._viewCache.get(component);
|
|
||||||
|
|
||||||
if (isPresent(cached)) {
|
|
||||||
throw new BaseException(
|
|
||||||
`The component ${stringify(component)} has already been compiled, its configuration can not be changed`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenArray(tree: any[], out: Array<Type|any[]>): void {
|
function flattenArray(tree: any[], out: Array<Type|any[]>): void {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Public API for compiler
|
// Public API for compiler
|
||||||
|
export {Compiler} from './linker/compiler';
|
||||||
export {ComponentFactory, ComponentRef} from './linker/component_factory';
|
export {ComponentFactory, ComponentRef} from './linker/component_factory';
|
||||||
export {ComponentFactoryResolver, NoComponentFactoryError} from './linker/component_factory_resolver';
|
export {ComponentFactoryResolver, NoComponentFactoryError} from './linker/component_factory_resolver';
|
||||||
export {ComponentResolver} from './linker/component_resolver';
|
export {ComponentResolver} from './linker/component_resolver';
|
||||||
|
45
modules/@angular/core/src/linker/compiler.ts
Normal file
45
modules/@angular/core/src/linker/compiler.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {BaseException} from '../facade/exceptions';
|
||||||
|
import {ConcreteType, Type, stringify} from '../facade/lang';
|
||||||
|
|
||||||
|
import {ComponentFactory} from './component_factory';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Low-level service for running the angular compiler duirng runtime
|
||||||
|
* to create {@link ComponentFactory}s, which
|
||||||
|
* can later be used to create and render a Component instance.
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
export class Compiler {
|
||||||
|
/**
|
||||||
|
* Loads the template and styles of a component and returns the associated `ComponentFactory`.
|
||||||
|
*/
|
||||||
|
compileComponentAsync<T>(component: ConcreteType<T>): Promise<ComponentFactory<T>> {
|
||||||
|
throw new BaseException(
|
||||||
|
`Runtime compiler is not loaded. Tried to compile ${stringify(component)}`);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Compiles the given component. All templates have to be either inline or compiled via
|
||||||
|
* `compileComponentAsync` before.
|
||||||
|
*/
|
||||||
|
compileComponentSync<T>(component: ConcreteType<T>): ComponentFactory<T> {
|
||||||
|
throw new BaseException(
|
||||||
|
`Runtime compiler is not loaded. Tried to compile ${stringify(component)}`);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Clears all caches
|
||||||
|
*/
|
||||||
|
clearCache(): void {}
|
||||||
|
/**
|
||||||
|
* Clears the cache for the given component.
|
||||||
|
*/
|
||||||
|
clearCacheFor(compType: Type) {}
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Inject, OpaqueToken, Optional, SkipSelf} from '../di';
|
import {Inject, OpaqueToken, Optional, SkipSelf} from '../di';
|
||||||
import {BaseException} from '../facade/exceptions';
|
import {BaseException} from '../facade/exceptions';
|
||||||
import {ClassWithConstructor, stringify} from '../facade/lang';
|
import {ConcreteType, stringify} from '../facade/lang';
|
||||||
|
|
||||||
import {ComponentFactory} from './component_factory';
|
import {ComponentFactory} from './component_factory';
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ class _NullComponentFactoryResolver implements ComponentFactoryResolver {
|
|||||||
*/
|
*/
|
||||||
export abstract class ComponentFactoryResolver {
|
export abstract class ComponentFactoryResolver {
|
||||||
static NULL: ComponentFactoryResolver = new _NullComponentFactoryResolver();
|
static NULL: ComponentFactoryResolver = new _NullComponentFactoryResolver();
|
||||||
abstract resolveComponentFactory<T>(component: ClassWithConstructor<T>): ComponentFactory<T>;
|
abstract resolveComponentFactory<T>(component: ConcreteType<T>): ComponentFactory<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CodegenComponentFactoryResolver implements ComponentFactoryResolver {
|
export class CodegenComponentFactoryResolver implements ComponentFactoryResolver {
|
||||||
|
@ -19,7 +19,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
|||||||
|
|
||||||
isReflectionEnabled(): boolean { return true; }
|
isReflectionEnabled(): boolean { return true; }
|
||||||
|
|
||||||
factory(t: ConcreteType): Function {
|
factory(t: ConcreteType<any>): Function {
|
||||||
switch (t.length) {
|
switch (t.length) {
|
||||||
case 0:
|
case 0:
|
||||||
return () => new t();
|
return () => new t();
|
||||||
|
@ -84,7 +84,7 @@ export interface TypeDecorator {
|
|||||||
/**
|
/**
|
||||||
* Generate a class from the definition and annotate it with {@link TypeDecorator#annotations}.
|
* Generate a class from the definition and annotate it with {@link TypeDecorator#annotations}.
|
||||||
*/
|
*/
|
||||||
Class(obj: ClassDefinition): ConcreteType;
|
Class(obj: ClassDefinition): ConcreteType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractAnnotation(annotation: any): any {
|
function extractAnnotation(annotation: any): any {
|
||||||
@ -219,7 +219,7 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
|||||||
* ```
|
* ```
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export function Class(clsDef: ClassDefinition): ConcreteType {
|
export function Class(clsDef: ClassDefinition): ConcreteType<any> {
|
||||||
var constructor = applyParams(
|
var constructor = applyParams(
|
||||||
clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');
|
clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');
|
||||||
var proto = constructor.prototype;
|
var proto = constructor.prototype;
|
||||||
@ -246,7 +246,7 @@ export function Class(clsDef: ClassDefinition): ConcreteType {
|
|||||||
(constructor as any /** TODO #9100 */)['overriddenName'] = `class${_nextClassId++}`;
|
(constructor as any /** TODO #9100 */)['overriddenName'] = `class${_nextClassId++}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ConcreteType>constructor;
|
return <ConcreteType<any>>constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
var Reflect = global.Reflect;
|
var Reflect = global.Reflect;
|
||||||
|
@ -10,7 +10,7 @@ import {TestComponentBuilder} from '@angular/compiler/testing';
|
|||||||
import {ComponentFixture, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
import {ComponentFixture, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||||
import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {isBlank, NumberWrapper,} from '../../src/facade/lang';
|
import {isBlank, NumberWrapper, ConcreteType,} from '../../src/facade/lang';
|
||||||
import {BaseException} from '../../src/facade/exceptions';
|
import {BaseException} from '../../src/facade/exceptions';
|
||||||
import {StringMapWrapper} from '../../src/facade/collection';
|
import {StringMapWrapper} from '../../src/facade/collection';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
@ -40,9 +40,9 @@ export function main() {
|
|||||||
var renderLog: RenderLog;
|
var renderLog: RenderLog;
|
||||||
var directiveLog: DirectiveLog;
|
var directiveLog: DirectiveLog;
|
||||||
|
|
||||||
function createCompFixture(
|
function createCompFixture<T>(
|
||||||
template: string, compType: Type = TestComponent,
|
template: string, compType: ConcreteType<T> = <any>TestComponent,
|
||||||
_tcb: TestComponentBuilder = null): ComponentFixture<any> {
|
_tcb: TestComponentBuilder = null): ComponentFixture<T> {
|
||||||
if (isBlank(_tcb)) {
|
if (isBlank(_tcb)) {
|
||||||
_tcb = tcb;
|
_tcb = tcb;
|
||||||
}
|
}
|
||||||
@ -58,18 +58,19 @@ export function main() {
|
|||||||
return nodes.map(node => node.injector.get(dirType));
|
return nodes.map(node => node.injector.get(dirType));
|
||||||
}
|
}
|
||||||
|
|
||||||
function _bindSimpleProp(
|
function _bindSimpleProp<T>(
|
||||||
bindAttr: string, compType: Type = TestComponent): ComponentFixture<any> {
|
bindAttr: string, compType: ConcreteType<T> = <any>TestComponent): ComponentFixture<T> {
|
||||||
var template = `<div ${bindAttr}></div>`;
|
var template = `<div ${bindAttr}></div>`;
|
||||||
return createCompFixture(template, compType);
|
return createCompFixture(template, compType);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _bindSimpleValue(
|
function _bindSimpleValue<T>(
|
||||||
expression: any, compType: Type = TestComponent): ComponentFixture<any> {
|
expression: any, compType: ConcreteType<T> = <any>TestComponent): ComponentFixture<T> {
|
||||||
return _bindSimpleProp(`[someProp]='${expression}'`, compType);
|
return _bindSimpleProp(`[someProp]='${expression}'`, compType);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _bindAndCheckSimpleValue(expression: any, compType: Type = TestComponent): string[] {
|
function _bindAndCheckSimpleValue<T>(
|
||||||
|
expression: any, compType: ConcreteType<T> = <any>TestComponent): string[] {
|
||||||
var ctx = _bindSimpleValue(expression, compType);
|
var ctx = _bindSimpleValue(expression, compType);
|
||||||
ctx.detectChanges(false);
|
ctx.detectChanges(false);
|
||||||
return renderLog.log;
|
return renderLog.log;
|
||||||
|
@ -1504,20 +1504,15 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
|
|
||||||
describe('error handling', () => {
|
describe('error handling', () => {
|
||||||
it('should report a meaningful error when a directive is missing annotation',
|
it('should report a meaningful error when a directive is missing annotation',
|
||||||
inject(
|
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
[TestComponentBuilder, AsyncTestCompleter],
|
tcb = tcb.overrideView(
|
||||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
MyComp,
|
||||||
tcb = tcb.overrideView(
|
new ViewMetadata({template: '', directives: [SomeDirectiveMissingAnnotation]}));
|
||||||
MyComp,
|
|
||||||
new ViewMetadata({template: '', directives: [SomeDirectiveMissingAnnotation]}));
|
|
||||||
|
|
||||||
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
|
expect(() => tcb.createAsync(MyComp))
|
||||||
expect(e.message).toEqual(
|
.toThrowError(
|
||||||
`No Directive annotation found on ${stringify(SomeDirectiveMissingAnnotation)}`);
|
`No Directive annotation found on ${stringify(SomeDirectiveMissingAnnotation)}`);
|
||||||
async.done();
|
}));
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report a meaningful error when a component is missing view annotation',
|
it('should report a meaningful error when a component is missing view annotation',
|
||||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
@ -1530,19 +1525,13 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should report a meaningful error when a directive is null',
|
it('should report a meaningful error when a directive is null',
|
||||||
inject(
|
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
[TestComponentBuilder, AsyncTestCompleter],
|
tcb = tcb.overrideView(MyComp, new ViewMetadata({directives: [[null]], template: ''}));
|
||||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
|
||||||
tcb =
|
|
||||||
tcb.overrideView(MyComp, new ViewMetadata({directives: [[null]], template: ''}));
|
|
||||||
|
|
||||||
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
|
expect(() => tcb.createAsync(MyComp))
|
||||||
expect(e.message).toEqual(
|
.toThrowError(
|
||||||
`Unexpected directive value 'null' on the View of component '${stringify(MyComp)}'`);
|
`Unexpected directive value 'null' on the View of component '${stringify(MyComp)}'`);
|
||||||
async.done();
|
}));
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should provide an error context when an error happens in DI',
|
it('should provide an error context when an error happens in DI',
|
||||||
inject(
|
inject(
|
||||||
@ -1642,22 +1631,17 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
|
|
||||||
if (!IS_DART) {
|
if (!IS_DART) {
|
||||||
it('should report a meaningful error when a directive is undefined',
|
it('should report a meaningful error when a directive is undefined',
|
||||||
inject(
|
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
[TestComponentBuilder, AsyncTestCompleter],
|
|
||||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
|
||||||
|
|
||||||
var undefinedValue: any = void(0);
|
var undefinedValue: any = void(0);
|
||||||
|
|
||||||
tcb = tcb.overrideView(
|
tcb = tcb.overrideView(
|
||||||
MyComp, new ViewMetadata({directives: [undefinedValue], template: ''}));
|
MyComp, new ViewMetadata({directives: [undefinedValue], template: ''}));
|
||||||
|
|
||||||
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
|
expect(() => tcb.createAsync(MyComp))
|
||||||
expect(e.message).toEqual(
|
.toThrowError(
|
||||||
`Unexpected directive value 'undefined' on the View of component '${stringify(MyComp)}'`);
|
`Unexpected directive value 'undefined' on the View of component '${stringify(MyComp)}'`);
|
||||||
async.done();
|
}));
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should specify a location of an error that happened during change detection (text)',
|
it('should specify a location of an error that happened during change detection (text)',
|
||||||
|
@ -261,18 +261,14 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw with descriptive error when query selectors are not present',
|
it('should throw with descriptive error when query selectors are not present',
|
||||||
inject(
|
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
[TestComponentBuilder, AsyncTestCompleter],
|
expect(
|
||||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
() => tcb.overrideTemplate(
|
||||||
tcb.overrideTemplate(
|
MyCompBroken0, '<has-null-query-condition></has-null-query-condition>')
|
||||||
MyCompBroken0, '<has-null-query-condition></has-null-query-condition>')
|
.createAsync(MyCompBroken0))
|
||||||
.createAsync(MyCompBroken0)
|
.toThrowError(
|
||||||
.catch((e) => {
|
`Can't construct a query for the property "errorTrigger" of "${stringify(HasNullQueryCondition)}" since the query selector wasn't defined.`);
|
||||||
expect(e.message).toEqual(
|
}));
|
||||||
`Can't construct a query for the property "errorTrigger" of "${stringify(HasNullQueryCondition)}" since the query selector wasn't defined.`);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('query for TemplateRef', () => {
|
describe('query for TemplateRef', () => {
|
||||||
|
@ -12,7 +12,7 @@ import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
|||||||
|
|
||||||
import {IS_DART} from '../../src/facade/lang';
|
import {IS_DART} from '../../src/facade/lang';
|
||||||
|
|
||||||
import {Component, Pipe, PipeTransform, provide, ViewMetadata, OpaqueToken, Injector} from '@angular/core';
|
import {Component, Pipe, PipeTransform, provide, ViewMetadata, PLATFORM_PIPES, OpaqueToken, Injector, forwardRef} from '@angular/core';
|
||||||
import {NgIf, NgClass} from '@angular/common';
|
import {NgIf, NgClass} from '@angular/common';
|
||||||
import {CompilerConfig} from '@angular/compiler';
|
import {CompilerConfig} from '@angular/compiler';
|
||||||
|
|
||||||
@ -170,8 +170,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
|
|
||||||
it('should support ngClass before a component and content projection inside of an ngIf',
|
it('should support ngClass before a component and content projection inside of an ngIf',
|
||||||
inject(
|
inject(
|
||||||
[TestComponentBuilder, AsyncTestCompleter],
|
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async: any) => {
|
||||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
|
||||||
tcb.overrideView(
|
tcb.overrideView(
|
||||||
MyComp1, new ViewMetadata({
|
MyComp1, new ViewMetadata({
|
||||||
template: `A<cmp-content *ngIf="true" [ngClass]="'red'">B</cmp-content>C`,
|
template: `A<cmp-content *ngIf="true" [ngClass]="'red'">B</cmp-content>C`,
|
||||||
@ -185,6 +184,15 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should handle mutual recursion entered from multiple sides - #7084',
|
||||||
|
inject(
|
||||||
|
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async: any) => {
|
||||||
|
tcb.createAsync(FakeRecursiveComp).then((fixture) => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('[]');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -207,3 +215,37 @@ class CustomPipe implements PipeTransform {
|
|||||||
@Component({selector: 'cmp-content', template: `<ng-content></ng-content>`})
|
@Component({selector: 'cmp-content', template: `<ng-content></ng-content>`})
|
||||||
class CmpWithNgContent {
|
class CmpWithNgContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'left',
|
||||||
|
template: `L<right *ngIf="false"></right>`,
|
||||||
|
directives: [
|
||||||
|
NgIf,
|
||||||
|
forwardRef(() => RightComp),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class LeftComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'right',
|
||||||
|
template: `R<left *ngIf="false"></left>`,
|
||||||
|
directives: [
|
||||||
|
NgIf,
|
||||||
|
forwardRef(() => LeftComp),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class RightComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fakeRecursiveComp',
|
||||||
|
template: `[<left *ngIf="false"></left><right *ngIf="false"></right>]`,
|
||||||
|
directives: [
|
||||||
|
NgIf,
|
||||||
|
forwardRef(() => LeftComp),
|
||||||
|
forwardRef(() => RightComp),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class FakeRecursiveComp {
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, beforeEachProviders, inject,} from '@angular/core/testing/testing_internal';
|
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, beforeEachProviders, inject,} from '@angular/core/testing/testing_internal';
|
||||||
import {fakeAsync, flushMicrotasks, tick, ComponentFixture} from '@angular/core/testing';
|
import {fakeAsync, flushMicrotasks, tick, ComponentFixture} from '@angular/core/testing';
|
||||||
import {TestComponentBuilder} from '@angular/compiler/testing';
|
import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||||
import {isBlank} from '../../src/facade/lang';
|
import {isBlank, ConcreteType} from '../../src/facade/lang';
|
||||||
import {Type, ViewContainerRef, TemplateRef, ElementRef, ChangeDetectorRef, ChangeDetectionStrategy, Directive, Component, DebugElement, forwardRef, Input, PipeTransform, Attribute, ViewMetadata, provide, Optional, Inject, Self, InjectMetadata, Pipe, Host, SkipSelfMetadata} from '@angular/core';
|
import {Type, ViewContainerRef, TemplateRef, ElementRef, ChangeDetectorRef, ChangeDetectionStrategy, Directive, Component, DebugElement, forwardRef, Input, PipeTransform, Attribute, ViewMetadata, provide, Optional, Inject, Self, InjectMetadata, Pipe, Host, SkipSelfMetadata} from '@angular/core';
|
||||||
import {NgIf, NgFor} from '@angular/common';
|
import {NgIf, NgFor} from '@angular/common';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
@ -242,10 +242,11 @@ class TestComp {
|
|||||||
export function main() {
|
export function main() {
|
||||||
var tcb: TestComponentBuilder;
|
var tcb: TestComponentBuilder;
|
||||||
|
|
||||||
function createCompFixture(
|
function createCompFixture<T>(
|
||||||
template: string, tcb: TestComponentBuilder, comp: Type = null): ComponentFixture<any> {
|
template: string, tcb: TestComponentBuilder,
|
||||||
|
comp: ConcreteType<T> = null): ComponentFixture<T> {
|
||||||
if (isBlank(comp)) {
|
if (isBlank(comp)) {
|
||||||
comp = TestComp;
|
comp = <any>TestComp;
|
||||||
}
|
}
|
||||||
return tcb
|
return tcb
|
||||||
.overrideView(
|
.overrideView(
|
||||||
@ -255,7 +256,7 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createComp(
|
function createComp(
|
||||||
template: string, tcb: TestComponentBuilder, comp: Type = null): DebugElement {
|
template: string, tcb: TestComponentBuilder, comp: ConcreteType<any> = null): DebugElement {
|
||||||
var fixture = createCompFixture(template, tcb, comp);
|
var fixture = createCompFixture(template, tcb, comp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
return fixture.debugElement;
|
return fixture.debugElement;
|
||||||
|
@ -6,14 +6,15 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AnimationEntryMetadata, ComponentFactory, ComponentResolver, Injectable, Injector, NgZone, OpaqueToken, ViewMetadata} from '../index';
|
import {AnimationEntryMetadata, Compiler, ComponentFactory, Injectable, Injector, NgZone, OpaqueToken, ViewMetadata} from '../index';
|
||||||
import {PromiseWrapper} from '../src/facade/async';
|
import {PromiseWrapper} from '../src/facade/async';
|
||||||
import {IS_DART, Type, isPresent} from '../src/facade/lang';
|
import {ConcreteType, IS_DART, Type, isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
import {ComponentFixture} from './component_fixture';
|
import {ComponentFixture} from './component_fixture';
|
||||||
import {tick} from './fake_async';
|
import {tick} from './fake_async';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class for inserting the root test component element in a platform independent way.
|
* An abstract class for inserting the root test component element in a platform independent way.
|
||||||
*
|
*
|
||||||
@ -118,20 +119,21 @@ export class TestComponentBuilder {
|
|||||||
/**
|
/**
|
||||||
* Builds and returns a ComponentFixture.
|
* Builds and returns a ComponentFixture.
|
||||||
*/
|
*/
|
||||||
createAsync(rootComponentType: Type): Promise<ComponentFixture<any>> {
|
createAsync<T>(rootComponentType: ConcreteType<T>): Promise<ComponentFixture<T>> {
|
||||||
let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false);
|
let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false);
|
||||||
let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null);
|
let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null);
|
||||||
|
let compiler: Compiler = this._injector.get(Compiler);
|
||||||
|
|
||||||
let initComponent = () => {
|
let initComponent = () => {
|
||||||
let promise: Promise<ComponentFactory<any>> =
|
let promise: Promise<ComponentFactory<any>> =
|
||||||
this._injector.get(ComponentResolver).resolveComponent(rootComponentType);
|
compiler.compileComponentAsync(rootComponentType);
|
||||||
return promise.then(componentFactory => this.createFromFactory(ngZone, componentFactory));
|
return promise.then(componentFactory => this.createFromFactory(ngZone, componentFactory));
|
||||||
};
|
};
|
||||||
|
|
||||||
return ngZone == null ? initComponent() : ngZone.run(initComponent);
|
return ngZone == null ? initComponent() : ngZone.run(initComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
createFakeAsync(rootComponentType: Type): ComponentFixture<any> {
|
createFakeAsync<T>(rootComponentType: ConcreteType<T>): ComponentFixture<T> {
|
||||||
let result: any /** TODO #9100 */;
|
let result: any /** TODO #9100 */;
|
||||||
let error: any /** TODO #9100 */;
|
let error: any /** TODO #9100 */;
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
@ -144,15 +146,16 @@ export class TestComponentBuilder {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
createSync<T>(rootComponentType: ConcreteType<T>): ComponentFixture<T> {
|
||||||
* @deprecated createSync will be replaced with the ability to precompile components from within
|
|
||||||
* the test.
|
|
||||||
*/
|
|
||||||
createSync<C>(componentFactory: ComponentFactory<C>): ComponentFixture<C> {
|
|
||||||
let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false);
|
let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false);
|
||||||
let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null);
|
let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null);
|
||||||
|
let compiler: Compiler = this._injector.get(Compiler);
|
||||||
|
|
||||||
|
let initComponent = () => {
|
||||||
|
return this.createFromFactory(
|
||||||
|
ngZone, this._injector.get(Compiler).compileComponentSync(rootComponentType));
|
||||||
|
};
|
||||||
|
|
||||||
let initComponent = () => this.createFromFactory(ngZone, componentFactory);
|
|
||||||
return ngZone == null ? initComponent() : ngZone.run(initComponent);
|
return ngZone == null ? initComponent() : ngZone.run(initComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,9 +77,7 @@ export interface Type extends Function {}
|
|||||||
/**
|
/**
|
||||||
* Runtime representation of a type that is constructable (non-abstract).
|
* Runtime representation of a type that is constructable (non-abstract).
|
||||||
*/
|
*/
|
||||||
export interface ConcreteType extends Type { new (...args: any[] /** TODO #9100 */): any; }
|
export interface ConcreteType<T> extends Type { new (...args: any[]): T; }
|
||||||
|
|
||||||
export interface ClassWithConstructor<T> { new (...args: any[]): T; }
|
|
||||||
|
|
||||||
export function getTypeNameForDebugging(type: Type): string {
|
export function getTypeNameForDebugging(type: Type): string {
|
||||||
if (type['name']) {
|
if (type['name']) {
|
||||||
|
19
tools/public_api_guard/compiler/index.d.ts
vendored
19
tools/public_api_guard/compiler/index.d.ts
vendored
@ -310,21 +310,24 @@ export declare class CompilerConfig {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class CompileTemplateMetadata {
|
export declare class CompileTemplateMetadata implements CompileStylesheetMetadata {
|
||||||
animations: CompileAnimationEntryMetadata[];
|
animations: CompileAnimationEntryMetadata[];
|
||||||
encapsulation: ViewEncapsulation;
|
encapsulation: ViewEncapsulation;
|
||||||
|
externalStylesheets: CompileStylesheetMetadata[];
|
||||||
interpolation: [string, string];
|
interpolation: [string, string];
|
||||||
|
moduleUrl: string;
|
||||||
ngContentSelectors: string[];
|
ngContentSelectors: string[];
|
||||||
styleUrls: string[];
|
styleUrls: string[];
|
||||||
styles: string[];
|
styles: string[];
|
||||||
template: string;
|
template: string;
|
||||||
templateUrl: string;
|
templateUrl: string;
|
||||||
constructor({encapsulation, template, templateUrl, styles, styleUrls, animations, ngContentSelectors, interpolation}?: {
|
constructor({encapsulation, template, templateUrl, styles, styleUrls, externalStylesheets, animations, ngContentSelectors, interpolation}?: {
|
||||||
encapsulation?: ViewEncapsulation;
|
encapsulation?: ViewEncapsulation;
|
||||||
template?: string;
|
template?: string;
|
||||||
templateUrl?: string;
|
templateUrl?: string;
|
||||||
styles?: string[];
|
styles?: string[];
|
||||||
styleUrls?: string[];
|
styleUrls?: string[];
|
||||||
|
externalStylesheets?: CompileStylesheetMetadata[];
|
||||||
ngContentSelectors?: string[];
|
ngContentSelectors?: string[];
|
||||||
animations?: CompileAnimationEntryMetadata[];
|
animations?: CompileAnimationEntryMetadata[];
|
||||||
interpolation?: [string, string];
|
interpolation?: [string, string];
|
||||||
@ -460,9 +463,8 @@ export declare class NormalizedComponentWithViewDirectives {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export declare class OfflineCompiler {
|
export declare class OfflineCompiler {
|
||||||
constructor(_directiveNormalizer: DirectiveNormalizer, _templateParser: TemplateParser, _styleCompiler: StyleCompiler, _viewCompiler: ViewCompiler, _outputEmitter: OutputEmitter, _xhr: XHR);
|
constructor(_directiveNormalizer: DirectiveNormalizer, _templateParser: TemplateParser, _styleCompiler: StyleCompiler, _viewCompiler: ViewCompiler, _outputEmitter: OutputEmitter);
|
||||||
compileTemplates(components: NormalizedComponentWithViewDirectives[]): SourceModule;
|
compileTemplates(components: NormalizedComponentWithViewDirectives[]): SourceModule[];
|
||||||
loadAndCompileStylesheet(stylesheetUrl: string, shim: boolean, suffix: string): Promise<StyleSheetSourceWithImports>;
|
|
||||||
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata): Promise<CompileDirectiveMetadata>;
|
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata): Promise<CompileDirectiveMetadata>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,9 +517,12 @@ export declare abstract class RenderTypes {
|
|||||||
renderer: CompileIdentifierMetadata;
|
renderer: CompileIdentifierMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class RuntimeCompiler implements ComponentResolver {
|
export declare class RuntimeCompiler implements ComponentResolver, Compiler {
|
||||||
constructor(_metadataResolver: CompileMetadataResolver, _templateNormalizer: DirectiveNormalizer, _templateParser: TemplateParser, _styleCompiler: StyleCompiler, _viewCompiler: ViewCompiler, _xhr: XHR, _genConfig: CompilerConfig);
|
constructor(_metadataResolver: CompileMetadataResolver, _templateNormalizer: DirectiveNormalizer, _templateParser: TemplateParser, _styleCompiler: StyleCompiler, _viewCompiler: ViewCompiler, _genConfig: CompilerConfig);
|
||||||
clearCache(): void;
|
clearCache(): void;
|
||||||
|
clearCacheFor(compType: Type): void;
|
||||||
|
compileComponentAsync<T>(compType: ConcreteType<T>): Promise<ComponentFactory<T>>;
|
||||||
|
compileComponentSync<T>(compType: ConcreteType<T>): ComponentFactory<T>;
|
||||||
resolveComponent(component: Type | string): Promise<ComponentFactory<any>>;
|
resolveComponent(component: Type | string): Promise<ComponentFactory<any>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
tools/public_api_guard/compiler/testing.d.ts
vendored
6
tools/public_api_guard/compiler/testing.d.ts
vendored
@ -1,4 +1,5 @@
|
|||||||
export declare class MockDirectiveResolver extends DirectiveResolver {
|
export declare class MockDirectiveResolver extends DirectiveResolver {
|
||||||
|
constructor(_injector: Injector);
|
||||||
resolve(type: Type): DirectiveMetadata;
|
resolve(type: Type): DirectiveMetadata;
|
||||||
setProvidersOverride(type: Type, providers: any[]): void;
|
setProvidersOverride(type: Type, providers: any[]): void;
|
||||||
setViewProvidersOverride(type: Type, viewProviders: any[]): void;
|
setViewProvidersOverride(type: Type, viewProviders: any[]): void;
|
||||||
@ -22,7 +23,7 @@ export declare class MockSchemaRegistry implements ElementSchemaRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export declare class MockViewResolver extends ViewResolver {
|
export declare class MockViewResolver extends ViewResolver {
|
||||||
constructor();
|
constructor(_injector: Injector);
|
||||||
overrideViewDirective(component: Type, from: Type, to: Type): void;
|
overrideViewDirective(component: Type, from: Type, to: Type): void;
|
||||||
resolve(component: Type): ViewMetadata;
|
resolve(component: Type): ViewMetadata;
|
||||||
setAnimations(component: Type, animations: AnimationEntryMetadata[]): void;
|
setAnimations(component: Type, animations: AnimationEntryMetadata[]): void;
|
||||||
@ -32,7 +33,8 @@ export declare class MockViewResolver extends ViewResolver {
|
|||||||
|
|
||||||
export declare class OverridingTestComponentBuilder extends TestComponentBuilder {
|
export declare class OverridingTestComponentBuilder extends TestComponentBuilder {
|
||||||
constructor(injector: Injector);
|
constructor(injector: Injector);
|
||||||
createAsync(rootComponentType: Type): Promise<ComponentFixture<any>>;
|
createAsync<T>(rootComponentType: ConcreteType<T>): Promise<ComponentFixture<T>>;
|
||||||
|
createSync<T>(rootComponentType: ConcreteType<T>): ComponentFixture<T>;
|
||||||
overrideAnimations(componentType: Type, animations: AnimationEntryMetadata[]): TestComponentBuilder;
|
overrideAnimations(componentType: Type, animations: AnimationEntryMetadata[]): TestComponentBuilder;
|
||||||
overrideDirective(componentType: Type, from: Type, to: Type): OverridingTestComponentBuilder;
|
overrideDirective(componentType: Type, from: Type, to: Type): OverridingTestComponentBuilder;
|
||||||
overrideProviders(type: Type, providers: any[]): OverridingTestComponentBuilder;
|
overrideProviders(type: Type, providers: any[]): OverridingTestComponentBuilder;
|
||||||
|
14
tools/public_api_guard/core/index.d.ts
vendored
14
tools/public_api_guard/core/index.d.ts
vendored
@ -207,7 +207,7 @@ export declare abstract class ChangeDetectorRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare function Class(clsDef: ClassDefinition): ConcreteType;
|
export declare function Class(clsDef: ClassDefinition): ConcreteType<any>;
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export interface ClassDefinition {
|
export interface ClassDefinition {
|
||||||
@ -226,6 +226,14 @@ export declare class CollectionChangeRecord {
|
|||||||
toString(): string;
|
toString(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export declare class Compiler {
|
||||||
|
clearCache(): void;
|
||||||
|
clearCacheFor(compType: Type): void;
|
||||||
|
compileComponentAsync<T>(component: ConcreteType<T>): Promise<ComponentFactory<T>>;
|
||||||
|
compileComponentSync<T>(component: ConcreteType<T>): ComponentFactory<T>;
|
||||||
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare var Component: ComponentMetadataFactory;
|
export declare var Component: ComponentMetadataFactory;
|
||||||
|
|
||||||
@ -254,7 +262,7 @@ export declare class ComponentFactory<C> {
|
|||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare abstract class ComponentFactoryResolver {
|
export declare abstract class ComponentFactoryResolver {
|
||||||
abstract resolveComponentFactory<T>(component: ClassWithConstructor<T>): ComponentFactory<T>;
|
abstract resolveComponentFactory<T>(component: ConcreteType<T>): ComponentFactory<T>;
|
||||||
static NULL: ComponentFactoryResolver;
|
static NULL: ComponentFactoryResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1267,7 +1275,7 @@ export interface TypeDecorator {
|
|||||||
annotations: any[];
|
annotations: any[];
|
||||||
(target: Object, propertyKey?: string | symbol, parameterIndex?: number): void;
|
(target: Object, propertyKey?: string | symbol, parameterIndex?: number): void;
|
||||||
<T extends Type>(type: T): T;
|
<T extends Type>(type: T): T;
|
||||||
Class(obj: ClassDefinition): ConcreteType;
|
Class(obj: ClassDefinition): ConcreteType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
|
6
tools/public_api_guard/core/testing.d.ts
vendored
6
tools/public_api_guard/core/testing.d.ts
vendored
@ -89,10 +89,10 @@ export declare function setBaseTestProviders(platformProviders: Array<Type | Pro
|
|||||||
export declare class TestComponentBuilder {
|
export declare class TestComponentBuilder {
|
||||||
protected _injector: Injector;
|
protected _injector: Injector;
|
||||||
constructor(_injector: Injector);
|
constructor(_injector: Injector);
|
||||||
createAsync(rootComponentType: Type): Promise<ComponentFixture<any>>;
|
createAsync<T>(rootComponentType: ConcreteType<T>): Promise<ComponentFixture<T>>;
|
||||||
createFakeAsync(rootComponentType: Type): ComponentFixture<any>;
|
createFakeAsync<T>(rootComponentType: ConcreteType<T>): ComponentFixture<T>;
|
||||||
protected createFromFactory<C>(ngZone: NgZone, componentFactory: ComponentFactory<C>): ComponentFixture<C>;
|
protected createFromFactory<C>(ngZone: NgZone, componentFactory: ComponentFactory<C>): ComponentFixture<C>;
|
||||||
/** @deprecated */ createSync<C>(componentFactory: ComponentFactory<C>): ComponentFixture<C>;
|
createSync<T>(rootComponentType: ConcreteType<T>): ComponentFixture<T>;
|
||||||
overrideAnimations(componentType: Type, animations: AnimationEntryMetadata[]): TestComponentBuilder;
|
overrideAnimations(componentType: Type, animations: AnimationEntryMetadata[]): TestComponentBuilder;
|
||||||
overrideDirective(componentType: Type, from: Type, to: Type): TestComponentBuilder;
|
overrideDirective(componentType: Type, from: Type, to: Type): TestComponentBuilder;
|
||||||
overrideProviders(type: Type, providers: any[]): TestComponentBuilder;
|
overrideProviders(type: Type, providers: any[]): TestComponentBuilder;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user