refactor(compiler): allow sync AOT compilation (#16832).
AOT compilation can be executed synchronously now, if the `ReosurceLoader` returns a string directly (and no `Promise`).
This commit is contained in:
parent
255d7226d1
commit
5af143e8e4
|
@ -38,7 +38,7 @@ export class CodeGenerator {
|
||||||
|
|
||||||
codegen(): Promise<any> {
|
codegen(): Promise<any> {
|
||||||
return this.compiler
|
return this.compiler
|
||||||
.compileAll(this.program.getSourceFiles().map(
|
.compileAllAsync(this.program.getSourceFiles().map(
|
||||||
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
|
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
|
||||||
.then(generatedModules => {
|
.then(generatedModules => {
|
||||||
generatedModules.forEach(generatedModule => {
|
generatedModules.forEach(generatedModule => {
|
||||||
|
|
|
@ -17,7 +17,11 @@ import {DiagnosticTemplateInfo} from '../../src/diagnostics/expression_diagnosti
|
||||||
import {getClassFromStaticSymbol, getClassMembers, getPipesTable, getSymbolQuery} from '../../src/diagnostics/typescript_symbols';
|
import {getClassFromStaticSymbol, getClassMembers, getPipesTable, getSymbolQuery} from '../../src/diagnostics/typescript_symbols';
|
||||||
import {Directory, MockAotContext} from '../mocks';
|
import {Directory, MockAotContext} from '../mocks';
|
||||||
|
|
||||||
const packages = path.join(__dirname, '../../../../../packages');
|
function calcRootPath() {
|
||||||
|
const moduleFilename = module.filename.replace(/\\/g, '/');
|
||||||
|
const distIndex = moduleFilename.indexOf('/dist/all');
|
||||||
|
return moduleFilename.substr(0, distIndex);
|
||||||
|
}
|
||||||
|
|
||||||
const realFiles = new Map<string, string>();
|
const realFiles = new Map<string, string>();
|
||||||
|
|
||||||
|
@ -40,7 +44,7 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost, Compiler
|
||||||
strictNullChecks: true,
|
strictNullChecks: true,
|
||||||
baseUrl: currentDirectory,
|
baseUrl: currentDirectory,
|
||||||
lib: ['lib.es2015.d.ts', 'lib.dom.d.ts'],
|
lib: ['lib.es2015.d.ts', 'lib.dom.d.ts'],
|
||||||
paths: {'@angular/*': [packages + '/*']}
|
paths: {'@angular/*': [calcRootPath() + '/packages/*']}
|
||||||
};
|
};
|
||||||
this.context = new MockAotContext(currentDirectory, files)
|
this.context = new MockAotContext(currentDirectory, files)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ export class AotCompiler {
|
||||||
|
|
||||||
clearCache() { this._metadataResolver.clearCache(); }
|
clearCache() { this._metadataResolver.clearCache(); }
|
||||||
|
|
||||||
compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
|
compileAllAsync(rootFiles: string[]): Promise<GeneratedFile[]> {
|
||||||
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
|
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
|
||||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||||
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
|
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
|
||||||
|
@ -56,6 +56,20 @@ export class AotCompiler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileAllSync(rootFiles: string[]): GeneratedFile[] {
|
||||||
|
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
|
||||||
|
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||||
|
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
|
||||||
|
ngModules.forEach(
|
||||||
|
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||||
|
ngModule.type.reference, true));
|
||||||
|
const sourceModules = files.map(
|
||||||
|
file => this._compileSrcFile(
|
||||||
|
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes, file.ngModules,
|
||||||
|
file.injectables));
|
||||||
|
return flatten(sourceModules);
|
||||||
|
}
|
||||||
|
|
||||||
private _compileSrcFile(
|
private _compileSrcFile(
|
||||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
|
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
|
||||||
|
|
|
@ -17,5 +17,5 @@ export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryRes
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
loadResource(path: string): Promise<string>;
|
loadResource(path: string): Promise<string>|string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {ResourceLoader} from './resource_loader';
|
||||||
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
|
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
|
||||||
import {PreparsedElementType, preparseElement} from './template_parser/template_preparser';
|
import {PreparsedElementType, preparseElement} from './template_parser/template_preparser';
|
||||||
import {UrlResolver} from './url_resolver';
|
import {UrlResolver} from './url_resolver';
|
||||||
import {SyncAsyncResult, isDefined, syntaxError} from './util';
|
import {SyncAsync, isDefined, syntaxError} from './util';
|
||||||
|
|
||||||
export interface PrenormalizedTemplateMetadata {
|
export interface PrenormalizedTemplateMetadata {
|
||||||
ngModuleType: any;
|
ngModuleType: any;
|
||||||
|
@ -35,7 +35,7 @@ export interface PrenormalizedTemplateMetadata {
|
||||||
|
|
||||||
@CompilerInjectable()
|
@CompilerInjectable()
|
||||||
export class DirectiveNormalizer {
|
export class DirectiveNormalizer {
|
||||||
private _resourceLoaderCache = new Map<string, Promise<string>>();
|
private _resourceLoaderCache = new Map<string, SyncAsync<string>>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _resourceLoader: ResourceLoader, private _urlResolver: UrlResolver,
|
private _resourceLoader: ResourceLoader, private _urlResolver: UrlResolver,
|
||||||
|
@ -53,19 +53,17 @@ export class DirectiveNormalizer {
|
||||||
(stylesheet) => { this._resourceLoaderCache.delete(stylesheet.moduleUrl !); });
|
(stylesheet) => { this._resourceLoaderCache.delete(stylesheet.moduleUrl !); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _fetch(url: string): Promise<string> {
|
private _fetch(url: string): SyncAsync<string> {
|
||||||
let result = this._resourceLoaderCache.get(url);
|
let result = this._resourceLoaderCache.get(url);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = this._resourceLoader.get(url) !;
|
result = this._resourceLoader.get(url);
|
||||||
this._resourceLoaderCache.set(url, result);
|
this._resourceLoaderCache.set(url, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeTemplate(prenormData: PrenormalizedTemplateMetadata):
|
normalizeTemplate(prenormData: PrenormalizedTemplateMetadata):
|
||||||
SyncAsyncResult<CompileTemplateMetadata> {
|
SyncAsync<CompileTemplateMetadata> {
|
||||||
let normalizedTemplateSync: CompileTemplateMetadata = null !;
|
|
||||||
let normalizedTemplateAsync: Promise<CompileTemplateMetadata> = undefined !;
|
|
||||||
if (isDefined(prenormData.template)) {
|
if (isDefined(prenormData.template)) {
|
||||||
if (isDefined(prenormData.templateUrl)) {
|
if (isDefined(prenormData.templateUrl)) {
|
||||||
throw syntaxError(
|
throw syntaxError(
|
||||||
|
@ -75,39 +73,33 @@ export class DirectiveNormalizer {
|
||||||
throw syntaxError(
|
throw syntaxError(
|
||||||
`The template specified for component ${stringify(prenormData.componentType)} is not a string`);
|
`The template specified for component ${stringify(prenormData.componentType)} is not a string`);
|
||||||
}
|
}
|
||||||
normalizedTemplateSync = this.normalizeTemplateSync(prenormData);
|
|
||||||
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync !);
|
|
||||||
} else if (isDefined(prenormData.templateUrl)) {
|
} else if (isDefined(prenormData.templateUrl)) {
|
||||||
if (typeof prenormData.templateUrl !== 'string') {
|
if (typeof prenormData.templateUrl !== 'string') {
|
||||||
throw syntaxError(
|
throw syntaxError(
|
||||||
`The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`);
|
`The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`);
|
||||||
}
|
}
|
||||||
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
|
|
||||||
} else {
|
} else {
|
||||||
throw syntaxError(
|
throw syntaxError(
|
||||||
`No template specified for component ${stringify(prenormData.componentType)}`);
|
`No template specified for component ${stringify(prenormData.componentType)}`);
|
||||||
}
|
}
|
||||||
|
return SyncAsync.then(
|
||||||
|
this.normalizeTemplateOnly(prenormData),
|
||||||
|
(result: CompileTemplateMetadata) => this.normalizeExternalStylesheets(result));
|
||||||
|
}
|
||||||
|
|
||||||
if (normalizedTemplateSync && normalizedTemplateSync.styleUrls.length === 0) {
|
normalizeTemplateOnly(prenomData: PrenormalizedTemplateMetadata):
|
||||||
// sync case
|
SyncAsync<CompileTemplateMetadata> {
|
||||||
return new SyncAsyncResult(normalizedTemplateSync);
|
let template: SyncAsync<string>;
|
||||||
|
let templateUrl: string;
|
||||||
|
if (prenomData.template != null) {
|
||||||
|
template = prenomData.template;
|
||||||
|
templateUrl = prenomData.moduleUrl;
|
||||||
} else {
|
} else {
|
||||||
// async case
|
templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl !);
|
||||||
return new SyncAsyncResult(
|
template = this._fetch(templateUrl);
|
||||||
null, normalizedTemplateAsync.then(
|
|
||||||
(normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate)));
|
|
||||||
}
|
}
|
||||||
}
|
return SyncAsync.then(
|
||||||
|
template, (template) => this.normalizeLoadedTemplate(prenomData, template, templateUrl));
|
||||||
normalizeTemplateSync(prenomData: PrenormalizedTemplateMetadata): CompileTemplateMetadata {
|
|
||||||
return this.normalizeLoadedTemplate(prenomData, prenomData.template !, prenomData.moduleUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizeTemplateAsync(prenomData: PrenormalizedTemplateMetadata):
|
|
||||||
Promise<CompileTemplateMetadata> {
|
|
||||||
const templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl !);
|
|
||||||
return this._fetch(templateUrl)
|
|
||||||
.then((value) => this.normalizeLoadedTemplate(prenomData, value, templateUrl));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeLoadedTemplate(
|
normalizeLoadedTemplate(
|
||||||
|
@ -162,37 +154,42 @@ export class DirectiveNormalizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeExternalStylesheets(templateMeta: CompileTemplateMetadata):
|
normalizeExternalStylesheets(templateMeta: CompileTemplateMetadata):
|
||||||
Promise<CompileTemplateMetadata> {
|
SyncAsync<CompileTemplateMetadata> {
|
||||||
return this._loadMissingExternalStylesheets(templateMeta.styleUrls)
|
return SyncAsync.then(
|
||||||
.then((externalStylesheets) => new CompileTemplateMetadata({
|
this._loadMissingExternalStylesheets(templateMeta.styleUrls),
|
||||||
encapsulation: templateMeta.encapsulation,
|
(externalStylesheets) => new CompileTemplateMetadata({
|
||||||
template: templateMeta.template,
|
encapsulation: templateMeta.encapsulation,
|
||||||
templateUrl: templateMeta.templateUrl,
|
template: templateMeta.template,
|
||||||
styles: templateMeta.styles,
|
templateUrl: templateMeta.templateUrl,
|
||||||
styleUrls: templateMeta.styleUrls,
|
styles: templateMeta.styles,
|
||||||
externalStylesheets: externalStylesheets,
|
styleUrls: templateMeta.styleUrls,
|
||||||
ngContentSelectors: templateMeta.ngContentSelectors,
|
externalStylesheets: externalStylesheets,
|
||||||
animations: templateMeta.animations,
|
ngContentSelectors: templateMeta.ngContentSelectors,
|
||||||
interpolation: templateMeta.interpolation,
|
animations: templateMeta.animations,
|
||||||
isInline: templateMeta.isInline,
|
interpolation: templateMeta.interpolation,
|
||||||
}));
|
isInline: templateMeta.isInline,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadMissingExternalStylesheets(
|
private _loadMissingExternalStylesheets(
|
||||||
styleUrls: string[],
|
styleUrls: string[],
|
||||||
loadedStylesheets:
|
loadedStylesheets:
|
||||||
Map<string, CompileStylesheetMetadata> = new Map<string, CompileStylesheetMetadata>()):
|
Map<string, CompileStylesheetMetadata> = new Map<string, CompileStylesheetMetadata>()):
|
||||||
Promise<CompileStylesheetMetadata[]> {
|
SyncAsync<CompileStylesheetMetadata[]> {
|
||||||
return Promise
|
return SyncAsync.then(
|
||||||
.all(styleUrls.filter((styleUrl) => !loadedStylesheets.has(styleUrl))
|
SyncAsync.all(styleUrls.filter((styleUrl) => !loadedStylesheets.has(styleUrl))
|
||||||
.map(styleUrl => this._fetch(styleUrl).then((loadedStyle) => {
|
.map(
|
||||||
const stylesheet = this.normalizeStylesheet(
|
styleUrl => SyncAsync.then(
|
||||||
new CompileStylesheetMetadata({styles: [loadedStyle], moduleUrl: styleUrl}));
|
this._fetch(styleUrl),
|
||||||
loadedStylesheets.set(styleUrl, stylesheet);
|
(loadedStyle) => {
|
||||||
return this._loadMissingExternalStylesheets(
|
const stylesheet =
|
||||||
stylesheet.styleUrls, loadedStylesheets);
|
this.normalizeStylesheet(new CompileStylesheetMetadata(
|
||||||
})))
|
{styles: [loadedStyle], moduleUrl: styleUrl}));
|
||||||
.then((_) => Array.from(loadedStylesheets.values()));
|
loadedStylesheets.set(styleUrl, stylesheet);
|
||||||
|
return this._loadMissingExternalStylesheets(
|
||||||
|
stylesheet.styleUrls, loadedStylesheets);
|
||||||
|
}))),
|
||||||
|
(_) => Array.from(loadedStylesheets.values()));
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeStylesheet(stylesheet: CompileStylesheetMetadata): CompileStylesheetMetadata {
|
normalizeStylesheet(stylesheet: CompileStylesheetMetadata): CompileStylesheetMetadata {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {jitStatements} from '../output/output_jit';
|
||||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||||
import {SummaryResolver} from '../summary_resolver';
|
import {SummaryResolver} from '../summary_resolver';
|
||||||
import {TemplateParser} from '../template_parser/template_parser';
|
import {TemplateParser} from '../template_parser/template_parser';
|
||||||
import {OutputContext, SyncAsyncResult} from '../util';
|
import {OutputContext, SyncAsync} from '../util';
|
||||||
import {ViewCompiler} from '../view_compiler/view_compiler';
|
import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,20 +51,20 @@ export class JitCompiler implements Compiler {
|
||||||
get injector(): Injector { return this._injector; }
|
get injector(): Injector { return this._injector; }
|
||||||
|
|
||||||
compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> {
|
compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> {
|
||||||
return this._compileModuleAndComponents(moduleType, true).syncResult !;
|
return SyncAsync.assertSync(this._compileModuleAndComponents(moduleType, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>> {
|
compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>> {
|
||||||
return this._compileModuleAndComponents(moduleType, false).asyncResult !;
|
return Promise.resolve(this._compileModuleAndComponents(moduleType, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
compileModuleAndAllComponentsSync<T>(moduleType: Type<T>): ModuleWithComponentFactories<T> {
|
compileModuleAndAllComponentsSync<T>(moduleType: Type<T>): ModuleWithComponentFactories<T> {
|
||||||
return this._compileModuleAndAllComponents(moduleType, true).syncResult !;
|
return SyncAsync.assertSync(this._compileModuleAndAllComponents(moduleType, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>):
|
compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>):
|
||||||
Promise<ModuleWithComponentFactories<T>> {
|
Promise<ModuleWithComponentFactories<T>> {
|
||||||
return this._compileModuleAndAllComponents(moduleType, false).asyncResult !;
|
return Promise.resolve(this._compileModuleAndAllComponents(moduleType, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
getNgContentSelectors(component: Type<any>): string[] {
|
getNgContentSelectors(component: Type<any>): string[] {
|
||||||
|
@ -97,36 +97,24 @@ export class JitCompiler implements Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
|
private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
|
||||||
SyncAsyncResult<NgModuleFactory<T>> {
|
SyncAsync<NgModuleFactory<T>> {
|
||||||
const loadingPromise = this._loadModules(moduleType, isSync);
|
return SyncAsync.then(this._loadModules(moduleType, isSync), () => {
|
||||||
const createResult = () => {
|
|
||||||
this._compileComponents(moduleType, null);
|
this._compileComponents(moduleType, null);
|
||||||
return this._compileModule(moduleType);
|
return this._compileModule(moduleType);
|
||||||
};
|
});
|
||||||
if (isSync) {
|
|
||||||
return new SyncAsyncResult(createResult());
|
|
||||||
} else {
|
|
||||||
return new SyncAsyncResult(null, loadingPromise.then(createResult));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileModuleAndAllComponents<T>(moduleType: Type<T>, isSync: boolean):
|
private _compileModuleAndAllComponents<T>(moduleType: Type<T>, isSync: boolean):
|
||||||
SyncAsyncResult<ModuleWithComponentFactories<T>> {
|
SyncAsync<ModuleWithComponentFactories<T>> {
|
||||||
const loadingPromise = this._loadModules(moduleType, isSync);
|
return SyncAsync.then(this._loadModules(moduleType, isSync), () => {
|
||||||
const createResult = () => {
|
|
||||||
const componentFactories: ComponentFactory<any>[] = [];
|
const componentFactories: ComponentFactory<any>[] = [];
|
||||||
this._compileComponents(moduleType, componentFactories);
|
this._compileComponents(moduleType, componentFactories);
|
||||||
return new ModuleWithComponentFactories(this._compileModule(moduleType), componentFactories);
|
return new ModuleWithComponentFactories(this._compileModule(moduleType), componentFactories);
|
||||||
};
|
});
|
||||||
if (isSync) {
|
|
||||||
return new SyncAsyncResult(createResult());
|
|
||||||
} else {
|
|
||||||
return new SyncAsyncResult(null, loadingPromise.then(createResult));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadModules(mainModule: any, isSync: boolean): Promise<any> {
|
private _loadModules(mainModule: any, isSync: boolean): SyncAsync<any> {
|
||||||
const loadingPromises: Promise<any>[] = [];
|
const loading: Promise<any>[] = [];
|
||||||
const mainNgModule = this._metadataResolver.getNgModuleMetadata(mainModule) !;
|
const mainNgModule = this._metadataResolver.getNgModuleMetadata(mainModule) !;
|
||||||
// Note: for runtime compilation, we want to transitively compile all modules,
|
// Note: for runtime compilation, we want to transitively compile all modules,
|
||||||
// so we also need to load the declared directives / pipes for all nested modules.
|
// so we also need to load the declared directives / pipes for all nested modules.
|
||||||
|
@ -137,13 +125,13 @@ export class JitCompiler implements Compiler {
|
||||||
const promise =
|
const promise =
|
||||||
this._metadataResolver.loadDirectiveMetadata(moduleMeta.type.reference, ref, isSync);
|
this._metadataResolver.loadDirectiveMetadata(moduleMeta.type.reference, ref, isSync);
|
||||||
if (promise) {
|
if (promise) {
|
||||||
loadingPromises.push(promise);
|
loading.push(promise);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this._filterJitIdentifiers(moduleMeta.declaredPipes)
|
this._filterJitIdentifiers(moduleMeta.declaredPipes)
|
||||||
.forEach((ref) => this._metadataResolver.getOrLoadPipeMetadata(ref));
|
.forEach((ref) => this._metadataResolver.getOrLoadPipeMetadata(ref));
|
||||||
});
|
});
|
||||||
return Promise.all(loadingPromises);
|
return SyncAsync.all(loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileModule<T>(moduleType: Type<T>): NgModuleFactory<T> {
|
private _compileModule<T>(moduleType: Type<T>): NgModuleFactory<T> {
|
||||||
|
|
|
@ -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 {Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, RendererType2, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef, ɵConsole as Console, ɵERROR_COMPONENT_TYPE, ɵccf as createComponentFactory, ɵstringify as stringify} from '@angular/core';
|
import {Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, RendererType2, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef, ɵConsole as Console, ɵERROR_COMPONENT_TYPE, ɵccf as createComponentFactory, ɵisPromise as isPromise, ɵstringify as stringify} from '@angular/core';
|
||||||
|
|
||||||
import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
|
import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
|
||||||
import {ngfactoryFilePath} from './aot/util';
|
import {ngfactoryFilePath} from './aot/util';
|
||||||
|
@ -24,7 +24,7 @@ import {PipeResolver} from './pipe_resolver';
|
||||||
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||||
import {SummaryResolver} from './summary_resolver';
|
import {SummaryResolver} from './summary_resolver';
|
||||||
import {getUrlScheme} from './url_resolver';
|
import {getUrlScheme} from './url_resolver';
|
||||||
import {MODULE_SUFFIX, ValueTransformer, noUndefined, syntaxError, visitValue} from './util';
|
import {MODULE_SUFFIX, SyncAsync, ValueTransformer, noUndefined, syntaxError, visitValue} from './util';
|
||||||
|
|
||||||
export type ErrorCollector = (error: any, type?: any) => void;
|
export type ErrorCollector = (error: any, type?: any) => void;
|
||||||
export const ERROR_COLLECTOR_TOKEN = new InjectionToken('ErrorCollector');
|
export const ERROR_COLLECTOR_TOKEN = new InjectionToken('ErrorCollector');
|
||||||
|
@ -170,7 +170,7 @@ export class CompileMetadataResolver {
|
||||||
return typeSummary && typeSummary.summaryKind === kind ? typeSummary : null;
|
return typeSummary && typeSummary.summaryKind === kind ? typeSummary : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDirectiveMetadata(ngModuleType: any, directiveType: any, isSync: boolean): Promise<any>|null {
|
loadDirectiveMetadata(ngModuleType: any, directiveType: any, isSync: boolean): SyncAsync<null> {
|
||||||
if (this._directiveCache.has(directiveType)) {
|
if (this._directiveCache.has(directiveType)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
this._directiveCache.set(directiveType, normalizedDirMeta);
|
this._directiveCache.set(directiveType, normalizedDirMeta);
|
||||||
this._summaryCache.set(directiveType, normalizedDirMeta.toSummary());
|
this._summaryCache.set(directiveType, normalizedDirMeta.toSummary());
|
||||||
return normalizedDirMeta;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (metadata.isComponent) {
|
if (metadata.isComponent) {
|
||||||
|
@ -222,16 +222,11 @@ export class CompileMetadataResolver {
|
||||||
animations: template.animations,
|
animations: template.animations,
|
||||||
interpolation: template.interpolation
|
interpolation: template.interpolation
|
||||||
});
|
});
|
||||||
if (templateMeta.syncResult) {
|
if (isPromise(templateMeta) && isSync) {
|
||||||
createDirectiveMetadata(templateMeta.syncResult);
|
this._reportError(componentStillLoadingError(directiveType), directiveType);
|
||||||
return null;
|
return null;
|
||||||
} else {
|
|
||||||
if (isSync) {
|
|
||||||
this._reportError(componentStillLoadingError(directiveType), directiveType);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return templateMeta.asyncResult !.then(createDirectiveMetadata);
|
|
||||||
}
|
}
|
||||||
|
return SyncAsync.then(templateMeta, createDirectiveMetadata);
|
||||||
} else {
|
} else {
|
||||||
// directive
|
// directive
|
||||||
createDirectiveMetadata(null);
|
createDirectiveMetadata(null);
|
||||||
|
|
|
@ -11,5 +11,5 @@
|
||||||
* to load templates.
|
* to load templates.
|
||||||
*/
|
*/
|
||||||
export class ResourceLoader {
|
export class ResourceLoader {
|
||||||
get(url: string): Promise<string>|null { return null; }
|
get(url: string): Promise<string>|string { return ''; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {ɵisPromise as isPromise} from '@angular/core';
|
||||||
|
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
|
|
||||||
export const MODULE_SUFFIX = '';
|
export const MODULE_SUFFIX = '';
|
||||||
|
@ -80,19 +82,28 @@ export class ValueTransformer implements ValueVisitor {
|
||||||
visitOther(value: any, context: any): any { return value; }
|
visitOther(value: any, context: any): any { return value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SyncAsyncResult<T> {
|
export type SyncAsync<T> = T | Promise<T>;
|
||||||
constructor(public syncResult: T|null, public asyncResult: Promise<T>|null = null) {
|
|
||||||
if (!asyncResult) {
|
export const SyncAsync = {
|
||||||
this.asyncResult = Promise.resolve(syncResult);
|
assertSync: <T>(value: SyncAsync<T>): T => {
|
||||||
|
if (isPromise(value)) {
|
||||||
|
throw new Error(`Illegal state: value cannot be a promise`);
|
||||||
}
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
then: <T, R>(value: SyncAsync<T>, cb: (value: T) => R | Promise<R>| SyncAsync<R>):
|
||||||
|
SyncAsync<R> => { return isPromise(value) ? value.then(cb) : cb(value);},
|
||||||
|
all: <T>(syncAsyncValues: SyncAsync<T>[]): SyncAsync<T[]> => {
|
||||||
|
return syncAsyncValues.some(isPromise) ? Promise.all(syncAsyncValues) : syncAsyncValues as T[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function syntaxError(msg: string): Error {
|
export function syntaxError(msg: string):
|
||||||
const error = Error(msg);
|
Error {
|
||||||
(error as any)[ERROR_SYNTAX_ERROR] = true;
|
const error = Error(msg);
|
||||||
return error;
|
(error as any)[ERROR_SYNTAX_ERROR] = true;
|
||||||
}
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
const ERROR_SYNTAX_ERROR = 'ngSyntaxError';
|
const ERROR_SYNTAX_ERROR = 'ngSyntaxError';
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import {GeneratedFile, toTypeScript} from '@angular/compiler';
|
import {GeneratedFile, toTypeScript} from '@angular/compiler';
|
||||||
import {NodeFlags} from '@angular/core/src/view/index';
|
import {NodeFlags} from '@angular/core/src/view/index';
|
||||||
import {async} from '@angular/core/testing';
|
|
||||||
import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped';
|
import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
@ -20,11 +19,11 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
let angularFiles = setup();
|
let angularFiles = setup();
|
||||||
|
|
||||||
describe('Quickstart', () => {
|
describe('Quickstart', () => {
|
||||||
it('should compile', async(() => compile([QUICKSTART, angularFiles]).then(({genFiles}) => {
|
it('should compile', () => {
|
||||||
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
|
const {genFiles} = compile([QUICKSTART, angularFiles]);
|
||||||
.toBeDefined();
|
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
||||||
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
||||||
})));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('aot source mapping', () => {
|
describe('aot source mapping', () => {
|
||||||
|
@ -51,12 +50,10 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
rootDir = {'app': appDir};
|
rootDir = {'app': appDir};
|
||||||
});
|
});
|
||||||
|
|
||||||
function compileApp(): Promise<GeneratedFile> {
|
function compileApp(): GeneratedFile {
|
||||||
return compile([rootDir, angularFiles])
|
const {genFiles} = compile([rootDir, angularFiles]);
|
||||||
.then(
|
return genFiles.find(
|
||||||
({genFiles}) => {return genFiles.find(
|
genFile => genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts'));
|
||||||
genFile =>
|
|
||||||
genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts'))});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function findLineAndColumn(
|
function findLineAndColumn(
|
||||||
|
@ -106,102 +103,95 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
|
|
||||||
function declareTests({ngUrl, templateDecorator}:
|
function declareTests({ngUrl, templateDecorator}:
|
||||||
{ngUrl: string, templateDecorator: (template: string) => string}) {
|
{ngUrl: string, templateDecorator: (template: string) => string}) {
|
||||||
it('should use the right source url in html parse errors', async(() => {
|
it('should use the right source url in html parse errors', () => {
|
||||||
appDir['app.component.ts'] =
|
appDir['app.component.ts'] = createComponentSource(templateDecorator('<div>\n </error>'));
|
||||||
createComponentSource(templateDecorator('<div>\n </error>'));
|
|
||||||
|
|
||||||
expectPromiseToThrow(
|
expect(() => compileApp())
|
||||||
compileApp(), new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:2`));
|
.toThrowError(new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:2`));
|
||||||
}));
|
});
|
||||||
|
|
||||||
it('should use the right source url in template parse errors', async(() => {
|
it('should use the right source url in template parse errors', () => {
|
||||||
appDir['app.component.ts'] = createComponentSource(
|
appDir['app.component.ts'] =
|
||||||
templateDecorator('<div>\n <div unknown="{{ctxProp}}"></div>'));
|
createComponentSource(templateDecorator('<div>\n <div unknown="{{ctxProp}}"></div>'));
|
||||||
|
|
||||||
expectPromiseToThrow(
|
expect(() => compileApp())
|
||||||
compileApp(), new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:7`));
|
.toThrowError(new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:7`));
|
||||||
}));
|
});
|
||||||
|
|
||||||
it('should create a sourceMap for the template', async(() => {
|
it('should create a sourceMap for the template', () => {
|
||||||
const template = 'Hello World!';
|
const template = 'Hello World!';
|
||||||
|
|
||||||
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
|
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
|
||||||
|
|
||||||
compileApp().then((genFile) => {
|
const genFile = compileApp();
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
const sourceMap = extractSourceMap(genSource) !;
|
const sourceMap = extractSourceMap(genSource) !;
|
||||||
expect(sourceMap.file).toEqual(genFile.genFileUrl);
|
expect(sourceMap.file).toEqual(genFile.genFileUrl);
|
||||||
|
|
||||||
// the generated file contains code that is not mapped to
|
// the generated file contains code that is not mapped to
|
||||||
// the template but rather to the original source file (e.g. import statements, ...)
|
// the template but rather to the original source file (e.g. import statements, ...)
|
||||||
const templateIndex = sourceMap.sources.indexOf(ngUrl);
|
const templateIndex = sourceMap.sources.indexOf(ngUrl);
|
||||||
expect(sourceMap.sourcesContent[templateIndex]).toEqual(template);
|
expect(sourceMap.sourcesContent[templateIndex]).toEqual(template);
|
||||||
|
|
||||||
// for the mapping to the original source file we don't store the source code
|
// for the mapping to the original source file we don't store the source code
|
||||||
// as we want to keep whatever TypeScript / ... produced for them.
|
// as we want to keep whatever TypeScript / ... produced for them.
|
||||||
const sourceIndex = sourceMap.sources.indexOf(ngComponentPath);
|
const sourceIndex = sourceMap.sources.indexOf(ngComponentPath);
|
||||||
expect(sourceMap.sourcesContent[sourceIndex]).toBe(' ');
|
expect(sourceMap.sourcesContent[sourceIndex]).toBe(' ');
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should map elements correctly to the source', async(() => {
|
it('should map elements correctly to the source', () => {
|
||||||
const template = '<div>\n <span></span></div>';
|
const template = '<div>\n <span></span></div>';
|
||||||
|
|
||||||
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
|
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
|
||||||
|
|
||||||
compileApp().then((genFile) => {
|
const genFile = compileApp();
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
const sourceMap = extractSourceMap(genSource) !;
|
const sourceMap = extractSourceMap(genSource) !;
|
||||||
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `'span'`)))
|
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `'span'`)))
|
||||||
.toEqual({line: 2, column: 3, source: ngUrl});
|
.toEqual({line: 2, column: 3, source: ngUrl});
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should map bindings correctly to the source', async(() => {
|
it('should map bindings correctly to the source', () => {
|
||||||
const template = `<div>\n <span [title]="someMethod()"></span></div>`;
|
const template = `<div>\n <span [title]="someMethod()"></span></div>`;
|
||||||
|
|
||||||
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
|
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
|
||||||
|
|
||||||
compileApp().then((genFile) => {
|
const genFile = compileApp();
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
const sourceMap = extractSourceMap(genSource) !;
|
const sourceMap = extractSourceMap(genSource) !;
|
||||||
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
|
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
|
||||||
.toEqual({line: 2, column: 9, source: ngUrl});
|
.toEqual({line: 2, column: 9, source: ngUrl});
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should map events correctly to the source', async(() => {
|
it('should map events correctly to the source', () => {
|
||||||
const template = `<div>\n <span (click)="someMethod()"></span></div>`;
|
const template = `<div>\n <span (click)="someMethod()"></span></div>`;
|
||||||
|
|
||||||
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
|
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
|
||||||
|
|
||||||
compileApp().then((genFile) => {
|
const genFile = compileApp();
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
const sourceMap = extractSourceMap(genSource) !;
|
const sourceMap = extractSourceMap(genSource) !;
|
||||||
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
|
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
|
||||||
.toEqual({line: 2, column: 9, source: ngUrl});
|
.toEqual({line: 2, column: 9, source: ngUrl});
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should map non template parts to the source file', async(() => {
|
it('should map non template parts to the source file', () => {
|
||||||
appDir['app.component.ts'] = createComponentSource(templateDecorator('Hello World!'));
|
appDir['app.component.ts'] = createComponentSource(templateDecorator('Hello World!'));
|
||||||
|
|
||||||
compileApp().then((genFile) => {
|
const genFile = compileApp();
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
const sourceMap = extractSourceMap(genSource) !;
|
const sourceMap = extractSourceMap(genSource) !;
|
||||||
expect(originalPositionFor(sourceMap, {line: 1, column: 0}))
|
expect(originalPositionFor(sourceMap, {line: 1, column: 0}))
|
||||||
.toEqual({line: 1, column: 0, source: ngComponentPath});
|
.toEqual({line: 1, column: 0, source: ngComponentPath});
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('errors', () => {
|
describe('errors', () => {
|
||||||
it('should only warn if not all arguments of an @Injectable class can be resolved',
|
it('should only warn if not all arguments of an @Injectable class can be resolved', () => {
|
||||||
async(() => {
|
const FILES: MockDirectory = {
|
||||||
const FILES: MockDirectory = {
|
app: {
|
||||||
app: {
|
'app.ts': `
|
||||||
'app.ts': `
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -209,20 +199,19 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
constructor(a: boolean) {}
|
constructor(a: boolean) {}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const warnSpy = spyOn(console, 'warn');
|
const warnSpy = spyOn(console, 'warn');
|
||||||
compile([FILES, angularFiles]).then(() => {
|
compile([FILES, angularFiles]);
|
||||||
expect(warnSpy).toHaveBeenCalledWith(
|
expect(warnSpy).toHaveBeenCalledWith(
|
||||||
`Warning: Can't resolve all parameters for MyService in /app/app.ts: (?). This will become an error in Angular v5.x`);
|
`Warning: Can't resolve all parameters for MyService in /app/app.ts: (?). This will become an error in Angular v5.x`);
|
||||||
});
|
|
||||||
|
|
||||||
}));
|
});
|
||||||
|
|
||||||
it('should be able to supress a null access', async(() => {
|
it('should be able to supress a null access', () => {
|
||||||
const FILES: MockDirectory = {
|
const FILES: MockDirectory = {
|
||||||
app: {
|
app: {
|
||||||
'app.ts': `
|
'app.ts': `
|
||||||
import {Component, NgModule} from '@angular/core';
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
interface Person { name: string; }
|
interface Person { name: string; }
|
||||||
|
@ -240,16 +229,16 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
})
|
})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
compile([FILES, angularFiles], {postCompile: expectNoDiagnostics});
|
compile([FILES, angularFiles], {postCompile: expectNoDiagnostics});
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add the preamble to generated files', async(() => {
|
it('should add the preamble to generated files', () => {
|
||||||
const FILES: MockDirectory = {
|
const FILES: MockDirectory = {
|
||||||
app: {
|
app: {
|
||||||
'app.ts': `
|
'app.ts': `
|
||||||
import { NgModule, Component } from '@angular/core';
|
import { NgModule, Component } from '@angular/core';
|
||||||
|
|
||||||
@Component({ template: '' })
|
@Component({ template: '' })
|
||||||
|
@ -258,24 +247,21 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
@NgModule({ declarations: [ AppComponent ] })
|
@NgModule({ declarations: [ AppComponent ] })
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const genFilePreamble = '/* Hello world! */';
|
const genFilePreamble = '/* Hello world! */';
|
||||||
compile([FILES, angularFiles]).then(({genFiles}) => {
|
const {genFiles} = compile([FILES, angularFiles]);
|
||||||
const genFile =
|
const genFile =
|
||||||
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
||||||
const genSource = toTypeScript(genFile, genFilePreamble);
|
const genSource = toTypeScript(genFile, genFilePreamble);
|
||||||
expect(genSource.startsWith(genFilePreamble)).toBe(true);
|
expect(genSource.startsWith(genFilePreamble)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('ComponentFactories', () => {
|
describe('ComponentFactories', () => {
|
||||||
it('should include inputs, outputs and ng-content selectors in the component factory',
|
it('should include inputs, outputs and ng-content selectors in the component factory', () => {
|
||||||
async(() => {
|
const FILES: MockDirectory = {
|
||||||
const FILES: MockDirectory = {
|
app: {
|
||||||
app: {
|
'app.ts': `
|
||||||
'app.ts': `
|
|
||||||
import {Component, NgModule, Input, Output} from '@angular/core';
|
import {Component, NgModule, Input, Output} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -295,31 +281,28 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
})
|
})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
compile([FILES, angularFiles]).then(({genFiles}) => {
|
const {genFiles} = compile([FILES, angularFiles]);
|
||||||
const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
|
const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
const createComponentFactoryCall =
|
const createComponentFactoryCall = /ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, '');
|
||||||
/ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, '');
|
// selector
|
||||||
// selector
|
expect(createComponentFactoryCall).toContain('my-comp');
|
||||||
expect(createComponentFactoryCall).toContain('my-comp');
|
// inputs
|
||||||
// inputs
|
expect(createComponentFactoryCall).toContain(`{aInputProp:'aInputName'}`);
|
||||||
expect(createComponentFactoryCall).toContain(`{aInputProp:'aInputName'}`);
|
// outputs
|
||||||
// outputs
|
expect(createComponentFactoryCall).toContain(`{aOutputProp:'aOutputName'}`);
|
||||||
expect(createComponentFactoryCall).toContain(`{aOutputProp:'aOutputName'}`);
|
// ngContentSelectors
|
||||||
// ngContentSelectors
|
expect(createComponentFactoryCall).toContain(`['*','child']`);
|
||||||
expect(createComponentFactoryCall).toContain(`['*','child']`);
|
});
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('generated templates', () => {
|
describe('generated templates', () => {
|
||||||
it('should not call `check` for directives without bindings nor ngDoCheck/ngOnInit',
|
it('should not call `check` for directives without bindings nor ngDoCheck/ngOnInit', () => {
|
||||||
async(() => {
|
const FILES: MockDirectory = {
|
||||||
const FILES: MockDirectory = {
|
app: {
|
||||||
app: {
|
'app.ts': `
|
||||||
'app.ts': `
|
|
||||||
import { NgModule, Component } from '@angular/core';
|
import { NgModule, Component } from '@angular/core';
|
||||||
|
|
||||||
@Component({ template: '' })
|
@Component({ template: '' })
|
||||||
|
@ -328,16 +311,15 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
@NgModule({ declarations: [ AppComponent ] })
|
@NgModule({ declarations: [ AppComponent ] })
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
compile([FILES, angularFiles]).then(({genFiles}) => {
|
const {genFiles} = compile([FILES, angularFiles]);
|
||||||
const genFile = genFiles.find(
|
const genFile =
|
||||||
gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
expect(genSource).not.toContain('check(');
|
expect(genSource).not.toContain('check(');
|
||||||
});
|
|
||||||
|
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('inheritance with summaries', () => {
|
describe('inheritance with summaries', () => {
|
||||||
|
@ -376,16 +358,15 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return compile([libInput, angularFiles], {useSummaries: true})
|
const {outDir: libOutDir} = compile([libInput, angularFiles], {useSummaries: true});
|
||||||
.then(({outDir}) => compile([outDir, appInput, angularFiles], {useSummaries: true}))
|
const {genFiles} = compile([libOutDir, appInput, angularFiles], {useSummaries: true});
|
||||||
.then(({genFiles}) => genFiles.find(gf => gf.srcFileUrl === '/app/main.ts'));
|
return genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should inherit ctor and lifecycle hooks from classes in other compilation units',
|
it('should inherit ctor and lifecycle hooks from classes in other compilation units', () => {
|
||||||
async(() => {
|
const libInput: MockDirectory = {
|
||||||
const libInput: MockDirectory = {
|
'lib': {
|
||||||
'lib': {
|
'base.ts': `
|
||||||
'base.ts': `
|
|
||||||
export class AParam {}
|
export class AParam {}
|
||||||
|
|
||||||
export class Base {
|
export class Base {
|
||||||
|
@ -393,11 +374,11 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
ngOnDestroy() {}
|
ngOnDestroy() {}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const appInput: MockDirectory = {
|
const appInput: MockDirectory = {
|
||||||
'app': {
|
'app': {
|
||||||
'main.ts': `
|
'main.ts': `
|
||||||
import {NgModule, Component} from '@angular/core';
|
import {NgModule, Component} from '@angular/core';
|
||||||
import {Base} from '../lib/base';
|
import {Base} from '../lib/base';
|
||||||
|
|
||||||
|
@ -409,21 +390,19 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
})
|
})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
compile([libInput, angularFiles], {useSummaries: true})
|
const {outDir: libOutDir} = compile([libInput, angularFiles], {useSummaries: true});
|
||||||
.then(({outDir}) => compile([outDir, appInput, angularFiles], {useSummaries: true}))
|
const {genFiles} = compile([libOutDir, appInput, angularFiles], {useSummaries: true});
|
||||||
.then(({genFiles}) => {
|
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
||||||
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
expect(toTypeScript(mainNgFactory))
|
||||||
expect(toTypeScript(mainNgFactory))
|
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`);
|
||||||
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`);
|
});
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should inherit ctor and lifecycle hooks from classes in other compilation units over 2 levels',
|
it('should inherit ctor and lifecycle hooks from classes in other compilation units over 2 levels',
|
||||||
async(() => {
|
() => {
|
||||||
const lib1Input: MockDirectory = {
|
const lib1Input: MockDirectory = {
|
||||||
'lib1': {
|
'lib1': {
|
||||||
'base.ts': `
|
'base.ts': `
|
||||||
|
@ -463,135 +442,134 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
compile([lib1Input, angularFiles], {useSummaries: true})
|
const {outDir: lib1OutDir} = compile([lib1Input, angularFiles], {useSummaries: true});
|
||||||
.then(({outDir}) => compile([outDir, lib2Input, angularFiles], {useSummaries: true}))
|
const {outDir: lib2OutDir} =
|
||||||
.then(({outDir}) => compile([outDir, appInput, angularFiles], {useSummaries: true}))
|
compile([lib1OutDir, lib2Input, angularFiles], {useSummaries: true});
|
||||||
.then(({genFiles}) => {
|
const {genFiles} = compile([lib2OutDir, appInput, angularFiles], {useSummaries: true});
|
||||||
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
||||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||||
expect(toTypeScript(mainNgFactory))
|
expect(toTypeScript(mainNgFactory))
|
||||||
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam_2]`);
|
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam_2]`);
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
describe('Injectable', () => {
|
describe('Injectable', () => {
|
||||||
it('should allow to inherit', async(() => {
|
it('should allow to inherit', () => {
|
||||||
compileParentAndChild({
|
const mainNgFactory = compileParentAndChild({
|
||||||
parentClassDecorator: '@Injectable()',
|
parentClassDecorator: '@Injectable()',
|
||||||
parentModuleDecorator: '@NgModule({providers: [Base]})',
|
parentModuleDecorator: '@NgModule({providers: [Base]})',
|
||||||
childClassDecorator: '@Injectable()',
|
childClassDecorator: '@Injectable()',
|
||||||
childModuleDecorator: '@NgModule({providers: [Extends]})',
|
childModuleDecorator: '@NgModule({providers: [Extends]})',
|
||||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
});
|
||||||
}));
|
expect(mainNgFactory).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should error if the child class has no matching decorator', async(() => {
|
it('should error if the child class has no matching decorator', () => {
|
||||||
compileParentAndChild({
|
expect(() => compileParentAndChild({
|
||||||
parentClassDecorator: '@Injectable()',
|
parentClassDecorator: '@Injectable()',
|
||||||
parentModuleDecorator: '@NgModule({providers: [Base]})',
|
parentModuleDecorator: '@NgModule({providers: [Base]})',
|
||||||
childClassDecorator: '',
|
childClassDecorator: '',
|
||||||
childModuleDecorator: '@NgModule({providers: [Extends]})',
|
childModuleDecorator: '@NgModule({providers: [Extends]})',
|
||||||
}).then(fail, (e) => {
|
}))
|
||||||
expect(e.message).toContain(
|
.toThrowError(
|
||||||
'Class Extends in /app/main.ts extends from a Injectable in another compilation unit without duplicating the decorator. ' +
|
'Class Extends in /app/main.ts extends from a Injectable in another compilation unit without duplicating the decorator. ' +
|
||||||
'Please add a Injectable or Pipe or Directive or Component or NgModule decorator to the class.');
|
'Please add a Injectable or Pipe or Directive or Component or NgModule decorator to the class.');
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Component', () => {
|
describe('Component', () => {
|
||||||
it('should allow to inherit', async(() => {
|
it('should allow to inherit', () => {
|
||||||
compileParentAndChild({
|
const mainNgFactory = compileParentAndChild({
|
||||||
parentClassDecorator: `@Component({template: ''})`,
|
parentClassDecorator: `@Component({template: ''})`,
|
||||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||||
childClassDecorator: `@Component({template: ''})`,
|
childClassDecorator: `@Component({template: ''})`,
|
||||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
childModuleDecorator: '@NgModule({declarations: [Extends]})'
|
||||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
});
|
||||||
}));
|
expect(mainNgFactory).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should error if the child class has no matching decorator', async(() => {
|
it('should error if the child class has no matching decorator', () => {
|
||||||
compileParentAndChild({
|
expect(() => compileParentAndChild({
|
||||||
parentClassDecorator: `@Component({template: ''})`,
|
parentClassDecorator: `@Component({template: ''})`,
|
||||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||||
childClassDecorator: '',
|
childClassDecorator: '',
|
||||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||||
}).then(fail, (e) => {
|
}))
|
||||||
expect(e.message).toContain(
|
.toThrowError(
|
||||||
'Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator. ' +
|
'Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator. ' +
|
||||||
'Please add a Directive or Component decorator to the class.');
|
'Please add a Directive or Component decorator to the class.');
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Directive', () => {
|
describe('Directive', () => {
|
||||||
it('should allow to inherit', async(() => {
|
it('should allow to inherit', () => {
|
||||||
compileParentAndChild({
|
const mainNgFactory = compileParentAndChild({
|
||||||
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
|
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
|
||||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||||
childClassDecorator: `@Directive({selector: '[someDir]'})`,
|
childClassDecorator: `@Directive({selector: '[someDir]'})`,
|
||||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
});
|
||||||
}));
|
expect(mainNgFactory).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should error if the child class has no matching decorator', async(() => {
|
it('should error if the child class has no matching decorator', () => {
|
||||||
compileParentAndChild({
|
expect(() => compileParentAndChild({
|
||||||
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
|
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
|
||||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||||
childClassDecorator: '',
|
childClassDecorator: '',
|
||||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||||
}).then(fail, (e) => {
|
}))
|
||||||
expect(e.message).toContain(
|
.toThrowError(
|
||||||
'Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator. ' +
|
'Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator. ' +
|
||||||
'Please add a Directive or Component decorator to the class.');
|
'Please add a Directive or Component decorator to the class.');
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Pipe', () => {
|
describe('Pipe', () => {
|
||||||
it('should allow to inherit', async(() => {
|
it('should allow to inherit', () => {
|
||||||
compileParentAndChild({
|
const mainNgFactory = compileParentAndChild({
|
||||||
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
|
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
|
||||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||||
childClassDecorator: `@Pipe({name: 'somePipe'})`,
|
childClassDecorator: `@Pipe({name: 'somePipe'})`,
|
||||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
});
|
||||||
}));
|
expect(mainNgFactory).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should error if the child class has no matching decorator', async(() => {
|
it('should error if the child class has no matching decorator', () => {
|
||||||
compileParentAndChild({
|
expect(() => compileParentAndChild({
|
||||||
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
|
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
|
||||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||||
childClassDecorator: '',
|
childClassDecorator: '',
|
||||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||||
}).then(fail, (e) => {
|
}))
|
||||||
expect(e.message).toContain(
|
.toThrowError(
|
||||||
'Class Extends in /app/main.ts extends from a Pipe in another compilation unit without duplicating the decorator. ' +
|
'Class Extends in /app/main.ts extends from a Pipe in another compilation unit without duplicating the decorator. ' +
|
||||||
'Please add a Pipe decorator to the class.');
|
'Please add a Pipe decorator to the class.');
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('NgModule', () => {
|
describe('NgModule', () => {
|
||||||
it('should allow to inherit', async(() => {
|
it('should allow to inherit', () => {
|
||||||
compileParentAndChild({
|
const mainNgFactory = compileParentAndChild({
|
||||||
parentClassDecorator: `@NgModule()`,
|
parentClassDecorator: `@NgModule()`,
|
||||||
parentModuleDecorator: '',
|
parentModuleDecorator: '',
|
||||||
childClassDecorator: `@NgModule()`,
|
childClassDecorator: `@NgModule()`,
|
||||||
childModuleDecorator: '',
|
childModuleDecorator: '',
|
||||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
});
|
||||||
}));
|
expect(mainNgFactory).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should error if the child class has no matching decorator', async(() => {
|
it('should error if the child class has no matching decorator', () => {
|
||||||
compileParentAndChild({
|
expect(() => compileParentAndChild({
|
||||||
parentClassDecorator: `@NgModule()`,
|
parentClassDecorator: `@NgModule()`,
|
||||||
parentModuleDecorator: '',
|
parentModuleDecorator: '',
|
||||||
childClassDecorator: '',
|
childClassDecorator: '',
|
||||||
childModuleDecorator: '',
|
childModuleDecorator: '',
|
||||||
}).then(fail, (e) => {
|
}))
|
||||||
expect(e.message).toContain(
|
.toThrowError(
|
||||||
'Class Extends in /app/main.ts extends from a NgModule in another compilation unit without duplicating the decorator. ' +
|
'Class Extends in /app/main.ts extends from a NgModule in another compilation unit without duplicating the decorator. ' +
|
||||||
'Please add a NgModule decorator to the class.');
|
'Please add a NgModule decorator to the class.');
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -624,11 +602,11 @@ describe('compiler (bundled Angular)', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Quickstart', () => {
|
describe('Quickstart', () => {
|
||||||
it('should compile', async(() => compile([QUICKSTART, angularFiles]).then(({genFiles}) => {
|
it('should compile', () => {
|
||||||
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
|
const {genFiles} = compile([QUICKSTART, angularFiles]);
|
||||||
.toBeDefined();
|
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
||||||
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
||||||
})));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Bundled library', () => {
|
describe('Bundled library', () => {
|
||||||
|
@ -662,7 +640,7 @@ describe('compiler (bundled Angular)', () => {
|
||||||
({fileName, content}) => ({fileName: `/node_modules${fileName}`, content})));
|
({fileName, content}) => ({fileName: `/node_modules${fileName}`, content})));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should compile', async(() => compile([LIBRARY_USING_APP, libraryFiles, angularFiles])));
|
it('should compile', () => compile([LIBRARY_USING_APP, libraryFiles, angularFiles]));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -767,8 +745,3 @@ const LIBRARY_USING_APP: MockDirectory = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function expectPromiseToThrow(p: Promise<any>, msg: RegExp) {
|
|
||||||
p.then(
|
|
||||||
() => { throw new Error('Expected to throw'); }, (e) => { expect(e.message).toMatch(msg); });
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AotCompiler, AotCompilerHost, AotCompilerOptions, CompileSummaryKind, GeneratedFile, createAotCompiler, toTypeScript} from '@angular/compiler';
|
import {AotCompiler, AotCompilerHost, AotCompilerOptions, CompileSummaryKind, GeneratedFile, createAotCompiler, toTypeScript} from '@angular/compiler';
|
||||||
import {fakeAsync, tick} from '@angular/core/testing';
|
|
||||||
|
|
||||||
import {MockDirectory, compile, setup} from './test_util';
|
import {MockDirectory, compile, setup} from './test_util';
|
||||||
|
|
||||||
|
@ -16,19 +15,12 @@ describe('aot summaries for jit', () => {
|
||||||
|
|
||||||
function compileApp(rootDir: MockDirectory, options: {useSummaries?: boolean} = {}):
|
function compileApp(rootDir: MockDirectory, options: {useSummaries?: boolean} = {}):
|
||||||
{genFiles: GeneratedFile[], outDir: MockDirectory} {
|
{genFiles: GeneratedFile[], outDir: MockDirectory} {
|
||||||
let result: {genFiles: GeneratedFile[], outDir: MockDirectory} = null !;
|
return compile([rootDir, angularFiles], options);
|
||||||
let error: Error|null = null;
|
|
||||||
compile([rootDir, angularFiles], options).then((r) => result = r, (e) => error = e);
|
|
||||||
tick();
|
|
||||||
if (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should create @Injectable summaries', fakeAsync(() => {
|
it('should create @Injectable summaries', () => {
|
||||||
const appDir = {
|
const appDir = {
|
||||||
'app.module.ts': `
|
'app.module.ts': `
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
export class Dep {}
|
export class Dep {}
|
||||||
|
@ -38,23 +30,23 @@ describe('aot summaries for jit', () => {
|
||||||
constructor(d: Dep) {}
|
constructor(d: Dep) {}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
const genSource = toTypeScript(genFile);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain('export function MyServiceNgSummary()');
|
||||||
expect(genSource).toContain('export function MyServiceNgSummary()');
|
// Note: CompileSummaryKind.Injectable = 3
|
||||||
// Note: CompileSummaryKind.Injectable = 3
|
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i0.MyService/);
|
||||||
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i0.MyService/);
|
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
|
||||||
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create @Pipe summaries', fakeAsync(() => {
|
it('should create @Pipe summaries', () => {
|
||||||
const appDir = {
|
const appDir = {
|
||||||
'app.module.ts': `
|
'app.module.ts': `
|
||||||
import { Pipe, NgModule } from '@angular/core';
|
import { Pipe, NgModule } from '@angular/core';
|
||||||
|
|
||||||
export class Dep {}
|
export class Dep {}
|
||||||
|
@ -67,23 +59,23 @@ describe('aot summaries for jit', () => {
|
||||||
@NgModule({declarations: [MyPipe]})
|
@NgModule({declarations: [MyPipe]})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
const genSource = toTypeScript(genFile);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain('export function MyPipeNgSummary()');
|
||||||
expect(genSource).toContain('export function MyPipeNgSummary()');
|
// Note: CompileSummaryKind.Pipe = 1
|
||||||
// Note: CompileSummaryKind.Pipe = 1
|
expect(genSource).toMatch(/summaryKind:0,\s*type:\{\s*reference:i0.MyPipe/);
|
||||||
expect(genSource).toMatch(/summaryKind:0,\s*type:\{\s*reference:i0.MyPipe/);
|
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
|
||||||
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create @Directive summaries', fakeAsync(() => {
|
it('should create @Directive summaries', () => {
|
||||||
const appDir = {
|
const appDir = {
|
||||||
'app.module.ts': `
|
'app.module.ts': `
|
||||||
import { Directive, NgModule } from '@angular/core';
|
import { Directive, NgModule } from '@angular/core';
|
||||||
|
|
||||||
export class Dep {}
|
export class Dep {}
|
||||||
|
@ -96,23 +88,23 @@ describe('aot summaries for jit', () => {
|
||||||
@NgModule({declarations: [MyDirective]})
|
@NgModule({declarations: [MyDirective]})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
const genSource = toTypeScript(genFile);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain('export function MyDirectiveNgSummary()');
|
||||||
expect(genSource).toContain('export function MyDirectiveNgSummary()');
|
// Note: CompileSummaryKind.Directive = 1
|
||||||
// Note: CompileSummaryKind.Directive = 1
|
expect(genSource).toMatch(/summaryKind:1,\s*type:\{\s*reference:i0.MyDirective/);
|
||||||
expect(genSource).toMatch(/summaryKind:1,\s*type:\{\s*reference:i0.MyDirective/);
|
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
|
||||||
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create @NgModule summaries', fakeAsync(() => {
|
it('should create @NgModule summaries', () => {
|
||||||
const appDir = {
|
const appDir = {
|
||||||
'app.module.ts': `
|
'app.module.ts': `
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
export class Dep {}
|
export class Dep {}
|
||||||
|
@ -122,23 +114,23 @@ describe('aot summaries for jit', () => {
|
||||||
constructor(d: Dep) {}
|
constructor(d: Dep) {}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
const genSource = toTypeScript(genFile);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain('export function MyModuleNgSummary()');
|
||||||
expect(genSource).toContain('export function MyModuleNgSummary()');
|
// Note: CompileSummaryKind.NgModule = 2
|
||||||
// Note: CompileSummaryKind.NgModule = 2
|
expect(genSource).toMatch(/summaryKind:2,\s*type:\{\s*reference:i0.MyModule/);
|
||||||
expect(genSource).toMatch(/summaryKind:2,\s*type:\{\s*reference:i0.MyModule/);
|
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
|
||||||
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should embed useClass provider summaries in @Directive summaries', fakeAsync(() => {
|
it('should embed useClass provider summaries in @Directive summaries', () => {
|
||||||
const appDir = {
|
const appDir = {
|
||||||
'app.service.ts': `
|
'app.service.ts': `
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
export class Dep {}
|
export class Dep {}
|
||||||
|
@ -148,7 +140,7 @@ describe('aot summaries for jit', () => {
|
||||||
constructor(d: Dep) {}
|
constructor(d: Dep) {}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
'app.module.ts': `
|
'app.module.ts': `
|
||||||
import { Directive, NgModule } from '@angular/core';
|
import { Directive, NgModule } from '@angular/core';
|
||||||
import { MyService } from './app.service';
|
import { MyService } from './app.service';
|
||||||
|
|
||||||
|
@ -161,22 +153,22 @@ describe('aot summaries for jit', () => {
|
||||||
@NgModule({declarations: [MyDirective]})
|
@NgModule({declarations: [MyDirective]})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
const genSource = toTypeScript(genFile);
|
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
||||||
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
// Note: CompileSummaryKind.Injectable = 3
|
||||||
// Note: CompileSummaryKind.Injectable = 3
|
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
|
||||||
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
|
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
|
||||||
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should embed useClass provider summaries into @NgModule summaries', fakeAsync(() => {
|
it('should embed useClass provider summaries into @NgModule summaries', () => {
|
||||||
const appDir = {
|
const appDir = {
|
||||||
'app.service.ts': `
|
'app.service.ts': `
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
export class Dep {}
|
export class Dep {}
|
||||||
|
@ -186,7 +178,7 @@ describe('aot summaries for jit', () => {
|
||||||
constructor(d: Dep) {}
|
constructor(d: Dep) {}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
'app.module.ts': `
|
'app.module.ts': `
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { MyService } from './app.service';
|
import { MyService } from './app.service';
|
||||||
|
|
||||||
|
@ -195,23 +187,22 @@ describe('aot summaries for jit', () => {
|
||||||
})
|
})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
const genSource = toTypeScript(genFile);
|
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
||||||
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
// Note: CompileSummaryKind.Injectable = 3
|
||||||
// Note: CompileSummaryKind.Injectable = 3
|
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
|
||||||
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
|
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
|
||||||
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should reference declared @Directive and @Pipe summaries in @NgModule summaries',
|
it('should reference declared @Directive and @Pipe summaries in @NgModule summaries', () => {
|
||||||
fakeAsync(() => {
|
const appDir = {
|
||||||
const appDir = {
|
'app.module.ts': `
|
||||||
'app.module.ts': `
|
|
||||||
import { Directive, Pipe, NgModule } from '@angular/core';
|
import { Directive, Pipe, NgModule } from '@angular/core';
|
||||||
|
|
||||||
@Directive({selector: '[myDir]'})
|
@Directive({selector: '[myDir]'})
|
||||||
|
@ -223,20 +214,20 @@ describe('aot summaries for jit', () => {
|
||||||
@NgModule({declarations: [MyDirective, MyPipe]})
|
@NgModule({declarations: [MyDirective, MyPipe]})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
const genSource = toTypeScript(genFile);
|
expect(genSource).toMatch(
|
||||||
expect(genSource).toMatch(
|
/export function MyModuleNgSummary()[^;]*,\s*MyDirectiveNgSummary,\s*MyPipeNgSummary\s*\]\s*;/);
|
||||||
/export function MyModuleNgSummary()[^;]*,\s*MyDirectiveNgSummary,\s*MyPipeNgSummary\s*\]\s*;/);
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should reference imported @NgModule summaries in @NgModule summaries', fakeAsync(() => {
|
it('should reference imported @NgModule summaries in @NgModule summaries', () => {
|
||||||
const appDir = {
|
const appDir = {
|
||||||
'app.module.ts': `
|
'app.module.ts': `
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
@NgModule()
|
@NgModule()
|
||||||
|
@ -245,20 +236,20 @@ describe('aot summaries for jit', () => {
|
||||||
@NgModule({imports: [MyImportedModule]})
|
@NgModule({imports: [MyImportedModule]})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
const genSource = toTypeScript(genFile);
|
expect(genSource).toMatch(
|
||||||
expect(genSource).toMatch(
|
/export function MyModuleNgSummary()[^;]*,\s*MyImportedModuleNgSummary\s*\]\s*;/);
|
||||||
/export function MyModuleNgSummary()[^;]*,\s*MyImportedModuleNgSummary\s*\]\s*;/);
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create and use reexports for imported NgModules ' +
|
it('should create and use reexports for imported NgModules ' +
|
||||||
'accross compilation units',
|
'accross compilation units',
|
||||||
fakeAsync(() => {
|
() => {
|
||||||
const lib1In = {
|
const lib1In = {
|
||||||
'lib1': {
|
'lib1': {
|
||||||
'module.ts': `
|
'module.ts': `
|
||||||
|
@ -348,5 +339,5 @@ describe('aot summaries for jit', () => {
|
||||||
expect(toTypeScript(lib3ReexportNgSummary))
|
expect(toTypeScript(lib3ReexportNgSummary))
|
||||||
.toContain(
|
.toContain(
|
||||||
`export {ReexportModule_2NgSummary as ReexportModule_3NgSummary} from '/lib2/reexport.ngsummary'`);
|
`export {ReexportModule_2NgSummary as ReexportModule_3NgSummary} from '/lib2/reexport.ngsummary'`);
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,16 +5,15 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {async} from '@angular/core/testing';
|
|
||||||
|
|
||||||
import {MockDirectory, compile, expectNoDiagnostics, setup} from './test_util';
|
import {MockDirectory, compile, expectNoDiagnostics, setup} from './test_util';
|
||||||
|
|
||||||
describe('regressions', () => {
|
describe('regressions', () => {
|
||||||
let angularFiles = setup();
|
let angularFiles = setup();
|
||||||
|
|
||||||
it('should compile components with empty templates', async(() => {
|
it('should compile components with empty templates', () => {
|
||||||
const appDir = {
|
const appDir = {
|
||||||
'app.module.ts': `
|
'app.module.ts': `
|
||||||
import { Component, NgModule } from '@angular/core';
|
import { Component, NgModule } from '@angular/core';
|
||||||
|
|
||||||
@Component({template: ''})
|
@Component({template: ''})
|
||||||
|
@ -23,14 +22,11 @@ describe('regressions', () => {
|
||||||
@NgModule({declarations: [EmptyComp]})
|
@NgModule({declarations: [EmptyComp]})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
compile([rootDir, angularFiles], {postCompile: expectNoDiagnostics}, {
|
const {genFiles} = compile(
|
||||||
noUnusedLocals: true,
|
[rootDir, angularFiles], {postCompile: expectNoDiagnostics},
|
||||||
noUnusedParameters: true
|
{noUnusedLocals: true, noUnusedParameters: true});
|
||||||
}).then((result) => {
|
expect(genFiles.find((f) => f.genFileUrl === '/app/app.module.ngfactory.ts')).toBeTruthy();
|
||||||
expect(result.genFiles.find((f) => f.genFileUrl === '/app/app.module.ngfactory.ts'))
|
});
|
||||||
.toBeTruthy();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -399,11 +399,11 @@ export class MockAotCompilerHost implements AotCompilerHost {
|
||||||
return importedFile.replace(EXT, '');
|
return importedFile.replace(EXT, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
loadResource(path: string): Promise<string> {
|
loadResource(path: string): string {
|
||||||
if (this.tsHost.fileExists(path)) {
|
if (this.tsHost.fileExists(path)) {
|
||||||
return Promise.resolve(this.tsHost.readFile(path));
|
return this.tsHost.readFile(path);
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject(new Error(`Resource ${path} not found.`))
|
throw new Error(`Resource ${path} not found.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -604,47 +604,43 @@ export function compile(
|
||||||
preCompile?: (program: ts.Program) => void,
|
preCompile?: (program: ts.Program) => void,
|
||||||
postCompile?: (program: ts.Program) => void,
|
postCompile?: (program: ts.Program) => void,
|
||||||
}& AotCompilerOptions = {},
|
}& AotCompilerOptions = {},
|
||||||
tsOptions: ts.CompilerOptions = {}):
|
tsOptions: ts.CompilerOptions = {}): {genFiles: GeneratedFile[], outDir: MockDirectory} {
|
||||||
Promise<{genFiles: GeneratedFile[], outDir: MockDirectory}> {
|
// when using summaries, always emit so the next step can use the results.
|
||||||
// Make sure we always return errors via the promise...
|
const emit = options.emit || options.useSummaries;
|
||||||
return Promise.resolve(null).then(() => {
|
const preCompile = options.preCompile || expectNoDiagnostics;
|
||||||
// when using summaries, always emit so the next step can use the results.
|
const postCompile = options.postCompile || expectNoDiagnostics;
|
||||||
const emit = options.emit || options.useSummaries;
|
const rootDirArr = toMockFileArray(rootDirs);
|
||||||
const preCompile = options.preCompile || expectNoDiagnostics;
|
const scriptNames = rootDirArr.map(entry => entry.fileName).filter(isSource);
|
||||||
const postCompile = options.postCompile || expectNoDiagnostics;
|
|
||||||
const rootDirArr = toMockFileArray(rootDirs);
|
|
||||||
const scriptNames = rootDirArr.map(entry => entry.fileName).filter(isSource);
|
|
||||||
|
|
||||||
const host = new MockCompilerHost(scriptNames, arrayToMockDir(rootDirArr));
|
const host = new MockCompilerHost(scriptNames, arrayToMockDir(rootDirArr));
|
||||||
const aotHost = new MockAotCompilerHost(host);
|
const aotHost = new MockAotCompilerHost(host);
|
||||||
if (options.useSummaries) {
|
if (options.useSummaries) {
|
||||||
aotHost.hideMetadata();
|
aotHost.hideMetadata();
|
||||||
aotHost.tsFilesOnly();
|
aotHost.tsFilesOnly();
|
||||||
|
}
|
||||||
|
const tsSettings = {...settings, ...tsOptions};
|
||||||
|
const program = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
|
||||||
|
if (preCompile) preCompile(program);
|
||||||
|
const {compiler, reflector} = createAotCompiler(aotHost, options);
|
||||||
|
const genFiles = compiler.compileAllSync(program.getSourceFiles().map(sf => sf.fileName));
|
||||||
|
genFiles.forEach((file) => {
|
||||||
|
const source = file.source || toTypeScript(file);
|
||||||
|
if (isSource(file.genFileUrl)) {
|
||||||
|
host.addScript(file.genFileUrl, source);
|
||||||
|
} else {
|
||||||
|
host.override(file.genFileUrl, source);
|
||||||
}
|
}
|
||||||
const tsSettings = {...settings, ...tsOptions};
|
|
||||||
const scripts = host.scriptNames.slice(0);
|
|
||||||
const program = ts.createProgram(scripts, tsSettings, host);
|
|
||||||
if (preCompile) preCompile(program);
|
|
||||||
const {compiler, reflector} = createAotCompiler(aotHost, options);
|
|
||||||
return compiler.compileAll(program.getSourceFiles().map(sf => sf.fileName)).then(genFiles => {
|
|
||||||
genFiles.forEach((file) => {
|
|
||||||
const source = file.source || toTypeScript(file);
|
|
||||||
isSource(file.genFileUrl) ? host.addScript(file.genFileUrl, source) :
|
|
||||||
host.override(file.genFileUrl, source);
|
|
||||||
});
|
|
||||||
const scripts = host.scriptNames.slice(0);
|
|
||||||
const newProgram = ts.createProgram(scripts, tsSettings, host);
|
|
||||||
if (postCompile) postCompile(newProgram);
|
|
||||||
if (emit) {
|
|
||||||
newProgram.emit();
|
|
||||||
}
|
|
||||||
let outDir: MockDirectory = {};
|
|
||||||
if (emit) {
|
|
||||||
outDir = arrayToMockDir(toMockFileArray([
|
|
||||||
host.writtenFiles, host.overrides
|
|
||||||
]).filter((entry) => !isSource(entry.fileName)));
|
|
||||||
}
|
|
||||||
return {genFiles, outDir};
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
const newProgram = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
|
||||||
|
if (postCompile) postCompile(newProgram);
|
||||||
|
if (emit) {
|
||||||
|
newProgram.emit();
|
||||||
|
}
|
||||||
|
let outDir: MockDirectory = {};
|
||||||
|
if (emit) {
|
||||||
|
outDir = arrayToMockDir(toMockFileArray([
|
||||||
|
host.writtenFiles, host.overrides
|
||||||
|
]).filter((entry) => !isSource(entry.fileName)));
|
||||||
|
}
|
||||||
|
return {genFiles, outDir};
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {ViewEncapsulation} from '@angular/core/src/metadata/view';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||||
|
|
||||||
import {SyncAsyncResult, noUndefined} from '../src/util';
|
import {noUndefined} from '../src/util';
|
||||||
|
|
||||||
import {SpyResourceLoader} from './spies';
|
import {SpyResourceLoader} from './spies';
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ function normalizeTemplate(normalizer: DirectiveNormalizer, o: {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeTemplateAsync(normalizer: DirectiveNormalizer, o: {
|
function normalizeTemplateOnly(normalizer: DirectiveNormalizer, o: {
|
||||||
ngModuleType?: any; componentType?: any; moduleUrl?: string; template?: string | null;
|
ngModuleType?: any; componentType?: any; moduleUrl?: string; template?: string | null;
|
||||||
templateUrl?: string | null;
|
templateUrl?: string | null;
|
||||||
styles?: string[];
|
styles?: string[];
|
||||||
|
@ -55,30 +55,7 @@ function normalizeTemplateAsync(normalizer: DirectiveNormalizer, o: {
|
||||||
encapsulation?: ViewEncapsulation | null;
|
encapsulation?: ViewEncapsulation | null;
|
||||||
animations?: CompileAnimationEntryMetadata[];
|
animations?: CompileAnimationEntryMetadata[];
|
||||||
}) {
|
}) {
|
||||||
return normalizer.normalizeTemplateAsync({
|
return normalizer.normalizeTemplateOnly({
|
||||||
ngModuleType: noUndefined(o.ngModuleType),
|
|
||||||
componentType: noUndefined(o.componentType),
|
|
||||||
moduleUrl: noUndefined(o.moduleUrl),
|
|
||||||
template: noUndefined(o.template),
|
|
||||||
templateUrl: noUndefined(o.templateUrl),
|
|
||||||
styles: noUndefined(o.styles),
|
|
||||||
styleUrls: noUndefined(o.styleUrls),
|
|
||||||
interpolation: noUndefined(o.interpolation),
|
|
||||||
encapsulation: noUndefined(o.encapsulation),
|
|
||||||
animations: noUndefined(o.animations)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeTemplateSync(normalizer: DirectiveNormalizer, o: {
|
|
||||||
ngModuleType?: any; componentType?: any; moduleUrl?: string; template?: string | null;
|
|
||||||
templateUrl?: string | null;
|
|
||||||
styles?: string[];
|
|
||||||
styleUrls?: string[];
|
|
||||||
interpolation?: [string, string] | null;
|
|
||||||
encapsulation?: ViewEncapsulation | null;
|
|
||||||
animations?: CompileAnimationEntryMetadata[];
|
|
||||||
}): CompileTemplateMetadata {
|
|
||||||
return normalizer.normalizeTemplateSync({
|
|
||||||
ngModuleType: noUndefined(o.ngModuleType),
|
ngModuleType: noUndefined(o.ngModuleType),
|
||||||
componentType: noUndefined(o.componentType),
|
componentType: noUndefined(o.componentType),
|
||||||
moduleUrl: noUndefined(o.moduleUrl),
|
moduleUrl: noUndefined(o.moduleUrl),
|
||||||
|
@ -194,10 +171,10 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('normalizeTemplateSync', () => {
|
describe('normalizeTemplateOnly sync', () => {
|
||||||
it('should store the template',
|
it('should store the template',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
const template = normalizeTemplateSync(normalizer, {
|
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
|
||||||
ngModuleType: null,
|
ngModuleType: null,
|
||||||
componentType: SomeComp,
|
componentType: SomeComp,
|
||||||
moduleUrl: SOME_MODULE_URL,
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
@ -214,7 +191,7 @@ export function main() {
|
||||||
|
|
||||||
it('should resolve styles on the annotation against the moduleUrl',
|
it('should resolve styles on the annotation against the moduleUrl',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
const template = normalizeTemplateSync(normalizer, {
|
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
|
||||||
ngModuleType: null,
|
ngModuleType: null,
|
||||||
componentType: SomeComp,
|
componentType: SomeComp,
|
||||||
moduleUrl: SOME_MODULE_URL,
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
@ -229,7 +206,7 @@ export function main() {
|
||||||
|
|
||||||
it('should resolve styles in the template against the moduleUrl',
|
it('should resolve styles in the template against the moduleUrl',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
const template = normalizeTemplateSync(normalizer, {
|
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
|
||||||
ngModuleType: null,
|
ngModuleType: null,
|
||||||
componentType: SomeComp,
|
componentType: SomeComp,
|
||||||
moduleUrl: SOME_MODULE_URL,
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
@ -244,7 +221,7 @@ export function main() {
|
||||||
|
|
||||||
it('should use ViewEncapsulation.Emulated by default',
|
it('should use ViewEncapsulation.Emulated by default',
|
||||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||||
const template = normalizeTemplateSync(normalizer, {
|
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
|
||||||
ngModuleType: null,
|
ngModuleType: null,
|
||||||
componentType: SomeComp,
|
componentType: SomeComp,
|
||||||
moduleUrl: SOME_MODULE_URL,
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
@ -262,7 +239,7 @@ export function main() {
|
||||||
[CompilerConfig, DirectiveNormalizer],
|
[CompilerConfig, DirectiveNormalizer],
|
||||||
(config: CompilerConfig, normalizer: DirectiveNormalizer) => {
|
(config: CompilerConfig, normalizer: DirectiveNormalizer) => {
|
||||||
config.defaultEncapsulation = ViewEncapsulation.None;
|
config.defaultEncapsulation = ViewEncapsulation.None;
|
||||||
const template = normalizeTemplateSync(normalizer, {
|
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
|
||||||
ngModuleType: null,
|
ngModuleType: null,
|
||||||
componentType: SomeComp,
|
componentType: SomeComp,
|
||||||
moduleUrl: SOME_MODULE_URL,
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
@ -284,7 +261,7 @@ export function main() {
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
|
||||||
resourceLoader: MockResourceLoader) => {
|
resourceLoader: MockResourceLoader) => {
|
||||||
resourceLoader.expect('package:some/module/sometplurl.html', 'a');
|
resourceLoader.expect('package:some/module/sometplurl.html', 'a');
|
||||||
normalizeTemplateAsync(normalizer, {
|
(<Promise<CompileTemplateMetadata>>normalizeTemplateOnly(normalizer, {
|
||||||
ngModuleType: null,
|
ngModuleType: null,
|
||||||
componentType: SomeComp,
|
componentType: SomeComp,
|
||||||
moduleUrl: SOME_MODULE_URL,
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
@ -293,7 +270,7 @@ export function main() {
|
||||||
templateUrl: 'sometplurl.html',
|
templateUrl: 'sometplurl.html',
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}).then((template: CompileTemplateMetadata) => {
|
})).then((template) => {
|
||||||
expect(template.template).toEqual('a');
|
expect(template.template).toEqual('a');
|
||||||
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
|
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
|
||||||
expect(template.isInline).toBe(false);
|
expect(template.isInline).toBe(false);
|
||||||
|
@ -308,7 +285,7 @@ export function main() {
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
|
||||||
resourceLoader: MockResourceLoader) => {
|
resourceLoader: MockResourceLoader) => {
|
||||||
resourceLoader.expect('package:some/module/tpl/sometplurl.html', '');
|
resourceLoader.expect('package:some/module/tpl/sometplurl.html', '');
|
||||||
normalizeTemplateAsync(normalizer, {
|
(<Promise<CompileTemplateMetadata>>normalizeTemplateOnly(normalizer, {
|
||||||
ngModuleType: null,
|
ngModuleType: null,
|
||||||
componentType: SomeComp,
|
componentType: SomeComp,
|
||||||
moduleUrl: SOME_MODULE_URL,
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
@ -317,7 +294,7 @@ export function main() {
|
||||||
templateUrl: 'tpl/sometplurl.html',
|
templateUrl: 'tpl/sometplurl.html',
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}).then((template: CompileTemplateMetadata) => {
|
})).then((template) => {
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
@ -331,7 +308,7 @@ export function main() {
|
||||||
resourceLoader: MockResourceLoader) => {
|
resourceLoader: MockResourceLoader) => {
|
||||||
resourceLoader.expect(
|
resourceLoader.expect(
|
||||||
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
|
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
|
||||||
normalizeTemplateAsync(normalizer, {
|
(<Promise<CompileTemplateMetadata>>normalizeTemplateOnly(normalizer, {
|
||||||
ngModuleType: null,
|
ngModuleType: null,
|
||||||
componentType: SomeComp,
|
componentType: SomeComp,
|
||||||
moduleUrl: SOME_MODULE_URL,
|
moduleUrl: SOME_MODULE_URL,
|
||||||
|
@ -340,7 +317,7 @@ export function main() {
|
||||||
templateUrl: 'tpl/sometplurl.html',
|
templateUrl: 'tpl/sometplurl.html',
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: []
|
styleUrls: []
|
||||||
}).then((template: CompileTemplateMetadata) => {
|
})).then((template) => {
|
||||||
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
|
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
@ -362,13 +339,13 @@ export function main() {
|
||||||
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
|
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
|
||||||
resourceLoader: SpyResourceLoader) => {
|
resourceLoader: SpyResourceLoader) => {
|
||||||
programResourceLoaderSpy(resourceLoader, {'package:some/module/test.css': 'a'});
|
programResourceLoaderSpy(resourceLoader, {'package:some/module/test.css': 'a'});
|
||||||
normalizer
|
(<Promise<CompileTemplateMetadata>>normalizer.normalizeExternalStylesheets(
|
||||||
.normalizeExternalStylesheets(compileTemplateMetadata({
|
compileTemplateMetadata({
|
||||||
template: '',
|
template: '',
|
||||||
templateUrl: '',
|
templateUrl: '',
|
||||||
styleUrls: ['package:some/module/test.css']
|
styleUrls: ['package:some/module/test.css']
|
||||||
}))
|
})))
|
||||||
.then((template: CompileTemplateMetadata) => {
|
.then((template) => {
|
||||||
expect(template.externalStylesheets.length).toBe(1);
|
expect(template.externalStylesheets.length).toBe(1);
|
||||||
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
|
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
|
||||||
moduleUrl: 'package:some/module/test.css',
|
moduleUrl: 'package:some/module/test.css',
|
||||||
|
@ -388,13 +365,13 @@ export function main() {
|
||||||
'package:some/module/test.css': 'a@import "test2.css"',
|
'package:some/module/test.css': 'a@import "test2.css"',
|
||||||
'package:some/module/test2.css': 'b'
|
'package:some/module/test2.css': 'b'
|
||||||
});
|
});
|
||||||
normalizer
|
(<Promise<CompileTemplateMetadata>>normalizer.normalizeExternalStylesheets(
|
||||||
.normalizeExternalStylesheets(compileTemplateMetadata({
|
compileTemplateMetadata({
|
||||||
template: '',
|
template: '',
|
||||||
templateUrl: '',
|
templateUrl: '',
|
||||||
styleUrls: ['package:some/module/test.css']
|
styleUrls: ['package:some/module/test.css']
|
||||||
}))
|
})))
|
||||||
.then((template: CompileTemplateMetadata) => {
|
.then((template) => {
|
||||||
expect(template.externalStylesheets.length).toBe(2);
|
expect(template.externalStylesheets.length).toBe(2);
|
||||||
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
|
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
|
||||||
moduleUrl: 'package:some/module/test.css',
|
moduleUrl: 'package:some/module/test.css',
|
||||||
|
@ -426,8 +403,8 @@ export function main() {
|
||||||
};
|
};
|
||||||
Promise
|
Promise
|
||||||
.all([
|
.all([
|
||||||
normalizeTemplateAsync(normalizer, prenormMeta),
|
normalizeTemplateOnly(normalizer, prenormMeta),
|
||||||
normalizeTemplateAsync(normalizer, prenormMeta)
|
normalizeTemplateOnly(normalizer, prenormMeta)
|
||||||
])
|
])
|
||||||
.then((templates: CompileTemplateMetadata[]) => {
|
.then((templates: CompileTemplateMetadata[]) => {
|
||||||
expect(templates[0].template).toEqual('a');
|
expect(templates[0].template).toEqual('a');
|
||||||
|
|
|
@ -7,18 +7,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {fakeAsync} from '@angular/core/testing/src/fake_async';
|
import {fakeAsync} from '@angular/core/testing/src/fake_async';
|
||||||
import {SyncAsyncResult, escapeRegExp, splitAtColon, utf8Encode} from '../src/util';
|
import {SyncAsync, escapeRegExp, splitAtColon, utf8Encode} from '../src/util';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('util', () => {
|
describe('util', () => {
|
||||||
describe('SyncAsyncResult', () => {
|
|
||||||
it('async value should default to Promise.resolve(syncValue)', fakeAsync(() => {
|
|
||||||
const syncValue = {};
|
|
||||||
const sar = new SyncAsyncResult(syncValue);
|
|
||||||
sar.asyncResult !.then((v: any) => expect(v).toBe(syncValue));
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('splitAtColon', () => {
|
describe('splitAtColon', () => {
|
||||||
it('should split when a single ":" is present', () => {
|
it('should split when a single ":" is present', () => {
|
||||||
expect(splitAtColon('a:b', [])).toEqual(['a', 'b']);
|
expect(splitAtColon('a:b', [])).toEqual(['a', 'b']);
|
||||||
|
|
Loading…
Reference in New Issue