Skip to content

Commit

Permalink
Merge branch 'master' of github.com:samchungy/zod-openapi
Browse files Browse the repository at this point in the history
  • Loading branch information
samchungy committed Dec 24, 2024
2 parents ae37c0f + c9ec881 commit 865c0a3
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 36 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zod-openapi",
"version": "4.2.0",
"version": "4.2.1",
"description": "Convert Zod Schemas to OpenAPI v3.x documentation",
"keywords": [
"typescript",
Expand Down Expand Up @@ -97,6 +97,6 @@
"entryPoint": "src/index.ts",
"template": "oss-npm-package",
"type": "package",
"version": "9.0.0-main-20240928013837"
"version": "9.1.0"
}
}
23 changes: 14 additions & 9 deletions src/create/schema/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ZodType, ZodTypeDef } from 'zod';

import { currentSymbol, previousSymbol } from '../../extendZodTypes';
import type { oas30, oas31 } from '../../openapi3-ts/dist';
import {
type ComponentsObject,
Expand Down Expand Up @@ -220,18 +221,22 @@ export const createSchemaOrRef = <
return existingRef;
}

const previous = zodSchema._def.zodOpenApi?.previous
? (createSchemaOrRef(zodSchema._def.zodOpenApi.previous, state, true) as
| RefObject
| undefined)
const previous = zodSchema._def.zodOpenApi?.[previousSymbol]
? (createSchemaOrRef(
zodSchema._def.zodOpenApi[previousSymbol],
state,
true,
) as RefObject | undefined)
: undefined;

const current =
zodSchema._def.zodOpenApi?.current &&
zodSchema._def.zodOpenApi.current !== zodSchema
? (createSchemaOrRef(zodSchema._def.zodOpenApi.current, state, true) as
| RefObject
| undefined)
zodSchema._def.zodOpenApi?.[currentSymbol] &&
zodSchema._def.zodOpenApi[currentSymbol] !== zodSchema
? (createSchemaOrRef(
zodSchema._def.zodOpenApi[currentSymbol],
state,
true,
) as RefObject | undefined)
: undefined;

const ref = zodSchema._def.zodOpenApi?.openapi?.ref ?? component?.ref;
Expand Down
15 changes: 11 additions & 4 deletions src/create/schema/metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,10 @@ describe('enhanceWithMetadata', () => {
"coerce": false,
"typeName": "ZodString",
"zodOpenApi": {
"current": [Circular],
"openapi": {
"ref": "foo",
},
Symbol(current): [Circular],
},
},
"and": [Function],
Expand Down Expand Up @@ -339,10 +339,10 @@ describe('enhanceWithMetadata', () => {
"coerce": false,
"typeName": "ZodString",
"zodOpenApi": {
"current": [Circular],
"openapi": {
"ref": "foo",
},
Symbol(current): [Circular],
},
},
"and": [Function],
Expand Down Expand Up @@ -604,10 +604,10 @@ describe('enhanceWithMetadata', () => {
"coerce": false,
"typeName": "ZodString",
"zodOpenApi": {
"current": [Circular],
"openapi": {
"ref": "foo",
},
Symbol(current): [Circular],
},
},
"and": [Function],
Expand Down Expand Up @@ -690,10 +690,10 @@ describe('enhanceWithMetadata', () => {
"coerce": false,
"typeName": "ZodString",
"zodOpenApi": {
"current": [Circular],
"openapi": {
"ref": "foo",
},
Symbol(current): [Circular],
},
},
"and": [Function],
Expand Down Expand Up @@ -934,4 +934,11 @@ describe('enhanceWithMetadata', () => {
]
`);
});

it('does not fail JSON serialization', () => {
const FooSchema = z.string().openapi({ ref: 'foo' });
expect(() => {
JSON.stringify(FooSchema);
}).not.toThrow();
});
});
1 change: 0 additions & 1 deletion src/entries/extend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ import { extendZodWithOpenApi } from '../extendZod';

extendZodWithOpenApi(z);

// eslint-disable-next-line @typescript-eslint/consistent-type-exports
export * from '../extendZodTypes'; // compatibility with < TS 5.0 as the export type * syntax is not supported
12 changes: 7 additions & 5 deletions src/extendZod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { z } from 'zod';

import { createSchema } from './create/schema/single';
import { extendZodWithOpenApi } from './extendZod';
import { currentSymbol, previousSymbol } from './extendZodTypes';

extendZodWithOpenApi(z);

Expand All @@ -22,16 +23,17 @@ describe('extendZodWithOpenApi', () => {
expect(a._def.zodOpenApi?.openapi?.description).toBe('test');
expect(b._def.zodOpenApi?.openapi?.description).toBe('test2');
expect(
b._def.zodOpenApi?.previous?._def.zodOpenApi?.openapi?.description,
b._def.zodOpenApi?.[previousSymbol]?._def.zodOpenApi?.openapi
?.description,
).toBe('test');
});

it('sets current metadata when a schema is used again', () => {
const a = z.string().openapi({ ref: 'a' });
const b = a.uuid();

expect(a._def.zodOpenApi?.current).toBe(a);
expect(b._def.zodOpenApi?.current).toBe(a);
expect(a._def.zodOpenApi?.[currentSymbol]).toBe(a);
expect(b._def.zodOpenApi?.[currentSymbol]).toBe(a);
});

it('adds ._def.zodOpenApi.openapi fields to a zod type', () => {
Expand All @@ -53,7 +55,7 @@ describe('extendZodWithOpenApi', () => {
const b = a.extend({ b: z.string() });

expect(a._def.zodOpenApi?.openapi?.ref).toBe('a');
expect(b._def.zodOpenApi?.previous).toStrictEqual(a);
expect(b._def.zodOpenApi?.[previousSymbol]).toStrictEqual(a);
});

it('removes previous openapi ref for an object when .omit or .pick is used', () => {
Expand All @@ -74,7 +76,7 @@ describe('extendZodWithOpenApi', () => {
});

expect(a._def.zodOpenApi?.openapi?.ref).toBe('a');
expect(b._def.zodOpenApi?.previous).toStrictEqual(a);
expect(b._def.zodOpenApi?.[previousSymbol]).toStrictEqual(a);
expect(c._def.zodOpenApi?.openapi).toEqual({});
expect(d._def.zodOpenApi?.openapi).toEqual({});

Expand Down
24 changes: 12 additions & 12 deletions src/extendZod.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ZodRawShape, ZodTypeDef, z } from 'zod';

import './extendZodTypes';
import { currentSymbol, previousSymbol } from './extendZodTypes';

type ZodOpenApiMetadataDef = NonNullable<ZodTypeDef['zodOpenApi']>;
type ZodOpenApiMetadata = ZodOpenApiMetadataDef['openapi'];
Expand Down Expand Up @@ -42,11 +42,11 @@ export function extendZodWithOpenApi(zod: typeof z) {
});

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
result._def.zodOpenApi.current = result;
result._def.zodOpenApi[currentSymbol] = result;

if (zodOpenApi) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
result._def.zodOpenApi.previous = this;
result._def.zodOpenApi[previousSymbol] = this;
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-return
Expand All @@ -63,13 +63,13 @@ export function extendZodWithOpenApi(zod: typeof z) {
if (def.zodOpenApi) {
const cloned = { ...def.zodOpenApi };
cloned.openapi = mergeOpenApi({ description: args[0] }, cloned.openapi);
cloned.previous = this;
cloned.current = result;
cloned[previousSymbol] = this;
cloned[currentSymbol] = result;
def.zodOpenApi = cloned;
} else {
def.zodOpenApi = {
openapi: { description: args[0] },
current: result,
[currentSymbol]: result,
};
}

Expand All @@ -88,11 +88,11 @@ export function extendZodWithOpenApi(zod: typeof z) {
if (zodOpenApi) {
const cloned = { ...zodOpenApi };
cloned.openapi = mergeOpenApi({}, cloned.openapi);
cloned.previous = this;
cloned[previousSymbol] = this;
extendResult._def.zodOpenApi = cloned;
} else {
extendResult._def.zodOpenApi = {
previous: this,
[previousSymbol]: this,
};
}

Expand All @@ -112,8 +112,8 @@ export function extendZodWithOpenApi(zod: typeof z) {
if (zodOpenApi) {
const cloned = { ...zodOpenApi };
cloned.openapi = mergeOpenApi({}, cloned.openapi);
delete cloned.previous;
delete cloned.current;
delete cloned[previousSymbol];
delete cloned[currentSymbol];
omitResult._def.zodOpenApi = cloned;
}

Expand All @@ -133,8 +133,8 @@ export function extendZodWithOpenApi(zod: typeof z) {
if (zodOpenApi) {
const cloned = { ...zodOpenApi };
cloned.openapi = mergeOpenApi({}, cloned.openapi);
delete cloned.previous;
delete cloned.current;
delete cloned[previousSymbol];
delete cloned[currentSymbol];
pickResult._def.zodOpenApi = cloned;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any
Expand Down
7 changes: 5 additions & 2 deletions src/extendZodTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ type SchemaObject = oas30.SchemaObject & oas31.SchemaObject;

type ReplaceDate<T> = T extends Date ? Date | string : T;

export const currentSymbol = Symbol('current');
export const previousSymbol = Symbol('previous');

/**
* zod-openapi metadata
*/
Expand Down Expand Up @@ -77,12 +80,12 @@ interface ZodOpenApiMetadataDef {
/**
* Used to keep track of the Zod Schema had `.openapi` called on it
*/
current?: ZodTypeAny;
[currentSymbol]?: ZodTypeAny;
/**
* Used to keep track of the previous Zod Schema that had `.openapi` called on it if another `.openapi` is called.
* This can also be present when .extend is called on an object.
*/
previous?: ZodTypeAny;
[previousSymbol]?: ZodTypeAny;
}

interface ZodOpenApiExtendMetadata {
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
"removeComments": false,
"target": "ES2022"
},
"exclude": ["lib*/**/*", "crackle.config.ts"],
"exclude": ["lib*/**/*", "crackle.config.ts", "dist", "api", "extend"],
"extends": "skuba/config/tsconfig.json"
}

0 comments on commit 865c0a3

Please sign in to comment.