diff --git a/.changeset/four-worms-leave.md b/.changeset/four-worms-leave.md new file mode 100644 index 000000000..d2b9c9d47 --- /dev/null +++ b/.changeset/four-worms-leave.md @@ -0,0 +1,5 @@ +--- +"openapi-typescript": patch +--- + +Add schema to postTransform options diff --git a/docs/6.x/node.md b/docs/6.x/node.md index 54394d740..9a5909aaa 100644 --- a/docs/6.x/node.md +++ b/docs/6.x/node.md @@ -122,6 +122,16 @@ Resultant diff with correctly-typed `file` property: + file?: Blob; ``` -Any [Schema Object](https://spec.openapis.org/oas/latest.html#schema-object) present in your schema will be run through this formatter (even remote ones!). Also be sure to check the `metadata` parameter for additional context that may be helpful. +#### transform / postTransform metadata -There are many other uses for this besides checking `format`. Because this must return a **string** you can produce any arbitrary TypeScript code you’d like (even your own custom types). +Any [Schema Object](https://spec.openapis.org/oas/latest.html#schema-object) present in your schema will be run through `transform`, prior to its conversion to a TypeScript AST node, and `postTransform` after its conversion, including remote schemas! + +The `metadata` parameter present on both `transform` and `postTransform` has additional context that may be helpful. + +| Property | Description | +|-|-| +| `metadata.path` | A [`$ref`](https://json-schema.org/understanding-json-schema/structuring#dollarref) URI string, pointing to the current schema object | +| `metadata.schema` | The schema object being transformed (only present for `postTransform`) | +| `metadata.ctx` | The GlobalContext object, containing + +There are many other uses for this besides checking `format`. Because `tranform` may return a **string** you can produce any arbitrary TypeScript code you’d like (even your own custom types). diff --git a/packages/openapi-typescript/src/transform/components-object.ts b/packages/openapi-typescript/src/transform/components-object.ts index 47a5a8f71..e5c100d26 100644 --- a/packages/openapi-typescript/src/transform/components-object.ts +++ b/packages/openapi-typescript/src/transform/components-object.ts @@ -34,16 +34,18 @@ export default function transformComponentsObject(componentsObject: ComponentsOb const items: ts.TypeElement[] = []; if (componentsObject[key]) { - for (const [name, item] of getEntries(componentsObject[key], ctx)) { + for (const [name, item] of getEntries(componentsObject[key], ctx)) { let subType = transformers[key](item, { path: createRef(["components", key, name]), + schema: item, ctx, }); let hasQuestionToken = false; if (ctx.transform) { - const result = ctx.transform(item as SchemaObject, { + const result = ctx.transform(item, { path: createRef(["components", key, name]), + schema: item, ctx, }); if (result) { diff --git a/packages/openapi-typescript/src/transform/index.ts b/packages/openapi-typescript/src/transform/index.ts index 52d7e5801..1e02d356d 100644 --- a/packages/openapi-typescript/src/transform/index.ts +++ b/packages/openapi-typescript/src/transform/index.ts @@ -15,7 +15,7 @@ const transformers: Record transformSchemaObject(node, { path: createRef(["$defs"]), ctx: options }), + $defs: (node, options) => transformSchemaObject(node, { path: createRef(["$defs"]), ctx: options, schema: node }), }; export default function transformSchema(schema: OpenAPI3, ctx: GlobalContext) { diff --git a/packages/openapi-typescript/src/types.ts b/packages/openapi-typescript/src/types.ts index 59dabcbe9..bd11ecca0 100644 --- a/packages/openapi-typescript/src/types.ts +++ b/packages/openapi-typescript/src/types.ts @@ -710,5 +710,6 @@ export type $defs = Record; /** generic options for most internal transform* functions */ export interface TransformNodeOptions { path?: string; + schema?: SchemaObject | ReferenceObject; ctx: GlobalContext; } diff --git a/packages/openapi-typescript/test/node-api.test.ts b/packages/openapi-typescript/test/node-api.test.ts index b0ff0c8f3..2ad04f7aa 100644 --- a/packages/openapi-typescript/test/node-api.test.ts +++ b/packages/openapi-typescript/test/node-api.test.ts @@ -1,7 +1,7 @@ import { fileURLToPath } from "node:url"; import ts from "typescript"; import openapiTS, { COMMENT_HEADER, astToString } from "../src/index.js"; -import type { OpenAPITSOptions } from "../src/types.js"; +import type { OpenAPITSOptions, ReferenceObject, SchemaObject } from "../src/types.js"; import type { TestCase } from "./test-helpers.js"; const EXAMPLES_DIR = new URL("../examples/", import.meta.url); @@ -537,6 +537,11 @@ export type operations = Record;`, components: { schemas: { Date: { type: "string", format: "date-time" }, + Set: { + "x-string-enum-to-set": true, + type: "string", + enum: ["low", "medium", "high"], + }, }, }, }, @@ -546,6 +551,8 @@ export interface components { schemas: { /** Format: date-time */ Date: DateOrTime; + /** @enum {string} */ + Set: Set<"low" | "medium" | "high">; }; responses: never; parameters: never; @@ -565,6 +572,32 @@ export type operations = Record;`, */ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("DateOrTime")); } + + // Previously, in order to access the schema in postTransform, + // you could resolve the schema using the path. + // Now, the schema is made available directly on the options. + // const schema = options.path + // ? options.ctx.resolve(options.path) + // : undefined; + const schema = options.schema; + + if ( + schema && + !("$ref" in schema) && + Object.hasOwn(schema, "x-string-enum-to-set") && + schema.type === "string" && + schema.enum?.every((enumMember) => { + return typeof enumMember === "string"; + }) + ) { + return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("Set"), [ + ts.factory.createUnionTypeNode( + schema.enum.map((value) => { + return ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(value)); + }), + ), + ]); + } }, }, },