diff --git a/lib/src/formatters/frameworks/json/base.ts b/lib/src/formatters/frameworks/json/base.ts index 4430b56..df62aa8 100644 --- a/lib/src/formatters/frameworks/json/base.ts +++ b/lib/src/formatters/frameworks/json/base.ts @@ -8,7 +8,7 @@ export default class BaseFramework { constructor(output: Output) { this.output = output; - this.outDir = output.outDir ?? appContext.projectConfigDir; + this.outDir = output.outDir ?? appContext.outDir; } get framework() { diff --git a/lib/src/formatters/frameworks/json/i18next.ts b/lib/src/formatters/frameworks/json/i18next.ts index 54c7c57..8f785cd 100644 --- a/lib/src/formatters/frameworks/json/i18next.ts +++ b/lib/src/formatters/frameworks/json/i18next.ts @@ -12,6 +12,11 @@ export default class I18NextFramework extends applyMixins( process( outputJsonFiles: Record> ) { + let moduleType: "commonjs" | "module" = "commonjs"; + if ("type" in this.output && this.output.type) { + moduleType = this.output.type; + } + const driverFile = new JavascriptOutputFile({ filename: "index", path: this.outDir, @@ -27,19 +32,29 @@ export default class I18NextFramework extends applyMixins( {} as Record ); - driverFile.content += this.generateImportStatements(outputJsonFiles); + if (moduleType === "module") { + driverFile.content += this.generateImportStatements(outputJsonFiles); - driverFile.content += `\n`; + driverFile.content += `\n`; - driverFile.content += this.generateDefaultExportString( - filesGroupedByVariantId - ); + driverFile.content += this.codegenDefaultExport( + this.generateExportedObjectString(filesGroupedByVariantId) + ); + } else { + driverFile.content += this.generateRequireStatements(outputJsonFiles); + + driverFile.content += `\n`; + + driverFile.content += this.codegenCommonJSModuleExports( + this.generateExportedObjectString(filesGroupedByVariantId) + ); + } return [driverFile]; } /** - * Generates the import statements for the driver file. One import per generated json file. + * Generates the import statements for the driver file with type "module". One import per generated json file. * @param outputJsonFiles - The output json files. * @returns The import statements, stringified. */ @@ -56,12 +71,30 @@ export default class I18NextFramework extends applyMixins( return importStatements; } + /** + * Generates the require statements for the driver file with type "commonjs". One require per generated json file. + * @param outputJsonFiles - The output json files. + * @returns The require statements, stringified. + */ + private generateRequireStatements( + outputJsonFiles: Record> + ) { + let requireStatements = ""; + for (const file of Object.values(outputJsonFiles)) { + requireStatements += this.codegenDefaultRequire( + this.sanitizeStringForJSVariableName(file.filename), + `./${file.filenameWithExtension}` + ); + } + return requireStatements; + } + /** * Generates the default export for the driver file. By default this is an object with the json imports grouped by variant id. * @param filesGroupedByVariantId - The files grouped by variant id. * @returns The default export, stringified. */ - private generateDefaultExportString( + private generateExportedObjectString( filesGroupedByVariantId: Record ) { const variantIds = Object.keys(filesGroupedByVariantId); @@ -85,6 +118,6 @@ export default class I18NextFramework extends applyMixins( defaultExportObjectString += `}`; - return this.codegenDefaultExport(defaultExportObjectString); + return defaultExportObjectString; } } diff --git a/lib/src/formatters/json.ts b/lib/src/formatters/json.ts index 14b3bfd..912b341 100644 --- a/lib/src/formatters/json.ts +++ b/lib/src/formatters/json.ts @@ -37,7 +37,7 @@ export default class JSONFormatter extends applyMixins( const variablesOutputFile = new JSONOutputFile({ filename: "variables", - path: this.outputDir, + path: this.outDir, }); for (let i = 0; i < data.textItems.length; i++) { @@ -47,7 +47,7 @@ export default class JSONFormatter extends applyMixins( outputJsonFiles[fileName] ??= new JSONOutputFile({ filename: fileName, - path: this.outputDir, + path: this.outDir, metadata: { variantId: textItem.variantId || "base" }, }); diff --git a/lib/src/formatters/mixins/javascriptCodegenMixin.ts b/lib/src/formatters/mixins/javascriptCodegenMixin.ts index f453c1e..f2028ef 100644 --- a/lib/src/formatters/mixins/javascriptCodegenMixin.ts +++ b/lib/src/formatters/mixins/javascriptCodegenMixin.ts @@ -15,8 +15,13 @@ export default function javascriptCodegenMixin( return str.replace(/[^a-zA-Z0-9]/g, "_"); } - protected codegenNamedImport(modules: NamedImport[], moduleName: string) { - const formattedModules = modules + /** + * Converts an array of modules into a string that can be used for a named import or require statement. + * @param modules array of { name: string, alias?: string }, each named import + * @returns a string of comma-separated module names/aliases, sorted + */ + protected formatNamedModules(modules: NamedImport[]) { + return modules .map((m) => { if (m.alias) { return `${m.name} as ${m.alias}`; @@ -25,18 +30,72 @@ export default function javascriptCodegenMixin( }) .sort() .join(", "); + } + + /** + * Creates a named import statement for one or more items from a module. + * Used for i18next type "module". + * @param modules array of { name: string, alias?: string }, each named import + * @param moduleName the name of the file or package to import from + * @returns i.e `import { foo, bar as name } from "./file";` + */ + protected codegenNamedImport(modules: NamedImport[], moduleName: string) { + const formattedModules = this.formatNamedModules(modules); return `import { ${formattedModules} } from "${moduleName}";\n`; } + /** + * Creates a named require statement for one or more items from a module. + * Used for i18next type "commonjs". + * @param modules array of { name: string, alias?: string }, each named import + * @param moduleName the name of the file or package to import from + * @returns i.e `const { foo, bar as name } = require("./file");` + */ + protected codegenNamedRequire(modules: NamedImport[], moduleName: string) { + const formattedModules = this.formatNamedModules(modules); + + return `const { ${formattedModules} } = require("${moduleName}");\n`; + } + + /** + * Creates a default import statement for i18next type "module". + * @param module the name of the module to import + * @param moduleName the name of the file or package to import from + * @returns i.e codegenDefaultImport("item", "./file") => `import item from "./file";` + */ protected codegenDefaultImport(module: string, moduleName: string) { return `import ${module} from "${moduleName}";\n`; } + /** + * Creates a default require statement for i18next type "commonjs". + * @param module the name of the module to import + * @param moduleName the name of the file or package to import from + * @returns i.e codegenDefaultRequire("item", "./file") => `const item = require("./file)";` + */ + protected codegenDefaultRequire(module: string, moduleName: string) { + return `const ${module} = require("${moduleName}");\n`; + } + + /** + * Creates a default export statement for i18next type "module". + * @param module the name of the module to export + * @returns i.e codegenDefaultExport("item") => "export default item;" + */ protected codegenDefaultExport(module: string) { return `export default ${module};`; } + /** + * Creates a module exports statement for i18next type "commonjs". + * @param module the name of the module to export + * @returns i.e codegenModuleExports("item") => "module.exports = item;" + */ + protected codegenCommonJSModuleExports(module: string) { + return `module.exports = ${module};`; + } + protected codegenPad(depth: number) { return " ".repeat(depth * this.indentSpaces); } diff --git a/lib/src/formatters/shared/base.ts b/lib/src/formatters/shared/base.ts index abb9cc1..09fc6d2 100644 --- a/lib/src/formatters/shared/base.ts +++ b/lib/src/formatters/shared/base.ts @@ -8,12 +8,12 @@ import appContext from "../../utils/appContext"; export default class BaseFormatter { protected output: Output; protected projectConfig: ProjectConfigYAML; - protected outputDir: string; + protected outDir: string; constructor(output: Output, projectConfig: ProjectConfigYAML) { this.output = output; this.projectConfig = projectConfig; - this.outputDir = output.outDir ?? appContext.outDir; + this.outDir = output.outDir ?? appContext.outDir; } protected async fetchAPIData(): Promise { diff --git a/lib/src/outputs/json.ts b/lib/src/outputs/json.ts index b26342f..21edefd 100644 --- a/lib/src/outputs/json.ts +++ b/lib/src/outputs/json.ts @@ -8,6 +8,7 @@ const ZBaseJSONOutput = ZBaseOutputFilters.extend({ const Zi18NextJSONOutput = ZBaseJSONOutput.extend({ framework: z.literal("i18next"), + type: z.literal("module").or(z.literal("commonjs")).optional(), }); export const ZJSONOutput = z.discriminatedUnion("framework", [