UNPKG

schema2typebox

Version:

Creates typebox code from JSON schemas

256 lines (248 loc) 9.05 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const globals_1 = require("@jest/globals"); const Array_1 = require("fp-ts/Array"); const node_fs_1 = __importDefault(require("node:fs")); const shelljs_1 = __importDefault(require("shelljs")); const programmatic_usage_1 = require("../src/programmatic-usage"); const util_1 = require("./util"); const SHELLJS_RETURN_CODE_OK = 0; (0, globals_1.describe)("programmatic usage - when running the programmatic usage", () => { (0, globals_1.test)("generated typebox names are based on title attribute", async () => { const dummySchema = ` { "title": "Contract", "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"] } `; const expectedTypebox = programmatic_usage_1.addCommentThatCodeIsGenerated.run(` import { Static, Type } from "@sinclair/typebox"; export type Contract = Static<typeof Contract>; export const Contract = Type.Object({name: Type.String()}, { $id: "Contract" }); `); await (0, util_1.expectEqualIgnoreFormatting)(await (0, programmatic_usage_1.schema2typebox)({ input: dummySchema }), expectedTypebox); }); (0, globals_1.describe)("when working with files containing $refs (sanity check of refparser library)", () => { (0, globals_1.test)("object with $ref pointing to external files in relative path", async () => { const dummySchema = ` { "title": "Contract", "type": "object", "properties": { "person": { "$ref": "person.json" }, "status": { "$ref": "status.json" } }, "required": ["person"] } `; const referencedPersonSchema = ` { "title": "Person", "type": "object", "properties": { "name": { "type": "string", "maxLength": 100 }, "age": { "type": "number", "minimum": 18 } }, "required": ["name", "age"] } `; const referencedStatusSchema = ` { "title": "Status", "enum": ["unknown", "accepted", "denied"] } `; const expectedTypebox = programmatic_usage_1.addCommentThatCodeIsGenerated.run(` import { Static, Type } from "@sinclair/typebox"; export type Contract = Static<typeof Contract>; export const Contract = Type.Object({ person: Type.Object({ name: Type.String({ maxLength: 100 }), age: Type.Number({ minimum: 18 }), }), status: Type.Optional( Type.Union([ Type.Literal("unknown"), Type.Literal("accepted"), Type.Literal("denied"), ]) ), }, { $id: "Contract" }); `); const inputPaths = ["person.json", "status.json"].flatMap((currItem) => { return (0, util_1.buildOsIndependentPath)([__dirname, "..", currItem]); }); (0, Array_1.zip)(inputPaths, [referencedPersonSchema, referencedStatusSchema]).map(([fileName, data]) => { return node_fs_1.default.writeFileSync(fileName, data, undefined); }); await (0, util_1.expectEqualIgnoreFormatting)(await (0, programmatic_usage_1.schema2typebox)({ input: dummySchema }), expectedTypebox); // cleanup generated files const { code: returnCode } = shelljs_1.default.rm("-f", inputPaths); expect(returnCode).toBe(SHELLJS_RETURN_CODE_OK); }); (0, globals_1.test)("object with $ref inside anyOf", async () => { const dummySchema = { anyOf: [{ $ref: "./cat.json" }, { $ref: "./dog.json" }], }; const referencedCatSchema = { title: "Cat", type: "object", properties: { type: { type: "string", const: "cat", }, name: { type: "string", maxLength: 100, }, }, required: ["type", "name"], }; const referencedDogSchema = { title: "Dog", type: "object", properties: { type: { type: "string", const: "dog", }, name: { type: "string", maxLength: 100, }, }, required: ["type", "name"], }; const expectedTypebox = programmatic_usage_1.addCommentThatCodeIsGenerated.run(` import { Static, Type } from "@sinclair/typebox"; export type T = Static<typeof T>; export const T = Type.Union([ Type.Object({ type: Type.Literal("cat"), name: Type.String({ maxLength: 100 }), }), Type.Object({ type: Type.Literal("dog"), name: Type.String({ maxLength: 100 }), }), ], { $id: "T" }); `); const inputPaths = ["cat.json", "dog.json"].flatMap((currItem) => { return (0, util_1.buildOsIndependentPath)([__dirname, "..", currItem]); }); (0, Array_1.zip)(inputPaths, [referencedCatSchema, referencedDogSchema]).map(([fileName, data]) => { return node_fs_1.default.writeFileSync(fileName, JSON.stringify(data), undefined); }); await (0, util_1.expectEqualIgnoreFormatting)(await (0, programmatic_usage_1.schema2typebox)({ input: JSON.stringify(dummySchema) }), expectedTypebox); // cleanup generated files const { code: returnCode } = shelljs_1.default.rm("-f", inputPaths); expect(returnCode).toBe(SHELLJS_RETURN_CODE_OK); }); // NOTE: This test might break if github adapts their links to raw github user // content. (0, globals_1.test)("object with $ref to remote files", async () => { const dummySchema = { anyOf: [ { $ref: "https://raw.githubusercontent.com/xddq/schema2typebox/main/examples/ref-to-remote-files/cat.json", }, { $ref: "https://raw.githubusercontent.com/xddq/schema2typebox/main/examples/ref-to-remote-files/dog.json", }, ], }; const expectedTypebox = programmatic_usage_1.addCommentThatCodeIsGenerated.run(` import { Static, Type } from "@sinclair/typebox"; export type T = Static<typeof T>; export const T = Type.Union( [ Type.Object({ type: Type.Literal("cat"), name: Type.String({ maxLength: 100 }), }), Type.Object({ type: Type.Literal("dog"), name: Type.String({ maxLength: 100 }), }), ], { $id: "T" } ); `); await (0, util_1.expectEqualIgnoreFormatting)(await (0, programmatic_usage_1.schema2typebox)({ input: JSON.stringify(dummySchema) }), expectedTypebox); }); }); (0, globals_1.test)("object with oneOf generates custom typebox TypeRegistry code", async () => { const dummySchema = ` { "type": "object", "properties": { "a": { "oneOf": [ { "type": "string" }, { "type": "number" } ] } }, "required": ["a"] } `; const expectedTypebox = programmatic_usage_1.addCommentThatCodeIsGenerated.run(` import { Kind, SchemaOptions, Static, TSchema, TUnion, Type, TypeRegistry, } from "@sinclair/typebox"; import { Value } from "@sinclair/typebox/value"; TypeRegistry.Set( "ExtendedOneOf", (schema: any, value) => 1 === schema.oneOf.reduce( (acc: number, schema: any) => acc + (Value.Check(schema, value) ? 1 : 0), 0 ) ); const OneOf = <T extends TSchema[]>( oneOf: [...T], options: SchemaOptions = {} ) => Type.Unsafe<Static<TUnion<T>>>({ ...options, [Kind]: "ExtendedOneOf", oneOf }); export type T = Static<typeof T>; export const T = Type.Object( { a: OneOf([Type.String(), Type.Number()]) }, { $id: "T" } ); `); await (0, util_1.expectEqualIgnoreFormatting)(await (0, programmatic_usage_1.schema2typebox)({ input: dummySchema }), expectedTypebox); }); }); //# sourceMappingURL=programmatic-usage.spec.js.map