diff --git a/package.json b/package.json index 8df9ce4..1751947 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ }, "dependencies": { "@formatjs/icu-messageformat-parser": "^2.7.8", - "chalk": "^4.0.0" + "chalk": "^4.0.0", + "flattie": "^1.1.1" }, "keywords": [ "cli", diff --git a/src/index.ts b/src/index.ts index d749911..37bb11d 100755 --- a/src/index.ts +++ b/src/index.ts @@ -5,18 +5,16 @@ import path from "node:path"; import chalk from "chalk"; import { runRules } from "./engine/rule-engine.ts"; -import { Config } from "./types.js"; +import type { Config } from "./types.js"; import { config } from "./config/default-config.ts"; const defaultConfig: Config = config; const configPath = path.join(__dirname, "../keeli.config.json"); -// Only start the routine running if the configuration file is found. +// Only start keeli if the keeli configuration file is found. if (fs.existsSync(configPath)) { - const userConfig: Partial = JSON.parse( - fs.readFileSync(configPath, "utf8") - ); + const userConfig: Config = JSON.parse(fs.readFileSync(configPath, "utf8")); const config = { ...defaultConfig, ...userConfig }; diff --git a/src/utils/file-helpers.spec.ts b/src/utils/file-helpers.spec.ts new file mode 100644 index 0000000..7ad3609 --- /dev/null +++ b/src/utils/file-helpers.spec.ts @@ -0,0 +1,74 @@ +import type { TranslationFiles } from "../types"; +import { flatten } from "./file-helpers"; + +describe("flatten", () => { + it("should flatten nested JavaScript objects", () => { + const nested = { + a: "hi", + b: { + a: null, + b: ["foo", "", null, "bar"], + d: "hello", + e: { + a: "yo", + b: undefined, + c: "sup", + d: 0, + f: [ + { foo: 123, bar: 123 }, + { foo: 465, bar: 456 }, + ], + }, + }, + c: "world", + } as unknown as TranslationFiles; + + const expected = { + a: "hi", + "b.b.0": "foo", + "b.b.1": "", + "b.b.3": "bar", + "b.d": "hello", + "b.e.a": "yo", + "b.e.c": "sup", + "b.e.d": 0, + "b.e.f.0.foo": 123, + "b.e.f.0.bar": 123, + "b.e.f.1.foo": 465, + "b.e.f.1.bar": 456, + c: "world", + }; + + expect(flatten(nested)).toEqual(expected); + }); + + it("should flatten a parsed nested JSON object", () => { + const nested = JSON.parse( + JSON.stringify({ + some: { + deeply: { + nested: { + message: "hello", + }, + }, + }, + another: { + super: { + deeply: { + nested: { + message: "world", + }, + }, + }, + }, + }) + ) as TranslationFiles; + + const expected = { + "some.deeply.nested.message": "hello", + "another.super.deeply.nested.message": "world", + }; + + expect(flatten(nested)).toEqual(expected); + }); +}); diff --git a/src/utils/file-helpers.ts b/src/utils/file-helpers.ts index f2fbade..c6cedaa 100644 --- a/src/utils/file-helpers.ts +++ b/src/utils/file-helpers.ts @@ -1,6 +1,11 @@ import path from "node:path"; import fs from "node:fs"; -import { Config, TranslationFiles } from "../types"; +import { flattie } from "flattie"; +import type { Config, TranslationFiles } from "../types"; + +export function flatten(obj: TranslationFiles): Record { + return flattie(obj, "."); +} /** * Import all of the translation files in the current project and bundle their data into a single object. @@ -31,7 +36,9 @@ export function loadLanguageFiles(config: Config): TranslationFiles { try { // TODO: convert to JSON before parsing if the file is not JSON. https://github.com/radiovisual/keeli/issues/2 - files[locale] = JSON.parse(fs.readFileSync(translatedFilePath, "utf8")); + files[locale] = flatten( + JSON.parse(fs.readFileSync(translatedFilePath, "utf8")) + ); } catch (err: unknown) { console.error( `There was an error trying to read the file at path: '${translatedFilePath}' for the locale: '${locale}'. Please ensure that this is a valid translation file and try again.`