refactor(comiler): various cleanups

This commit is contained in:
Tobias Bosch 2016-11-17 20:11:55 -08:00 committed by Chuck Jazdzewski
parent adeea5d86a
commit 3c06a5dc25
6 changed files with 89 additions and 80 deletions

View File

@ -54,13 +54,13 @@ export class CompilerHost implements AotCompilerHost {
throw new Error('Resolution of relative paths requires a containing file.'); throw new Error('Resolution of relative paths requires a containing file.');
} }
// Any containing file gives the same result for absolute imports // Any containing file gives the same result for absolute imports
containingFile = path.join(this.basePath, 'index.ts'); containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
} }
m = m.replace(EXT, ''); m = m.replace(EXT, '');
const resolved = const resolved =
ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context) ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context)
.resolvedModule; .resolvedModule;
return resolved ? resolved.resolvedFileName : null; return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
}; };
/** /**

View File

@ -32,8 +32,8 @@ export class Extractor {
const programSymbols: compiler.StaticSymbol[] = const programSymbols: compiler.StaticSymbol[] =
extractProgramSymbols(this.program, this.staticReflector, this.compilerHost, this.options); extractProgramSymbols(this.program, this.staticReflector, this.compilerHost, this.options);
const {ngModules, files} = compiler.analyzeAndValidateNgModules( const {ngModules, files} =
programSymbols, {transitiveModules: true}, this.metadataResolver); compiler.analyzeAndValidateNgModules(programSymbols, {}, this.metadataResolver);
return compiler.loadNgModuleDirectives(ngModules).then(() => { return compiler.loadNgModuleDirectives(ngModules).then(() => {
const errors: compiler.ParseError[] = []; const errors: compiler.ParseError[] = [];

View File

@ -48,7 +48,7 @@ export class PathMappedCompilerHost extends CompilerHost {
throw new Error('Resolution of relative paths requires a containing file.'); throw new Error('Resolution of relative paths requires a containing file.');
} }
// Any containing file gives the same result for absolute imports // Any containing file gives the same result for absolute imports
containingFile = path.join(this.basePath, 'index.ts'); containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
} }
for (const root of this.options.rootDirs || ['']) { for (const root of this.options.rootDirs || ['']) {
const rootedContainingFile = path.join(root, containingFile); const rootedContainingFile = path.join(root, containingFile);
@ -58,7 +58,7 @@ export class PathMappedCompilerHost extends CompilerHost {
if (this.options.traceResolution) { if (this.options.traceResolution) {
console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName); console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName);
} }
return resolved.resolvedFileName; return this.getCanonicalFileName(resolved.resolvedFileName);
} }
} }
} }
@ -86,8 +86,7 @@ export class PathMappedCompilerHost extends CompilerHost {
} }
const resolvable = (candidate: string) => { const resolvable = (candidate: string) => {
const resolved = const resolved = this.moduleNameToFileName(candidate, importedFile);
this.getCanonicalFileName(this.moduleNameToFileName(candidate, importedFile));
return resolved && resolved.replace(EXT, '') === importedFile.replace(EXT, ''); return resolved && resolved.replace(EXT, '') === importedFile.replace(EXT, '');
}; };
@ -133,7 +132,7 @@ export class PathMappedCompilerHost extends CompilerHost {
} }
} else { } else {
const sf = this.getSourceFile(rootedPath); const sf = this.getSourceFile(rootedPath);
sf.fileName = this.getCanonicalFileName(sf.fileName); sf.fileName = sf.fileName;
const metadata = this.metadataCollector.getMetadata(sf); const metadata = this.metadataCollector.getMetadata(sf);
return metadata ? [metadata] : []; return metadata ? [metadata] : [];
} }

View File

@ -46,14 +46,9 @@ export class AotCompiler {
clearCache() { this._metadataResolver.clearCache(); } clearCache() { this._metadataResolver.clearCache(); }
compileAll(rootFiles: string[]): Promise<SourceModule[]> { compileAll(rootFiles: string[]): Promise<SourceModule[]> {
const options = { const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options);
transitiveModules: true,
excludeFilePattern: this._options.excludeFilePattern,
includeFilePattern: this._options.includeFilePattern
};
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, options);
const {ngModuleByPipeOrDirective, files, ngModules} = const {ngModuleByPipeOrDirective, files, ngModules} =
analyzeAndValidateNgModules(programSymbols, options, this._metadataResolver); analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
return loadNgModuleDirectives(ngModules).then(() => { return loadNgModuleDirectives(ngModules).then(() => {
const sourceModules = files.map( const sourceModules = files.map(
file => this._compileSrcFile( file => this._compileSrcFile(
@ -288,7 +283,7 @@ export interface NgAnalyzedModules {
// Returns all the source files and a mapping from modules to directives // Returns all the source files and a mapping from modules to directives
export function analyzeNgModules( export function analyzeNgModules(
programStaticSymbols: StaticSymbol[], programStaticSymbols: StaticSymbol[],
options: {transitiveModules: boolean, includeFilePattern?: RegExp, excludeFilePattern?: RegExp}, options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
metadataResolver: CompileMetadataResolver): NgAnalyzedModules { metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const {ngModules, symbolsMissingModule} = const {ngModules, symbolsMissingModule} =
_createNgModules(programStaticSymbols, options, metadataResolver); _createNgModules(programStaticSymbols, options, metadataResolver);
@ -296,7 +291,8 @@ export function analyzeNgModules(
} }
export function analyzeAndValidateNgModules( export function analyzeAndValidateNgModules(
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean}, programStaticSymbols: StaticSymbol[],
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
metadataResolver: CompileMetadataResolver): NgAnalyzedModules { metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const result = analyzeNgModules(programStaticSymbols, options, metadataResolver); const result = analyzeNgModules(programStaticSymbols, options, metadataResolver);
if (result.symbolsMissingModule && result.symbolsMissingModule.length) { if (result.symbolsMissingModule && result.symbolsMissingModule.length) {
@ -370,31 +366,27 @@ export function extractProgramSymbols(
staticReflector: StaticReflector, files: string[], staticReflector: StaticReflector, files: string[],
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] { options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] {
const staticSymbols: StaticSymbol[] = []; const staticSymbols: StaticSymbol[] = [];
files files.filter(fileName => _filterFileByPatterns(fileName, options)).forEach(sourceFile => {
.filter( const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
fileName => _filterFileByPatterns( if (!moduleMetadata) {
fileName, options.includeFilePattern, options.includeFilePattern)) console.log(`WARNING: no metadata found for ${sourceFile}`);
.forEach(sourceFile => { return;
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile); }
if (!moduleMetadata) {
console.log(`WARNING: no metadata found for ${sourceFile}`);
return;
}
const metadata = moduleMetadata['metadata']; const metadata = moduleMetadata['metadata'];
if (!metadata) { if (!metadata) {
return; return;
} }
for (const symbol of Object.keys(metadata)) { for (const symbol of Object.keys(metadata)) {
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') { if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
// Ignore symbols that are only included to record error information. // Ignore symbols that are only included to record error information.
continue; continue;
} }
staticSymbols.push(staticReflector.findDeclaration(sourceFile, symbol, sourceFile)); staticSymbols.push(staticReflector.getStaticSymbol(sourceFile, symbol));
} }
}); });
return staticSymbols; return staticSymbols;
} }
@ -404,7 +396,7 @@ export function extractProgramSymbols(
// are also declared by a module. // are also declared by a module.
function _createNgModules( function _createNgModules(
programStaticSymbols: StaticSymbol[], programStaticSymbols: StaticSymbol[],
options: {transitiveModules: boolean, includeFilePattern?: RegExp, excludeFilePattern?: RegExp}, options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
metadataResolver: CompileMetadataResolver): metadataResolver: CompileMetadataResolver):
{ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} { {ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} {
const ngModules = new Map<any, CompileNgModuleMetadata>(); const ngModules = new Map<any, CompileNgModuleMetadata>();
@ -412,9 +404,7 @@ function _createNgModules(
const ngModulePipesAndDirective = new Set<StaticSymbol>(); const ngModulePipesAndDirective = new Set<StaticSymbol>();
const addNgModule = (staticSymbol: any) => { const addNgModule = (staticSymbol: any) => {
if (ngModules.has(staticSymbol) || if (ngModules.has(staticSymbol) || !_filterFileByPatterns(staticSymbol.filePath, options)) {
!_filterFileByPatterns(
staticSymbol.filePath, options.includeFilePattern, options.excludeFilePattern)) {
return false; return false;
} }
const ngModule = metadataResolver.getUnloadedNgModuleMetadata(staticSymbol, false, false); const ngModule = metadataResolver.getUnloadedNgModuleMetadata(staticSymbol, false, false);
@ -422,10 +412,8 @@ function _createNgModules(
ngModules.set(ngModule.type.reference, ngModule); ngModules.set(ngModule.type.reference, ngModule);
ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference)); ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference));
ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference)); ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference));
if (options.transitiveModules) { // For every input module add the list of transitively included modules
// For every input modules add the list of transitively included modules ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.type.reference));
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.type.reference));
}
} }
return !!ngModule; return !!ngModule;
}; };
@ -444,13 +432,13 @@ function _createNgModules(
} }
function _filterFileByPatterns( function _filterFileByPatterns(
fileName: string, includeFilePattern: RegExp, excludeFilePattern: RegExp) { fileName: string, options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}) {
let match = true; let match = true;
if (includeFilePattern) { if (options.includeFilePattern) {
match = match && !!includeFilePattern.exec(fileName); match = match && !!options.includeFilePattern.exec(fileName);
} }
if (excludeFilePattern) { if (options.excludeFilePattern) {
match = match && !excludeFilePattern.exec(fileName); match = match && !options.excludeFilePattern.exec(fileName);
} }
return match; return match;
} }

View File

@ -26,13 +26,13 @@ const ANGULAR_IMPORT_LOCATIONS = {
* templates statically. * templates statically.
*/ */
export class StaticReflector implements ReflectorReader { export class StaticReflector implements ReflectorReader {
private typeCache = new Map<string, StaticSymbol>(); private staticSymbolCache = new Map<string, StaticSymbol>();
private declarationCache = new Map<string, StaticSymbol>();
private annotationCache = new Map<StaticSymbol, any[]>(); private annotationCache = new Map<StaticSymbol, any[]>();
private propertyCache = new Map<StaticSymbol, {[key: string]: any}>(); private propertyCache = new Map<StaticSymbol, {[key: string]: any}>();
private parameterCache = new Map<StaticSymbol, any[]>(); private parameterCache = new Map<StaticSymbol, any[]>();
private metadataCache = new Map<string, {[key: string]: any}>(); private metadataCache = new Map<string, {[key: string]: any}>();
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>(); private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
private declarationMap = new Map<string, StaticSymbol>();
private opaqueToken: StaticSymbol; private opaqueToken: StaticSymbol;
constructor(private host: AotCompilerHost) { this.initializeConversionMap(); } constructor(private host: AotCompilerHost) { this.initializeConversionMap(); }
@ -204,10 +204,10 @@ export class StaticReflector implements ReflectorReader {
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol { getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
const memberSuffix = members ? `.${ members.join('.')}` : ''; const memberSuffix = members ? `.${ members.join('.')}` : '';
const key = `"${declarationFile}".${name}${memberSuffix}`; const key = `"${declarationFile}".${name}${memberSuffix}`;
let result = this.typeCache.get(key); let result = this.staticSymbolCache.get(key);
if (!result) { if (!result) {
result = new StaticSymbol(declarationFile, name, members); result = new StaticSymbol(declarationFile, name, members);
this.typeCache.set(key, result); this.staticSymbolCache.set(key, result);
} }
return result; return result;
} }
@ -220,15 +220,20 @@ export class StaticReflector implements ReflectorReader {
} }
return resolvedModulePath; return resolvedModulePath;
}; };
const cacheKey = `${filePath}|${symbolName}`;
let staticSymbol = this.declarationCache.get(cacheKey);
if (staticSymbol) {
return staticSymbol;
}
const metadata = this.getModuleMetadata(filePath); const metadata = this.getModuleMetadata(filePath);
if (metadata) { if (metadata) {
// If we have metadata for the symbol, this is the original exporting location. // If we have metadata for the symbol, this is the original exporting location.
if (metadata['metadata'][symbolName]) { if (metadata['metadata'][symbolName]) {
return this.getStaticSymbol(filePath, symbolName); staticSymbol = this.getStaticSymbol(filePath, symbolName);
} }
// If no, try to find the symbol in one of the re-export location // If no, try to find the symbol in one of the re-export location
if (metadata['exports']) { if (!staticSymbol && metadata['exports']) {
// Try and find the symbol in the list of explicitly re-exported symbols. // Try and find the symbol in the list of explicitly re-exported symbols.
for (const moduleExport of metadata['exports']) { for (const moduleExport of metadata['exports']) {
if (moduleExport.export) { if (moduleExport.export) {
@ -244,43 +249,43 @@ export class StaticReflector implements ReflectorReader {
if (typeof exportSymbol !== 'string') { if (typeof exportSymbol !== 'string') {
symName = exportSymbol.name; symName = exportSymbol.name;
} }
return this.resolveExportedSymbol(resolveModule(moduleExport.from), symName); staticSymbol = this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
} }
} }
} }
// Try to find the symbol via export * directives. if (!staticSymbol) {
for (const moduleExport of metadata['exports']) { // Try to find the symbol via export * directives.
if (!moduleExport.export) { for (const moduleExport of metadata['exports']) {
const resolvedModule = resolveModule(moduleExport.from); if (!moduleExport.export) {
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName); const resolvedModule = resolveModule(moduleExport.from);
if (candidateSymbol) return candidateSymbol; const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
if (candidateSymbol) {
staticSymbol = candidateSymbol;
break;
}
}
} }
} }
} }
} }
return null; this.declarationCache.set(cacheKey, staticSymbol);
return staticSymbol;
} }
findDeclaration(module: string, symbolName: string, containingFile?: string): StaticSymbol { findDeclaration(module: string, symbolName: string, containingFile?: string): StaticSymbol {
const cacheKey = `${module}|${symbolName}|${containingFile}`;
let symbol = this.declarationMap.get(cacheKey);
if (symbol) {
return symbol;
}
try { try {
const filePath = this.host.moduleNameToFileName(module, containingFile); const filePath = this.host.moduleNameToFileName(module, containingFile);
let symbol: StaticSymbol;
if (!filePath) { if (!filePath) {
// If the file cannot be found the module is probably referencing a declared module // If the file cannot be found the module is probably referencing a declared module
// for which there is no disambiguating file and we also don't need to track // for which there is no disambiguating file and we also don't need to track
// re-exports. Just use the module name. // re-exports. Just use the module name.
return this.getStaticSymbol(module, symbolName); symbol = this.getStaticSymbol(module, symbolName);
} else {
symbol = this.resolveExportedSymbol(filePath, symbolName) ||
this.getStaticSymbol(filePath, symbolName);
} }
let symbol = this.resolveExportedSymbol(filePath, symbolName) ||
this.getStaticSymbol(filePath, symbolName);
this.declarationMap.set(cacheKey, symbol);
return symbol; return symbol;
} catch (e) { } catch (e) {
console.error(`can't resolve module ${module} from ${containingFile}`); console.error(`can't resolve module ${module} from ${containingFile}`);

View File

@ -502,6 +502,21 @@ describe('StaticReflector', () => {
expect(symbol.name).toEqual('Thirty'); expect(symbol.name).toEqual('Thirty');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts'); expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts');
}); });
it('should cache tracing a named export', () => {
const moduleNameToFileNameSpy = spyOn(host, 'moduleNameToFileName').and.callThrough();
const getMetadataForSpy = spyOn(host, 'getMetadataFor').and.callThrough();
reflector.findDeclaration('./reexport/reexport', 'One', '/tmp/src/main.ts');
moduleNameToFileNameSpy.calls.reset();
getMetadataForSpy.calls.reset();
const symbol = reflector.findDeclaration('./reexport/reexport', 'One', '/tmp/src/main.ts');
expect(moduleNameToFileNameSpy.calls.count()).toBe(1);
expect(getMetadataForSpy.calls.count()).toBe(0);
expect(symbol.name).toEqual('One');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
});
}); });
class MockAotCompilerHost implements AotCompilerHost { class MockAotCompilerHost implements AotCompilerHost {
@ -550,7 +565,7 @@ class MockAotCompilerHost implements AotCompilerHost {
if (modulePath.indexOf('.') === 0) { if (modulePath.indexOf('.') === 0) {
const baseName = pathTo(containingFile, modulePath); const baseName = pathTo(containingFile, modulePath);
const tsName = baseName + '.ts'; const tsName = baseName + '.ts';
if (this.getMetadataFor(tsName)) { if (this._getMetadataFor(tsName)) {
return tsName; return tsName;
} }
return baseName + '.d.ts'; return baseName + '.d.ts';
@ -558,7 +573,9 @@ class MockAotCompilerHost implements AotCompilerHost {
return '/tmp/' + modulePath + '.d.ts'; return '/tmp/' + modulePath + '.d.ts';
} }
getMetadataFor(moduleId: string): any { getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); }
private _getMetadataFor(moduleId: string): any {
const data: {[key: string]: any} = { const data: {[key: string]: any} = {
'/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{ '/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{
'__symbolic': 'module', '__symbolic': 'module',