diff --git a/zui/src/transforms/json-schema-to-zui/index.ts b/zui/src/transforms/json-schema-to-zui/index.ts index 80451bdd..791d47ba 100644 --- a/zui/src/transforms/json-schema-to-zui/index.ts +++ b/zui/src/transforms/json-schema-to-zui/index.ts @@ -23,8 +23,9 @@ import { zuiKey } from '../../ui/constants' import { JsonSchema7Type } from '../zui-to-json-schema/parseDef' import { parseSchema } from './parsers/parseSchema' import { ZuiExtensionObject } from '../../ui/types' +import { JSONSchemaExtended } from './types' -const jsonSchemaToZodStr = (schema: any): string => { +export const jsonSchemaToZodStr = (schema: JSONSchemaExtended): string => { return parseSchema(schema, { seen: new Map(), path: [], diff --git a/zui/src/transforms/json-schema-to-zui/json-schema-to-zui.test.ts b/zui/src/transforms/json-schema-to-zui/json-schema-to-zui.test.ts index 78492889..1f5042e4 100644 --- a/zui/src/transforms/json-schema-to-zui/json-schema-to-zui.test.ts +++ b/zui/src/transforms/json-schema-to-zui/json-schema-to-zui.test.ts @@ -1,8 +1,9 @@ import { describe, expect, test } from 'vitest' import { ZodTypeAny, z } from '../../z/index' import { zuiKey } from '../../ui/constants' -import { jsonSchemaToZui, traverseZodDefinitions } from '.' +import { jsonSchemaToZodStr, jsonSchemaToZui, traverseZodDefinitions } from '.' import { zuiToJsonSchema } from '../zui-to-json-schema/zui-extension' +import { JSONSchema7 } from 'json-schema' const testZuiConversion = (zuiObject: ZodTypeAny) => { const jsonSchema = zuiToJsonSchema(zuiObject) @@ -177,4 +178,44 @@ describe('Coercion deserialization', () => { const asZui = jsonSchemaToZui(schema) expect(asZui._def[zuiKey]?.coerce).toStrictEqual(true) }) + + it('should convert unresolved refs to zod refs', async () => { + const schema: JSONSchema7 = { + type: 'object', + properties: { + foo: { $ref: '#Foo' }, + }, + required: ['foo'], + } + const zStr = jsonSchemaToZodStr(schema) + await expect(zStr).toMatchWithoutFormatting(` + z.object({ foo: z.ref("#Foo") }) + `) + }) + + // TODO: decide if we want to support dereferencing; if not, remove this test, otherwise fix it + it.skip('should resolve local refs', async () => { + const schema: JSONSchema7 = { + $defs: { + Foo: { + type: 'string', + enum: ['foo'], + }, + Bar: { + type: 'string', + enum: ['bar'], + }, + }, + type: 'object', + properties: { + foo: { $ref: '#/$defs/Foo' }, + bar: { $ref: '#/$defs/Bar' }, + }, + required: ['foo', 'bar'], + } + const zStr = jsonSchemaToZodStr(schema) + await expect(zStr).toMatchWithoutFormatting(` + z.object({ foo: z.literal("foo"), bar: z.literal("bar") }) + `) + }) }) diff --git a/zui/src/transforms/json-schema-to-zui/parsers/parseRef.ts b/zui/src/transforms/json-schema-to-zui/parsers/parseRef.ts new file mode 100644 index 00000000..360326cf --- /dev/null +++ b/zui/src/transforms/json-schema-to-zui/parsers/parseRef.ts @@ -0,0 +1,5 @@ +import { JsonSchemaObject } from '../types' + +export const parseRef = (schema: JsonSchemaObject & { $ref: string }) => { + return `z.ref('${schema.$ref}')` +} diff --git a/zui/src/transforms/json-schema-to-zui/parsers/parseSchema.ts b/zui/src/transforms/json-schema-to-zui/parsers/parseSchema.ts index 242aa92b..0e41c52a 100644 --- a/zui/src/transforms/json-schema-to-zui/parsers/parseSchema.ts +++ b/zui/src/transforms/json-schema-to-zui/parsers/parseSchema.ts @@ -14,6 +14,7 @@ import { parseObject } from './parseObject' import { parseString } from './parseString' import { parseOneOf } from './parseOneOf' import { parseNullable } from './parseNullable' +import { parseRef } from './parseRef' import { ParserSelector, Refs, JsonSchemaObject, JsonSchema, Serializable, JSONSchemaExtended } from '../types' import { parseDiscriminator } from './parseDiscriminator' @@ -113,6 +114,8 @@ const selectParser: ParserSelector = (schema, refs) => { return parseNull(schema) } else if (its.a.conditional(schema)) { return parseIfThenElse(schema, refs) + } else if (its.a.ref(schema)) { + return parseRef(schema) } else { return parseDefault(schema) } @@ -173,5 +176,6 @@ export const its = { ): x is JsonSchemaObject & { oneOf: JsonSchema[] } => x.oneOf !== undefined, + ref: (x: JsonSchemaObject): x is JsonSchemaObject & { $ref: string } => x.$ref !== undefined, }, }