diff --git a/eslint.config.js b/eslint.config.js index 43ddca8..44e3ab6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -39,6 +39,10 @@ export default typedConfig( ...tsConfigs.strictTypeChecked, ...tsConfigs.stylisticTypeChecked, ], + rules: { + // causing error when passing empty object as initial value + "@typescript-eslint/prefer-reduce-type-parameter": "off", + }, }, { plugins: { diff --git a/src/transformers/get-options.spec.ts b/src/transformers/get-options.spec.ts new file mode 100644 index 0000000..71f36df --- /dev/null +++ b/src/transformers/get-options.spec.ts @@ -0,0 +1,24 @@ +import { Type } from "@sinclair/typebox"; +import { beforeAll, beforeEach, expect, it, vi } from "vitest"; + +const getOption = vi.fn().mockReturnValue({ type: "mocked" }); + +let component: typeof import("./get-options"); + +beforeAll(async () => { + vi.doMock("./get-option", () => ({ getOption })); + component = await import("./get-options"); +}); + +beforeEach(() => { + vi.clearAllMocks(); +}); + +it("should pass schemas of props in a TObject to get-option", () => { + const schema = Type.Object({ + foo: Type.String(), + }); + const result = component.getOptions(schema); + expect(getOption).toBeCalledWith(schema.properties.foo); + expect(result).toEqual({ foo: { type: "mocked" } }); +}); diff --git a/src/transformers/get-options.ts b/src/transformers/get-options.ts new file mode 100644 index 0000000..f059d3a --- /dev/null +++ b/src/transformers/get-options.ts @@ -0,0 +1,24 @@ +import type { Static, TObject } from "@sinclair/typebox"; +import type { Options } from "yargs"; + +import { getOption } from "./get-option"; + +export function getOptions( + schema: Schema, +): Record, Options> { + const options = Object.entries(schema.properties).reduce( + (acc, [key, value]) => { + const option = getOption(value); + const result = { + [key]: option, + }; + + Object.assign(acc, result); + + return acc; + }, + {} as Record, Options>, + ); + + return options; +} diff --git a/src/transformers/index.ts b/src/transformers/index.ts index a5946aa..b8e3d50 100644 --- a/src/transformers/index.ts +++ b/src/transformers/index.ts @@ -2,5 +2,6 @@ export * from "./array"; export * from "./boolean"; export * from "./choice"; export * from "./get-option"; +export * from "./get-options"; export * from "./number"; export * from "./string"; diff --git a/tests/get-options.test.ts b/tests/get-options.test.ts new file mode 100644 index 0000000..3c851c9 --- /dev/null +++ b/tests/get-options.test.ts @@ -0,0 +1,46 @@ +import { Type } from "@sinclair/typebox"; +import { beforeAll, beforeEach, expect, it, vi } from "vitest"; + +let component: typeof import("@/index"); + +beforeAll(async () => { + component = await import("@/index"); +}); + +beforeEach(() => { + vi.clearAllMocks(); +}); + +it("should transform schemas of props in a TObject to yargs options", () => { + const schema = Type.Object({ + page: Type.Number({ description: "page number" }), + size: Type.Number({ description: "page size", default: 10 }), + query: Type.Optional(Type.String()), + sort: Type.Optional( + Type.Array(Type.Union([Type.Literal("id"), Type.Literal("createdAt")])), + ), + order: Type.Union([Type.Literal("asc"), Type.Literal("desc")], { + default: "asc", + }), + pretty: Type.Boolean({ description: "pretty print" }), + }); + const result = component.getOptions(schema); + expect(result).toEqual({ + page: { type: "number", requiresArg: true, description: "page number" }, + size: { + type: "number", + requiresArg: false, + default: 10, + description: "page size", + }, + query: { type: "string", requiresArg: false }, + sort: { type: "array", requiresArg: false, choices: ["id", "createdAt"] }, + order: { + type: "string", + requiresArg: false, + choices: ["asc", "desc"], + default: "asc", + }, + pretty: { type: "boolean", requiresArg: true, description: "pretty print" }, + }); +});