diff --git a/src/cli/cli.ts b/src/cli/cli.ts index d3df4b37..9edc3709 100755 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -104,6 +104,12 @@ export const cli = y type: 'string', conflicts: ['null-as-default-value', 'key-as-default-value'] }) + .option('newline-at-eof', { + alias: 'nl', + describe: 'Add newline at the end of the generated file', + default: false, + type: 'boolean' + }) .group(['format', 'format-indentation', 'sort', 'clean', 'replace'], 'Output') .group(['key-as-default-value', 'null-as-default-value', 'string-as-default-value'], 'Extracted key value (defaults to empty string)') .conflicts('key-as-default-value', 'null-as-default-value') @@ -145,7 +151,8 @@ extractTask.setPostProcessors(postProcessors); // Compiler const compiler: CompilerInterface = CompilerFactory.create(cli.format, { - indentation: cli.formatIndentation + indentation: cli.formatIndentation, + eofNewline: cli.newlineAtEof }); extractTask.setCompiler(compiler); diff --git a/src/compilers/core.compiler.ts b/src/compilers/core.compiler.ts new file mode 100644 index 00000000..7d36af91 --- /dev/null +++ b/src/compilers/core.compiler.ts @@ -0,0 +1,21 @@ +import { CompilerInterface } from './compiler.interface'; +import { TranslationCollection } from '../utils/translation.collection'; + +export abstract class CoreCompiler implements CompilerInterface { + public abstract extension: string; + public eofNewline = false; + + protected constructor(options?: any) { + if (options && typeof options.eofNewline !== 'undefined') { + this.eofNewline = options.eofNewline; + } + } + + public compile(collection: TranslationCollection): string { + return this.compileSpecific(collection) + (this.eofNewline ? '\n' : ''); + } + + protected abstract compileSpecific(collection: TranslationCollection): string; + + public abstract parse(contents: string): TranslationCollection; +} diff --git a/src/compilers/json.compiler.ts b/src/compilers/json.compiler.ts index d24adfb2..a496982c 100644 --- a/src/compilers/json.compiler.ts +++ b/src/compilers/json.compiler.ts @@ -1,21 +1,22 @@ -import { CompilerInterface } from './compiler.interface'; import { TranslationCollection } from '../utils/translation.collection'; import { stripBOM } from '../utils/utils'; import { flatten } from 'flat'; +import { CoreCompiler } from './core.compiler'; -export class JsonCompiler implements CompilerInterface { +export class JsonCompiler extends CoreCompiler { public indentation: string = '\t'; public extension: string = 'json'; public constructor(options?: any) { + super(options); if (options && typeof options.indentation !== 'undefined') { this.indentation = options.indentation; } } - public compile(collection: TranslationCollection): string { + protected compileSpecific(collection: TranslationCollection): string { return JSON.stringify(collection.values, null, this.indentation); } diff --git a/src/compilers/namespaced-json.compiler.ts b/src/compilers/namespaced-json.compiler.ts index c4fa943d..1729270a 100644 --- a/src/compilers/namespaced-json.compiler.ts +++ b/src/compilers/namespaced-json.compiler.ts @@ -1,21 +1,22 @@ -import { CompilerInterface } from './compiler.interface'; import { TranslationCollection } from '../utils/translation.collection'; import { stripBOM } from '../utils/utils'; import { flatten, unflatten } from 'flat'; +import { CoreCompiler } from './core.compiler'; -export class NamespacedJsonCompiler implements CompilerInterface { +export class NamespacedJsonCompiler extends CoreCompiler { public indentation: string = '\t'; public extension = 'json'; public constructor(options?: any) { + super(options); if (options && typeof options.indentation !== 'undefined') { this.indentation = options.indentation; } } - public compile(collection: TranslationCollection): string { + protected compileSpecific(collection: TranslationCollection): string { const values: {} = unflatten(collection.values, { object: true }); diff --git a/src/compilers/po.compiler.ts b/src/compilers/po.compiler.ts index af6d2831..2c5c0a63 100644 --- a/src/compilers/po.compiler.ts +++ b/src/compilers/po.compiler.ts @@ -1,9 +1,9 @@ -import { CompilerInterface } from './compiler.interface'; import { TranslationCollection, TranslationType } from '../utils/translation.collection'; import { po } from 'gettext-parser'; +import { CoreCompiler } from './core.compiler'; -export class PoCompiler implements CompilerInterface { +export class PoCompiler extends CoreCompiler { public extension: string = 'po'; /** @@ -11,9 +11,11 @@ export class PoCompiler implements CompilerInterface { */ public domain: string = ''; - public constructor(options?: any) {} + public constructor(options?: any) { + super(options); + } - public compile(collection: TranslationCollection): string { + protected compileSpecific(collection: TranslationCollection): string { const data = { charset: 'utf-8', headers: { diff --git a/tests/compilers/namespaced-json.compiler.spec.ts b/tests/compilers/namespaced-json.compiler.spec.ts index 48a61556..f620194d 100644 --- a/tests/compilers/namespaced-json.compiler.spec.ts +++ b/tests/compilers/namespaced-json.compiler.spec.ts @@ -5,9 +5,11 @@ import { NamespacedJsonCompiler } from '../../src/compilers/namespaced-json.comp describe('NamespacedJsonCompiler', () => { let compiler: NamespacedJsonCompiler; + let eofNewlineCompiler: NamespacedJsonCompiler; beforeEach(() => { compiler = new NamespacedJsonCompiler(); + eofNewlineCompiler = new NamespacedJsonCompiler({ eofNewline: true }); }); it('should flatten keys on parse', () => { @@ -35,6 +37,8 @@ describe('NamespacedJsonCompiler', () => { }); const result: string = compiler.compile(collection); expect(result).to.equal('{\n\t"NAMESPACE": {\n\t\t"KEY": {\n\t\t\t"FIRST_KEY": "",\n\t\t\t"SECOND_KEY": "VALUE"\n\t\t}\n\t}\n}'); + const resultNewline: string = eofNewlineCompiler.compile(collection); + expect(resultNewline).to.equal('{\n\t"NAMESPACE": {\n\t\t"KEY": {\n\t\t\t"FIRST_KEY": "",\n\t\t\t"SECOND_KEY": "VALUE"\n\t\t}\n\t}\n}\n'); }); it('should preserve numeric values on compile', () => { @@ -45,6 +49,8 @@ describe('NamespacedJsonCompiler', () => { }); const result: string = compiler.compile(collection); expect(result).to.equal('{\n\t"option": {\n\t\t"0": "",\n\t\t"1": "",\n\t\t"2": ""\n\t}\n}'); + const resultNewline: string = eofNewlineCompiler.compile(collection); + expect(resultNewline).to.equal('{\n\t"option": {\n\t\t"0": "",\n\t\t"1": "",\n\t\t"2": ""\n\t}\n}\n'); }); it('should use custom indentation chars', () => { @@ -66,5 +72,7 @@ describe('NamespacedJsonCompiler', () => { }); const result: string = compiler.compile(collection); expect(result).to.equal('{\n\t"BROWSE": "",\n\t"LOGIN": ""\n}'); + const resultNewline: string = eofNewlineCompiler.compile(collection); + expect(resultNewline).to.equal('{\n\t"BROWSE": "",\n\t"LOGIN": ""\n}\n'); }); }); diff --git a/tests/compilers/po.compiler.spec.ts b/tests/compilers/po.compiler.spec.ts index 8d0fd298..8f104f81 100644 --- a/tests/compilers/po.compiler.spec.ts +++ b/tests/compilers/po.compiler.spec.ts @@ -5,9 +5,11 @@ import { PoCompiler } from '../../src/compilers/po.compiler'; describe('PoCompiler', () => { let compiler: PoCompiler; + let eofNewlineCompiler: PoCompiler; beforeEach(() => { compiler = new PoCompiler(); + eofNewlineCompiler = new PoCompiler({ eofNewline: true }); }); it('should still include html ', () => { @@ -17,6 +19,8 @@ describe('PoCompiler', () => { }); const result: Buffer = Buffer.from(compiler.compile(collection)); expect(result.toString('utf8')).to.equal('msgid ""\nmsgstr ""\n"mime-version: 1.0\\n"\n"Content-Type: text/plain; charset=utf-8\\n"\n"Content-Transfer-Encoding: 8bit\\n"\n\nmsgid "A test"\nmsgstr "Un test"\n\nmsgid "With a lot of html included"\nmsgstr "Avec beaucoup d\'html inclus"'); + const resultNewline: Buffer = Buffer.from(eofNewlineCompiler.compile(collection)); + expect(resultNewline.toString('utf8')).to.equal('msgid ""\nmsgstr ""\n"mime-version: 1.0\\n"\n"Content-Type: text/plain; charset=utf-8\\n"\n"Content-Transfer-Encoding: 8bit\\n"\n\nmsgid "A test"\nmsgstr "Un test"\n\nmsgid "With a lot of html included"\nmsgstr "Avec beaucoup d\'html inclus"\n'); }); });