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