diff --git a/docs/src/content/docs/cli.md b/docs/src/content/docs/cli.md index 37448ff07..7ddca1a92 100644 --- a/docs/src/content/docs/cli.md +++ b/docs/src/content/docs/cli.md @@ -55,6 +55,7 @@ _Thanks, [@psmyrdek](https://github.com/psmyrdek)!_ | `--support-array-length` | | `false` | Generate tuples using array `minItems` / `maxItems` | | `--alphabetize` | | `false` | Sort types alphabetically | | `--exclude-deprecated` | | `false` | Exclude deprecated fields from types | +| `--root-types` | | `false` | Export schemas types at root level | ### `--path-params-as-types` diff --git a/packages/openapi-fetch/examples/sveltekit/src/app.d.ts b/packages/openapi-fetch/examples/sveltekit/src/app.d.ts deleted file mode 100644 index f59b884c5..000000000 --- a/packages/openapi-fetch/examples/sveltekit/src/app.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -// See https://kit.svelte.dev/docs/types#app -// for information about these interfaces -declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface Platform {} - } -} - -export {}; diff --git a/packages/openapi-typescript/bin/cli.js b/packages/openapi-typescript/bin/cli.js index 0e568e46e..96f67f574 100755 --- a/packages/openapi-typescript/bin/cli.js +++ b/packages/openapi-typescript/bin/cli.js @@ -28,6 +28,7 @@ Options --path-params-as-types (optional) Substitute path parameter names with their respective types --alphabetize (optional) Sort types alphabetically --exclude-deprecated (optional) Exclude deprecated fields from types + --root-types (optional) Export schemas types at root level `; const OUTPUT_FILE = "FILE"; @@ -44,19 +45,7 @@ if (args.includes("-it")) errorAndExit(`The -it alias has been deprecated. Use " const flags = parser(args, { array: ["header"], - boolean: [ - "help", - "version", - "defaultNonNullable", - "emptyObjectsUnknown", - "immutableTypes", - "contentNever", - "exportType", - "supportArrayLength", - "pathParamsAsTypes", - "alphabetize", - "excludeDeprecated", - ], + boolean: ["help", "version", "defaultNonNullable", "emptyObjectsUnknown", "immutableTypes", "contentNever", "exportType", "supportArrayLength", "pathParamsAsTypes", "alphabetize", "excludeDeprecated", "rootTypes"], string: ["auth", "header", "headersObject", "httpMethod"], alias: { header: ["x"], @@ -107,6 +96,7 @@ async function generateSchema(pathToSpec) { pathParamsAsTypes: flags.pathParamsAsTypes, alphabetize: flags.alphabetize, excludeDeprecated: flags.excludeDeprecated, + rootTypes: flags.rootTypes, }); // output diff --git a/packages/openapi-typescript/package.json b/packages/openapi-typescript/package.json index ef2263e9c..224e75892 100644 --- a/packages/openapi-typescript/package.json +++ b/packages/openapi-typescript/package.json @@ -6,6 +6,12 @@ "name": "Drew Powers", "email": "drew@pow.rs" }, + "contributors": [ + { + "name": "Jean Smaug", + "url": "https://maximeblanc.fr" + } + ], "license": "MIT", "bin": { "openapi-typescript": "bin/cli.js" diff --git a/packages/openapi-typescript/src/index.ts b/packages/openapi-typescript/src/index.ts index 4ce63225f..86af778d0 100644 --- a/packages/openapi-typescript/src/index.ts +++ b/packages/openapi-typescript/src/index.ts @@ -12,7 +12,6 @@ import transformResponseObject from "./transform/response-object.js"; import transformSchemaObject from "./transform/schema-object.js"; import transformSchemaObjectMap from "./transform/schema-object-map.js"; import { error, escObjKey, getDefaultFetch, getEntries, getSchemaObjectComment, indent } from "./utils.js"; - export * from "./types.js"; // expose all types to consumers const EMPTY_OBJECT_RE = /^\s*\{?\s*\}?\s*$/; @@ -55,6 +54,7 @@ async function openapiTS(schema: string | URL | OpenAPI3 | Readable, options: Op silent: options.silent ?? false, supportArrayLength: options.supportArrayLength ?? false, excludeDeprecated: options.excludeDeprecated ?? false, + rootTypes: options.rootTypes ?? false, }; // 1. load schema (and subschemas) @@ -119,6 +119,14 @@ async function openapiTS(schema: string | URL | OpenAPI3 | Readable, options: Op // 2c. root schema const rootTypes = transformSchema(allSchemas["."].schema as OpenAPI3, ctx); + const typedComponents = (allSchemas["."].schema as OpenAPI3).components!; + + if (options.rootTypes) { + for (const schema of Object.keys(typedComponents.schemas as object)) { + output.push(`export type ${schema} = external["."]["components"]["schemas"]["${schema}"];\n`); + } + } + for (const k of Object.keys(rootTypes)) { if (rootTypes[k] && !EMPTY_OBJECT_RE.test(rootTypes[k])) { output.push(options.exportType ? `export type ${k} = ${rootTypes[k]};` : `export interface ${k} ${rootTypes[k]}`, ""); diff --git a/packages/openapi-typescript/src/types.ts b/packages/openapi-typescript/src/types.ts index 87ce34f6c..0d5718faa 100644 --- a/packages/openapi-typescript/src/types.ts +++ b/packages/openapi-typescript/src/types.ts @@ -648,6 +648,8 @@ export interface OpenAPITSOptions { httpMethod?: string; /** (optional) Export type instead of interface */ exportType?: boolean; + /** (optional) Generate schemas types at root level */ + rootTypes?: boolean; /** (optional) Generate tuples using array minItems / maxItems */ supportArrayLength?: boolean; /** (optional) Substitute path parameter names with their respective types */ @@ -709,6 +711,7 @@ export interface GlobalContext { silent: boolean; supportArrayLength: boolean; excludeDeprecated: boolean; + rootTypes: boolean; } export type $defs = Record; diff --git a/packages/openapi-typescript/test/components-object.test.ts b/packages/openapi-typescript/test/components-object.test.ts index 1ab7bd925..553cf3ace 100644 --- a/packages/openapi-typescript/test/components-object.test.ts +++ b/packages/openapi-typescript/test/components-object.test.ts @@ -17,6 +17,7 @@ const options: GlobalContext = { supportArrayLength: false, transform: undefined, excludeDeprecated: false, + rootTypes: false, }; const basicSchema: ComponentsObject = { diff --git a/packages/openapi-typescript/test/header-object.test.ts b/packages/openapi-typescript/test/header-object.test.ts index d3bba46a8..d2a00217e 100644 --- a/packages/openapi-typescript/test/header-object.test.ts +++ b/packages/openapi-typescript/test/header-object.test.ts @@ -19,6 +19,7 @@ const options: TransformHeaderObjectOptions = { supportArrayLength: false, transform: undefined, excludeDeprecated: false, + rootTypes: false, }, }; diff --git a/packages/openapi-typescript/test/index.test.ts b/packages/openapi-typescript/test/index.test.ts index dc1254a14..c390c7e2a 100644 --- a/packages/openapi-typescript/test/index.test.ts +++ b/packages/openapi-typescript/test/index.test.ts @@ -1283,6 +1283,64 @@ export type $defs = Record; export type external = Record; +export type operations = Record; +`); + }); + }); + + describe("rootTypes helpers", () => { + test("should be added only when used", async () => { + const generated = await openapiTS( + { + openapi: "3.1", + info: { title: "Test", version: "1.0" }, + components: { + schemas: { + User: { + allOf: [ + { + type: "object", + properties: { firstName: { type: "string" }, lastName: { type: "string" } }, + }, + { + type: "object", + properties: { middleName: { type: "string" } }, + }, + ], + required: ["firstName", "lastName"], + }, + }, + }, + }, + { rootTypes: true }, + ); + expect(generated).toBe(`${BOILERPLATE}${WITH_REQUIRED_TYPE_HELPERS} +export type User = external["."]["components"]["schemas"]["User"]; + +export type paths = Record; + +export type webhooks = Record; + +export interface components { + schemas: { + User: WithRequired<{ + firstName?: string; + lastName?: string; + } & { + middleName?: string; + }, "firstName" | "lastName">; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} + +export type $defs = Record; + +export type external = Record; + export type operations = Record; `); }); diff --git a/packages/openapi-typescript/test/load.test.ts b/packages/openapi-typescript/test/load.test.ts index d0c0251ee..0ba4f4efd 100644 --- a/packages/openapi-typescript/test/load.test.ts +++ b/packages/openapi-typescript/test/load.test.ts @@ -117,6 +117,7 @@ async function load(schema: URL | Subschema | Readable, options?: Partial { diff --git a/packages/openapi-typescript/test/request-body-object.test.ts b/packages/openapi-typescript/test/request-body-object.test.ts index ea313534e..28e354928 100644 --- a/packages/openapi-typescript/test/request-body-object.test.ts +++ b/packages/openapi-typescript/test/request-body-object.test.ts @@ -19,6 +19,7 @@ const options: TransformRequestBodyObjectOptions = { supportArrayLength: false, transform: undefined, excludeDeprecated: false, + rootTypes: false, }, }; diff --git a/packages/openapi-typescript/test/response-object.test.ts b/packages/openapi-typescript/test/response-object.test.ts index dcdb98e65..401ffcfd6 100644 --- a/packages/openapi-typescript/test/response-object.test.ts +++ b/packages/openapi-typescript/test/response-object.test.ts @@ -19,6 +19,7 @@ const options: TransformResponseObjectOptions = { supportArrayLength: false, transform: undefined, excludeDeprecated: false, + rootTypes: false, }, }; diff --git a/packages/openapi-typescript/test/schema-object.test.ts b/packages/openapi-typescript/test/schema-object.test.ts index 0d60e5014..e45987976 100644 --- a/packages/openapi-typescript/test/schema-object.test.ts +++ b/packages/openapi-typescript/test/schema-object.test.ts @@ -19,6 +19,7 @@ const options: TransformSchemaObjectOptions = { supportArrayLength: false, transform: undefined, excludeDeprecated: false, + rootTypes: false, }, }; diff --git a/packages/openapi-typescript/test/webhooks-object.test.ts b/packages/openapi-typescript/test/webhooks-object.test.ts index 6414e4339..57cfdd2ef 100644 --- a/packages/openapi-typescript/test/webhooks-object.test.ts +++ b/packages/openapi-typescript/test/webhooks-object.test.ts @@ -17,6 +17,7 @@ const options: GlobalContext = { supportArrayLength: false, transform: undefined, excludeDeprecated: false, + rootTypes: false, }; describe("Webhooks Object", () => {