Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/src/formatters/frameworks/json/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
49 changes: 41 additions & 8 deletions lib/src/formatters/frameworks/json/i18next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export default class I18NextFramework extends applyMixins(
process(
outputJsonFiles: Record<string, JSONOutputFile<{ variantId: string }>>
) {
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,
Expand All @@ -27,19 +32,29 @@ export default class I18NextFramework extends applyMixins(
{} as Record<string, OutputFile[]>
);

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.
*/
Expand All @@ -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<string, JSONOutputFile<{ variantId: string }>>
) {
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<string, OutputFile[]>
) {
const variantIds = Object.keys(filesGroupedByVariantId);
Expand All @@ -85,6 +118,6 @@ export default class I18NextFramework extends applyMixins(

defaultExportObjectString += `}`;

return this.codegenDefaultExport(defaultExportObjectString);
return defaultExportObjectString;
}
}
4 changes: 2 additions & 2 deletions lib/src/formatters/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand All @@ -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" },
});

Expand Down
63 changes: 61 additions & 2 deletions lib/src/formatters/mixins/javascriptCodegenMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ export default function javascriptCodegenMixin<TBase extends Constructor>(
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}`;
Expand All @@ -25,18 +30,72 @@ export default function javascriptCodegenMixin<TBase extends Constructor>(
})
.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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to make a codeNamedRequire method as well? You mentioned that we're not currently using codegenNamedImport but if we're going to keep that method around, I would expect us to have the corresponding module method. If we don't think they will be needed, then I think we should delete the existing unused one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and added it - if we don't think they'll ever be of use, we can delete them both instead.

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);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/src/formatters/shared/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import appContext from "../../utils/appContext";
export default class BaseFormatter<APIDataType = unknown> {
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<APIDataType> {
Expand Down
1 change: 1 addition & 0 deletions lib/src/outputs/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", [
Expand Down