diff --git a/modules/@angular/compiler-cli/src/codegen.ts b/modules/@angular/compiler-cli/src/codegen.ts index dd7d6bce9b..ba7d2da1c7 100644 --- a/modules/@angular/compiler-cli/src/codegen.ts +++ b/modules/@angular/compiler-cli/src/codegen.ts @@ -62,30 +62,23 @@ export class CodeGenerator { return path.join(this.options.genDir, relativePath); } - codegen(): Promise { - return this.compiler - .compileAll( - this.program.getSourceFiles().map(sf => this.ngHost.getCanonicalFileName(sf.fileName))) - .then(generatedModules => { - generatedModules.forEach(generatedModule => { - const sourceFile = this.program.getSourceFile(generatedModule.fileUrl); - const emitPath = this.calculateEmitPath(generatedModule.moduleUrl); - this.host.writeFile( - emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]); - }); - }); + codegen(options: {transitiveModules: boolean}): Promise { + const staticSymbols = + extractProgramSymbols(this.program, this.staticReflector, this.ngHost, this.options); + + return this.compiler.compileModules(staticSymbols, options).then(generatedModules => { + generatedModules.forEach(generatedModule => { + const sourceFile = this.program.getSourceFile(generatedModule.fileUrl); + const emitPath = this.calculateEmitPath(generatedModule.moduleUrl); + this.host.writeFile( + emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]); + }); + }); } static create( options: AngularCompilerOptions, cliOptions: NgcCliOptions, program: ts.Program, - compilerHost: ts.CompilerHost, ngHostContext?: NgHostContext, - ngHost?: NgHost): CodeGenerator { - if (!ngHost) { - const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0; - ngHost = usePathMapping ? - new PathMappedNgHost(program, compilerHost, options, ngHostContext) : - new NgHost(program, compilerHost, options, ngHostContext); - } + compilerHost: ts.CompilerHost, ngHost?: NgHost): CodeGenerator { const transFile = cliOptions.i18nFile; const locale = cliOptions.locale; let transContent: string = ''; @@ -100,9 +93,7 @@ export class CodeGenerator { debug: options.debug === true, translations: transContent, i18nFormat: cliOptions.i18nFormat, - locale: cliOptions.locale, - excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : - GENERATED_FILES + locale: cliOptions.locale }); return new CodeGenerator(options, program, compilerHost, reflector, aotCompiler, ngHost); } @@ -111,10 +102,37 @@ export class CodeGenerator { export function extractProgramSymbols( program: ts.Program, staticReflector: compiler.StaticReflector, ngHost: NgHost, options: AngularCompilerOptions): compiler.StaticSymbol[] { - return compiler.extractProgramSymbols( - staticReflector, program.getSourceFiles().map(sf => ngHost.getCanonicalFileName(sf.fileName)), - { - excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : - GENERATED_FILES + // Compare with false since the default should be true + const skipFileNames = + options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES; + + const staticSymbols: compiler.StaticSymbol[] = []; + + program.getSourceFiles() + .filter(sourceFile => !skipFileNames.test(sourceFile.fileName)) + .forEach(sourceFile => { + const absSrcPath = ngHost.getCanonicalFileName(sourceFile.fileName); + + const moduleMetadata = staticReflector.getModuleMetadata(absSrcPath); + if (!moduleMetadata) { + console.log(`WARNING: no metadata found for ${absSrcPath}`); + return; + } + + const metadata = moduleMetadata['metadata']; + + if (!metadata) { + return; + } + + for (const symbol of Object.keys(metadata)) { + if (metadata[symbol] && metadata[symbol].__symbolic == 'error') { + // Ignore symbols that are only included to record error information. + continue; + } + staticSymbols.push(staticReflector.findDeclaration(absSrcPath, symbol, absSrcPath)); + } }); + + return staticSymbols; } diff --git a/modules/@angular/compiler-cli/src/main.ts b/modules/@angular/compiler-cli/src/main.ts index 265687c6e0..73312909ee 100644 --- a/modules/@angular/compiler-cli/src/main.ts +++ b/modules/@angular/compiler-cli/src/main.ts @@ -19,7 +19,9 @@ import {CodeGenerator} from './codegen'; function codegen( ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.NgcCliOptions, program: ts.Program, host: ts.CompilerHost) { - return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen(); + return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen({ + transitiveModules: true + }); } // CLI entry point diff --git a/modules/@angular/compiler/src/aot/compiler.ts b/modules/@angular/compiler/src/aot/compiler.ts index d58488df9a..1d05155eee 100644 --- a/modules/@angular/compiler/src/aot/compiler.ts +++ b/modules/@angular/compiler/src/aot/compiler.ts @@ -23,14 +23,100 @@ import {CompiledStylesheet, StyleCompiler} from '../style_compiler'; import {TemplateParser} from '../template_parser/template_parser'; import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler'; -import {AotCompilerOptions} from './compiler_options'; -import {StaticReflector} from './static_reflector'; import {StaticSymbol} from './static_symbol'; export class SourceModule { constructor(public fileUrl: string, public moduleUrl: string, public source: string) {} } +export interface NgAnalyzedModules { + ngModules: CompileNgModuleMetadata[]; + ngModuleByPipeOrDirective: Map; + files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>; + symbolsMissingModule?: StaticSymbol[]; +} + +// Returns all the source files and a mapping from modules to directives +export function analyzeNgModules( + programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean}, + metadataResolver: CompileMetadataResolver): NgAnalyzedModules { + const {ngModules, symbolsMissingModule} = + _createNgModules(programStaticSymbols, options, metadataResolver); + return _analyzeNgModules(ngModules, symbolsMissingModule); +} + + +export function analyzeAndValidateNgModules( + programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean}, + metadataResolver: CompileMetadataResolver): NgAnalyzedModules { + const result = analyzeNgModules(programStaticSymbols, options, metadataResolver); + if (result.symbolsMissingModule && result.symbolsMissingModule.length) { + const messages = result.symbolsMissingModule.map( + s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`); + throw new Error(messages.join('\n')); + } + return result; +} + +// Wait for the directives in the given modules have been loaded +export function loadNgModuleDirectives(ngModules: CompileNgModuleMetadata[]) { + return Promise + .all(ListWrapper.flatten(ngModules.map( + (ngModule) => ngModule.transitiveModule.directiveLoaders.map(loader => loader())))) + .then(() => {}); +} + +function _analyzeNgModules( + ngModuleMetas: CompileNgModuleMetadata[], + symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules { + const moduleMetasByRef = new Map(); + ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule)); + const ngModuleByPipeOrDirective = new Map(); + const ngModulesByFile = new Map(); + const ngDirectivesByFile = new Map(); + const filePaths = new Set(); + + // Looping over all modules to construct: + // - a map from file to modules `ngModulesByFile`, + // - a map from file to directives `ngDirectivesByFile`, + // - a map from directive/pipe to module `ngModuleByPipeOrDirective`. + ngModuleMetas.forEach((ngModuleMeta) => { + const srcFileUrl = ngModuleMeta.type.reference.filePath; + filePaths.add(srcFileUrl); + ngModulesByFile.set( + srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference)); + + ngModuleMeta.declaredDirectives.forEach((dirIdentifier) => { + const fileUrl = dirIdentifier.reference.filePath; + filePaths.add(fileUrl); + ngDirectivesByFile.set( + fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirIdentifier.reference)); + ngModuleByPipeOrDirective.set(dirIdentifier.reference, ngModuleMeta); + }); + ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => { + const fileUrl = pipeIdentifier.reference.filePath; + filePaths.add(fileUrl); + ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta); + }); + }); + + const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = []; + + filePaths.forEach((srcUrl) => { + const directives = ngDirectivesByFile.get(srcUrl) || []; + const ngModules = ngModulesByFile.get(srcUrl) || []; + files.push({srcUrl, directives, ngModules}); + }); + + return { + // map directive/pipe to module + ngModuleByPipeOrDirective, + // list modules and directives for every source file + files, + ngModules: ngModuleMetas, symbolsMissingModule + }; +} + export class AotCompiler { private _animationCompiler = new AnimationCompiler(); @@ -40,20 +126,14 @@ export class AotCompiler { private _dirWrapperCompiler: DirectiveWrapperCompiler, private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter, private _localeId: string, private _translationFormat: string, - private _animationParser: AnimationParser, private _staticReflector: StaticReflector, - private _options: AotCompilerOptions) {} + private _animationParser: AnimationParser) {} clearCache() { this._metadataResolver.clearCache(); } - compileAll(rootFiles: string[]): Promise { - const options = { - transitiveModules: true, - excludeFilePattern: this._options.excludeFilePattern, - includeFilePattern: this._options.includeFilePattern - }; - const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, options); + compileModules(staticSymbols: StaticSymbol[], options: {transitiveModules: boolean}): + Promise { const {ngModuleByPipeOrDirective, files, ngModules} = - analyzeAndValidateNgModules(programSymbols, options, this._metadataResolver); + analyzeAndValidateNgModules(staticSymbols, options, this._metadataResolver); return loadNgModuleDirectives(ngModules).then(() => { const sourceModules = files.map( file => this._compileSrcFile( @@ -278,133 +358,11 @@ function _splitTypescriptSuffix(path: string): string[] { return [path, '']; } -export interface NgAnalyzedModules { - ngModules: CompileNgModuleMetadata[]; - ngModuleByPipeOrDirective: Map; - files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>; - symbolsMissingModule?: StaticSymbol[]; -} - -// Returns all the source files and a mapping from modules to directives -export function analyzeNgModules( - programStaticSymbols: StaticSymbol[], - options: {transitiveModules: boolean, includeFilePattern?: RegExp, excludeFilePattern?: RegExp}, - metadataResolver: CompileMetadataResolver): NgAnalyzedModules { - const {ngModules, symbolsMissingModule} = - _createNgModules(programStaticSymbols, options, metadataResolver); - return _analyzeNgModules(ngModules, symbolsMissingModule); -} - -export function analyzeAndValidateNgModules( - programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean}, - metadataResolver: CompileMetadataResolver): NgAnalyzedModules { - const result = analyzeNgModules(programStaticSymbols, options, metadataResolver); - if (result.symbolsMissingModule && result.symbolsMissingModule.length) { - const messages = result.symbolsMissingModule.map( - s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`); - throw new Error(messages.join('\n')); - } - return result; -} - -// Wait for the directives in the given modules have been loaded -export function loadNgModuleDirectives(ngModules: CompileNgModuleMetadata[]) { - return Promise - .all(ListWrapper.flatten(ngModules.map( - (ngModule) => ngModule.transitiveModule.directiveLoaders.map(loader => loader())))) - .then(() => {}); -} - -function _analyzeNgModules( - ngModuleMetas: CompileNgModuleMetadata[], - symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules { - const moduleMetasByRef = new Map(); - ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule)); - const ngModuleByPipeOrDirective = new Map(); - const ngModulesByFile = new Map(); - const ngDirectivesByFile = new Map(); - const filePaths = new Set(); - - // Looping over all modules to construct: - // - a map from file to modules `ngModulesByFile`, - // - a map from file to directives `ngDirectivesByFile`, - // - a map from directive/pipe to module `ngModuleByPipeOrDirective`. - ngModuleMetas.forEach((ngModuleMeta) => { - const srcFileUrl = ngModuleMeta.type.reference.filePath; - filePaths.add(srcFileUrl); - ngModulesByFile.set( - srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference)); - - ngModuleMeta.declaredDirectives.forEach((dirIdentifier) => { - const fileUrl = dirIdentifier.reference.filePath; - filePaths.add(fileUrl); - ngDirectivesByFile.set( - fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirIdentifier.reference)); - ngModuleByPipeOrDirective.set(dirIdentifier.reference, ngModuleMeta); - }); - ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => { - const fileUrl = pipeIdentifier.reference.filePath; - filePaths.add(fileUrl); - ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta); - }); - }); - - const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = []; - - filePaths.forEach((srcUrl) => { - const directives = ngDirectivesByFile.get(srcUrl) || []; - const ngModules = ngModulesByFile.get(srcUrl) || []; - files.push({srcUrl, directives, ngModules}); - }); - - return { - // map directive/pipe to module - ngModuleByPipeOrDirective, - // list modules and directives for every source file - files, - ngModules: ngModuleMetas, symbolsMissingModule - }; -} - -export function extractProgramSymbols( - staticReflector: StaticReflector, files: string[], - options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] { - const staticSymbols: StaticSymbol[] = []; - files - .filter( - fileName => _filterFileByPatterns( - fileName, options.includeFilePattern, options.includeFilePattern)) - .forEach(sourceFile => { - const moduleMetadata = staticReflector.getModuleMetadata(sourceFile); - if (!moduleMetadata) { - console.log(`WARNING: no metadata found for ${sourceFile}`); - return; - } - - const metadata = moduleMetadata['metadata']; - - if (!metadata) { - return; - } - - for (const symbol of Object.keys(metadata)) { - if (metadata[symbol] && metadata[symbol].__symbolic == 'error') { - // Ignore symbols that are only included to record error information. - continue; - } - staticSymbols.push(staticReflector.findDeclaration(sourceFile, symbol, sourceFile)); - } - }); - - return staticSymbols; -} - // Load the NgModules and check // that all directives / pipes that are present in the program // are also declared by a module. function _createNgModules( - programStaticSymbols: StaticSymbol[], - options: {transitiveModules: boolean, includeFilePattern?: RegExp, excludeFilePattern?: RegExp}, + programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean}, metadataResolver: CompileMetadataResolver): {ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} { const ngModules = new Map(); @@ -412,9 +370,7 @@ function _createNgModules( const ngModulePipesAndDirective = new Set(); const addNgModule = (staticSymbol: any) => { - if (ngModules.has(staticSymbol) || - !_filterFileByPatterns( - staticSymbol.filePath, options.includeFilePattern, options.excludeFilePattern)) { + if (ngModules.has(staticSymbol)) { return false; } const ngModule = metadataResolver.getUnloadedNgModuleMetadata(staticSymbol, false, false); @@ -442,15 +398,3 @@ function _createNgModules( return {ngModules: Array.from(ngModules.values()), symbolsMissingModule}; } - -function _filterFileByPatterns( - fileName: string, includeFilePattern: RegExp, excludeFilePattern: RegExp) { - let match = true; - if (includeFilePattern) { - match = match && !!includeFilePattern.exec(fileName); - } - if (excludeFilePattern) { - match = match && !excludeFilePattern.exec(fileName); - } - return match; -} \ No newline at end of file diff --git a/modules/@angular/compiler/src/aot/compiler_factory.ts b/modules/@angular/compiler/src/aot/compiler_factory.ts index f8eb93a392..71d3e9e91b 100644 --- a/modules/@angular/compiler/src/aot/compiler_factory.ts +++ b/modules/@angular/compiler/src/aot/compiler_factory.ts @@ -31,11 +31,15 @@ import {ViewCompiler} from '../view_compiler/view_compiler'; import {AotCompiler} from './compiler'; import {AotCompilerHost} from './compiler_host'; -import {AotCompilerOptions} from './compiler_options'; import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities'; import {StaticReflector} from './static_reflector'; - +export interface AotCompilerOptions { + debug?: boolean; + locale?: string; + i18nFormat?: string; + translations?: string; +} /** * Creates a new AotCompiler based on options and a host. @@ -70,6 +74,6 @@ export function createAotCompiler(ngHost: AotCompilerHost, options: AotCompilerO new ViewCompiler(config, elementSchemaRegistry), new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console), new NgModuleCompiler(), new TypeScriptEmitter(ngHost), options.locale, options.i18nFormat, - new AnimationParser(elementSchemaRegistry), staticReflector, options); + new AnimationParser(elementSchemaRegistry)); return {compiler, reflector: staticReflector}; } diff --git a/modules/@angular/compiler/src/aot/compiler_options.ts b/modules/@angular/compiler/src/aot/compiler_options.ts deleted file mode 100644 index 8794d88840..0000000000 --- a/modules/@angular/compiler/src/aot/compiler_options.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -export interface AotCompilerOptions { - debug?: boolean; - locale?: string; - i18nFormat?: string; - translations?: string; - includeFilePattern?: RegExp; - excludeFilePattern?: RegExp; -}