Skip to content

Commit bfc97ff

Browse files
mprobstIgorMinar
authored andcommitted
refactor(i18n): extract Extractor from extract_i18n (angular#12417)
I put an extractor into your extract so you can extract while you extract. This allows integrators to call Extractor as a library. Also refactors Extractor a bit so that callers need fewer arguments or arguments that are at the right semantic level. The refactoring causes no function change.
1 parent 57051f0 commit bfc97ff

File tree

3 files changed

+174
-141
lines changed

3 files changed

+174
-141
lines changed

modules/@angular/compiler-cli/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
export {CodeGenerator} from './src/codegen';
10+
export {Extractor} from './src/extractor';
1011
export {NodeReflectorHostContext, ReflectorHost, ReflectorHostContext} from './src/reflector_host';
1112
export {StaticReflector, StaticReflectorHost, StaticSymbol} from './src/static_reflector';
1213

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

Lines changed: 16 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,27 @@
1717
import 'reflect-metadata';
1818

1919
import * as compiler from '@angular/compiler';
20-
import {Component, NgModule, ViewEncapsulation} from '@angular/core';
20+
import * as tsc from '@angular/tsc-wrapped';
2121
import * as path from 'path';
2222
import * as ts from 'typescript';
23-
import * as tsc from '@angular/tsc-wrapped';
24-
import {Console} from './private_import_core';
25-
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
26-
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
27-
import {StaticReflector, StaticSymbol} from './static_reflector';
23+
24+
import {Extractor} from './extractor';
2825

2926
function extract(
3027
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
3128
program: ts.Program, host: ts.CompilerHost) {
32-
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
33-
const extractor = Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, htmlParser);
29+
const resourceLoader: compiler.ResourceLoader = {
30+
get: (s: string) => {
31+
if (!host.fileExists(s)) {
32+
// TODO: We should really have a test for error cases like this!
33+
throw new Error(`Compilation failed. Resource file not found: ${s}`);
34+
}
35+
return Promise.resolve(host.readFile(s));
36+
}
37+
};
38+
const extractor =
39+
Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, resourceLoader);
40+
3441
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
3542

3643
return (bundlePromise).then(messageBundle => {
@@ -46,6 +53,7 @@ function extract(
4653
case 'xliff':
4754
case 'xlf':
4855
default:
56+
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
4957
ext = 'xlf';
5058
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
5159
break;
@@ -56,139 +64,6 @@ function extract(
5664
});
5765
}
5866

59-
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
60-
61-
export class Extractor {
62-
constructor(
63-
private program: ts.Program, public host: ts.CompilerHost,
64-
private staticReflector: StaticReflector, private messageBundle: compiler.MessageBundle,
65-
private reflectorHost: ReflectorHost,
66-
private metadataResolver: compiler.CompileMetadataResolver,
67-
private directiveNormalizer: compiler.DirectiveNormalizer) {}
68-
69-
private readFileMetadata(absSourcePath: string): FileMetadata {
70-
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
71-
const result: FileMetadata = {components: [], ngModules: [], fileUrl: absSourcePath};
72-
if (!moduleMetadata) {
73-
console.log(`WARNING: no metadata found for ${absSourcePath}`);
74-
return result;
75-
}
76-
const metadata = moduleMetadata['metadata'];
77-
const symbols = metadata && Object.keys(metadata);
78-
if (!symbols || !symbols.length) {
79-
return result;
80-
}
81-
for (const symbol of symbols) {
82-
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
83-
// Ignore symbols that are only included to record error information.
84-
continue;
85-
}
86-
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
87-
const annotations = this.staticReflector.annotations(staticType);
88-
annotations.forEach((annotation) => {
89-
if (annotation instanceof NgModule) {
90-
result.ngModules.push(staticType);
91-
} else if (annotation instanceof Component) {
92-
result.components.push(staticType);
93-
}
94-
});
95-
}
96-
return result;
97-
}
98-
99-
extract(): Promise<compiler.MessageBundle> {
100-
const filePaths =
101-
this.program.getSourceFiles().map(sf => sf.fileName).filter(f => !GENERATED_FILES.test(f));
102-
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
103-
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
104-
ngModules.push(...fileMeta.ngModules);
105-
return ngModules;
106-
}, <StaticSymbol[]>[]);
107-
const analyzedNgModules = compiler.analyzeModules(ngModules, this.metadataResolver);
108-
const errors: compiler.ParseError[] = [];
109-
110-
let bundlePromise =
111-
Promise
112-
.all(fileMetas.map((fileMeta) => {
113-
const url = fileMeta.fileUrl;
114-
return Promise.all(fileMeta.components.map(compType => {
115-
const compMeta = this.metadataResolver.getDirectiveMetadata(<any>compType);
116-
const ngModule = analyzedNgModules.ngModuleByDirective.get(compType);
117-
if (!ngModule) {
118-
throw new Error(
119-
`Cannot determine the module for component ${compMeta.type.name}!`);
120-
}
121-
return Promise
122-
.all([compMeta, ...ngModule.transitiveModule.directives].map(
123-
dirMeta =>
124-
this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
125-
.then((normalizedCompWithDirectives) => {
126-
const compMeta = normalizedCompWithDirectives[0];
127-
const html = compMeta.template.template;
128-
const interpolationConfig =
129-
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
130-
errors.push(
131-
...this.messageBundle.updateFromTemplate(html, url, interpolationConfig));
132-
});
133-
}));
134-
}))
135-
.then(_ => this.messageBundle);
136-
137-
if (errors.length) {
138-
throw new Error(errors.map(e => e.toString()).join('\n'));
139-
}
140-
141-
return bundlePromise;
142-
}
143-
144-
static create(
145-
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
146-
compilerHost: ts.CompilerHost, htmlParser: compiler.I18NHtmlParser,
147-
reflectorHostContext?: ReflectorHostContext): Extractor {
148-
const resourceLoader: compiler.ResourceLoader = {
149-
get: (s: string) => {
150-
if (!compilerHost.fileExists(s)) {
151-
// TODO: We should really have a test for error cases like this!
152-
throw new Error(`Compilation failed. Resource file not found: ${s}`);
153-
}
154-
return Promise.resolve(compilerHost.readFile(s));
155-
}
156-
};
157-
158-
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
159-
const reflectorHost = new ReflectorHost(program, compilerHost, options, reflectorHostContext);
160-
const staticReflector = new StaticReflector(reflectorHost);
161-
StaticAndDynamicReflectionCapabilities.install(staticReflector);
162-
163-
const config = new compiler.CompilerConfig({
164-
genDebugInfo: options.debug === true,
165-
defaultEncapsulation: ViewEncapsulation.Emulated,
166-
logBindingUpdate: false,
167-
useJit: false
168-
});
169-
170-
const normalizer =
171-
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
172-
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
173-
const resolver = new compiler.CompileMetadataResolver(
174-
new compiler.NgModuleResolver(staticReflector),
175-
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
176-
elementSchemaRegistry, staticReflector);
177-
178-
// TODO(vicb): implicit tags & attributes
179-
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
180-
181-
return new Extractor(
182-
program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver, normalizer);
183-
}
184-
}
185-
186-
interface FileMetadata {
187-
fileUrl: string;
188-
components: StaticSymbol[];
189-
ngModules: StaticSymbol[];
190-
}
191-
19267
// Entry point
19368
if (require.main === module) {
19469
const args = require('minimist')(process.argv.slice(2));
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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+
* TODO(vicb): factorize code with the CodeGenerator
14+
*/
15+
// Must be imported first, because angular2 decorators throws on load.
16+
import 'reflect-metadata';
17+
18+
import * as compiler from '@angular/compiler';
19+
import {Component, NgModule, ViewEncapsulation} from '@angular/core';
20+
import * as tsc from '@angular/tsc-wrapped';
21+
import * as path from 'path';
22+
import * as ts from 'typescript';
23+
24+
import {Console} from './private_import_core';
25+
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
26+
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
27+
import {StaticReflector, StaticSymbol} from './static_reflector';
28+
29+
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
30+
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
31+
32+
export class Extractor {
33+
constructor(
34+
private options: tsc.AngularCompilerOptions, private program: ts.Program,
35+
public host: ts.CompilerHost, private staticReflector: StaticReflector,
36+
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
37+
private metadataResolver: compiler.CompileMetadataResolver,
38+
private directiveNormalizer: compiler.DirectiveNormalizer) {}
39+
40+
private readFileMetadata(absSourcePath: string): FileMetadata {
41+
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
42+
const result: FileMetadata = {components: [], ngModules: [], fileUrl: absSourcePath};
43+
if (!moduleMetadata) {
44+
console.log(`WARNING: no metadata found for ${absSourcePath}`);
45+
return result;
46+
}
47+
const metadata = moduleMetadata['metadata'];
48+
const symbols = metadata && Object.keys(metadata);
49+
if (!symbols || !symbols.length) {
50+
return result;
51+
}
52+
for (const symbol of symbols) {
53+
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
54+
// Ignore symbols that are only included to record error information.
55+
continue;
56+
}
57+
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
58+
const annotations = this.staticReflector.annotations(staticType);
59+
annotations.forEach((annotation) => {
60+
if (annotation instanceof NgModule) {
61+
result.ngModules.push(staticType);
62+
} else if (annotation instanceof Component) {
63+
result.components.push(staticType);
64+
}
65+
});
66+
}
67+
return result;
68+
}
69+
70+
extract(): Promise<compiler.MessageBundle> {
71+
const skipFileNames = (this.options.generateCodeForLibraries === false) ?
72+
GENERATED_OR_DTS_FILES :
73+
GENERATED_FILES;
74+
const filePaths =
75+
this.program.getSourceFiles().map(sf => sf.fileName).filter(f => !skipFileNames.test(f));
76+
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
77+
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
78+
ngModules.push(...fileMeta.ngModules);
79+
return ngModules;
80+
}, <StaticSymbol[]>[]);
81+
const analyzedNgModules = compiler.analyzeModules(ngModules, this.metadataResolver);
82+
const errors: compiler.ParseError[] = [];
83+
84+
let bundlePromise =
85+
Promise
86+
.all(fileMetas.map((fileMeta) => {
87+
const url = fileMeta.fileUrl;
88+
return Promise.all(fileMeta.components.map(compType => {
89+
const compMeta = this.metadataResolver.getDirectiveMetadata(<any>compType);
90+
const ngModule = analyzedNgModules.ngModuleByDirective.get(compType);
91+
if (!ngModule) {
92+
throw new Error(
93+
`Cannot determine the module for component ${compMeta.type.name}!`);
94+
}
95+
return Promise
96+
.all([compMeta, ...ngModule.transitiveModule.directives].map(
97+
dirMeta =>
98+
this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
99+
.then((normalizedCompWithDirectives) => {
100+
const compMeta = normalizedCompWithDirectives[0];
101+
const html = compMeta.template.template;
102+
const interpolationConfig =
103+
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
104+
errors.push(
105+
...this.messageBundle.updateFromTemplate(html, url, interpolationConfig));
106+
});
107+
}));
108+
}))
109+
.then(_ => this.messageBundle);
110+
111+
if (errors.length) {
112+
throw new Error(errors.map(e => e.toString()).join('\n'));
113+
}
114+
115+
return bundlePromise;
116+
}
117+
118+
static create(
119+
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
120+
compilerHost: ts.CompilerHost, resourceLoader: compiler.ResourceLoader,
121+
reflectorHost?: ReflectorHost): Extractor {
122+
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
123+
124+
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
125+
if (!reflectorHost) reflectorHost = new ReflectorHost(program, compilerHost, options);
126+
const staticReflector = new StaticReflector(reflectorHost);
127+
StaticAndDynamicReflectionCapabilities.install(staticReflector);
128+
129+
const config = new compiler.CompilerConfig({
130+
genDebugInfo: options.debug === true,
131+
defaultEncapsulation: ViewEncapsulation.Emulated,
132+
logBindingUpdate: false,
133+
useJit: false
134+
});
135+
136+
const normalizer =
137+
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
138+
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
139+
const resolver = new compiler.CompileMetadataResolver(
140+
new compiler.NgModuleResolver(staticReflector),
141+
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
142+
elementSchemaRegistry, staticReflector);
143+
144+
// TODO(vicb): implicit tags & attributes
145+
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
146+
147+
return new Extractor(
148+
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver,
149+
normalizer);
150+
}
151+
}
152+
153+
interface FileMetadata {
154+
fileUrl: string;
155+
components: StaticSymbol[];
156+
ngModules: StaticSymbol[];
157+
}

0 commit comments

Comments
 (0)