Skip to content

Commit 2a5bd2f

Browse files
tboschchuckjaz
authored andcommitted
refactor(compiler): Reintroduce ReflectorHost and move Extractor into @angular/compiler
1 parent 3c06a5d commit 2a5bd2f

File tree

9 files changed

+173
-130
lines changed

9 files changed

+173
-130
lines changed

modules/@angular/compiler-cli/src/codegen.ts

+6-14
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ const PREAMBLE = `/**
3636
export class CodeGenerator {
3737
constructor(
3838
private options: AngularCompilerOptions, private program: ts.Program,
39-
public host: ts.CompilerHost, private staticReflector: compiler.StaticReflector,
40-
private compiler: compiler.AotCompiler, private ngCompilerHost: CompilerHost) {}
39+
public host: ts.CompilerHost, private compiler: compiler.AotCompiler,
40+
private ngCompilerHost: CompilerHost) {}
4141

4242
// Write codegen in a directory structure matching the sources.
4343
private calculateEmitPath(filePath: string): string {
@@ -96,26 +96,18 @@ export class CodeGenerator {
9696
}
9797
transContent = readFileSync(transFile, 'utf8');
9898
}
99-
const {compiler: aotCompiler, reflector} = compiler.createAotCompiler(ngCompilerHost, {
99+
const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, {
100100
debug: options.debug === true,
101101
translations: transContent,
102102
i18nFormat: cliOptions.i18nFormat,
103103
locale: cliOptions.locale,
104104
excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES :
105105
GENERATED_FILES
106106
});
107-
return new CodeGenerator(
108-
options, program, tsCompilerHost, reflector, aotCompiler, ngCompilerHost);
107+
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
109108
}
110109
}
111110

112-
export function extractProgramSymbols(
113-
program: ts.Program, staticReflector: compiler.StaticReflector, compilerHost: CompilerHost,
114-
options: AngularCompilerOptions): compiler.StaticSymbol[] {
115-
return compiler.extractProgramSymbols(
116-
staticReflector,
117-
program.getSourceFiles().map(sf => compilerHost.getCanonicalFileName(sf.fileName)), {
118-
excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES :
119-
GENERATED_FILES
120-
});
111+
export function excludeFilePattern(options: AngularCompilerOptions): RegExp {
112+
return options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
121113
}

modules/@angular/compiler-cli/src/extract_i18n.ts

+1-11
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,7 @@ import {Extractor} from './extractor';
2424
function extract(
2525
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
2626
program: ts.Program, host: ts.CompilerHost) {
27-
const resourceLoader: compiler.ResourceLoader = {
28-
get: (s: string) => {
29-
if (!host.fileExists(s)) {
30-
// TODO: We should really have a test for error cases like this!
31-
throw new Error(`Compilation failed. Resource file not found: ${s}`);
32-
}
33-
return Promise.resolve(host.readFile(s));
34-
}
35-
};
36-
const extractor =
37-
Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, resourceLoader);
27+
const extractor = Extractor.create(ngOptions, cliOptions.i18nFormat, program, host);
3828

3929
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
4030

modules/@angular/compiler-cli/src/extractor.ts

+10-66
Original file line numberDiff line numberDiff line change
@@ -14,84 +14,28 @@
1414
import 'reflect-metadata';
1515

1616
import * as compiler from '@angular/compiler';
17-
import {ViewEncapsulation} from '@angular/core';
1817
import * as tsc from '@angular/tsc-wrapped';
1918
import * as ts from 'typescript';
2019

21-
import {extractProgramSymbols} from './codegen';
20+
import {excludeFilePattern} from './codegen';
2221
import {CompilerHost} from './compiler_host';
2322

2423
export class Extractor {
2524
constructor(
26-
private options: tsc.AngularCompilerOptions, private program: ts.Program,
27-
public host: ts.CompilerHost, private staticReflector: compiler.StaticReflector,
28-
private messageBundle: compiler.MessageBundle, private compilerHost: CompilerHost,
29-
private metadataResolver: compiler.CompileMetadataResolver) {}
25+
private ngExtractor: compiler.Extractor, private ngCompilerHost: CompilerHost,
26+
private program: ts.Program) {}
3027

3128
extract(): Promise<compiler.MessageBundle> {
32-
const programSymbols: compiler.StaticSymbol[] =
33-
extractProgramSymbols(this.program, this.staticReflector, this.compilerHost, this.options);
34-
35-
const {ngModules, files} =
36-
compiler.analyzeAndValidateNgModules(programSymbols, {}, this.metadataResolver);
37-
return compiler.loadNgModuleDirectives(ngModules).then(() => {
38-
const errors: compiler.ParseError[] = [];
39-
40-
files.forEach(file => {
41-
const compMetas: compiler.CompileDirectiveMetadata[] = [];
42-
file.directives.forEach(directiveType => {
43-
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
44-
if (dirMeta && dirMeta.isComponent) {
45-
compMetas.push(dirMeta);
46-
}
47-
});
48-
compMetas.forEach(compMeta => {
49-
const html = compMeta.template.template;
50-
const interpolationConfig =
51-
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
52-
errors.push(
53-
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
54-
});
55-
});
56-
57-
if (errors.length) {
58-
throw new Error(errors.map(e => e.toString()).join('\n'));
59-
}
60-
61-
return this.messageBundle;
62-
});
29+
return this.ngExtractor.extract(this.program.getSourceFiles().map(
30+
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
6331
}
6432

6533
static create(
6634
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
67-
tsCompilerHost: ts.CompilerHost, resourceLoader: compiler.ResourceLoader,
68-
ngCompilerHost?: CompilerHost): Extractor {
69-
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
70-
71-
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
35+
tsCompilerHost: ts.CompilerHost, ngCompilerHost?: CompilerHost): Extractor {
7236
if (!ngCompilerHost) ngCompilerHost = new CompilerHost(program, tsCompilerHost, options);
73-
const staticReflector = new compiler.StaticReflector(ngCompilerHost);
74-
compiler.StaticAndDynamicReflectionCapabilities.install(staticReflector);
75-
76-
const config = new compiler.CompilerConfig({
77-
genDebugInfo: options.debug === true,
78-
defaultEncapsulation: ViewEncapsulation.Emulated,
79-
logBindingUpdate: false,
80-
useJit: false
81-
});
82-
83-
const normalizer =
84-
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
85-
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
86-
const resolver = new compiler.CompileMetadataResolver(
87-
new compiler.NgModuleResolver(staticReflector),
88-
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
89-
elementSchemaRegistry, normalizer, staticReflector);
90-
91-
// TODO(vicb): implicit tags & attributes
92-
const messageBundle = new compiler.MessageBundle(htmlParser, [], {});
93-
94-
return new Extractor(
95-
options, program, tsCompilerHost, staticReflector, messageBundle, ngCompilerHost, resolver);
37+
const {extractor: ngExtractor} = compiler.Extractor.create(
38+
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
39+
return new Extractor(ngExtractor, ngCompilerHost, program);
9640
}
97-
}
41+
}

modules/@angular/compiler/src/aot/compiler_host.ts

+5-25
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,17 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {ImportResolver} from '../output/path_util';
10+
11+
import {StaticReflectorHost} from './static_reflector';
912
import {StaticSymbol} from './static_symbol';
1013

14+
1115
/**
1216
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
1317
* services and from underlying file systems.
1418
*/
15-
export interface AotCompilerHost {
16-
/**
17-
* Return a ModuleMetadata for the given module.
18-
* Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
19-
* produced and the module has exported variables or classes with decorators. Module metadata can
20-
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
21-
*
22-
* @param modulePath is a string identifier for a module as an absolute path.
23-
* @returns the metadata for the given module.
24-
*/
25-
getMetadataFor(modulePath: string): {[key: string]: any}[];
26-
27-
/**
28-
* Converts a module name that is used in an `import` to a file path.
29-
* I.e.
30-
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
31-
*/
32-
moduleNameToFileName(moduleName: string, containingFile: string): string;
33-
34-
/**
35-
* Converts a file path to a module name that can be used as an `import.
36-
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
37-
*/
38-
fileNameToModuleName(importedFile: string, containingFile: string): string;
39-
19+
export interface AotCompilerHost extends StaticReflectorHost, ImportResolver {
4020
/**
4121
* Loads a resource (e.g. html / css)
4222
*/

modules/@angular/compiler/src/aot/static_reflector.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
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';
1010
import {ReflectorReader} from '../private_import_core';
11-
import {AotCompilerHost} from './compiler_host';
1211
import {StaticSymbol} from './static_symbol';
1312

1413
const SUPPORTED_SCHEMA_VERSION = 2;
@@ -21,6 +20,30 @@ const ANGULAR_IMPORT_LOCATIONS = {
2120
provider: '@angular/core/src/di/provider'
2221
};
2322

23+
/**
24+
* The host of the StaticReflector disconnects the implementation from TypeScript / other language
25+
* services and from underlying file systems.
26+
*/
27+
export interface StaticReflectorHost {
28+
/**
29+
* Return a ModuleMetadata for the given module.
30+
* Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
31+
* produced and the module has exported variables or classes with decorators. Module metadata can
32+
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
33+
*
34+
* @param modulePath is a string identifier for a module as an absolute path.
35+
* @returns the metadata for the given module.
36+
*/
37+
getMetadataFor(modulePath: string): {[key: string]: any}[];
38+
39+
/**
40+
* Converts a module name that is used in an `import` to a file path.
41+
* I.e.
42+
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
43+
*/
44+
moduleNameToFileName(moduleName: string, containingFile: string): string;
45+
}
46+
2447
/**
2548
* A static reflector implements enough of the Reflector API that is necessary to compile
2649
* templates statically.
@@ -35,7 +58,7 @@ export class StaticReflector implements ReflectorReader {
3558
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
3659
private opaqueToken: StaticSymbol;
3760

38-
constructor(private host: AotCompilerHost) { this.initializeConversionMap(); }
61+
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
3962

4063
importUri(typeOrFunc: StaticSymbol): string {
4164
const staticSymbol = this.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
10+
/**
11+
* Extract i18n messages from source code
12+
*/
13+
import {ViewEncapsulation} from '@angular/core';
14+
15+
import {analyzeAndValidateNgModules, extractProgramSymbols, loadNgModuleDirectives} from '../aot/compiler';
16+
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
17+
import {StaticReflector, StaticReflectorHost} from '../aot/static_reflector';
18+
import {CompileDirectiveMetadata} from '../compile_metadata';
19+
import {CompilerConfig} from '../config';
20+
import {DirectiveNormalizer} from '../directive_normalizer';
21+
import {DirectiveResolver} from '../directive_resolver';
22+
import {CompileMetadataResolver} from '../metadata_resolver';
23+
import {HtmlParser} from '../ml_parser/html_parser';
24+
import {InterpolationConfig} from '../ml_parser/interpolation_config';
25+
import {NgModuleResolver} from '../ng_module_resolver';
26+
import {ParseError} from '../parse_util';
27+
import {PipeResolver} from '../pipe_resolver';
28+
import {Console} from '../private_import_core';
29+
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
30+
import {createOfflineCompileUrlResolver} from '../url_resolver';
31+
32+
import {I18NHtmlParser} from './i18n_html_parser';
33+
import {MessageBundle} from './message_bundle';
34+
35+
export interface ExtractorOptions {
36+
includeFilePattern?: RegExp;
37+
excludeFilePattern?: RegExp;
38+
}
39+
40+
/**
41+
* The host of the Extractor disconnects the implementation from TypeScript / other language
42+
* services and from underlying file systems.
43+
*/
44+
export interface ExtractorHost extends StaticReflectorHost {
45+
/**
46+
* Loads a resource (e.g. html / css)
47+
*/
48+
loadResource(path: string): Promise<string>;
49+
}
50+
51+
export class Extractor {
52+
constructor(
53+
private options: ExtractorOptions, public host: ExtractorHost,
54+
private staticReflector: StaticReflector, private messageBundle: MessageBundle,
55+
private metadataResolver: CompileMetadataResolver) {}
56+
57+
extract(rootFiles: string[]): Promise<MessageBundle> {
58+
const programSymbols = extractProgramSymbols(this.staticReflector, rootFiles, this.options);
59+
const {ngModuleByPipeOrDirective, files, ngModules} =
60+
analyzeAndValidateNgModules(programSymbols, this.options, this.metadataResolver);
61+
return loadNgModuleDirectives(ngModules).then(() => {
62+
const errors: ParseError[] = [];
63+
64+
files.forEach(file => {
65+
const compMetas: CompileDirectiveMetadata[] = [];
66+
file.directives.forEach(directiveType => {
67+
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
68+
if (dirMeta && dirMeta.isComponent) {
69+
compMetas.push(dirMeta);
70+
}
71+
});
72+
compMetas.forEach(compMeta => {
73+
const html = compMeta.template.template;
74+
const interpolationConfig =
75+
InterpolationConfig.fromArray(compMeta.template.interpolation);
76+
errors.push(
77+
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
78+
});
79+
});
80+
81+
if (errors.length) {
82+
throw new Error(errors.map(e => e.toString()).join('\n'));
83+
}
84+
85+
return this.messageBundle;
86+
});
87+
}
88+
89+
static create(host: ExtractorHost, options: ExtractorOptions):
90+
{extractor: Extractor, staticReflector: StaticReflector} {
91+
const htmlParser = new I18NHtmlParser(new HtmlParser());
92+
93+
const urlResolver = createOfflineCompileUrlResolver();
94+
const staticReflector = new StaticReflector(host);
95+
StaticAndDynamicReflectionCapabilities.install(staticReflector);
96+
97+
const config = new CompilerConfig({
98+
genDebugInfo: false,
99+
defaultEncapsulation: ViewEncapsulation.Emulated,
100+
logBindingUpdate: false,
101+
useJit: false
102+
});
103+
104+
const normalizer = new DirectiveNormalizer(
105+
{get: (url: string) => host.loadResource(url)}, urlResolver, htmlParser, config);
106+
const elementSchemaRegistry = new DomElementSchemaRegistry();
107+
const resolver = new CompileMetadataResolver(
108+
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
109+
new PipeResolver(staticReflector), elementSchemaRegistry, normalizer, staticReflector);
110+
111+
// TODO(vicb): implicit tags & attributes
112+
const messageBundle = new MessageBundle(htmlParser, [], {});
113+
114+
const extractor = new Extractor(options, host, staticReflector, messageBundle, resolver);
115+
return {extractor, staticReflector};
116+
}
117+
}

modules/@angular/compiler/src/i18n/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ export {Serializer} from './serializers/serializer';
1212
export {Xliff} from './serializers/xliff';
1313
export {Xmb} from './serializers/xmb';
1414
export {Xtb} from './serializers/xtb';
15+
export * from './extractor';

modules/@angular/compiler/src/output/path_util.ts

+4
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@
1010
* Interface that defines how import statements should be generated.
1111
*/
1212
export abstract class ImportResolver {
13+
/**
14+
* Converts a file path to a module name that can be used as an `import.
15+
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
16+
*/
1317
abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string;
1418
}

0 commit comments

Comments
 (0)