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/shared/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export default class BaseFormatter<OutputFileType, APIDataType = unknown> {

public async format(): Promise<void> {
const data = await this.fetchAPIData();
const files = await this.transformAPIData(data);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not zod related, but this is a sync method.

const files = this.transformAPIData(data);
await this.writeFiles(files);
}

Expand Down
68 changes: 29 additions & 39 deletions lib/src/http/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,39 @@ const ZBaseVariable = z.object({
name: z.string(),
});

const ZVariableNumber = ZBaseVariable.merge(
z.object({
type: z.literal("number"),
data: z.object({
example: z.union([z.number(), z.string()]),
fallback: z.union([z.number(), z.string()]).optional(),
}),
})
);
const ZVariableNumber = ZBaseVariable.extend({
type: z.literal("number"),
data: z.object({
example: z.union([z.number(), z.string()]),
fallback: z.union([z.number(), z.string()]).optional(),
}),
});

const ZVariableString = ZBaseVariable.merge(
z.object({
type: z.literal("string"),
data: z.object({
example: z.string(),
fallback: z.string().optional(),
}),
})
);
const ZVariableString = ZBaseVariable.extend({
type: z.literal("string"),
data: z.object({
example: z.string(),
fallback: z.string().optional(),
}),
});

const ZVariableHyperlink = ZBaseVariable.merge(
z.object({
type: z.literal("hyperlink"),
data: z.object({
text: z.string(),
url: z.string(),
}),
})
);
const ZVariableHyperlink = ZBaseVariable.extend({
type: z.literal("hyperlink"),
data: z.object({
text: z.string(),
url: z.string(),
}),
});

const ZVariableList = ZBaseVariable.merge(
z.object({
type: z.literal("list"),
data: z.array(z.string()),
})
);
const ZVariableList = ZBaseVariable.extend({
type: z.literal("list"),
data: z.array(z.string()),
});

const ZVariableMap = ZBaseVariable.merge(
z.object({
type: z.literal("map"),
data: z.record(z.string()),
})
);
const ZVariableMap = ZBaseVariable.extend({
type: z.literal("map"),
data: z.record(z.string(), z.string()),
});

const ZVariable = z.discriminatedUnion("type", [
ZVariableString,
Expand Down
9 changes: 5 additions & 4 deletions lib/src/outputs/android.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { z } from "zod";
import { ZBaseOutputFilters } from "./shared";

export const ZAndroidOutput = ZBaseOutputFilters.extend({
format: z.literal("android"),
framework: z.undefined(),
Copy link
Copy Markdown
Contributor

@marla-hoggard marla-hoggard Mar 31, 2026

Choose a reason for hiding this comment

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

I moved the framework: undefined definition to ZBaseOutputFilters so it only needs to be declared on a subtype if it's not undefined, rather than manually including undefined on every subtype.

}).strict();
export const ZAndroidOutput = z.strictObject(
ZBaseOutputFilters.extend({
format: z.literal("android"),
}).shape
);
9 changes: 5 additions & 4 deletions lib/src/outputs/iosStrings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { z } from "zod";
import { ZBaseOutputFilters } from "./shared";

export const ZIOSStringsOutput = ZBaseOutputFilters.extend({
format: z.literal("ios-strings"),
framework: z.undefined(),
}).strict();
export const ZIOSStringsOutput = z.strictObject(
ZBaseOutputFilters.extend({
format: z.literal("ios-strings"),
}).shape
);
9 changes: 5 additions & 4 deletions lib/src/outputs/iosStringsDict.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { z } from "zod";
import { ZBaseOutputFilters } from "./shared";

export const ZIOSStringsDictOutput = ZBaseOutputFilters.extend({
format: z.literal("ios-stringsdict"),
framework: z.undefined(),
}).strict();
export const ZIOSStringsDictOutput = z.strictObject(
ZBaseOutputFilters.extend({
format: z.literal("ios-stringsdict"),
}).shape
);
31 changes: 19 additions & 12 deletions lib/src/outputs/json.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { z } from "zod";
import { ZBaseOutputFilters } from "./shared";

const ZBaseJSONOutput = ZBaseOutputFilters.extend({
format: z.literal("json"),
framework: z.undefined(),
}).strict();
const ZBaseJSONOutput = z.strictObject(
ZBaseOutputFilters.extend({
format: z.literal("json"),
}).shape
);

const Zi18NextJSONOutput = ZBaseJSONOutput.extend({
framework: z.literal("i18next"),
type: z.literal("module").or(z.literal("commonjs")).optional(),
}).strict();
const Zi18NextJSONOutput = z.strictObject(
ZBaseOutputFilters.extend({
format: z.literal("json"),
framework: z.literal("i18next"),
type: z.literal("module").or(z.literal("commonjs")).optional(),
}).shape
);

const ZVueI18nJSONOutput = ZBaseJSONOutput.extend({
framework: z.literal("vue-i18n"),
type: z.literal("module").or(z.literal("commonjs")).optional(),
}).strict();
const ZVueI18nJSONOutput = z.strictObject(
ZBaseOutputFilters.extend({
format: z.literal("json"),
framework: z.literal("vue-i18n"),
type: z.literal("module").or(z.literal("commonjs")).optional(),
}).shape
);

export const ZJSONOutput = z.discriminatedUnion("framework", [
ZBaseJSONOutput,
Expand Down
9 changes: 5 additions & 4 deletions lib/src/outputs/jsonICU.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { z } from "zod";
import { ZBaseOutputFilters } from "./shared";

export const ZJSONICUOutput = ZBaseOutputFilters.extend({
format: z.literal("json_icu"),
framework: z.undefined(),
}).strict();
export const ZJSONICUOutput = z.strictObject(
ZBaseOutputFilters.extend({
format: z.literal("json_icu"),
}).shape
);
1 change: 1 addition & 0 deletions lib/src/outputs/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ZTagsFilter, ZTextStatus } from "../http/types";
* They are all optional by default unless otherwise specified in the output config.
*/
export const ZBaseOutputFilters = z.object({
framework: z.undefined().optional(),
projects: z.array(z.object({ id: z.string() })).optional(),
components: z
.object({
Expand Down
2 changes: 1 addition & 1 deletion lib/src/services/apiToken/promptForApiToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ export const validate = async (token: string) => {
* @returns The collected token
*/
export default async function promptForApiToken() {
// @ts-expect-error - Enquirer types are not updated for the validate function
const response = await prompt<{ token: string }>({
type: "input",
name: "token",
message: "What is your API key?",
// @ts-expect-error - Enquirer types are not updated for the validate function
validate,
});

Expand Down
10 changes: 6 additions & 4 deletions lib/src/services/projectConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { ZBaseOutputFilters } from "../outputs/shared";
import { ZOutput } from "../outputs";
import DittoError, { ErrorType } from "../utils/DittoError";

const ZProjectConfigYAML = ZBaseOutputFilters.extend({
outputs: z.array(ZOutput),
}).strict();
const ZProjectConfigYAML = z.strictObject(
ZBaseOutputFilters.extend({
outputs: z.array(ZOutput),
}).shape
);

export type ProjectConfigYAML = z.infer<typeof ZProjectConfigYAML>;

Expand Down Expand Up @@ -63,7 +65,7 @@ function readProjectConfigData(
throw new DittoError({
type: ErrorType.ConfigParseError,
data: {
formattedError: JSON.stringify(parsedYAML.error.flatten(), null, 2),
formattedError: z.prettifyError(parsedYAML.error),
messagePrefix: "There is an error in your project config file.",
},
});
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dittowords/cli",
"version": "5.5.0",
"version": "5.5.1",
"description": "Command Line Interface for Ditto (dittowords.com).",
"license": "MIT",
"main": "bin/ditto.js",
Expand Down Expand Up @@ -81,7 +81,7 @@
"js-yaml": "^4.1.0",
"memfs": "^4.7.7",
"ora": "^5.0.0",
"zod": "^3.24.2"
"zod": "^4.0.0"
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,css,json}": "prettier --write"
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4695,7 +4695,7 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==

zod@^3.24.2:
version "3.24.2"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3"
integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==
zod@^4.0.0:
version "4.3.6"
resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a"
integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==
Loading