Skip to content

Commit

Permalink
track and enforce whitespace and key ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
jwbay committed Oct 23, 2016
1 parent eb8e242 commit becb79c
Show file tree
Hide file tree
Showing 17 changed files with 325 additions and 14 deletions.
34 changes: 27 additions & 7 deletions src/LocalizationFolder.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import { IFileMap } from './';
import { IFileMap, lineEndings } from './';
import stringify = require('json-stable-stringify');

interface IHashMap { [filename: string]: string; }

export default class LocalizationFolder {
private hashes: IHashMap;

constructor(
private files: IFileMap,
private primaryLanguage: string,
private isReportMode: boolean
) { }
) {
this.hashes = {};
}

public populateFromDisk(filesToCreate: string[]) {
const filesReadFromDisk = Object.keys(this.files).map(name => {
const fileContent = fs.readFileSync(name, 'utf8');
this.files[name] = JSON.parse(fileContent);
this.hashes[name] = crypto.createHash('md5').update(fileContent).digest('hex');
return path.basename(name, '.json');
});
const dirname = path.dirname(Object.keys(this.files)[0]);
Expand All @@ -28,21 +36,33 @@ export default class LocalizationFolder {

const filename = path.join(dirname, file + '.json').split(path.sep).join('/');
this.files[filename] = {};
this.hashes[filename] = '';
}
}

public flushToDisk(jsonSpacing: string | number, jsonLineEndings: 'LF' | 'CRLF') {
public flushToDisk(jsonSpacing: string | number, jsonLineEndings: lineEndings) {
const changedFiles: string[] = [];

Object.keys(this.files).forEach(name => {
let fileContent = stringify(this.files[name], { space: jsonSpacing });
if (jsonLineEndings === 'CRLF') {
fileContent = fileContent.replace(/\n/g, '\r\n');
}

const hash = crypto.createHash('md5').update(fileContent).digest('hex');
if (this.hashes[name] !== hash) {
changedFiles.push(name);
}

if (!this.isReportMode) {
let fileContent = stringify(this.files[name], { space: jsonSpacing });
if (jsonLineEndings === 'CRLF') {
fileContent = fileContent.replace(/\n/g, '\r\n');
}
fs.writeFileSync(name, fileContent, { encoding: 'utf8' });
}

this.hashes[name] = null;
this.files[name] = null;
});

return changedFiles;
}

public getSourceObject() {
Expand Down
19 changes: 13 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ export interface IOptions {
/** Space value used for JSON.stringify when writing JSON files to disk */
space?: string | number;
/** Line endings used when writing JSON files to disk */
lineEndings?: 'LF' | 'CRLF';
lineEndings?: lineEndings;
}

export interface IDirectoryMap { [directory: string]: IFileMap; }
export interface IFileMap { [filename: string]: Object; }
export type lineEndings = 'LF' | 'CRLF';
type localizationValue = { [key: string]: string } | string;
type lineEndings = 'LF' | 'CRLF';

export default function sync({
check: isReportMode = false,
Expand All @@ -38,6 +38,7 @@ export default function sync({
let record: ActionRecorder;
let hasAnyErrors = false;
let hasAnyChanges = false;
let hasValueChanges = false;
for (const currentDirectory of Object.keys(directories)) {
const folder = new LocalizationFolder(directories[currentDirectory], primaryLanguage, isReportMode);
folder.populateFromDisk(createFiles);
Expand All @@ -52,19 +53,25 @@ export default function sync({
record = new ActionRecorder(filename, isReportMode);
syncObjects(sourceObject, folder.getTargetObject(filename));
record.flushToConsole();
hasAnyChanges = hasAnyChanges || record.hasAnyActions();
hasValueChanges = hasValueChanges || record.hasAnyActions();
hasAnyErrors = hasAnyErrors || record.hasAnyErrors();
}

folder.flushToDisk(jsonSpacing, lineEndings.toUpperCase() as lineEndings);
const changedFiles = folder.flushToDisk(jsonSpacing, lineEndings.toUpperCase() as lineEndings);
hasAnyChanges = hasAnyChanges || changedFiles.length > 0;
}

if (hasAnyErrors) {
throw new Error('[i18next-json-sync] found keys unsafe to synchronize');
}

if (isReportMode && hasAnyChanges) {
throw new Error('[i18next-json-sync] check failed');
if (isReportMode) {
if (hasValueChanges) {
throw new Error('[i18next-json-sync] check failed -- keys are out of sync. Run again without check mode to synchronize files');
}
if (hasAnyChanges) {
throw new Error('[i18next-json-sync] check failed -- files have unordered keys or unexpected whitespace. Run again without check mode to correct files');
}
}

function groupFilesByDirectory(allFiles: string[]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ exports[`test adds-removes-keys-report:locales/pt-BR.json 1`] = `
exports[`test adds-removes-keys-report:stderr.txt 1`] = `
"
THROWN:
Error: [i18next-json-sync] check failed
Error: [i18next-json-sync] check failed -- keys are out of sync. Run again without check mode to synchronize files
...
"
`;
Expand Down
27 changes: 27 additions & 0 deletions tests/check-key-order/__snapshots__/runner.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
exports[`test check-key-order:locales/en.json 1`] = `
"{
keep_one: en,
asdf: {
keep_two: en
}
}"
`;

exports[`test check-key-order:locales/pt-BR.json 1`] = `
"{
asdf: {
keep_two: pt-BR
},
keep_one: pt-BR
}"
`;

exports[`test check-key-order:stderr.txt 1`] = `
"
THROWN:
Error: [i18next-json-sync] check failed -- files have unordered keys or unexpected whitespace. Run again without check mode to correct files
...
"
`;

exports[`test check-key-order:stdout.txt 1`] = `""`;
5 changes: 5 additions & 0 deletions tests/check-key-order/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IOptions } from '../../src';
const options: IOptions = {
check: true
};
export = options;
6 changes: 6 additions & 0 deletions tests/check-key-order/project/locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"keep_one": "en",
"asdf": {
"keep_two": "en"
}
}
6 changes: 6 additions & 0 deletions tests/check-key-order/project/locales/pt-BR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"asdf": {
"keep_two": "pt-BR"
},
"keep_one": "pt-BR"
}
103 changes: 103 additions & 0 deletions tests/check-plurals-already-in-sync/__snapshots__/runner.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
exports[`test check-plurals-already-in-sync:locales/de.json 1`] = `
"{
add: {
also_not_0_x: also not a plural,
brick: brick en,
brick_plural: bricks en,
not_plural_x: not a plural
},
keep: {
other_one: other one de,
other_value: other two de,
value: value de,
value_plural: values de
},
remove: {
},
replace: {
book: book de,
book_plural: books de,
keep_one: one de,
keep_two: two de
}
}"
`;

exports[`test check-plurals-already-in-sync:locales/en.json 1`] = `
"{
add: {
also_not_0_x: also not a plural,
brick: brick en,
brick_plural: bricks en,
not_plural_x: not a plural
},
keep: {
other_one: other one en,
other_value: other two en,
value: value en,
value_plural: values en
},
remove: {
},
replace: {
book: book en,
book_plural: books en,
keep_one: one en,
keep_two: two en
}
}"
`;

exports[`test check-plurals-already-in-sync:locales/ja.json 1`] = `
"{
add: {
also_not_0_x: also not a plural,
brick_0: bricks en,
not_plural_x: not a plural
},
keep: {
other_one: other one ja,
other_value: other two ja,
value_0: values ja
},
remove: {
},
replace: {
book_0: books en,
keep_one: one ja,
keep_two: two ja
}
}"
`;

exports[`test check-plurals-already-in-sync:locales/ru.json 1`] = `
"{
add: {
also_not_0_x: also not a plural,
brick_0: bricks en,
brick_1: bricks en,
brick_2: bricks en,
not_plural_x: not a plural
},
keep: {
other_one: other one ru,
other_value: other two ru,
value_0: values ru 0,
value_1: values ru 1,
value_2: values ru 2
},
remove: {
},
replace: {
book_0: books en,
book_1: books en,
book_2: books en,
keep_one: one ru,
keep_two: two ru
}
}"
`;

exports[`test check-plurals-already-in-sync:stderr.txt 1`] = `""`;

exports[`test check-plurals-already-in-sync:stdout.txt 1`] = `""`;
5 changes: 5 additions & 0 deletions tests/check-plurals-already-in-sync/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IOptions } from '../../src';
const options: IOptions = {
check: true
};
export = options;
22 changes: 22 additions & 0 deletions tests/check-plurals-already-in-sync/project/locales/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"add": {
"also_not_0_x": "also not a plural",
"brick": "brick en",
"brick_plural": "bricks en",
"not_plural_x": "not a plural"
},
"keep": {
"other_one": "other one de",
"other_value": "other two de",
"value": "value de",
"value_plural": "values de"
},
"remove": {
},
"replace": {
"book": "book de",
"book_plural": "books de",
"keep_one": "one de",
"keep_two": "two de"
}
}
22 changes: 22 additions & 0 deletions tests/check-plurals-already-in-sync/project/locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"add": {
"also_not_0_x": "also not a plural",
"brick": "brick en",
"brick_plural": "bricks en",
"not_plural_x": "not a plural"
},
"keep": {
"other_one": "other one en",
"other_value": "other two en",
"value": "value en",
"value_plural": "values en"
},
"remove": {
},
"replace": {
"book": "book en",
"book_plural": "books en",
"keep_one": "one en",
"keep_two": "two en"
}
}
19 changes: 19 additions & 0 deletions tests/check-plurals-already-in-sync/project/locales/ja.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"add": {
"also_not_0_x": "also not a plural",
"brick_0": "bricks en",
"not_plural_x": "not a plural"
},
"keep": {
"other_one": "other one ja",
"other_value": "other two ja",
"value_0": "values ja"
},
"remove": {
},
"replace": {
"book_0": "books en",
"keep_one": "one ja",
"keep_two": "two ja"
}
}
25 changes: 25 additions & 0 deletions tests/check-plurals-already-in-sync/project/locales/ru.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"add": {
"also_not_0_x": "also not a plural",
"brick_0": "bricks en",
"brick_1": "bricks en",
"brick_2": "bricks en",
"not_plural_x": "not a plural"
},
"keep": {
"other_one": "other one ru",
"other_value": "other two ru",
"value_0": "values ru 0",
"value_1": "values ru 1",
"value_2": "values ru 2"
},
"remove": {
},
"replace": {
"book_0": "books en",
"book_1": "books en",
"book_2": "books en",
"keep_one": "one ru",
"keep_two": "two ru"
}
}
Loading

0 comments on commit becb79c

Please sign in to comment.