Skip to main content
Glama
transformJSFile.test.ts22.7 kB
import { readFile } from 'node:fs/promises'; import { resolve } from 'node:path'; import { cond, enu, gender, insert, md, nest, t } from '@intlayer/core'; import { fileContent } from '@intlayer/core/file'; import { type CustomIntlayerConfig, type Dictionary, Locales, } from '@intlayer/types'; import { defu } from 'defu'; import { describe, expect, it, vi } from 'vitest'; import { transformJSFile } from './transformJSFile'; // Compute absolute path to the test content file const testFilePath = resolve( process.cwd(), 'src/writeContentDeclaration/_test.content.ts' ); vi.mock('@intlayer/config/built', () => ({ default: { content: { baseDir: process.cwd(), } as CustomIntlayerConfig, }, })); const file = (path: string) => fileContent(path, `${process.cwd()}/src`, process.cwd()); // Read the file contents as string for the transform const initialFileContentString = await readFile(testFilePath, 'utf-8'); // Import after globals are set so `file()` can resolve paths const { default: initialFileContent } = await import('./_test.content'); describe('transformJSFile', () => { it('add new string entries', async () => { const dictionary: Dictionary = defu( { content: { title: 'Hello', }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); // Title should be replaced with a string literal "Hello" expect(result).toContain('title: "Hello"'); }); it('add new translation entries', async () => { const dictionary: Dictionary = defu( { content: { title: t({ en: 'Hello', fr: 'Bonjour', es: 'Hola', }), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('title: t({'); expect(result).toContain('en: "Hello"'); expect(result).toContain('fr: "Bonjour"'); expect(result).toContain('es: "Hola"'); }); it('update existing translation entries', async () => { const dictionary: Dictionary = defu( { content: { welcomeMessage: t({ en: 'Hello', fr: 'Bonjour', es: 'Hola', }), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('welcomeMessage: t({'); expect(result).toContain('en: "Hello"'); expect(result).toContain('fr: "Bonjour"'); expect(result).toContain('es: "Hola"'); }); it('add new translation entries in an array', async () => { const dictionary: Dictionary = defu( { content: { arrayOfTranslations: [ t({ en: 'Hello 3', fr: 'Bonjour 3', }), t({ en: 'Hello 2', fr: 'Bonjour 2', }), ], }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('arrayOfTranslations: ['); expect(result).toContain('t({'); expect(result).toContain('en: "Hello 3"'); expect(result).toContain('fr: "Bonjour 3"'); expect(result).toContain('t({'); expect(result).toContain('en: "Hello 2"'); expect(result).toContain('fr: "Bonjour 2"'); }); it('add new primitive entries (number, boolean, null)', async () => { // Note: defu skips null values by design, so we construct the dictionary directly const dictionary: Dictionary = { ...initialFileContent, content: { ...initialFileContent.content, newNumber: 42, newBoolean: true, newNull: null as any, }, }; const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('newNumber: 42'); expect(result).toContain('newBoolean: true'); expect(result).toContain('newNull: null'); }); it('supports hyphenated locale keys and keeps proper quoting', async () => { const dictionary: Dictionary = defu( { content: { hyphenTrans: t({ en: 'Hi', 'en-GB': 'Hello', }), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('hyphenTrans: t({'); expect(result).toContain('en: "Hi"'); expect(result).toContain('"en-GB": "Hello"'); }); it('skips translation updates when values are not plain strings', async () => { const dictionary: Dictionary = defu( { content: { // Simulate non-string translation values; implementation should skip badTrans: t({ en: 123 as any }), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).not.toContain('badTrans: t({'); }); it('update translation entries locale in an array or translation', async () => { const dictionary: Dictionary = defu( { content: { contentMultilingual: t({ en: 'Hello 3', fr: 'Bonjour 3', }), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('contentMultilingual: t({'); expect(result).toContain('en: "Hello 3"'); }); it('update translation entries locale in an markdown', async () => { const dictionary: Dictionary = defu( { content: { markdownMultilingual: md(t({ en: 'Hello 3', fr: 'Bonjour 3' })), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('markdownMultilingual: md(t({'); expect(result).toContain('en: "Hello 3"'); expect(result).toContain('fr: "Bonjour 3"'); }); it('update translation entries locale in an markdown', async () => { const dictionary: Dictionary = defu( { content: { markdownMultilingual2: md(t({ en: 'Hello 3', fr: 'Bonjour 3' })), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('markdownMultilingual2: md('); expect(result).toContain('en: "Hello 3"'); expect(result).toContain('fr: "Bonjour 3"'); }); it('works with ESM default export (object literal)', async () => { const esmContent = `export default { key: 'x', content: { existing: 'value' } };`; const dict: Dictionary = { content: { title: 'Hello' } } as any; const result = await transformJSFile(esmContent, dict); expect(result).toContain('title: "Hello"'); expect(result).toContain('export default'); }); it('works with ESM default export (identifier)', async () => { const esmContent = `const d = { key: 'x', content: { existing: 'value' } };\nexport default d;`; const dict: Dictionary = { content: { title: 'Hello' } } as any; const result = await transformJSFile(esmContent, dict); expect(result).toContain('title: "Hello"'); expect(result).toContain('export default d'); }); it('works with CJS module.exports assignment', async () => { const cjsContent = `module.exports = { key: 'x', content: { existing: 'value' } };`; const dict: Dictionary = { content: { title: 'Hello' } } as any; const result = await transformJSFile(cjsContent, dict); expect(result).toContain('title: "Hello"'); expect(result).toContain('module.exports'); }); it('works with CJS exports.default assignment', async () => { const cjsContent = `exports.default = { key: 'x', content: { existing: 'value' } };`; const dict: Dictionary = { content: { title: 'Hello' } } as any; const result = await transformJSFile(cjsContent, dict); expect(result).toContain('title: "Hello"'); expect(result).toContain('exports.default'); }); it('adds enum node', async () => { const dictionary: Dictionary = defu( { content: { enumTest: enu({ '0': 'none', '1': 'one', '>1': 'many' }), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('enumTest: enu({'); expect(result).toContain('"0": "none"'); expect(result).toContain('"1": "one"'); expect(result).toContain('">1": "many"'); }); it('adds markdown nodes (inline and from file)', async () => { const dictionary: Dictionary = defu( { content: { mdInline: md('# Title'), mdFromFile: md(file('./file.md')), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('mdInline: md("# Title")'); expect(result).toContain('mdFromFile: md(file("./file.md"))'); }); it('adds cond, gender, insert, and nest nodes', async () => { const dictionary: Dictionary = defu( { content: { condNode: cond({ true: 'y', false: 'n', fallback: 'f' }), genderNode: gender({ male: 'm', female: 'f', fallback: 'x' }), insertNode: insert('Hello {{name}}'), nestNode: nest('code'), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('condNode: cond({'); expect(result).toContain('"true": "y"'); expect(result).toContain('"false": "n"'); expect(result).toContain('fallback: "f"'); expect(result).toContain('genderNode: gender({'); expect(result).toContain('male: "m"'); expect(result).toContain('female: "f"'); expect(result).toContain('fallback: "x"'); expect(result).toContain('insertNode: insert("Hello {{name}}")'); expect(result).toContain('nestNode: nest("code")'); }); it('adds a new string element to an existing array', async () => { const dictionary: Dictionary = defu( { content: { arrayContent: [ 'string', 'string2', 'string3', 'string4', 'string5', 'string6', ], }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('arrayContent: ['); expect(result).toContain('"string6"'); }); it('creates a new array property with a t() node element', async () => { const dictionary: Dictionary = defu( { content: { arrayWithTranslations: [ t({ en: 'Hello', fr: 'Bonjour', }), ], }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('arrayWithTranslations: ['); expect(result).toContain('t({'); expect(result).toContain('en: "Hello"'); expect(result).toContain('fr: "Bonjour"'); }); it('update translation entries with fallback locale', async () => { const dictionary: Dictionary = defu( { content: { welcomeMessage: 'Hello', }, }, initialFileContent ); const result = await transformJSFile( initialFileContentString, dictionary, Locales.ENGLISH ); expect(result).toContain('welcomeMessage: t({'); expect(result).toContain('en: "Hello"'); }); it('adds missing import for t() when adding new translation', async () => { const fileWithoutImports = `export default { key: 'test', content: { existing: 'value' } };`; const dict: Dictionary = { key: 'test', content: { title: t({ en: 'Hello', fr: 'Bonjour' }), }, }; const result = await transformJSFile(fileWithoutImports, dict); // Check that the import was added expect(result).toContain('import { t } from "intlayer"'); // Check that the content was added expect(result).toContain('title: t({ en: "Hello", fr: "Bonjour" })'); }); it('adds missing import for enu() when adding enumeration', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { count: enu({ '0': 'none', '1': 'one', '>1': 'many' }), }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { enu } from "intlayer"'); expect(result).toContain('count: enu({'); }); it('adds missing import for cond() when adding condition', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { status: cond({ true: 'yes', false: 'no', fallback: 'maybe' }), }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { cond } from "intlayer"'); expect(result).toContain('status: cond({'); }); it('adds missing import for gender() when adding gender node', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { pronoun: gender({ male: 'he', female: 'she', fallback: 'they' }), }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { gender } from "intlayer"'); expect(result).toContain('pronoun: gender({'); }); it('adds missing import for insert() when adding insertion', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { greeting: insert('Hello {{name}}'), }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { insert } from "intlayer"'); expect(result).toContain('greeting: insert("Hello {{name}}")'); }); it('adds missing import for md() when adding markdown', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { description: md('# Title'), }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { md } from "intlayer"'); expect(result).toContain('description: md("# Title")'); }); it('adds missing imports for md() and file() when adding markdown with file', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { readme: md(file('./README.md')), }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { md } from "intlayer"'); expect(result).toContain('import { file } from "intlayer/file"'); expect(result).toContain('readme: md(file("./README.md"))'); }); it('adds missing import for nest() when adding nested content', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { nested: nest('other-key'), }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { nest } from "intlayer"'); expect(result).toContain('nested: nest("other-key")'); }); it('adds multiple missing imports at once', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { title: t({ en: 'Hello' }), count: enu({ '0': 'none', '1': 'one' }), description: md('# Title'), }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { enu, md, t } from "intlayer"'); }); it('does not duplicate existing imports', async () => { const fileWithExistingImport = `import { t } from "intlayer";\nexport default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { title: t({ en: 'Hello' }), }, }; const result = await transformJSFile(fileWithExistingImport, dict); // Should not add a second import const importCount = (result.match(/import \{ t \} from "intlayer"/g) || []) .length; expect(importCount).toBe(1); }); it('adds to existing import statement when some imports are missing', async () => { const fileWithPartialImport = `import { t } from "intlayer";\nexport default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { title: t({ en: 'Hello' }), count: enu({ '0': 'none' }), }, }; const result = await transformJSFile(fileWithPartialImport, dict); // Should add enu to existing import expect(result).toContain('import { enu, t } from "intlayer"'); }); it('adds imports for translations within arrays', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { messages: [t({ en: 'Hello', fr: 'Bonjour' })], }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { t } from "intlayer"'); expect(result).toContain('messages: [ t({'); }); it('adds imports for insert() with nested translation', async () => { const fileWithoutImports = `export default { key: 'test', content: {} };`; const dict: Dictionary = { key: 'test', content: { greeting: insert(t({ en: 'Hello {{name}}', fr: 'Bonjour {{name}}' })), }, }; const result = await transformJSFile(fileWithoutImports, dict); expect(result).toContain('import { insert, t } from "intlayer"'); expect(result).toContain('greeting: insert(t({'); }); it('update translation entries with fallback locale', async () => { const dictionary: Dictionary = defu( { content: { welcomeMessage: 'Hello', }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('welcomeMessage: t({'); expect(result).toContain('en: "Hello"'); }); it('update translation entries with fallback locale in an array', async () => { const dictionary: Dictionary = defu( { content: { arrayOfTranslations: ['Hello 3', 'Hello 2'], }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('arrayOfTranslations: ['); expect(result).toContain('en: "Hello 3"'); }); it('update translation entries with fallback locale in an array or translation', async () => { const dictionary: Dictionary = defu( { content: { arrayOfTranslations: ['Hello 3', 'Hello 2'], }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('arrayOfTranslations: ['); expect(result).toContain('en: "Hello 3"'); }); it('update translation entries locale in an array of translations', async () => { const dictionary: Dictionary = defu( { content: { translationOfArray: ['Hello 3', 'Hello 2'], }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('translationOfArray: ['); expect(result).toContain('en: "Hello 3"'); expect(result).toContain('en: "Hello 2"'); }); it('update translation entries locale in an markdown with fallback locale', async () => { const dictionary: Dictionary = defu( { content: { markdownMultilingual: md('Hello 3'), }, }, initialFileContent ); const result = await transformJSFile( initialFileContentString, dictionary, Locales.ENGLISH ); expect(result).toContain('markdownMultilingual: md('); expect(result).toContain('en: "Hello 3"'); expect(result).toContain('fr: "## test fr"'); }); it('update translation entries locale in an markdown with fallback locale', async () => { const dictionary: Dictionary = defu( { content: { markdownMultilingual2: md('Hello 3'), }, }, initialFileContent ); const result = await transformJSFile( initialFileContentString, dictionary, Locales.ENGLISH ); expect(result).toContain('markdownMultilingual2: md(t({'); expect(result).toContain('en: "Hello 3"'); expect(result).toContain('fr: "## test fr"'); }); it('updates nested translations within conditional nodes', async () => { const dictionary: Dictionary = defu( { content: { expandCollapseToggle: cond({ true: t({ en: 'Show all', fr: 'Afficher tout', es: 'Mostrar todo', de: 'Mehr anzeigen', pl: 'Pokaż wszystko', }), false: t({ en: 'Show less', fr: 'Afficher moins', es: 'Mostrar menos', de: 'Weniger anzeigen', pl: 'Pokaż mniej', }), }), }, }, initialFileContent ); const result = await transformJSFile(initialFileContentString, dictionary); expect(result).toContain('expandCollapseToggle: cond({'); expect(result).toContain('true: t({'); expect(result).toContain('en: "Show all"'); expect(result).toContain('fr: "Afficher tout"'); expect(result).toContain('es: "Mostrar todo"'); expect(result).toContain('de: "Mehr anzeigen"'); expect(result).toContain('pl: "Pokaż wszystko"'); expect(result).toContain('false: t({'); expect(result).toContain('en: "Show less"'); expect(result).toContain('fr: "Afficher moins"'); expect(result).toContain('es: "Mostrar menos"'); expect(result).toContain('de: "Weniger anzeigen"'); expect(result).toContain('pl: "Pokaż mniej"'); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server