fix(compiler): walk third party modules (#12453)

fixes #11889
fixes #12428
This commit is contained in:
Victor Berchet
2016-10-24 13:28:23 -07:00
committed by Igor Minar
parent bfc97ff2cd
commit a838aba756
24 changed files with 463 additions and 239 deletions

View File

@ -508,7 +508,7 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
}
/**
* Metadata regarding compilation of a directive.
* Metadata regarding compilation of a module.
*/
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
type: CompileTypeMetadata;
@ -568,6 +568,7 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
export class TransitiveCompileNgModuleMetadata {
directivesSet = new Set<Type<any>>();
pipesSet = new Set<Type<any>>();
constructor(
public modules: CompileNgModuleMetadata[], public providers: CompileProviderMetadata[],
public entryComponents: CompileTypeMetadata[], public directives: CompileDirectiveMetadata[],
@ -580,11 +581,13 @@ export class TransitiveCompileNgModuleMetadata {
export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifier>(items: T[]):
T[] {
const map = new Map<any, T>();
items.forEach((item) => {
if (!map.get(item.identifier.reference)) {
map.set(item.identifier.reference, item);
}
});
return MapWrapper.values(map);
}

View File

@ -378,15 +378,15 @@ export class CompileMetadataResolver {
}
private _getTypeDescriptor(type: Type<any>): string {
if (this._directiveResolver.resolve(type, false) !== null) {
if (this._directiveResolver.resolve(type, false)) {
return 'directive';
}
if (this._pipeResolver.resolve(type, false) !== null) {
if (this._pipeResolver.resolve(type, false)) {
return 'pipe';
}
if (this._ngModuleResolver.resolve(type, false) !== null) {
if (this._ngModuleResolver.resolve(type, false)) {
return 'module';
}
@ -508,7 +508,7 @@ export class CompileMetadataResolver {
let isOptional = false;
let query: Query = null;
let viewQuery: Query = null;
var token: any = null;
let token: any = null;
if (Array.isArray(param)) {
param.forEach((paramEntry) => {
if (paramEntry instanceof Host) {
@ -605,19 +605,20 @@ export class CompileMetadataResolver {
} else if (isValidType(provider)) {
compileProvider = this.getTypeMetadata(provider, staticTypeModuleUrl(provider));
} else {
let providersInfo = (<string[]>providers.reduce(
(soFar: string[], seenProvider: any, seenProviderIdx: number) => {
if (seenProviderIdx < providerIdx) {
soFar.push(`${stringify(seenProvider)}`);
} else if (seenProviderIdx == providerIdx) {
soFar.push(`?${stringify(seenProvider)}?`);
} else if (seenProviderIdx == providerIdx + 1) {
soFar.push('...');
}
return soFar;
},
[]))
.join(', ');
const providersInfo =
(<string[]>providers.reduce(
(soFar: string[], seenProvider: any, seenProviderIdx: number) => {
if (seenProviderIdx < providerIdx) {
soFar.push(`${stringify(seenProvider)}`);
} else if (seenProviderIdx == providerIdx) {
soFar.push(`?${stringify(seenProvider)}?`);
} else if (seenProviderIdx == providerIdx + 1) {
soFar.push('...');
}
return soFar;
},
[]))
.join(', ');
throw new Error(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`);

View File

@ -13,6 +13,7 @@ import {AnimationParser} from './animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from './directive_wrapper_compiler';
import {ListWrapper, MapWrapper} from './facade/collection';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
import {CompileMetadataResolver} from './metadata_resolver';
import {NgModuleCompiler} from './ng_module_compiler';
@ -23,28 +24,63 @@ import {TemplateParser} from './template_parser/template_parser';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
export class SourceModule {
constructor(public moduleUrl: string, public source: string) {}
constructor(public fileUrl: string, public moduleUrl: string, public source: string) {}
}
export class NgModulesSummary {
constructor(
public ngModuleByDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
public ngModules: CompileNgModuleMetadata[]) {}
}
// Returns all the source files and a mapping from modules to directives
export function analyzeNgModules(
ngModules: StaticSymbol[], metadataResolver: CompileMetadataResolver): {
ngModuleByDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>
} {
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
export function analyzeModules(
ngModules: StaticSymbol[], metadataResolver: CompileMetadataResolver) {
// For every input modules add the list of transitively included modules
ngModules.forEach(ngModule => {
const modMeta = metadataResolver.getNgModuleMetadata(ngModule);
modMeta.transitiveModule.modules.forEach(
modMeta => { moduleMetasByRef.set(modMeta.type.reference, modMeta); });
});
const ngModuleMetas = MapWrapper.values(moduleMetasByRef);
const ngModuleByDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
const modules: CompileNgModuleMetadata[] = [];
const ngModulesByFile = new Map<string, StaticSymbol[]>();
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
const srcFileUrls = new Set<string>();
// Looping over all modules to construct:
// - a map from files to modules `ngModulesByFile`,
// - a map from files to directives `ngDirectivesByFile`,
// - a map from modules to directives `ngModuleByDirective`.
ngModuleMetas.forEach((ngModuleMeta) => {
const srcFileUrl = ngModuleMeta.type.reference.filePath;
srcFileUrls.add(srcFileUrl);
ngModulesByFile.set(
srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference));
ngModules.forEach((ngModule) => {
const ngModuleMeta = metadataResolver.getNgModuleMetadata(<any>ngModule);
modules.push(ngModuleMeta);
ngModuleMeta.declaredDirectives.forEach((dirMeta: CompileDirectiveMetadata) => {
const fileUrl = dirMeta.type.reference.filePath;
srcFileUrls.add(fileUrl);
ngDirectivesByFile.set(
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirMeta.type.reference));
ngModuleByDirective.set(dirMeta.type.reference, ngModuleMeta);
});
});
return new NgModulesSummary(ngModuleByDirective, modules);
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
srcFileUrls.forEach((srcUrl) => {
const directives = ngDirectivesByFile.get(srcUrl) || [];
const ngModules = ngModulesByFile.get(srcUrl) || [];
files.push({srcUrl, directives, ngModules});
});
return {
// map from modules to declared directives
ngModuleByDirective,
// list of modules and directives for every source file
files,
};
}
export class OfflineCompiler {
@ -59,19 +95,26 @@ export class OfflineCompiler {
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
private _localeId: string, private _translationFormat: string) {}
analyzeModules(ngModules: StaticSymbol[]): NgModulesSummary {
return analyzeModules(ngModules, this._metadataResolver);
}
clearCache() {
this._directiveNormalizer.clearCache();
this._metadataResolver.clearCache();
}
compile(
moduleUrl: string, ngModulesSummary: NgModulesSummary, directives: StaticSymbol[],
ngModules: StaticSymbol[]): Promise<SourceModule[]> {
const fileSuffix = _splitTypescriptSuffix(moduleUrl)[1];
compileModules(ngModules: StaticSymbol[]): Promise<SourceModule[]> {
const {ngModuleByDirective, files} = analyzeNgModules(ngModules, this._metadataResolver);
const sourceModules = files.map(
file => this._compileSrcFile(
file.srcUrl, ngModuleByDirective, file.directives, file.ngModules));
return Promise.all(sourceModules)
.then((modules: SourceModule[][]) => ListWrapper.flatten(modules));
}
private _compileSrcFile(
srcFileUrl: string, ngModuleByDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], ngModules: StaticSymbol[]): Promise<SourceModule[]> {
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
const statements: o.Statement[] = [];
const exportedVars: string[] = [];
const outputSourceModules: SourceModule[] = [];
@ -91,7 +134,7 @@ export class OfflineCompiler {
if (!compMeta.isComponent) {
return Promise.resolve(null);
}
const ngModule = ngModulesSummary.ngModuleByDirective.get(dirType);
const ngModule = ngModuleByDirective.get(dirType);
if (!ngModule) {
throw new Error(`Cannot determine the module for component ${compMeta.type.name}!`);
}
@ -106,7 +149,8 @@ export class OfflineCompiler {
// compile styles
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
outputSourceModules.push(this._codgenStyles(compiledStyleSheet, fileSuffix));
outputSourceModules.push(
this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
});
// compile components
@ -119,8 +163,9 @@ export class OfflineCompiler {
}))
.then(() => {
if (statements.length > 0) {
outputSourceModules.unshift(this._codegenSourceModule(
_ngfactoryModuleUrl(moduleUrl), statements, exportedVars));
const srcModule = this._codegenSourceModule(
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
outputSourceModules.unshift(srcModule);
}
return outputSourceModules;
});
@ -209,18 +254,21 @@ export class OfflineCompiler {
return viewResult.viewFactoryVar;
}
private _codgenStyles(stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
private _codgenStyles(
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
_resolveStyleStatements(stylesCompileResult, fileSuffix);
return this._codegenSourceModule(
_stylesModuleUrl(
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
fileUrl, _stylesModuleUrl(
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
stylesCompileResult.statements, [stylesCompileResult.stylesVar]);
}
private _codegenSourceModule(
moduleUrl: string, statements: o.Statement[], exportedVars: string[]): SourceModule {
fileUrl: string, moduleUrl: string, statements: o.Statement[],
exportedVars: string[]): SourceModule {
return new SourceModule(
moduleUrl, this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
fileUrl, moduleUrl,
this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
}
}