From 8a24b8c8c623637a3e21a337209610a48761c22b Mon Sep 17 00:00:00 2001 From: Olivier Combe Date: Mon, 24 Apr 2017 16:04:35 +0200 Subject: [PATCH] feat(compiler-cli): add i18n parameters to tsconfig You can now add parameters to your tsconfig that will be used when you don't provide cli parameters. It works with ng-xi18n and ngc. Since they both use some parameters with a similar name, those options in tsconfig can have a slightly different name compared to the cli parameters. Closes #16232 Fixes #16235 --- packages/compiler-cli/src/codegen.ts | 34 ++++++--- packages/compiler-cli/src/extract_i18n.ts | 5 +- packages/compiler-cli/src/extractor.ts | 11 +-- packages/compiler-cli/src/main.ts | 7 +- packages/compiler-cli/src/ngtools_api.ts | 2 +- packages/compiler-cli/test/codegen_spec.ts | 85 ++++++++++++++++++++++ tools/@angular/tsc-wrapped/src/options.ts | 21 ++++++ 7 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 packages/compiler-cli/test/codegen_spec.ts diff --git a/packages/compiler-cli/src/codegen.ts b/packages/compiler-cli/src/codegen.ts index 569ee37bd3b80..8b32e49fc7979 100644 --- a/packages/compiler-cli/src/codegen.ts +++ b/packages/compiler-cli/src/codegen.ts @@ -61,17 +61,22 @@ export class CodeGenerator { ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) : new CompilerHost(program, options, context); } - let transContent: string = ''; - if (cliOptions.i18nFile) { - if (!cliOptions.locale) { + + const transFile = cliOptions.i18nFile || options.i18nFile; + const locale = cliOptions.locale || options.translationLocale; + let translations: string = ''; + if (transFile) { + if (!locale) { throw new Error( - `The translation file (${cliOptions.i18nFile}) locale must be provided. Use the --locale option.`); + `The translation file (${transFile}) locale must be provided. Use the --locale option.`); } - transContent = readFileSync(cliOptions.i18nFile, 'utf8'); + translations = readFileSync(transFile, 'utf8'); } + + const optMissingTranslation = cliOptions.missingTranslation || options.missingTranslation; let missingTranslation = MissingTranslationStrategy.Warning; - if (cliOptions.missingTranslation) { - switch (cliOptions.missingTranslation) { + if (optMissingTranslation) { + switch (optMissingTranslation) { case 'error': missingTranslation = MissingTranslationStrategy.Error; break; @@ -83,13 +88,20 @@ export class CodeGenerator { break; default: throw new Error( - `Unknown option for missingTranslation (${cliOptions.missingTranslation}). Use either error, warning or ignore.`); + `Unknown option for missingTranslation (${optMissingTranslation}). Use either error, warning or ignore.`); } } + + let i18nFormat = cliOptions.i18nFormat || options.i18nTranslationFormat || options.i18nFormat; + if (i18nFormat && i18nFormat.toLowerCase() === 'xmb') { + i18nFormat = 'xtb'; + } + const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, { - translations: transContent, - i18nFormat: cliOptions.i18nFormat, - locale: cliOptions.locale, missingTranslation, + translations, + i18nFormat, + locale, + missingTranslation, enableLegacyTemplate: options.enableLegacyTemplate !== false, genFilePreamble: PREAMBLE, }); diff --git a/packages/compiler-cli/src/extract_i18n.ts b/packages/compiler-cli/src/extract_i18n.ts index c8aec764a09ea..82769fb212827 100644 --- a/packages/compiler-cli/src/extract_i18n.ts +++ b/packages/compiler-cli/src/extract_i18n.ts @@ -22,8 +22,9 @@ import {Extractor} from './extractor'; function extract( ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions, program: ts.Program, host: ts.CompilerHost): Promise { - return Extractor.create(ngOptions, program, host, cliOptions.locale) - .extract(cliOptions.i18nFormat !, cliOptions.outFile); + const format = cliOptions.i18nFormat || ngOptions.i18nFormat || null; + const outFile = cliOptions.outFile || ngOptions.outFile || null; + return Extractor.create(ngOptions, program, host, cliOptions.locale).extract(format, outFile); } // Entry point diff --git a/packages/compiler-cli/src/extractor.ts b/packages/compiler-cli/src/extractor.ts index c8073b89c6643..c672a0feb20d5 100644 --- a/packages/compiler-cli/src/extractor.ts +++ b/packages/compiler-cli/src/extractor.ts @@ -27,7 +27,7 @@ export class Extractor { public host: ts.CompilerHost, private ngCompilerHost: CompilerHost, private program: ts.Program) {} - extract(formatName: string, outFile: string|null): Promise { + extract(formatName: string|null, outFile: string|null): Promise { // Checks the format and returns the extension const ext = this.getExtension(formatName); @@ -48,8 +48,8 @@ export class Extractor { return this.ngExtractor.extract(files); } - serialize(bundle: compiler.MessageBundle, formatName: string): string { - const format = formatName.toLowerCase(); + serialize(bundle: compiler.MessageBundle, formatName: string|null): string { + const format = (formatName || 'xlf').toLowerCase(); let serializer: compiler.Serializer; switch (format) { @@ -70,7 +70,7 @@ export class Extractor { (sourcePath: string) => sourcePath.replace(path.join(this.options.basePath, '/'), '')); } - getExtension(formatName: string): string { + getExtension(formatName: string|null): string { const format = (formatName || 'xlf').toLowerCase(); switch (format) { @@ -98,7 +98,8 @@ export class Extractor { new CompilerHost(program, options, context); } - const {extractor: ngExtractor} = compiler.Extractor.create(ngCompilerHost, locale || null); + const {extractor: ngExtractor} = + compiler.Extractor.create(ngCompilerHost, locale || options.locale || null); return new Extractor(options, ngExtractor, tsCompilerHost, ngCompilerHost, program); } diff --git a/packages/compiler-cli/src/main.ts b/packages/compiler-cli/src/main.ts index db462e2db893e..1759d0e3ab6d0 100644 --- a/packages/compiler-cli/src/main.ts +++ b/packages/compiler-cli/src/main.ts @@ -17,14 +17,17 @@ import {isSyntaxError} from '@angular/compiler'; import {CodeGenerator} from './codegen'; -function codegen( +function codegenFct( ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.NgcCliOptions, program: ts.Program, host: ts.CompilerHost) { return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen(); } export function main( - args: any, consoleError: (s: string) => void = console.error): Promise { + args: any, consoleError: (s: string) => void = console.error, + codegen: + (n: tsc.AngularCompilerOptions, c: tsc.NgcCliOptions, p: ts.Program, h: ts.CompilerHost) => + Promise = codegenFct): Promise { const project = args.p || args.project || '.'; const cliOptions = new tsc.NgcCliOptions(args); diff --git a/packages/compiler-cli/src/ngtools_api.ts b/packages/compiler-cli/src/ngtools_api.ts index 457278e8b4a1e..9842d200da3ad 100644 --- a/packages/compiler-cli/src/ngtools_api.ts +++ b/packages/compiler-cli/src/ngtools_api.ts @@ -150,6 +150,6 @@ export class NgTools_InternalApi_NG_2 { const extractor = Extractor.create( options.angularCompilerOptions, options.program, options.host, locale, hostContext); - return extractor.extract(options.i18nFormat !, options.outFile || null); + return extractor.extract(options.i18nFormat || null, options.outFile || null); } } diff --git a/packages/compiler-cli/test/codegen_spec.ts b/packages/compiler-cli/test/codegen_spec.ts new file mode 100644 index 0000000000000..70bff71c910e9 --- /dev/null +++ b/packages/compiler-cli/test/codegen_spec.ts @@ -0,0 +1,85 @@ +/** + * @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 + */ + +import {ɵReflectionCapabilities, ɵreflector} from '@angular/core'; +import * as tsc from '@angular/tsc-wrapped'; +import {makeTempDir} from '@angular/tsc-wrapped/test/test_support'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as ts from 'typescript'; + +import {main} from '../src/main'; + + +describe('codegen', () => { + let basePath: string; + let write: (fileName: string, content: string) => void; + + beforeEach(() => { + basePath = makeTempDir(); + write = (fileName: string, content: string) => { + fs.writeFileSync(path.join(basePath, fileName), content, {encoding: 'utf-8'}); + }; + const nodeModulesPath = path.resolve(basePath, 'node_modules'); + fs.mkdirSync(nodeModulesPath); + fs.symlinkSync(path.resolve(__dirname, '..', '..'), path.resolve(nodeModulesPath, '@angular')); + }); + + // Restore reflector since AoT compiler will update it with a new static reflector + afterEach(() => { ɵreflector.updateCapabilities(new ɵReflectionCapabilities()); }); + + it('should use i18n params without error', (done) => { + write('tsconfig.json', `{ + "compilerOptions": { + "experimentalDecorators": true, + "types": [], + "outDir": "built", + "declaration": true, + "module": "es2015", + "moduleResolution": "node" + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "missingTranslation": "error", + "foo": "bar" + }, + "files": ["test.ts"] + }`); + + write('test.ts', `const A = 1;`); + + spyOn(console, 'error').and.callThrough(); + + const codegen = jasmine + .createSpy( + 'codegenSpy', + (ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.NgcCliOptions, + program: ts.Program, host: ts.CompilerHost) => { + expect(ngOptions.missingTranslation).toBe('error'); + expect(ngOptions.i18nFormat).toBeUndefined(); + expect(ngOptions.locale).toBeUndefined(); + expect((ngOptions).foo).toEqual('bar'); + + expect(cliOptions.missingTranslation).toBe('ignore'); + expect(cliOptions.i18nFormat).toBeNull(); + expect(cliOptions.locale).toBeNull(); + expect((cliOptions).foo).toBeUndefined(); + + return Promise.resolve(); + }) + .and.callThrough(); + + main({p: basePath, missingTranslation: 'ignore', foo: 'bar'}, console.error, codegen) + .then(() => { + expect(console.error).not.toHaveBeenCalled(); + expect(codegen).toHaveBeenCalled(); + done(); + }) + .catch(e => done.fail(e)); + }); +}); diff --git a/tools/@angular/tsc-wrapped/src/options.ts b/tools/@angular/tsc-wrapped/src/options.ts index 4e3c7e9df3886..a9dbab5024517 100644 --- a/tools/@angular/tsc-wrapped/src/options.ts +++ b/tools/@angular/tsc-wrapped/src/options.ts @@ -81,6 +81,27 @@ interface Options extends ts.CompilerOptions { // Whether to enable support for