From da77b580c95789e615e4edeee2b7cd4fe8babee4 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Wed, 23 Nov 2016 12:08:42 -0800 Subject: [PATCH] Revert "refactor(compiler): Reintroduce `ReflectorHost` and move `Extractor` into `@angular/compiler`" This reverts commit 64bd672e3af793e210d876244869cb42b007e29e. --- modules/@angular/compiler-cli/src/codegen.ts | 20 ++- .../@angular/compiler-cli/src/extract_i18n.ts | 12 +- .../@angular/compiler-cli/src/extractor.ts | 76 ++++++++++-- .../compiler/src/aot/compiler_host.ts | 30 ++++- .../compiler/src/aot/static_reflector.ts | 27 +--- .../@angular/compiler/src/i18n/extractor.ts | 117 ------------------ modules/@angular/compiler/src/i18n/index.ts | 1 - .../@angular/compiler/src/output/path_util.ts | 4 - .../test/aot/static_reflector_spec.ts | 16 ++- 9 files changed, 130 insertions(+), 173 deletions(-) delete mode 100644 modules/@angular/compiler/src/i18n/extractor.ts diff --git a/modules/@angular/compiler-cli/src/codegen.ts b/modules/@angular/compiler-cli/src/codegen.ts index 8e9dfff358..6e552fe617 100644 --- a/modules/@angular/compiler-cli/src/codegen.ts +++ b/modules/@angular/compiler-cli/src/codegen.ts @@ -36,8 +36,8 @@ const PREAMBLE = `/** export class CodeGenerator { constructor( private options: AngularCompilerOptions, private program: ts.Program, - public host: ts.CompilerHost, private compiler: compiler.AotCompiler, - private ngCompilerHost: CompilerHost) {} + public host: ts.CompilerHost, private staticReflector: compiler.StaticReflector, + private compiler: compiler.AotCompiler, private ngCompilerHost: CompilerHost) {} // Write codegen in a directory structure matching the sources. private calculateEmitPath(filePath: string): string { @@ -96,7 +96,7 @@ export class CodeGenerator { } transContent = readFileSync(transFile, 'utf8'); } - const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, { + const {compiler: aotCompiler, reflector} = compiler.createAotCompiler(ngCompilerHost, { debug: options.debug === true, translations: transContent, i18nFormat: cliOptions.i18nFormat, @@ -104,10 +104,18 @@ export class CodeGenerator { excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES }); - return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost); + return new CodeGenerator( + options, program, tsCompilerHost, reflector, aotCompiler, ngCompilerHost); } } -export function excludeFilePattern(options: AngularCompilerOptions): RegExp { - return options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES; +export function extractProgramSymbols( + program: ts.Program, staticReflector: compiler.StaticReflector, compilerHost: CompilerHost, + options: AngularCompilerOptions): compiler.StaticSymbol[] { + return compiler.extractProgramSymbols( + staticReflector, + program.getSourceFiles().map(sf => compilerHost.getCanonicalFileName(sf.fileName)), { + excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : + GENERATED_FILES + }); } diff --git a/modules/@angular/compiler-cli/src/extract_i18n.ts b/modules/@angular/compiler-cli/src/extract_i18n.ts index 16de0992bf..4efcf1cbea 100644 --- a/modules/@angular/compiler-cli/src/extract_i18n.ts +++ b/modules/@angular/compiler-cli/src/extract_i18n.ts @@ -24,7 +24,17 @@ import {Extractor} from './extractor'; function extract( ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions, program: ts.Program, host: ts.CompilerHost) { - const extractor = Extractor.create(ngOptions, cliOptions.i18nFormat, program, host); + const resourceLoader: compiler.ResourceLoader = { + get: (s: string) => { + if (!host.fileExists(s)) { + // TODO: We should really have a test for error cases like this! + throw new Error(`Compilation failed. Resource file not found: ${s}`); + } + return Promise.resolve(host.readFile(s)); + } + }; + const extractor = + Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, resourceLoader); const bundlePromise: Promise = extractor.extract(); diff --git a/modules/@angular/compiler-cli/src/extractor.ts b/modules/@angular/compiler-cli/src/extractor.ts index 33d1f89d27..38913416be 100644 --- a/modules/@angular/compiler-cli/src/extractor.ts +++ b/modules/@angular/compiler-cli/src/extractor.ts @@ -14,28 +14,84 @@ import 'reflect-metadata'; import * as compiler from '@angular/compiler'; +import {ViewEncapsulation} from '@angular/core'; import * as tsc from '@angular/tsc-wrapped'; import * as ts from 'typescript'; -import {excludeFilePattern} from './codegen'; +import {extractProgramSymbols} from './codegen'; import {CompilerHost} from './compiler_host'; export class Extractor { constructor( - private ngExtractor: compiler.Extractor, private ngCompilerHost: CompilerHost, - private program: ts.Program) {} + private options: tsc.AngularCompilerOptions, private program: ts.Program, + public host: ts.CompilerHost, private staticReflector: compiler.StaticReflector, + private messageBundle: compiler.MessageBundle, private compilerHost: CompilerHost, + private metadataResolver: compiler.CompileMetadataResolver) {} extract(): Promise { - return this.ngExtractor.extract(this.program.getSourceFiles().map( - sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName))) + const programSymbols: compiler.StaticSymbol[] = + extractProgramSymbols(this.program, this.staticReflector, this.compilerHost, this.options); + + const {ngModules, files} = + compiler.analyzeAndValidateNgModules(programSymbols, {}, this.metadataResolver); + return compiler.loadNgModuleDirectives(ngModules).then(() => { + const errors: compiler.ParseError[] = []; + + files.forEach(file => { + const compMetas: compiler.CompileDirectiveMetadata[] = []; + file.directives.forEach(directiveType => { + const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType); + if (dirMeta && dirMeta.isComponent) { + compMetas.push(dirMeta); + } + }); + compMetas.forEach(compMeta => { + const html = compMeta.template.template; + const interpolationConfig = + compiler.InterpolationConfig.fromArray(compMeta.template.interpolation); + errors.push( + ...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig)); + }); + }); + + if (errors.length) { + throw new Error(errors.map(e => e.toString()).join('\n')); + } + + return this.messageBundle; + }); } static create( options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program, - tsCompilerHost: ts.CompilerHost, ngCompilerHost?: CompilerHost): Extractor { + tsCompilerHost: ts.CompilerHost, resourceLoader: compiler.ResourceLoader, + ngCompilerHost?: CompilerHost): Extractor { + const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser()); + + const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver(); if (!ngCompilerHost) ngCompilerHost = new CompilerHost(program, tsCompilerHost, options); - const {extractor: ngExtractor} = compiler.Extractor.create( - ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)}); - return new Extractor(ngExtractor, ngCompilerHost, program); + const staticReflector = new compiler.StaticReflector(ngCompilerHost); + compiler.StaticAndDynamicReflectionCapabilities.install(staticReflector); + + const config = new compiler.CompilerConfig({ + genDebugInfo: options.debug === true, + defaultEncapsulation: ViewEncapsulation.Emulated, + logBindingUpdate: false, + useJit: false + }); + + const normalizer = + new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config); + const elementSchemaRegistry = new compiler.DomElementSchemaRegistry(); + const resolver = new compiler.CompileMetadataResolver( + new compiler.NgModuleResolver(staticReflector), + new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), + elementSchemaRegistry, normalizer, staticReflector); + + // TODO(vicb): implicit tags & attributes + const messageBundle = new compiler.MessageBundle(htmlParser, [], {}); + + return new Extractor( + options, program, tsCompilerHost, staticReflector, messageBundle, ngCompilerHost, resolver); } -} +} \ No newline at end of file diff --git a/modules/@angular/compiler/src/aot/compiler_host.ts b/modules/@angular/compiler/src/aot/compiler_host.ts index 2d108578ef..ef5a37443c 100644 --- a/modules/@angular/compiler/src/aot/compiler_host.ts +++ b/modules/@angular/compiler/src/aot/compiler_host.ts @@ -6,17 +6,37 @@ * found in the LICENSE file at https://angular.io/license */ -import {ImportResolver} from '../output/path_util'; - -import {StaticReflectorHost} from './static_reflector'; import {StaticSymbol} from './static_symbol'; - /** * The host of the AotCompiler disconnects the implementation from TypeScript / other language * services and from underlying file systems. */ -export interface AotCompilerHost extends StaticReflectorHost, ImportResolver { +export interface AotCompilerHost { + /** + * Return a ModuleMetadata for the given module. + * Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is + * produced and the module has exported variables or classes with decorators. Module metadata can + * also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata. + * + * @param modulePath is a string identifier for a module as an absolute path. + * @returns the metadata for the given module. + */ + getMetadataFor(modulePath: string): {[key: string]: any}[]; + + /** + * Converts a module name that is used in an `import` to a file path. + * I.e. + * `path/to/containingFile.ts` containing `import {...} from 'module-name'`. + */ + moduleNameToFileName(moduleName: string, containingFile: string): string; + + /** + * Converts a file path to a module name that can be used as an `import. + * I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`. + */ + fileNameToModuleName(importedFile: string, containingFile: string): string; + /** * Loads a resource (e.g. html / css) */ diff --git a/modules/@angular/compiler/src/aot/static_reflector.ts b/modules/@angular/compiler/src/aot/static_reflector.ts index f07aa7e8d3..460769dfb7 100644 --- a/modules/@angular/compiler/src/aot/static_reflector.ts +++ b/modules/@angular/compiler/src/aot/static_reflector.ts @@ -8,6 +8,7 @@ import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {ReflectorReader} from '../private_import_core'; +import {AotCompilerHost} from './compiler_host'; import {StaticSymbol} from './static_symbol'; const SUPPORTED_SCHEMA_VERSION = 2; @@ -20,30 +21,6 @@ const ANGULAR_IMPORT_LOCATIONS = { provider: '@angular/core/src/di/provider' }; -/** - * The host of the StaticReflector disconnects the implementation from TypeScript / other language - * services and from underlying file systems. - */ -export interface StaticReflectorHost { - /** - * Return a ModuleMetadata for the given module. - * Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is - * produced and the module has exported variables or classes with decorators. Module metadata can - * also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata. - * - * @param modulePath is a string identifier for a module as an absolute path. - * @returns the metadata for the given module. - */ - getMetadataFor(modulePath: string): {[key: string]: any}[]; - - /** - * Converts a module name that is used in an `import` to a file path. - * I.e. - * `path/to/containingFile.ts` containing `import {...} from 'module-name'`. - */ - moduleNameToFileName(moduleName: string, containingFile: string): string; -} - /** * A static reflector implements enough of the Reflector API that is necessary to compile * templates statically. @@ -58,7 +35,7 @@ export class StaticReflector implements ReflectorReader { private conversionMap = new Map any>(); private opaqueToken: StaticSymbol; - constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); } + constructor(private host: AotCompilerHost) { this.initializeConversionMap(); } importUri(typeOrFunc: StaticSymbol): string { const staticSymbol = this.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, ''); diff --git a/modules/@angular/compiler/src/i18n/extractor.ts b/modules/@angular/compiler/src/i18n/extractor.ts deleted file mode 100644 index b1bad2f136..0000000000 --- a/modules/@angular/compiler/src/i18n/extractor.ts +++ /dev/null @@ -1,117 +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 - */ - - -/** - * Extract i18n messages from source code - */ -import {ViewEncapsulation} from '@angular/core'; - -import {analyzeAndValidateNgModules, extractProgramSymbols, loadNgModuleDirectives} from '../aot/compiler'; -import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities'; -import {StaticReflector, StaticReflectorHost} from '../aot/static_reflector'; -import {CompileDirectiveMetadata} from '../compile_metadata'; -import {CompilerConfig} from '../config'; -import {DirectiveNormalizer} from '../directive_normalizer'; -import {DirectiveResolver} from '../directive_resolver'; -import {CompileMetadataResolver} from '../metadata_resolver'; -import {HtmlParser} from '../ml_parser/html_parser'; -import {InterpolationConfig} from '../ml_parser/interpolation_config'; -import {NgModuleResolver} from '../ng_module_resolver'; -import {ParseError} from '../parse_util'; -import {PipeResolver} from '../pipe_resolver'; -import {Console} from '../private_import_core'; -import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry'; -import {createOfflineCompileUrlResolver} from '../url_resolver'; - -import {I18NHtmlParser} from './i18n_html_parser'; -import {MessageBundle} from './message_bundle'; - -export interface ExtractorOptions { - includeFilePattern?: RegExp; - excludeFilePattern?: RegExp; -} - -/** - * The host of the Extractor disconnects the implementation from TypeScript / other language - * services and from underlying file systems. - */ -export interface ExtractorHost extends StaticReflectorHost { - /** - * Loads a resource (e.g. html / css) - */ - loadResource(path: string): Promise; -} - -export class Extractor { - constructor( - private options: ExtractorOptions, public host: ExtractorHost, - private staticReflector: StaticReflector, private messageBundle: MessageBundle, - private metadataResolver: CompileMetadataResolver) {} - - extract(rootFiles: string[]): Promise { - const programSymbols = extractProgramSymbols(this.staticReflector, rootFiles, this.options); - const {ngModuleByPipeOrDirective, files, ngModules} = - analyzeAndValidateNgModules(programSymbols, this.options, this.metadataResolver); - return loadNgModuleDirectives(ngModules).then(() => { - const errors: ParseError[] = []; - - files.forEach(file => { - const compMetas: CompileDirectiveMetadata[] = []; - file.directives.forEach(directiveType => { - const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType); - if (dirMeta && dirMeta.isComponent) { - compMetas.push(dirMeta); - } - }); - compMetas.forEach(compMeta => { - const html = compMeta.template.template; - const interpolationConfig = - InterpolationConfig.fromArray(compMeta.template.interpolation); - errors.push( - ...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig)); - }); - }); - - if (errors.length) { - throw new Error(errors.map(e => e.toString()).join('\n')); - } - - return this.messageBundle; - }); - } - - static create(host: ExtractorHost, options: ExtractorOptions): - {extractor: Extractor, staticReflector: StaticReflector} { - const htmlParser = new I18NHtmlParser(new HtmlParser()); - - const urlResolver = createOfflineCompileUrlResolver(); - const staticReflector = new StaticReflector(host); - StaticAndDynamicReflectionCapabilities.install(staticReflector); - - const config = new CompilerConfig({ - genDebugInfo: false, - defaultEncapsulation: ViewEncapsulation.Emulated, - logBindingUpdate: false, - useJit: false - }); - - const normalizer = new DirectiveNormalizer( - {get: (url: string) => host.loadResource(url)}, urlResolver, htmlParser, config); - const elementSchemaRegistry = new DomElementSchemaRegistry(); - const resolver = new CompileMetadataResolver( - new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector), - new PipeResolver(staticReflector), elementSchemaRegistry, normalizer, staticReflector); - - // TODO(vicb): implicit tags & attributes - const messageBundle = new MessageBundle(htmlParser, [], {}); - - const extractor = new Extractor(options, host, staticReflector, messageBundle, resolver); - return {extractor, staticReflector}; - } -} diff --git a/modules/@angular/compiler/src/i18n/index.ts b/modules/@angular/compiler/src/i18n/index.ts index aca404c024..67b61a891f 100644 --- a/modules/@angular/compiler/src/i18n/index.ts +++ b/modules/@angular/compiler/src/i18n/index.ts @@ -12,4 +12,3 @@ export {Serializer} from './serializers/serializer'; export {Xliff} from './serializers/xliff'; export {Xmb} from './serializers/xmb'; export {Xtb} from './serializers/xtb'; -export * from './extractor'; \ No newline at end of file diff --git a/modules/@angular/compiler/src/output/path_util.ts b/modules/@angular/compiler/src/output/path_util.ts index bf8615cf68..ed5a570f80 100644 --- a/modules/@angular/compiler/src/output/path_util.ts +++ b/modules/@angular/compiler/src/output/path_util.ts @@ -10,9 +10,5 @@ * Interface that defines how import statements should be generated. */ export abstract class ImportResolver { - /** - * Converts a file path to a module name that can be used as an `import. - * I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`. - */ abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string; } diff --git a/modules/@angular/compiler/test/aot/static_reflector_spec.ts b/modules/@angular/compiler/test/aot/static_reflector_spec.ts index 91dd008e18..d65275377a 100644 --- a/modules/@angular/compiler/test/aot/static_reflector_spec.ts +++ b/modules/@angular/compiler/test/aot/static_reflector_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler'; +import {AotCompilerHost, StaticReflector, StaticSymbol} from '@angular/compiler'; import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {MetadataCollector} from '@angular/tsc-wrapped'; import * as ts from 'typescript'; @@ -18,11 +18,11 @@ const TS_EXT = /(^.|(?!\.d)..)\.ts$/; describe('StaticReflector', () => { const noContext = new StaticSymbol('', ''); - let host: StaticReflectorHost; + let host: AotCompilerHost; let reflector: StaticReflector; beforeEach(() => { - host = new MockStaticReflectorHost(); + host = new MockAotCompilerHost(); reflector = new StaticReflector(host); }); @@ -519,9 +519,17 @@ describe('StaticReflector', () => { }); -class MockStaticReflectorHost implements StaticReflectorHost { +class MockAotCompilerHost implements AotCompilerHost { private collector = new MetadataCollector(); + constructor() {} + + loadResource(filePath: string): Promise { throw new Error('Should not be called!'); } + + fileNameToModuleName(importedFilePath: string, containingFilePath: string): string { + throw new Error('Should not be called!'); + } + // In tests, assume that symbols are not re-exported moduleNameToFileName(modulePath: string, containingFile?: string): string { function splitPath(path: string): string[] { return path.split(/\/|\\/g); }