From 5b976fd01b078966539cde79c4b44089db4120e8 Mon Sep 17 00:00:00 2001 From: Joshua Sosso Date: Mon, 28 Oct 2024 11:46:52 -0500 Subject: [PATCH 1/3] add ref support --- tooling/json-schema-to-jtd/src/index.ts | 74 ++++++++++++++++++++---- tooling/json-schema-to-jtd/src/models.ts | 13 ++++- vitest.workspace.js | 2 +- 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/tooling/json-schema-to-jtd/src/index.ts b/tooling/json-schema-to-jtd/src/index.ts index 353d5ff4..722c47d8 100644 --- a/tooling/json-schema-to-jtd/src/index.ts +++ b/tooling/json-schema-to-jtd/src/index.ts @@ -12,31 +12,44 @@ import { isJsonSchemaEnum, isJsonSchemaObject, isJsonSchemaRecord, + isJsonSchemaRef, isJsonSchemaScalarType, type JsonSchemaArray, type JsonSchemaEnum, type JsonSchemaObject, type JsonSchemaRecord, + JsonSchemaRef, type JsonSchemaScalarType, type JsonSchemaType, } from "./models"; export * from "./models"; -export function jsonSchemaToJtdSchema(input: JsonSchemaType): Schema { +export interface JsonSchemaContext { + parentRefs: string[]; + rootSchema?: any; +} + +export function jsonSchemaToJtdSchema( + input: JsonSchemaType, + context: JsonSchemaContext = { parentRefs: [] }, +): Schema { if (isJsonSchemaScalarType(input)) { - return jsonSchemaScalarToJtdScalar(input); + return jsonSchemaScalarToJtdScalar(input, context); } if (isJsonSchemaEnum(input)) { - return jsonSchemaEnumToJtdEnum(input); + return jsonSchemaEnumToJtdEnum(input, context); } if (isJsonSchemaObject(input)) { - return jsonSchemaObjectToJtdObject(input); + return jsonSchemaObjectToJtdObject(input, context); } if (isJsonSchemaArray(input)) { - return jsonSchemaArrayToJtdArray(input); + return jsonSchemaArrayToJtdArray(input, context); } if (isJsonSchemaRecord(input)) { - return jsonSchemaRecordToJtdRecord(input); + return jsonSchemaRecordToJtdRecord(input, context); + } + if (isJsonSchemaRef(input)) { + return jsonSchemaRefToJtdRef(input, context); } console.warn( @@ -53,7 +66,10 @@ export function jsonSchemaToJtdSchema(input: JsonSchemaType): Schema { } satisfies SchemaFormEmpty; } -export function jsonSchemaEnumToJtdEnum(input: JsonSchemaEnum): Schema { +export function jsonSchemaEnumToJtdEnum( + input: JsonSchemaEnum, + _: JsonSchemaContext, +): Schema { const enumTypes = input.anyOf.map((val) => val.type); const isNotStringEnum = enumTypes.includes("integer") || enumTypes.includes("number"); @@ -76,6 +92,7 @@ export function jsonSchemaEnumToJtdEnum(input: JsonSchemaEnum): Schema { export function jsonSchemaScalarToJtdScalar( input: JsonSchemaScalarType, + _: JsonSchemaContext, ): Schema { const meta = { id: input.$id ?? input.title, @@ -113,7 +130,10 @@ export function jsonSchemaScalarToJtdScalar( } } -export function jsonSchemaObjectToJtdObject(input: JsonSchemaObject): Schema { +export function jsonSchemaObjectToJtdObject( + input: JsonSchemaObject, + _: JsonSchemaContext, +): Schema { const result: SchemaFormProperties = { properties: {}, strict: @@ -145,7 +165,10 @@ export function jsonSchemaObjectToJtdObject(input: JsonSchemaObject): Schema { return result; } -export function jsonSchemaArrayToJtdArray(input: JsonSchemaArray) { +export function jsonSchemaArrayToJtdArray( + input: JsonSchemaArray, + _: JsonSchemaContext, +) { const result: SchemaFormElements = { elements: jsonSchemaToJtdSchema(input.items), metadata: { @@ -156,7 +179,10 @@ export function jsonSchemaArrayToJtdArray(input: JsonSchemaArray) { return result; } -export function jsonSchemaRecordToJtdRecord(input: JsonSchemaRecord): Schema { +export function jsonSchemaRecordToJtdRecord( + input: JsonSchemaRecord, + _: JsonSchemaContext, +): Schema { const types: Schema[] = []; Object.keys(input.patternProperties).forEach((key) => { const pattern = input.patternProperties[key]; @@ -172,7 +198,6 @@ export function jsonSchemaRecordToJtdRecord(input: JsonSchemaRecord): Schema { return {}; } const result: SchemaFormValues = { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion values: types[0]!, metadata: { id: input.$id ?? input.title, @@ -181,3 +206,30 @@ export function jsonSchemaRecordToJtdRecord(input: JsonSchemaRecord): Schema { }; return result; } + +export function jsonSchemaRefToJtdRef( + input: JsonSchemaRef, + context: JsonSchemaContext, +): Schema { + if (context.parentRefs.includes(input.$ref)) { + const parts = input.$ref.split("/"); + const refId = parts[parts.length - 1]; + if (!refId) return {}; + return { + ref: refId, + }; + } + const parts = input.$ref.split("/"); + let subSchema = context.rootSchema ?? {}; + for (const part of parts) { + if (part === "#") continue; + subSchema = subSchema[part]; + } + const r = context.parentRefs; + r.push(input.$ref); + const result = jsonSchemaToJtdSchema(subSchema, { + ...context, + parentRefs: r, + }); + return result; +} diff --git a/tooling/json-schema-to-jtd/src/models.ts b/tooling/json-schema-to-jtd/src/models.ts index d8c83d00..02b27f42 100644 --- a/tooling/json-schema-to-jtd/src/models.ts +++ b/tooling/json-schema-to-jtd/src/models.ts @@ -42,7 +42,7 @@ export function isJsonSchemaScalarType( if (!("type" in input)) { return false; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + return JsonSchemaScalarTypeValues.includes(input.type as any); } @@ -56,7 +56,7 @@ export function isJsonSchemaNullType(input: any): input is JsonSchemaNullType { if (!("type" in input)) { return false; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + return JsonSchemaNullTypeValues.includes(input.type); } @@ -145,3 +145,12 @@ export function isJsonSchemaEnum(input: any): input is JsonSchemaEnum { } return true; } +export interface JsonSchemaRef extends JsonSchemaTypeBase { + $ref: string; +} +export function isJsonSchemaRef(input: unknown): input is JsonSchemaRef { + if (typeof input !== "object" || !input) { + return false; + } + return "$ref" in input && typeof input.$ref === "string"; +} diff --git a/vitest.workspace.js b/vitest.workspace.js index b62bb907..3eb2e856 100644 --- a/vitest.workspace.js +++ b/vitest.workspace.js @@ -13,5 +13,5 @@ export default defineWorkspace([ "./tooling/jtd-utils/vite.config.ts", "./tooling/schema/vite.config.ts", "./tooling/schema-typebox-adapter/vite.config.ts", - "./tests/clients/client-typescript/vite.config.ts", + "./tests/clients/ts/vite.config.ts", ]); From 2b82c03f8a9ba303ded5238bd093d8eb85d32cfc Mon Sep 17 00:00:00 2001 From: Joshua Sosso Date: Mon, 28 Oct 2024 11:48:38 -0500 Subject: [PATCH 2/3] add beta tag --- tooling/json-schema-to-jtd/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/json-schema-to-jtd/package.json b/tooling/json-schema-to-jtd/package.json index e8eff754..411d6208 100644 --- a/tooling/json-schema-to-jtd/package.json +++ b/tooling/json-schema-to-jtd/package.json @@ -1,6 +1,6 @@ { "name": "json-schema-to-jtd", - "version": "0.63.2", + "version": "0.63.3-beta.1", "license": "MIT", "author": { "name": "joshmossas", From 5b9101f80d28425f24ac280f354ca8b96bde1c19 Mon Sep 17 00:00:00 2001 From: Joshua Sosso Date: Mon, 28 Oct 2024 12:07:12 -0500 Subject: [PATCH 3/3] quick fix --- tooling/json-schema-to-jtd/package.json | 2 +- tooling/json-schema-to-jtd/src/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tooling/json-schema-to-jtd/package.json b/tooling/json-schema-to-jtd/package.json index 411d6208..0af95d1e 100644 --- a/tooling/json-schema-to-jtd/package.json +++ b/tooling/json-schema-to-jtd/package.json @@ -1,6 +1,6 @@ { "name": "json-schema-to-jtd", - "version": "0.63.3-beta.1", + "version": "0.63.3-beta.2", "license": "MIT", "author": { "name": "joshmossas", diff --git a/tooling/json-schema-to-jtd/src/index.ts b/tooling/json-schema-to-jtd/src/index.ts index 722c47d8..44350139 100644 --- a/tooling/json-schema-to-jtd/src/index.ts +++ b/tooling/json-schema-to-jtd/src/index.ts @@ -223,6 +223,7 @@ export function jsonSchemaRefToJtdRef( let subSchema = context.rootSchema ?? {}; for (const part of parts) { if (part === "#") continue; + if (!subSchema[part]) return {}; subSchema = subSchema[part]; } const r = context.parentRefs;