diff --git a/modules/@angular/compiler-cli/integrationtest/ngtools_src/app.component.html b/modules/@angular/compiler-cli/integrationtest/ngtools_src/app.component.html
index 5a532db930..56549a9fba 100644
--- a/modules/@angular/compiler-cli/integrationtest/ngtools_src/app.component.html
+++ b/modules/@angular/compiler-cli/integrationtest/ngtools_src/app.component.html
@@ -1,5 +1,5 @@
-
hello world
+
hello world
lazy
diff --git a/modules/@angular/compiler-cli/integrationtest/test/test_ngtools_api.ts b/modules/@angular/compiler-cli/integrationtest/test/test_ngtools_api.ts
index 66f89b6baf..99d1a31116 100644
--- a/modules/@angular/compiler-cli/integrationtest/test/test_ngtools_api.ts
+++ b/modules/@angular/compiler-cli/integrationtest/test/test_ngtools_api.ts
@@ -19,7 +19,6 @@ import {AngularCompilerOptions, CodeGenerator, CompilerHostContext, NodeCompiler
const glob = require('glob');
-
/**
* Main method.
* Standalone program that executes codegen using the ngtools API and tests that files were
@@ -30,6 +29,7 @@ function main() {
Promise.resolve()
.then(() => codeGenTest())
+ .then(() => i18nTest())
.then(() => lazyRoutesTest())
.then(() => {
console.log('All done!');
@@ -42,7 +42,6 @@ function main() {
});
}
-
function codeGenTest() {
const basePath = path.join(__dirname, '../ngtools_src');
const project = path.join(basePath, 'tsconfig-build.json');
@@ -109,6 +108,67 @@ function codeGenTest() {
});
}
+function i18nTest() {
+ const basePath = path.join(__dirname, '../ngtools_src');
+ const project = path.join(basePath, 'tsconfig-build.json');
+ const readResources: string[] = [];
+ const wroteFiles: string[] = [];
+
+ const config = tsc.readConfiguration(project, basePath);
+ const hostContext = new NodeCompilerHostContext();
+ const delegateHost = ts.createCompilerHost(config.parsed.options, true);
+ const host: ts.CompilerHost = Object.assign(
+ {}, delegateHost,
+ {writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
+ const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
+
+ config.ngOptions.basePath = basePath;
+
+ console.log(`>>> running i18n extraction for ${project}`);
+ return __NGTOOLS_PRIVATE_API_2
+ .extractI18n({
+ basePath,
+ compilerOptions: config.parsed.options, program, host,
+ angularCompilerOptions: config.ngOptions,
+ i18nFormat: 'xlf',
+ readResource: (fileName: string) => {
+ readResources.push(fileName);
+ return hostContext.readResource(fileName);
+ },
+ })
+ .then(() => {
+ console.log(`>>> i18n extraction done, asserting read and wrote files`);
+
+ const allFiles = glob.sync(path.join(basePath, '**/*'), {nodir: true});
+
+ assert(wroteFiles.length == 1, `Expected a single message bundle file.`);
+
+ assert(
+ wroteFiles[0].endsWith('/ngtools_src/messages.xlf'),
+ `Expected the bundle file to be "message.xlf".`);
+
+ allFiles.forEach((fileName: string) => {
+ // Skip tsconfig.
+ if (fileName.match(/tsconfig-build.json$/)) {
+ return;
+ }
+
+ // Assert that file was read.
+ if (fileName.match(/\.css$/) || fileName.match(/\.html$/)) {
+ assert(
+ readResources.indexOf(fileName) != -1,
+ `Expected resource "${fileName}" to be read.`);
+ }
+ });
+
+ console.log(`done, no errors.`);
+ })
+ .catch((e: any) => {
+ console.error(e.stack);
+ console.error('Extraction failed');
+ throw e;
+ });
+}
function lazyRoutesTest() {
const basePath = path.join(__dirname, '../ngtools_src');
diff --git a/modules/@angular/compiler-cli/src/extract_i18n.ts b/modules/@angular/compiler-cli/src/extract_i18n.ts
index 5562150ab5..0a137ff529 100644
--- a/modules/@angular/compiler-cli/src/extract_i18n.ts
+++ b/modules/@angular/compiler-cli/src/extract_i18n.ts
@@ -14,41 +14,15 @@
// Must be imported first, because angular2 decorators throws on load.
import 'reflect-metadata';
-import * as compiler from '@angular/compiler';
import * as tsc from '@angular/tsc-wrapped';
-import * as path from 'path';
import * as ts from 'typescript';
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 bundlePromise: Promise = extractor.extract();
-
- return (bundlePromise).then(messageBundle => {
- let ext: string;
- let serializer: compiler.Serializer;
- const format = (cliOptions.i18nFormat || 'xlf').toLowerCase();
-
- switch (format) {
- case 'xmb':
- ext = 'xmb';
- serializer = new compiler.Xmb();
- break;
- case 'xliff':
- case 'xlf':
- default:
- ext = 'xlf';
- serializer = new compiler.Xliff();
- break;
- }
-
- const dstPath = path.join(ngOptions.genDir, `messages.${ext}`);
- host.writeFile(dstPath, messageBundle.write(serializer), false);
- });
+ program: ts.Program, host: ts.CompilerHost): Promise {
+ return Extractor.create(ngOptions, program, host).extract(cliOptions.i18nFormat);
}
// Entry point
diff --git a/modules/@angular/compiler-cli/src/extractor.ts b/modules/@angular/compiler-cli/src/extractor.ts
index af49d16372..b51e1ce0d7 100644
--- a/modules/@angular/compiler-cli/src/extractor.ts
+++ b/modules/@angular/compiler-cli/src/extractor.ts
@@ -15,27 +15,70 @@ import 'reflect-metadata';
import * as compiler from '@angular/compiler';
import * as tsc from '@angular/tsc-wrapped';
+import * as path from 'path';
import * as ts from 'typescript';
-import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';
+import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
+import {PathMappedCompilerHost} from './path_mapped_compiler_host';
export class Extractor {
constructor(
- private ngExtractor: compiler.Extractor, private ngCompilerHost: CompilerHost,
+ private options: tsc.AngularCompilerOptions, private ngExtractor: compiler.Extractor,
+ public host: ts.CompilerHost, private ngCompilerHost: CompilerHost,
private program: ts.Program) {}
- extract(): Promise {
- return this.ngExtractor.extract(this.program.getSourceFiles().map(
- sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)));
+ extract(formatName: string): Promise {
+ // Checks the format and returns the extension
+ const ext = this.getExtension(formatName);
+
+ const files = this.program.getSourceFiles().map(
+ sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName));
+
+ const promiseBundle = this.ngExtractor.extract(files);
+
+ return promiseBundle.then(bundle => {
+ const content = this.serialize(bundle, ext);
+ const dstPath = path.join(this.options.genDir, `messages.${ext}`);
+ this.host.writeFile(dstPath, content, false);
+ });
+ }
+
+ serialize(bundle: compiler.MessageBundle, ext: string): string {
+ let serializer: compiler.Serializer;
+
+ switch (ext) {
+ case 'xmb':
+ serializer = new compiler.Xmb();
+ break;
+ case 'xlf':
+ default:
+ serializer = new compiler.Xliff();
+ }
+
+ return bundle.write(serializer);
+ }
+
+ getExtension(formatName: string): string {
+ const format = (formatName || 'xlf').toLowerCase();
+
+ if (format === 'xmb') return 'xmb';
+ if (format === 'xlf' || format === 'xlif') return 'xlf';
+
+ throw new Error('Unsupported format "${formatName}"');
}
static create(
- options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
- moduleResolverHost: ts.ModuleResolutionHost, ngCompilerHost?: CompilerHost): Extractor {
- if (!ngCompilerHost)
- ngCompilerHost =
- new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
+ options: tsc.AngularCompilerOptions, program: ts.Program, tsCompilerHost: ts.CompilerHost,
+ compilerHostContext?: CompilerHostContext, ngCompilerHost?: CompilerHost): Extractor {
+ if (!ngCompilerHost) {
+ const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
+ const context = compilerHostContext || new ModuleResolutionHostAdapter(tsCompilerHost);
+ ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) :
+ new CompilerHost(program, options, context);
+ }
+
const {extractor: ngExtractor} = compiler.Extractor.create(ngCompilerHost);
- return new Extractor(ngExtractor, ngCompilerHost, program);
+
+ return new Extractor(options, ngExtractor, tsCompilerHost, ngCompilerHost, program);
}
}
diff --git a/modules/@angular/compiler-cli/src/ngtools_api.ts b/modules/@angular/compiler-cli/src/ngtools_api.ts
index 486a72f595..4fa9ab56e9 100644
--- a/modules/@angular/compiler-cli/src/ngtools_api.ts
+++ b/modules/@angular/compiler-cli/src/ngtools_api.ts
@@ -19,10 +19,10 @@ import * as ts from 'typescript';
import {CodeGenerator} from './codegen';
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
+import {Extractor} from './extractor';
import {listLazyRoutesOfModule} from './ngtools_impl';
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
-
export interface NgTools_InternalApi_NG2_CodeGen_Options {
basePath: string;
compilerOptions: ts.CompilerOptions;
@@ -50,9 +50,18 @@ export interface NgTools_InternalApi_NG2_ListLazyRoutes_Options {
// Every new property under this line should be optional.
}
-
export interface NgTools_InternalApi_NG_2_LazyRouteMap { [route: string]: string; }
+export interface NgTools_InternalApi_NG2_ExtractI18n_Options {
+ basePath: string;
+ compilerOptions: ts.CompilerOptions;
+ program: ts.Program;
+ host: ts.CompilerHost;
+ angularCompilerOptions: AngularCompilerOptions;
+ i18nFormat: string;
+ readResource: (fileName: string) => Promise;
+ // Every new property under this line should be optional.
+}
/**
* A ModuleResolutionHostAdapter that overrides the readResource() method with the one
@@ -94,7 +103,6 @@ export class NgTools_InternalApi_NG_2 {
return codeGenerator.codegen();
}
-
/**
* @internal
* @private
@@ -124,4 +132,19 @@ export class NgTools_InternalApi_NG_2 {
},
{});
}
+
+ /**
+ * @internal
+ * @private
+ */
+ static extractI18n(options: NgTools_InternalApi_NG2_ExtractI18n_Options): Promise {
+ const hostContext: CompilerHostContext =
+ new CustomLoaderModuleResolutionHostAdapter(options.readResource, options.host);
+
+ // Create the i18n extractor.
+ const extractor = Extractor.create(
+ options.angularCompilerOptions, options.program, options.host, hostContext);
+
+ return extractor.extract(options.i18nFormat);
+ }
}
diff --git a/modules/@angular/compiler/src/i18n/extractor.ts b/modules/@angular/compiler/src/i18n/extractor.ts
index 16e92c6499..a46e641b59 100644
--- a/modules/@angular/compiler/src/i18n/extractor.ts
+++ b/modules/@angular/compiler/src/i18n/extractor.ts
@@ -28,7 +28,6 @@ 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';