From bf5d6f8611d0597597ad15dffe4dc532a1fb991f Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Fri, 3 Jan 2025 13:58:31 -0700 Subject: [PATCH 1/3] v8.0.0 --- packages/openapi-typescript/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/openapi-typescript/package.json b/packages/openapi-typescript/package.json index 3202e6d65..1a303cad6 100644 --- a/packages/openapi-typescript/package.json +++ b/packages/openapi-typescript/package.json @@ -1,7 +1,7 @@ { "name": "openapi-typescript", "description": "Convert OpenAPI 3.0 & 3.1 schemas to TypeScript", - "version": "7.5.0", + "version": "8.0.0", "author": { "name": "Drew Powers", "email": "drew@pow.rs" From 6a08b34a4d9c8b356d71d01dd84bb1096e4ac41c Mon Sep 17 00:00:00 2001 From: Duncan Beevers Date: Fri, 3 Jan 2025 13:00:30 -0800 Subject: [PATCH 2/3] Fix prefixItems / minItems / maxItems tuple generation (#2053) * Simplify minItems / maxItems tuple generation Closes #2048 * fixup! Simplify minItems / maxItems tuple generation Account for immutable: true * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation * fixup! Simplify minItems / maxItems tuple generation --- .changeset/clean-phones-deliver.md | 11 + .../examples/simple-example.ts | 3 +- .../src/transform/schema-object.ts | 140 +++---- .../transform/schema-object/array.test.ts | 375 +++++++++++++++++- .../transform/schema-object/object.test.ts | 6 +- 5 files changed, 452 insertions(+), 83 deletions(-) create mode 100644 .changeset/clean-phones-deliver.md diff --git a/.changeset/clean-phones-deliver.md b/.changeset/clean-phones-deliver.md new file mode 100644 index 000000000..953a0d10e --- /dev/null +++ b/.changeset/clean-phones-deliver.md @@ -0,0 +1,11 @@ +--- +"openapi-typescript": major +--- + +Extract types generation for Array-type schemas to `transformArraySchemaObject` method. +Throw error when OpenAPI `items` is array. +Generate correct number of union members for `minItems` * `maxItems` unions. +Generate readonly tuple members for `minItems` & `maxItems` unions. +Generate readonly spread member for `prefixItems` tuple. +Preserve `prefixItems` type members in `minItems` & `maxItems` tuples. +Generate spread member for `prefixItems` tuple with no `minItems` / `maxItems` constraints. diff --git a/packages/openapi-typescript/examples/simple-example.ts b/packages/openapi-typescript/examples/simple-example.ts index 0c2580003..82a2427ea 100644 --- a/packages/openapi-typescript/examples/simple-example.ts +++ b/packages/openapi-typescript/examples/simple-example.ts @@ -245,7 +245,8 @@ export interface operations { }; content: { "application/json": [ - string + string, + ...unknown[] ]; }; }; diff --git a/packages/openapi-typescript/src/transform/schema-object.ts b/packages/openapi-typescript/src/transform/schema-object.ts index a41ce0e69..983a92018 100644 --- a/packages/openapi-typescript/src/transform/schema-object.ts +++ b/packages/openapi-typescript/src/transform/schema-object.ts @@ -10,6 +10,7 @@ import { UNDEFINED, UNKNOWN, addJSDocComment, + astToString, oapiRef, tsArrayLiteralExpression, tsEnum, @@ -25,7 +26,7 @@ import { tsWithRequired, } from "../lib/ts.js"; import { createDiscriminatorProperty, createRef, getEntries } from "../lib/utils.js"; -import type { ReferenceObject, SchemaObject, TransformNodeOptions } from "../types.js"; +import type { ArraySubtype, ReferenceObject, SchemaObject, TransformNodeOptions } from "../types.js"; /** * Transform SchemaObject nodes (4.8.24) @@ -273,6 +274,74 @@ export function transformSchemaObjectWithComposition( return finalType; } +type ArraySchemaObject = SchemaObject & ArraySubtype; +function isArraySchemaObject(schemaObject: SchemaObject | ArraySchemaObject): schemaObject is ArraySchemaObject { + return schemaObject.type === "array"; +} + +function padTupleMembers(length: number, itemType: ts.TypeNode, prefixTypes: readonly ts.TypeNode[]) { + return Array.from({ length }).map((_, index) => { + return prefixTypes[index] ?? itemType; + }); +} + +function toOptionsReadonly( + members: TMembers, + options: TransformNodeOptions, +): TMembers | ts.TypeOperatorNode { + return options.ctx.immutable ? ts.factory.createTypeOperatorNode(ts.SyntaxKind.ReadonlyKeyword, members) : members; +} + +/* Transform Array schema object */ +function transformArraySchemaObject(schemaObject: ArraySchemaObject, options: TransformNodeOptions): ts.TypeNode { + const prefixTypes = (schemaObject.prefixItems ?? []).map((item) => transformSchemaObject(item, options)); + + if (Array.isArray(schemaObject.items)) { + throw new Error(`${options.path}: invalid property items. Expected Schema Object, got Array`); + } + + const itemType = schemaObject.items ? transformSchemaObject(schemaObject.items, options) : UNKNOWN; + + // The minimum number of tuple members to return + const min: number = + options.ctx.arrayLength && typeof schemaObject.minItems === "number" && schemaObject.minItems >= 0 + ? schemaObject.minItems + : 0; + const max: number | undefined = + options.ctx.arrayLength && + typeof schemaObject.maxItems === "number" && + schemaObject.maxItems >= 0 && + min <= schemaObject.maxItems + ? schemaObject.maxItems + : undefined; + + // "30" is an arbitrary number but roughly around when TS starts to struggle with tuple inference in practice + const MAX_CODE_SIZE = 30; + const estimateCodeSize = max === undefined ? min : (max * (max + 1) - min * (min - 1)) / 2; + const shouldGeneratePermutations = (min !== 0 || max !== undefined) && estimateCodeSize < MAX_CODE_SIZE; + + // if maxItems is set, then return a union of all permutations of possible tuple types + if (shouldGeneratePermutations && max !== undefined) { + return tsUnion( + Array.from({ length: max - min + 1 }).map((_, index) => + toOptionsReadonly(ts.factory.createTupleTypeNode(padTupleMembers(index + min, itemType, prefixTypes)), options), + ), + ); + } + + // if maxItems not set, then return a simple tuple type the length of `min` + const spreadType = ts.factory.createArrayTypeNode(itemType); + const tupleType = + shouldGeneratePermutations || prefixTypes.length + ? ts.factory.createTupleTypeNode([ + ...padTupleMembers(Math.max(min, prefixTypes.length), itemType, prefixTypes), + ts.factory.createRestTypeNode(toOptionsReadonly(spreadType, options)), + ]) + : spreadType; + + return toOptionsReadonly(tupleType, options); +} + /** * Handle SchemaObject minus composition (anyOf/allOf/oneOf) */ @@ -312,73 +381,8 @@ function transformSchemaObjectCore(schemaObject: SchemaObject, options: Transfor } // type: array (with support for tuples) - if (schemaObject.type === "array") { - // default to `unknown[]` - let itemType: ts.TypeNode = UNKNOWN; - // tuple type - if (schemaObject.prefixItems || Array.isArray(schemaObject.items)) { - const prefixItems = schemaObject.prefixItems ?? (schemaObject.items as (SchemaObject | ReferenceObject)[]); - itemType = ts.factory.createTupleTypeNode(prefixItems.map((item) => transformSchemaObject(item, options))); - } - // standard array type - else if (schemaObject.items) { - if ("type" in schemaObject.items && schemaObject.items.type === "array") { - itemType = ts.factory.createArrayTypeNode(transformSchemaObject(schemaObject.items, options)); - } else { - itemType = transformSchemaObject(schemaObject.items, options); - } - } - - const min: number = - typeof schemaObject.minItems === "number" && schemaObject.minItems >= 0 ? schemaObject.minItems : 0; - const max: number | undefined = - typeof schemaObject.maxItems === "number" && schemaObject.maxItems >= 0 && min <= schemaObject.maxItems - ? schemaObject.maxItems - : undefined; - const estimateCodeSize = typeof max !== "number" ? min : (max * (max + 1) - min * (min - 1)) / 2; - if ( - options.ctx.arrayLength && - (min !== 0 || max !== undefined) && - estimateCodeSize < 30 // "30" is an arbitrary number but roughly around when TS starts to struggle with tuple inference in practice - ) { - if (min === max) { - const elements: ts.TypeNode[] = []; - for (let i = 0; i < min; i++) { - elements.push(itemType); - } - return tsUnion([ts.factory.createTupleTypeNode(elements)]); - } else if ((schemaObject.maxItems as number) > 0) { - // if maxItems is set, then return a union of all permutations of possible tuple types - const members: ts.TypeNode[] = []; - // populate 1 short of min … - for (let i = 0; i <= (max ?? 0) - min; i++) { - const elements: ts.TypeNode[] = []; - for (let j = min; j < i + min; j++) { - elements.push(itemType); - } - members.push(ts.factory.createTupleTypeNode(elements)); - } - return tsUnion(members); - } - // if maxItems not set, then return a simple tuple type the length of `min` - else { - const elements: ts.TypeNode[] = []; - for (let i = 0; i < min; i++) { - elements.push(itemType); - } - elements.push(ts.factory.createRestTypeNode(ts.factory.createArrayTypeNode(itemType))); - return ts.factory.createTupleTypeNode(elements); - } - } - - const finalType = - ts.isTupleTypeNode(itemType) || ts.isArrayTypeNode(itemType) - ? itemType - : ts.factory.createArrayTypeNode(itemType); // wrap itemType in array type, but only if not a tuple or array already - - return options.ctx.immutable - ? ts.factory.createTypeOperatorNode(ts.SyntaxKind.ReadonlyKeyword, finalType) - : finalType; + if (isArraySchemaObject(schemaObject)) { + return transformArraySchemaObject(schemaObject, options); } // polymorphic, or 3.1 nullable diff --git a/packages/openapi-typescript/test/transform/schema-object/array.test.ts b/packages/openapi-typescript/test/transform/schema-object/array.test.ts index 59a2fddb7..454ef4ff0 100644 --- a/packages/openapi-typescript/test/transform/schema-object/array.test.ts +++ b/packages/openapi-typescript/test/transform/schema-object/array.test.ts @@ -19,19 +19,13 @@ describe("transformSchemaObject > array", () => { }, ], [ - "tuple > tuple items", + "array > heterogeneous items", { given: { type: "array", - items: [{ type: "string" }, { type: "number" }], - minItems: 2, - maxItems: 2, + items: { anyOf: [{ type: "number" }, { type: "string" }] }, }, - want: `[ - string, - number -]`, - // options: DEFAULT_OPTIONS, + want: "(number | string)[]", }, ], [ @@ -45,7 +39,8 @@ describe("transformSchemaObject > array", () => { want: `[ number, number, - number + number, + ...number[] ]`, // options: DEFAULT_OPTIONS, }, @@ -93,6 +88,28 @@ describe("transformSchemaObject > array", () => { // options: DEFAULT_OPTIONS, }, ], + [ + "options > arrayLength: false > minItems: 0", + { + given: { type: "array", items: { type: "string" }, minItems: 0 }, + want: "string[]", + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: false }, + }, + }, + ], + [ + "options > arrayLength: false > minItems: 1", + { + given: { type: "array", items: { type: "string" }, minItems: 1 }, + want: "string[]", + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: false }, + }, + }, + ], [ "options > arrayLength: true > default", { @@ -104,6 +121,28 @@ describe("transformSchemaObject > array", () => { }, }, ], + [ + "options > arrayLength: true > minItems: 0", + { + given: { type: "array", items: { type: "string" }, minItems: 0 }, + want: "string[]", + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true }, + }, + }, + ], + [ + "options > arrayLength: true, immutable: true > minItems: 0", + { + given: { type: "array", items: { type: "string" }, minItems: 0 }, + want: "readonly string[]", + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true, immutable: true }, + }, + }, + ], [ "options > arrayLength: true > minItems: 1", { @@ -118,6 +157,50 @@ describe("transformSchemaObject > array", () => { }, }, ], + [ + "options > arrayLength: true, immutable: true > minItems: 1", + { + given: { type: "array", items: { type: "string" }, minItems: 1 }, + want: `readonly [ + string, + ...readonly string[] +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true, immutable: true }, + }, + }, + ], + [ + "options > arrayLength: true > minItems: 2", + { + given: { type: "array", items: { type: "string" }, minItems: 2 }, + want: `[ + string, + string, + ...string[] +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true }, + }, + }, + ], + [ + "options > arrayLength: true, immutable: true > minItems: 2", + { + given: { type: "array", items: { type: "string" }, minItems: 2 }, + want: `readonly [ + string, + string, + ...readonly string[] +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true, immutable: true }, + }, + }, + ], [ "options > arrayLength: true > maxItems: 2", { @@ -135,6 +218,55 @@ describe("transformSchemaObject > array", () => { }, }, ], + [ + "options > arrayLength: true, immutable: true > maxItems: 2", + { + given: { type: "array", items: { type: "string" }, maxItems: 2 }, + want: `readonly [ +] | readonly [ + string +] | readonly [ + string, + string +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true, immutable: true }, + }, + }, + ], + [ + "options > arrayLength: true > minItems: 1, maxItems: 2", + { + given: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 2 }, + want: `[ + string +] | [ + string, + string +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true }, + }, + }, + ], + [ + "options > arrayLength: true, immutable: true > minItems: 1, maxItems: 2", + { + given: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 2 }, + want: `readonly [ + string +] | readonly [ + string, + string +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true, immutable: true }, + }, + }, + ], [ "options > arrayLength: true > maxItems: 20", { @@ -146,6 +278,17 @@ describe("transformSchemaObject > array", () => { }, }, ], + [ + "options > arrayLength: true, immutable: true > maxItems: 20", + { + given: { type: "array", items: { type: "string" }, maxItems: 20 }, + want: "readonly string[]", + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true, immutable: true }, + }, + }, + ], [ "options > arrayLength: true > minItems: 2, maxItems: 2", { @@ -160,6 +303,193 @@ describe("transformSchemaObject > array", () => { }, }, ], + [ + "options > arrayLength: true > prefixItems, minItems: 2, maxItems: 2", + { + given: { + type: "array", + items: { type: "string" }, + prefixItems: [ + { type: "string", enum: ["calcium"] }, + { type: "string", enum: ["magnesium"] }, + ], + minItems: 2, + maxItems: 2, + }, + want: `[ + "calcium", + "magnesium" +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true }, + }, + }, + ], + [ + "options > arrayLength: true > no items, prefixItems, minItems: 2, maxItems: 3", + { + given: { + type: "array", + prefixItems: [ + { type: "string", enum: ["calcium"] }, + { type: "string", enum: ["magnesium"] }, + ], + minItems: 2, + maxItems: 3, + }, + want: `[ + "calcium", + "magnesium" +] | [ + "calcium", + "magnesium", + unknown +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true }, + }, + }, + ], + [ + "options > arrayLength: true > no items, prefixItems, minItems: 3", + { + given: { + type: "array", + prefixItems: [ + { type: "string", enum: ["calcium"] }, + { type: "string", enum: ["magnesium"] }, + ], + minItems: 3, + }, + want: `[ + "calcium", + "magnesium", + unknown, + ...unknown[] +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true }, + }, + }, + ], + [ + "options > arrayLength: true > no items, prefixItems, minItems: 2, maxItems: 5", + { + given: { + type: "array", + prefixItems: [ + { type: "string", enum: ["calcium"] }, + { type: "string", enum: ["magnesium"] }, + { type: "string", enum: ["tungsten"] }, + ], + minItems: 2, + maxItems: 5, + }, + want: `[ + "calcium", + "magnesium" +] | [ + "calcium", + "magnesium", + "tungsten" +] | [ + "calcium", + "magnesium", + "tungsten", + unknown +] | [ + "calcium", + "magnesium", + "tungsten", + unknown, + unknown +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true }, + }, + }, + ], + [ + "options > arrayLength: true, immutable: true > prefixItems, minItems: 3", + { + given: { + type: "array", + items: { type: "string" }, + prefixItems: [{ type: "string" }, { type: "number" }], + minItems: 3, + }, + want: `readonly [ + string, + number, + string, + ...readonly string[] +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true, immutable: true }, + }, + }, + ], + [ + "options > arrayLength: true, immutable: true > prefixItems, minItems: 3, maxItems: 3", + { + given: { + type: "array", + items: { type: "string" }, + prefixItems: [{ type: "string" }, { type: "number" }], + minItems: 3, + maxItems: 3, + }, + want: `readonly [ + string, + number, + string +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true, immutable: true }, + }, + }, + ], + [ + "options > arrayLength: false, immutable: true > prefixItems, minItems: 3, maxItems: 5", + { + given: { + type: "array", + items: { type: "string" }, + prefixItems: [{ type: "string" }, { type: "number" }], + minItems: 3, + maxItems: 5, + }, + want: `readonly [ + string, + number, + ...readonly string[] +]`, + options: { + ...DEFAULT_OPTIONS, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: false, immutable: true }, + }, + }, + ], + [ + "options > arrayLength: false > prefixItems, items: false", + { + given: { + type: "array", + items: false, + prefixItems: [{ type: "string", enum: ["professor"] }], + }, + want: `[ + "professor", + ...unknown[] +]`, + }, + ], [ "options > immutable: true", { @@ -185,7 +515,8 @@ describe("transformSchemaObject > array", () => { want: `readonly [ number, number, - number + number, + ...readonly number[] ]`, options: { ...DEFAULT_OPTIONS, @@ -200,6 +531,7 @@ describe("transformSchemaObject > array", () => { testName, async () => { const result = astToString(transformSchemaObject(given, options)); + // console.log(result); if (want instanceof URL) { expect(result).toMatchFileSnapshot(fileURLToPath(want)); } else { @@ -209,4 +541,25 @@ describe("transformSchemaObject > array", () => { ci?.timeout, ); } + + const invalidTests: TestCase[] = [ + [ + "error > items is array", + { + given: { + type: "array", + items: [{ type: "number" }, { type: "string" }], + }, + want: "#/components/schemas/schema-object: invalid property items. Expected Schema Object, got Array", + }, + ], + ]; + + for (const [testName, { given, want, options = DEFAULT_OPTIONS, ci }] of invalidTests) { + test.skipIf(ci?.skipIf)(testName, () => { + expect(() => { + transformSchemaObject(given, options); + }).toThrowError(want.toString()); + }); + } }); diff --git a/packages/openapi-typescript/test/transform/schema-object/object.test.ts b/packages/openapi-typescript/test/transform/schema-object/object.test.ts index 60290fc57..801dadb9e 100644 --- a/packages/openapi-typescript/test/transform/schema-object/object.test.ts +++ b/packages/openapi-typescript/test/transform/schema-object/object.test.ts @@ -376,7 +376,7 @@ describe("transformSchemaObject > object", () => { }, ], [ - "options > two-dimensional array", + "options > array of tuples", { given: { type: "object", @@ -384,7 +384,7 @@ describe("transformSchemaObject > object", () => { array: { type: "array", items: { - items: [ + prefixItems: [ { type: "string", }, @@ -407,7 +407,7 @@ describe("transformSchemaObject > object", () => { }`, options: { ...DEFAULT_OPTIONS, - ctx: { ...DEFAULT_OPTIONS.ctx }, + ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true }, }, }, ], From c8708e261479b40c0099eca88745a089f8f2a5e8 Mon Sep 17 00:00:00 2001 From: Duncan Beevers Date: Sat, 4 Jan 2025 07:31:57 -0800 Subject: [PATCH 3/3] Fix colliding enumValues exports (#2051) * Add schema to postTransform options (#2049) * Add schema to postTransform options Closes #2013 * fixup! Add schema to postTransform options Closes #2013 * fixup! Add schema to postTransform options Closes #2013 * fixup! Add schema to postTransform options * Add Netlify badge (#2065) * Only check GitHub token for docs build when update needed * Add Netlify badge * Use link * [ci] release (#2084) Co-authored-by: github-actions[bot] * Fix issue templates (#2088) * Add feat issues to projects (#2090) * Use const arrays for OpenAPI enum types Preserve capitalization of named, exported enum values. Type exported const values as const, instead of their location within the operations or components schemas. Derive and export types for enum values from concrete values in const arrays. Use derived enum value types in operations and components schemas. Use non-conflicting variable names for composed OpenAPI enums (anyOf: [enum1, enum2]) * Export type-predicates along when enumValues is true --------- Co-authored-by: Drew Powers Co-authored-by: openapi-ts-bot Co-authored-by: github-actions[bot] --- .changeset/shaggy-impalas-develop.md | 5 + .../01_openapi-typescript-bug.yml | 7 +- .../01_openapi-typescript-feat.yml | 5 +- .../ISSUE_TEMPLATE/02_openapi-fetch-bug.yml | 5 +- .../ISSUE_TEMPLATE/02_openapi-fetch-feat.yml | 5 +- .../03_openapi-react-query-bug.yml | 6 +- .../03_openapi-react-query-feat.yml | 3 +- .github/ISSUE_TEMPLATE/04_swr-openapi-bug.yml | 6 +- .../ISSUE_TEMPLATE/04_swr-openapi-feat.yml | 3 +- docs/.vitepress/config.ts | 2 +- docs/.vitepress/en.ts | 2 +- docs/.vitepress/shared.ts | 2 +- docs/.vitepress/theme/CustomLayout.vue | 24 +- docs/6.x/node.md | 14 +- docs/scripts/update-contributors.js | 29 +- packages/openapi-typescript/CHANGELOG.md | 6 + .../examples/digital-ocean-api.ts | 6 +- packages/openapi-typescript/src/index.ts | 2 +- packages/openapi-typescript/src/lib/ts.ts | 159 +++++--- .../src/transform/components-object.ts | 6 +- .../openapi-typescript/src/transform/index.ts | 67 ++-- .../src/transform/operation-object.ts | 4 +- .../src/transform/schema-object.ts | 359 +++++++++++++----- packages/openapi-typescript/src/types.ts | 3 +- packages/openapi-typescript/test/cjs.test.js | 2 +- packages/openapi-typescript/test/cli.test.ts | 4 +- .../openapi-typescript/test/index.test.ts | 8 +- .../openapi-typescript/test/lib/ts.test.ts | 59 ++- .../openapi-typescript/test/node-api.test.ts | 302 ++++++++++++--- .../openapi-typescript/test/test-helpers.ts | 2 +- packages/swr-openapi/package.json | 4 +- 31 files changed, 802 insertions(+), 309 deletions(-) create mode 100644 .changeset/shaggy-impalas-develop.md diff --git a/.changeset/shaggy-impalas-develop.md b/.changeset/shaggy-impalas-develop.md new file mode 100644 index 000000000..21e238f23 --- /dev/null +++ b/.changeset/shaggy-impalas-develop.md @@ -0,0 +1,5 @@ +--- +"openapi-typescript": major +--- + +Export enumValues as const arrays. Derive schema types from literal values. diff --git a/.github/ISSUE_TEMPLATE/01_openapi-typescript-bug.yml b/.github/ISSUE_TEMPLATE/01_openapi-typescript-bug.yml index 3dfa26f92..620772127 100644 --- a/.github/ISSUE_TEMPLATE/01_openapi-typescript-bug.yml +++ b/.github/ISSUE_TEMPLATE/01_openapi-typescript-bug.yml @@ -6,18 +6,21 @@ labels: - triage body: - type: input + id: version attributes: label: openapi-typescript version placeholder: x.x.x validations: required: true - type: input + id: node attributes: label: Node.js version placeholder: 20.x.x validations: required: true - type: input + id: os attributes: label: OS + version placeholder: macOS 15.1.1 @@ -45,14 +48,14 @@ body: required: true - type: checkboxes id: required - label: Required attributes: + label: Required options: - label: My OpenAPI schema is valid and passes the [Redocly validator](https://redocly.com/docs/cli/commands/lint/) (`npx @redocly/cli@latest lint`) required: true - type: checkboxes id: extra - label: Extra attributes: + label: Extra options: - label: I’m willing to open a PR (see [CONTRIBUTING.md](https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-typescript/CONTRIBUTING.md)) diff --git a/.github/ISSUE_TEMPLATE/01_openapi-typescript-feat.yml b/.github/ISSUE_TEMPLATE/01_openapi-typescript-feat.yml index 28fdf9344..0461ea317 100644 --- a/.github/ISSUE_TEMPLATE/01_openapi-typescript-feat.yml +++ b/.github/ISSUE_TEMPLATE/01_openapi-typescript-feat.yml @@ -1,9 +1,10 @@ name: "openapi-typescript: Feature request" description: Propose new functionality or a breaking change -title: "" labels: - openapi-ts - enhancement +projects: + - openapi-ts/2 body: - type: textarea id: description @@ -21,7 +22,7 @@ body: required: true - type: checkboxes id: extra - label: Extra attributes: + label: Extra options: - label: I’m willing to open a PR (see [CONTRIBUTING.md](https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-typescript/CONTRIBUTING.md)) diff --git a/.github/ISSUE_TEMPLATE/02_openapi-fetch-bug.yml b/.github/ISSUE_TEMPLATE/02_openapi-fetch-bug.yml index f53726675..0cf42230b 100644 --- a/.github/ISSUE_TEMPLATE/02_openapi-fetch-bug.yml +++ b/.github/ISSUE_TEMPLATE/02_openapi-fetch-bug.yml @@ -6,8 +6,9 @@ labels: - triage body: - type: input + id: version attributes: - label: Version + label: openapi-fetch version placeholder: x.x.x validations: required: true @@ -33,7 +34,7 @@ body: required: true - type: checkboxes id: extra - label: Extra attributes: + label: Extra options: - label: I’m willing to open a PR (see [CONTRIBUTING.md](https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-fetch/CONTRIBUTING.md)) diff --git a/.github/ISSUE_TEMPLATE/02_openapi-fetch-feat.yml b/.github/ISSUE_TEMPLATE/02_openapi-fetch-feat.yml index 241269a0b..829e340bc 100644 --- a/.github/ISSUE_TEMPLATE/02_openapi-fetch-feat.yml +++ b/.github/ISSUE_TEMPLATE/02_openapi-fetch-feat.yml @@ -1,9 +1,10 @@ name: "openapi-fetch: Feature request" description: Propose new functionality or a breaking change -title: "" labels: - openapi-fetch - enhancement +projects: + - openapi-ts/3 body: - type: textarea id: description @@ -21,7 +22,7 @@ body: required: true - type: checkboxes id: extra - label: Extra attributes: + label: Extra options: - label: I’m willing to open a PR (see [CONTRIBUTING.md](https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-fetch/CONTRIBUTING.md)) diff --git a/.github/ISSUE_TEMPLATE/03_openapi-react-query-bug.yml b/.github/ISSUE_TEMPLATE/03_openapi-react-query-bug.yml index 563c33b2e..378293bf8 100644 --- a/.github/ISSUE_TEMPLATE/03_openapi-react-query-bug.yml +++ b/.github/ISSUE_TEMPLATE/03_openapi-react-query-bug.yml @@ -6,8 +6,9 @@ labels: - triage body: - type: input + id: version attributes: - label: Version + label: openapi-react-query version placeholder: x.x.x validations: required: true @@ -26,6 +27,7 @@ body: validations: required: true - type: textarea + id: expected attributes: label: Expected result description: (In case it’s not obvious) @@ -33,7 +35,7 @@ body: required: true - type: checkboxes id: extra - label: Extra attributes: + label: Extra options: - label: I’m willing to open a PR (see [CONTRIBUTING.md](https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-react-query/CONTRIBUTING.md)) diff --git a/.github/ISSUE_TEMPLATE/03_openapi-react-query-feat.yml b/.github/ISSUE_TEMPLATE/03_openapi-react-query-feat.yml index c20560c1e..f2b8d4f1d 100644 --- a/.github/ISSUE_TEMPLATE/03_openapi-react-query-feat.yml +++ b/.github/ISSUE_TEMPLATE/03_openapi-react-query-feat.yml @@ -1,6 +1,5 @@ name: "openapi-react-query: Feature request" description: Propose new functionality or a breaking change -title: "" labels: - openapi-react-query - enhancement @@ -21,7 +20,7 @@ body: required: true - type: checkboxes id: extra - label: Extra attributes: + label: Extra options: - label: I’m willing to open a PR (see [CONTRIBUTING.md](https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-react-query/CONTRIBUTING.md)) diff --git a/.github/ISSUE_TEMPLATE/04_swr-openapi-bug.yml b/.github/ISSUE_TEMPLATE/04_swr-openapi-bug.yml index 63cb36f05..6feba784f 100644 --- a/.github/ISSUE_TEMPLATE/04_swr-openapi-bug.yml +++ b/.github/ISSUE_TEMPLATE/04_swr-openapi-bug.yml @@ -5,8 +5,9 @@ labels: - bug body: - type: input + id: version attributes: - label: Version + label: swr-openapi version placeholder: x.x.x validations: required: true @@ -25,6 +26,7 @@ body: validations: required: true - type: textarea + id: expected attributes: label: Expected result description: (In case it’s not obvious) @@ -32,7 +34,7 @@ body: required: true - type: checkboxes id: extra - label: Extra attributes: + label: Extra options: - label: I’m willing to open a PR (see [CONTRIBUTING.md](https://github.com/openapi-ts/openapi-typescript/blob/main/packages/swr-openapi/CONTRIBUTING.md)) diff --git a/.github/ISSUE_TEMPLATE/04_swr-openapi-feat.yml b/.github/ISSUE_TEMPLATE/04_swr-openapi-feat.yml index 56da4825b..bc9c564b3 100644 --- a/.github/ISSUE_TEMPLATE/04_swr-openapi-feat.yml +++ b/.github/ISSUE_TEMPLATE/04_swr-openapi-feat.yml @@ -1,6 +1,5 @@ name: "swr-openapi: Feature request" description: Propose new functionality or a breaking change -title: "" labels: - swr-openapi - enhancement @@ -21,7 +20,7 @@ body: required: true - type: checkboxes id: extra - label: Extra attributes: + label: Extra options: - label: I’m willing to open a PR (see [CONTRIBUTING.md](https://github.com/openapi-ts/openapi-typescript/blob/main/packages/swr-openapi/CONTRIBUTING.md)) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 8556147d0..55da861e9 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -4,7 +4,7 @@ import zh from "./zh"; import ja from "./ja"; import shared from "./shared"; import { transformerTwoslash } from "@shikijs/vitepress-twoslash"; -import { ModuleKind, ModuleResolutionKind } from "typescript"; +import { ModuleResolutionKind } from "typescript"; // https://vitepress.dev/reference/site-config export default defineConfig({ diff --git a/docs/.vitepress/en.ts b/docs/.vitepress/en.ts index e7471111b..a15fe27cb 100644 --- a/docs/.vitepress/en.ts +++ b/docs/.vitepress/en.ts @@ -139,7 +139,7 @@ export default defineConfig({ ], footer: { message: - 'Released under the MIT License.', + 'Released under the MIT License. Powered by Netlify.', }, }, }); diff --git a/docs/.vitepress/shared.ts b/docs/.vitepress/shared.ts index d531543f4..7b575d66e 100644 --- a/docs/.vitepress/shared.ts +++ b/docs/.vitepress/shared.ts @@ -26,7 +26,7 @@ const shared: UserConfig = { themeConfig: { siteTitle: false, logo: "/assets/openapi-ts.svg", - outline: 'deep', + outline: "deep", search: { provider: "algolia", options: { diff --git a/docs/.vitepress/theme/CustomLayout.vue b/docs/.vitepress/theme/CustomLayout.vue index 0a71a2955..6fd09798d 100644 --- a/docs/.vitepress/theme/CustomLayout.vue +++ b/docs/.vitepress/theme/CustomLayout.vue @@ -39,7 +39,7 @@ const { Layout } = DefaultTheme; - + @@ -147,6 +150,25 @@ const { Layout } = DefaultTheme; height: 3rem; width: auto; } + +.sidebar-hosting { + color: var(--vp-c-text-3); + font-size: 0.75rem; + font-weight: 500; + letter-spacing: 0.0625em; + margin-block-start: 2rem; + text-transform: uppercase; + + a { + color: var(--vp-c-brand-1); + text-decoration: underline; + + &:hover, + &:focus-visible { + color: var(--vp-c-brand-2); + } + } +}