diff --git a/bin/cli.js b/bin/cli.js index 49d7baf2c..08d6d0ee4 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -15,6 +15,8 @@ Options --help display this --output, -o specify output file --prettier-config (optional) specify path to Prettier config file + --raw-schema (optional) Read from raw schema instead of document + --version (optional) Schema version (must be present for raw schemas) `, { flags: { @@ -25,6 +27,12 @@ Options prettierConfig: { type: "string", }, + rawSchema: { + type: "boolean" + }, + version: { + type: "number" + } }, } ); @@ -48,6 +56,8 @@ const timeStart = process.hrtime(); const result = swaggerToTS(spec, { prettierConfig: cli.flags.prettierConfig, + rawSchema: cli.flags.rawSchema, + version: cli.flags.version }); // Write to file if specifying output diff --git a/src/index.ts b/src/index.ts index ae3c62701..9955fb368 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,13 @@ import path from "path"; import prettier from "prettier"; import { swaggerVersion } from "./utils"; -import { OpenAPI2, OpenAPI3, SwaggerToTSOptions } from "./types"; +import { + OpenAPI2, + OpenAPI2Schemas, + OpenAPI3, + OpenAPI3Schemas, + SwaggerToTSOptions, +} from "./types"; import v2 from "./v2"; import v3 from "./v3"; @@ -14,19 +20,21 @@ export const WARNING_MESSAGE = `/** `; export default function swaggerToTS( - schema: OpenAPI2 | OpenAPI3, + schema: OpenAPI2 | OpenAPI2Schemas | OpenAPI3 | OpenAPI3Schemas, options?: SwaggerToTSOptions ): string { // generate types for V2 and V3 - const version = swaggerVersion(schema); + const version = + (options && options.version) || + swaggerVersion(schema as OpenAPI2 | OpenAPI3); let output = `${WARNING_MESSAGE}`; switch (version) { case 2: { - output = output.concat(v2(schema as OpenAPI2, options)); + output = output.concat(v2(schema as OpenAPI2 | OpenAPI2Schemas, options)); break; } case 3: { - output = output.concat(v3(schema as OpenAPI3, options)); + output = output.concat(v3(schema as OpenAPI3 | OpenAPI3Schemas, options)); break; } } diff --git a/src/types/OpenAPI2.ts b/src/types/OpenAPI2.ts index 4b8e63363..e83b82393 100644 --- a/src/types/OpenAPI2.ts +++ b/src/types/OpenAPI2.ts @@ -4,8 +4,12 @@ * the parts that swagger-to-ts needs to know about. */ +export interface OpenAPI2Schemas { + [key: string]: OpenAPI2SchemaObject; +} + export interface OpenAPI2 { - definitions?: { [key: string]: OpenAPI2SchemaObject }; + definitions?: OpenAPI2Schemas; swagger: string; [key: string]: any; // handle other properties beyond swagger-to-ts’ concern } diff --git a/src/types/OpenAPI3.ts b/src/types/OpenAPI3.ts index d180d3adf..a45a465ff 100644 --- a/src/types/OpenAPI3.ts +++ b/src/types/OpenAPI3.ts @@ -4,12 +4,18 @@ * the parts that swagger-to-ts needs to know about. */ +export interface OpenAPI3Schemas { + [key: string]: OpenAPI3SchemaObject | OpenAPI3Reference; +} + +export interface OpenAPI3Components { + schemas: OpenAPI3Schemas; + responses?: OpenAPI3Schemas; +} + export interface OpenAPI3 { openapi: string; - components: { - schemas: { [key: string]: OpenAPI3SchemaObject | OpenAPI3Reference }; - responses?: { [key: string]: OpenAPI3SchemaObject | OpenAPI3Reference }; - }; + components: OpenAPI3Components; [key: string]: any; // handle other properties beyond swagger-to-ts’ concern } diff --git a/src/types/index.ts b/src/types/index.ts index 26ca6699b..271a3f855 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -18,4 +18,8 @@ export interface SwaggerToTSOptions { schemaObject: OpenAPI2SchemaObject | OpenAPI3SchemaObject, property: Property ) => Property; + /** (optional) Parsing input document as raw schema rather than OpenAPI document */ + rawSchema?: boolean; + /** (optional) OpenAPI version. Must be present if parsing raw schema */ + version?: number; } diff --git a/src/utils.ts b/src/utils.ts index e6467e51d..2ebc3fd47 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -95,8 +95,8 @@ export function swaggerVersion(definition: OpenAPI2 | OpenAPI3): 2 | 3 { } /** Convert $ref to TS ref */ -export function transformRef(ref: string): string { - const parts = ref.replace(/^#\//, "").split("/"); +export function transformRef(ref: string, root = ""): string { + const parts = ref.replace(/^#\//, root).split("/"); return `${parts[0]}["${parts.slice(1).join('"]["')}"]`; } diff --git a/src/v2.ts b/src/v2.ts index ddde238c2..063c4c646 100644 --- a/src/v2.ts +++ b/src/v2.ts @@ -1,5 +1,10 @@ import propertyMapper from "./property-mapper"; -import { OpenAPI2, OpenAPI2SchemaObject, SwaggerToTSOptions } from "./types"; +import { + OpenAPI2, + OpenAPI2SchemaObject, + OpenAPI2Schemas, + SwaggerToTSOptions, +} from "./types"; import { comment, nodeType, @@ -29,25 +34,36 @@ export const PRIMITIVES: { [key: string]: "boolean" | "string" | "number" } = { }; export default function generateTypesV2( - schema: OpenAPI2, + input: OpenAPI2 | OpenAPI2Schemas, options?: SwaggerToTSOptions ): string { - if (!schema.definitions) { - throw new Error( - `⛔️ 'definitions' missing from schema https://swagger.io/specification/v2/#definitions-object` - ); + const rawSchema = options && options.rawSchema; + + let definitions: OpenAPI2Schemas; + + if (rawSchema) { + definitions = input as OpenAPI2Schemas; + } else { + const document = input as OpenAPI2; + + if (!document.definitions) { + throw new Error( + `⛔️ 'definitions' missing from schema https://swagger.io/specification/v2/#definitions-object` + ); + } + definitions = document.definitions; } // propertyMapper const propertyMapped = options - ? propertyMapper(schema.definitions, options.propertyMapper) - : schema.definitions; + ? propertyMapper(definitions, options.propertyMapper) + : definitions; // type conversions function transform(node: OpenAPI2SchemaObject): string { switch (nodeType(node)) { case "ref": { - return transformRef(node.$ref); + return transformRef(node.$ref, rawSchema ? "definitions/" : ""); } case "string": case "number": diff --git a/src/v3.ts b/src/v3.ts index 6b33fe5bb..401152b3b 100644 --- a/src/v3.ts +++ b/src/v3.ts @@ -1,5 +1,11 @@ import propertyMapper from "./property-mapper"; -import { OpenAPI3, OpenAPI3SchemaObject, SwaggerToTSOptions } from "./types"; +import { + OpenAPI3, + OpenAPI3Components, + OpenAPI3SchemaObject, + OpenAPI3Schemas, + SwaggerToTSOptions, +} from "./types"; import { comment, nodeType, @@ -24,25 +30,34 @@ export const PRIMITIVES: { [key: string]: "boolean" | "string" | "number" } = { }; export default function generateTypesV3( - schema: OpenAPI3, + input: OpenAPI3 | OpenAPI3Schemas, options?: SwaggerToTSOptions ): string { - if (!schema.components || !schema.components.schemas) { - throw new Error( - `⛔️ 'components' missing from schema https://swagger.io/specification` - ); + const { rawSchema = false } = options || {}; + let components: OpenAPI3Components; + + if (rawSchema) { + components = { schemas: input }; + } else { + components = (input as OpenAPI3).components; + + if (!components || !components.schemas) { + throw new Error( + `⛔️ 'components' missing from schema https://swagger.io/specification` + ); + } } // propertyMapper const propertyMapped = options - ? propertyMapper(schema.components.schemas, options.propertyMapper) - : schema.components.schemas; + ? propertyMapper(components.schemas, options.propertyMapper) + : components.schemas; // type converter function transform(node: OpenAPI3SchemaObject): string { switch (nodeType(node)) { case "ref": { - return transformRef(node.$ref); + return transformRef(node.$ref, rawSchema ? "schemas/" : ""); } case "string": case "number": @@ -139,17 +154,22 @@ export default function generateTypesV3( return output; } + if (rawSchema) { + const schemas = createKeys(propertyMapped, Object.keys(propertyMapped)); + + return `export interface schemas { + ${schemas} + }`; + } + const schemas = `schemas: { ${createKeys(propertyMapped, Object.keys(propertyMapped))} }`; - const responses = !schema.components.responses + const responses = !components.responses ? `` : `responses: { - ${createKeys( - schema.components.responses, - Object.keys(schema.components.responses) - )} + ${createKeys(components.responses, Object.keys(components.responses))} }`; // note: make sure that base-level schemas are required