diff --git a/.changeset/smooth-panthers-drop.md b/.changeset/smooth-panthers-drop.md new file mode 100644 index 00000000..70c34430 --- /dev/null +++ b/.changeset/smooth-panthers-drop.md @@ -0,0 +1,5 @@ +--- +"openapi-zod-client": patch +--- + +export all schemas types when --export-types === true diff --git a/lib/src/template-context.ts b/lib/src/template-context.ts index 48a61ea7..b323a2f6 100644 --- a/lib/src/template-context.ts +++ b/lib/src/template-context.ts @@ -53,15 +53,18 @@ export const getZodClientTemplateContext = ( data.schemas[normalizeString(name)] = wrapWithLazyIfNeeded(name); } - for (const ref in depsGraphs.deepDependencyGraph) { - const isCircular = ref && depsGraphs.deepDependencyGraph[ref]?.has(ref); - const ctx: TsConversionContext = { nodeByRef: {}, resolver: result.resolver, visitedsRefs: {} }; - - // Specifically check isCircular if shouldExportAllTypes is false. Either should cause shouldGenerateType to be true. - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldGenerateType = options?.shouldExportAllTypes || isCircular; - const schemaName = shouldGenerateType ? result.resolver.resolveRef(ref).normalized : undefined; - if (shouldGenerateType && schemaName && !data.types[schemaName]) { + const getTypescript = ({ + ctx, + ref, + shouldGenerateType, + schemaName, + }: { + ctx: TsConversionContext; + ref?: string; + shouldGenerateType: boolean; + schemaName?: string | undefined; + }) => { + if (ref && shouldGenerateType && schemaName && !data.types[schemaName]) { const node = getTypescriptFromOpenApi({ schema: result.resolver.getSchemaByRef(ref), ctx, @@ -92,6 +95,35 @@ export const getZodClientTemplateContext = ( } } } + }; + + for (const schema in data.schemas) { + const ctx: TsConversionContext = { nodeByRef: {}, resolver: result.resolver, visitedsRefs: {} }; + const shouldGenerateType = !!options?.shouldExportAllTypes; + const { ref, normalized } = result.resolver.resolveSchemaName(schema) ?? {}; + const schemaName = shouldGenerateType ? normalized : undefined; + getTypescript({ + ctx, + ref, + shouldGenerateType, + schemaName, + }); + } + + for (const ref in depsGraphs.deepDependencyGraph) { + const isCircular = ref && depsGraphs.deepDependencyGraph[ref]?.has(ref); + const ctx: TsConversionContext = { nodeByRef: {}, resolver: result.resolver, visitedsRefs: {} }; + + // Specifically check isCircular if shouldExportAllTypes is false. Either should cause shouldGenerateType to be true. + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const shouldGenerateType = !!(options?.shouldExportAllTypes || isCircular); + const schemaName = shouldGenerateType ? result.resolver.resolveRef(ref).normalized : undefined; + getTypescript({ + ctx, + ref, + shouldGenerateType, + schemaName, + }); } // TODO diff --git a/lib/tests/allOf-infer-required-only-item.test.ts b/lib/tests/allOf-infer-required-only-item.test.ts index af566267..5aec6ae2 100644 --- a/lib/tests/allOf-infer-required-only-item.test.ts +++ b/lib/tests/allOf-infer-required-only-item.test.ts @@ -73,15 +73,15 @@ test("allOf-infer-required-only-item", async () => { "import { makeApi, Zodios, type ZodiosOptions } from "@zodios/core"; import { z } from "zod"; + type user = Partial<{ + name: string; + email: string; + }>; type userResponse = Partial<{ user: user & { name: string; }; }>; - type user = Partial<{ - name: string; - email: string; - }>; const user: z.ZodType = z .object({ name: z.string(), email: z.string() }) diff --git a/lib/tests/anyOf-behavior.test.ts b/lib/tests/anyOf-behavior.test.ts index d1732c85..b8335558 100644 --- a/lib/tests/anyOf-behavior.test.ts +++ b/lib/tests/anyOf-behavior.test.ts @@ -1,8 +1,6 @@ import { describe, test, expect } from "vitest"; import { z } from "zod"; -import { makeSchemaResolver } from "../src/makeSchemaResolver.js"; import { getZodSchema } from "../src/openApiToZod.js"; -import { asComponentSchema } from "../src/utils.js"; import { CodeMeta } from "../src/CodeMeta.js"; import { OpenAPIObject } from "openapi3-ts"; import { generateZodClientFromOpenAPI } from "../src/generateZodClientFromOpenAPI.js"; diff --git a/lib/tests/enum-null.test.ts b/lib/tests/enum-null.test.ts index ba377504..a634e72c 100644 --- a/lib/tests/enum-null.test.ts +++ b/lib/tests/enum-null.test.ts @@ -12,39 +12,39 @@ test("enum-null", async () => { }, components: { schemas: { - "Null1": { + Null1: { type: "string", enum: [null], }, - "Null2": { + Null2: { type: "string", enum: ["a", null], }, - "Null3": { + Null3: { type: "string", enum: ["a", null], - nullable: true + nullable: true, }, - "Null4": { + Null4: { type: "string", enum: [null], - nullable: true + nullable: true, }, - "Compound": { + Compound: { type: "object", properties: { - "field": { + field: { oneOf: [ - { $ref: '#/components/schemas/Null1' }, - { $ref: '#/components/schemas/Null2' }, - { $ref: '#/components/schemas/Null3' }, - { $ref: '#/components/schemas/Null4' }, - { type: "string" } - ] - } - } - } - } + { $ref: "#/components/schemas/Null1" }, + { $ref: "#/components/schemas/Null2" }, + { $ref: "#/components/schemas/Null3" }, + { $ref: "#/components/schemas/Null4" }, + { type: "string" }, + ], + }, + }, + }, + }, }, paths: { "/sample": { @@ -56,9 +56,9 @@ test("enum-null", async () => { "application/json": { schema: { $ref: "#/components/schemas/Null1", - } - } - } + }, + }, + }, }, "400": { description: "null with a string", @@ -66,9 +66,9 @@ test("enum-null", async () => { "application/json": { schema: { $ref: "#/components/schemas/Null2", - } - } - } + }, + }, + }, }, "401": { description: "null with a string and nullable", @@ -76,9 +76,9 @@ test("enum-null", async () => { "application/json": { schema: { $ref: "#/components/schemas/Null3", - } - } - } + }, + }, + }, }, "402": { description: "null with nullable", @@ -86,9 +86,9 @@ test("enum-null", async () => { "application/json": { schema: { $ref: "#/components/schemas/Null4", - } - } - } + }, + }, + }, }, "403": { description: "object that references null", @@ -96,9 +96,9 @@ test("enum-null", async () => { "application/json": { schema: { $ref: "#/components/schemas/Compound", - } - } - } + }, + }, + }, }, }, }, @@ -106,23 +106,27 @@ test("enum-null", async () => { }, }; - const output = await generateZodClientFromOpenAPI({ disableWriteToFile: true, openApiDoc, options: { shouldExportAllTypes: true } }); + const output = await generateZodClientFromOpenAPI({ + disableWriteToFile: true, + openApiDoc, + options: { shouldExportAllTypes: true }, + }); expect(output).toMatchInlineSnapshot(` "import { makeApi, Zodios, type ZodiosOptions } from "@zodios/core"; import { z } from "zod"; - type Compound = Partial<{ - field: Null1 | Null2 | Null3 | Null4 | string; - }>; type Null1 = null; type Null2 = "a" | null; type Null3 = "a" | null; type Null4 = null; + type Compound = Partial<{ + field: Null1 | Null2 | Null3 | Null4 | string; + }>; - const Null1 = z.literal(null); - const Null2 = z.enum(["a", null]); - const Null3 = z.enum(["a", null]); - const Null4 = z.literal(null); + const Null1: z.ZodType = z.literal(null); + const Null2: z.ZodType = z.enum(["a", null]); + const Null3: z.ZodType = z.enum(["a", null]); + const Null4: z.ZodType = z.literal(null); const Compound: z.ZodType = z .object({ field: z.union([Null1, Null2, Null3, Null4, z.string()]) }) .partial() diff --git a/lib/tests/export-all-types.test.ts b/lib/tests/export-all-types.test.ts index 6169d27f..bfb62d43 100644 --- a/lib/tests/export-all-types.test.ts +++ b/lib/tests/export-all-types.test.ts @@ -152,9 +152,12 @@ describe("export-all-types", () => { endpointsGroups: {}, emittedType: { Author: true, + Features: true, + Id: true, Settings: true, Playlist: true, Song: true, + Title: true, }, options: { withAlias: false, @@ -173,12 +176,13 @@ describe("export-all-types", () => { "import { makeApi, Zodios, type ZodiosOptions } from "@zodios/core"; import { z } from "zod"; - type Playlist = Partial<{ - name: string; - author: Author; - songs: Array; - }> & - Settings; + type Title = string; + type Id = number; + type Features = Array; + type Settings = Partial<{ + theme_color: string; + features: Features; + }>; type Author = Partial<{ name: (string | null) | number | null; title: Title; @@ -186,21 +190,20 @@ describe("export-all-types", () => { mail: string; settings: Settings; }>; - type Title = string; - type Id = number; - type Settings = Partial<{ - theme_color: string; - features: Features; - }>; - type Features = Array; type Song = Partial<{ name: string; duration: number; }>; + type Playlist = Partial<{ + name: string; + author: Author; + songs: Array; + }> & + Settings; - const Title = z.string(); - const Id = z.number(); - const Features = z.array(z.string()); + const Title: z.ZodType = z.string(); + const Id: z.ZodType<Id> = z.number(); + const Features: z.ZodType<Features> = z.array(z.string()); const Settings: z.ZodType<Settings> = z .object({ theme_color: z.string(), features: Features.min(1) }) .partial() diff --git a/lib/tests/jsdoc.test.ts b/lib/tests/jsdoc.test.ts index ba91812e..301efa0d 100644 --- a/lib/tests/jsdoc.test.ts +++ b/lib/tests/jsdoc.test.ts @@ -98,6 +98,9 @@ test("jsdoc", async () => { expect(output).toMatchInlineSnapshot(`"import { makeApi, Zodios, type ZodiosOptions } from "@zodios/core"; import { z } from "zod"; +type SimpleObject = Partial<{ + str: string; +}>; type ComplexObject = Partial<{ /** * A string with example tag @@ -156,9 +159,6 @@ type ComplexObject = Partial<{ */ refArray: Array<SimpleObject>; }>; -type SimpleObject = Partial<{ - str: string; -}>; const SimpleObject: z.ZodType<SimpleObject> = z .object({ str: z.string() }) diff --git a/lib/tests/recursive-schema.test.ts b/lib/tests/recursive-schema.test.ts index 5339a54b..c7a5c93c 100644 --- a/lib/tests/recursive-schema.test.ts +++ b/lib/tests/recursive-schema.test.ts @@ -321,7 +321,9 @@ describe("recursive-schema", () => { }, ctx, }) - ).toMatchInlineSnapshot('"z.object({ recursiveUser: UserWithFriends, basic: z.number() }).partial().passthrough()"'); + ).toMatchInlineSnapshot( + '"z.object({ recursiveUser: UserWithFriends, basic: z.number() }).partial().passthrough()"' + ); expect(ctx).toMatchInlineSnapshot(` { "resolver": { @@ -346,7 +348,6 @@ describe("recursive-schema", () => { someProp: { type: "boolean" }, }, }); - expect(getZodiosEndpointDefinitionList(openApiDoc)).toMatchInlineSnapshot(` { "deepDependencyGraph": {