diff --git a/tooling/json-schema-to-jtd/package.json b/tooling/json-schema-to-jtd/package.json index ce3985fb..68a0cc81 100644 --- a/tooling/json-schema-to-jtd/package.json +++ b/tooling/json-schema-to-jtd/package.json @@ -2,8 +2,13 @@ "name": "json-schema-to-jtd", "version": "0.65.2", "license": "MIT", - "author": { "name": "joshmossas", "url": "https://github.com/joshmossas" }, - "bugs": { "url": "https://github.com/modiimedia/arri/issues" }, + "author": { + "name": "joshmossas", + "url": "https://github.com/joshmossas" + }, + "bugs": { + "url": "https://github.com/modiimedia/arri/issues" + }, "repository": { "type": "git", "url": "https://github.com/modiimedia/arri.git", @@ -13,6 +18,10 @@ "main": "./dist/index.cjs", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", - "files": ["dist"], - "dependencies": { "jtd-utils": "workspace:*" } + "files": [ + "dist" + ], + "dependencies": { + "jtd-utils": "workspace:*" + } } diff --git a/tooling/json-schema-to-jtd/src/index.ts b/tooling/json-schema-to-jtd/src/index.ts index 353d5ff4..44350139 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,31 @@ 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; + if (!subSchema[part]) return {}; + 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"; +}