UNPKG

@baqhub/cli

Version:

The official command line interface for the BAQ federated app platform.

168 lines (167 loc) 7.6 kB
import { isDefined, unreachable, } from "@baqhub/sdk"; import camelCase from "lodash/camelCase.js"; import isEmpty from "lodash/isEmpty.js"; import pickBy from "lodash/pickBy.js"; import { pascalCase } from "../helpers/case.js"; import { toPropNamespaceName, toRefNamespaceName, toSchemaNamespaceName, } from "./schemaToTs.js"; function enumOrDefault(enumValues, defaultType, options) { if (enumValues && enumValues.length > 0) { const types = enumValues.map(v => `IO.literal(${JSON.stringify(v)})`); return types.length > 1 ? `IO.union([${types.join(", ")}])` : types[0]; } if (!options || isEmpty(pickBy(options, isDefined))) { return `${defaultType}()`; } return `${defaultType}(${JSON.stringify(options)})`; } function valuesOrDefault(values, defaultType) { if (values && values.length > 0) { return values.length > 1 ? `IO.union([${values.join(", ")}])` : values[0]; } return defaultType; } export function schemaToIo(schema) { function schemaToIoInternal(path, schema) { // Definitions. const definitions = Object.entries(schema.definitions || {}) .map(([key, subSchema]) => { const typeName = `R${toRefNamespaceName(key)}`; const ioName = pascalCase(key); const newPath = `${path}${toRefNamespaceName(key)}.`; const subSchemaIo = schemaToIoInternal(newPath, subSchema); return `const ${typeName}: RType<${newPath}Type> = IO.recursion("${ioName}", () => ${subSchemaIo})`; }) .join("\n\n"); // Schema. const returnType = (() => { switch (schema.type) { case "object": { const mapProperty = (key, subSchema) => { if (subSchema.removed) { return undefined; } const newPath = `${path}${toPropNamespaceName(key)}.`; return `${camelCase(key)}: ${schemaToIoInternal(newPath, subSchema)}`; }; const requiredProperties = Object.entries(schema.properties) .map(([key, subSchema]) => { if (subSchema.optional) { return undefined; } return mapProperty(key, subSchema); }) .filter(isDefined) .join(","); const optionalProperties = Object.entries(schema.properties) .map(([key, subSchema]) => { if (!subSchema.optional) { return undefined; } return mapProperty(key, subSchema); }) .filter(isDefined) .join(","); if (optionalProperties.length === 0) { return `IO.object({${requiredProperties}})`; } if (requiredProperties.length === 0) { return `IO.partialObject({${optionalProperties}})`; } return `SchemaIO.object({${requiredProperties}}, {${optionalProperties}})`; } case "array": { const newPath = `${path}Items.`; const itemsSchema = schemaToIoInternal(newPath, schema.items); const options = { minItems: schema.minItems, maxItems: schema.maxItems, distinctItems: schema.distinctItems, }; if (isEmpty(pickBy(options, isDefined))) { return `SchemaIO.array(${itemsSchema})`; } return `SchemaIO.array(${itemsSchema}, ${JSON.stringify(options)})`; } case "ref": return `R${toRefNamespaceName(schema.ref)}`; case "ref_name": return "IO.string"; case "self": case "schema": return "IO.unknown"; case "never": return "IO.never"; case "union": { const func = schema.strict ? "IO.exclusiveUnion" : "IO.union"; const schemas = schema.schemas .map((subSchema, index) => { const newPath = `${path}${toSchemaNamespaceName(index)}.`; return schemaToIoInternal(newPath, subSchema); }) .join(", "); return `${func}([${schemas}])`; } case "intersection": { const schemas = schema.schemas .map((subSchema, index) => { const newPath = `${path}${toSchemaNamespaceName(index)}.`; return schemaToIoInternal(newPath, subSchema); }) .join(", "); return `IO.intersection([${schemas}])`; } case "map": { const newPath = `${path}Values.`; const valuesSchema = schemaToIoInternal(newPath, schema.values); return `IO.record(IO.string, ${valuesSchema})`; } case "boolean": return enumOrDefault(schema.enum, "SchemaIO.boolean"); case "string": { const options = { minLength: schema.minLength, maxLength: schema.maxLength, }; return enumOrDefault(schema.enum, `SchemaIO.string`, options); } case "int": { const options = { min: schema.min, max: schema.max, }; return enumOrDefault(schema.enum, `SchemaIO.int`, options); } case "number": { const options = { min: schema.min, max: schema.max, }; return enumOrDefault(schema.enum, `SchemaIO.number`, options); } case "tag_link": { const tagValues = schema.enum?.map(v => `TagLink.io("${v}")`); return valuesOrDefault(tagValues, "AnyTagLink.io()"); } case "blob_link": { const types = schema.contentTypes?.map(v => `BlobLink.io("${v}")`); return valuesOrDefault(types, "AnyBlobLink.io()"); } case "entity_link": return "EntityLink.io()"; case "record_link": { const recordTypes = schema.recordTypes?.map(v => `RecordLink.ioOf("${v.entity}", "${v.recordId}")`); return valuesOrDefault(recordTypes, "AnyRecordLink.io()"); } case "version_link": throw new Error("Not supported."); default: unreachable(schema); } })(); if (!definitions) { return returnType; } return `(() => {${definitions}\n\nreturn ${returnType};})()`; } return schemaToIoInternal("", schema); }