openapi-typescript
Advanced tools
Comparing version 3.2.3 to 3.3.0-next.0
@@ -7,3 +7,3 @@ #!/usr/bin/env node | ||
const meow = require("meow"); | ||
const { default: swaggerToTS } = require("../dist/cjs/index.js"); | ||
const { default: openapiTS } = require("../dist/cjs/index.js"); | ||
const { loadSpec } = require("./loaders"); | ||
@@ -76,3 +76,3 @@ | ||
// 2. generate schema (the main part!) | ||
const result = swaggerToTS(spec, { | ||
const result = openapiTS(spec, { | ||
immutableTypes: cli.flags.immutableTypes, | ||
@@ -79,0 +79,0 @@ prettierConfig: cli.flags.prettierConfig, |
import { OpenAPI2, OpenAPI3, SchemaObject, SwaggerToTSOptions } from "./types"; | ||
export * from "./types"; | ||
export declare const WARNING_MESSAGE = "/**\n* This file was auto-generated by openapi-typescript.\n* Do not make direct changes to the file.\n*/\n\n\n"; | ||
export default function swaggerToTS(schema: OpenAPI2 | OpenAPI3 | Record<string, SchemaObject>, options?: SwaggerToTSOptions): string; | ||
export default function openapiTS(schema: OpenAPI2 | OpenAPI3 | Record<string, SchemaObject>, options?: SwaggerToTSOptions): string; |
@@ -30,6 +30,7 @@ "use strict"; | ||
`; | ||
function swaggerToTS(schema, options) { | ||
function openapiTS(schema, options) { | ||
const version = (options && options.version) || utils_1.swaggerVersion(schema); | ||
let output = `${exports.WARNING_MESSAGE} | ||
${index_1.transformAll(schema, { | ||
formatter: options && typeof options.formatter === "function" ? options.formatter : undefined, | ||
immutableTypes: (options && options.immutableTypes) || false, | ||
@@ -59,3 +60,3 @@ rawSchema: options && options.rawSchema, | ||
} | ||
exports.default = swaggerToTS; | ||
exports.default = openapiTS; | ||
//# sourceMappingURL=index.js.map |
@@ -1,4 +0,5 @@ | ||
import { HeaderObject } from "../types"; | ||
export declare function transformHeaderObjMap(headerMap: Record<string, HeaderObject>, { immutableTypes }: { | ||
import { HeaderObject, SchemaFormatter } from "../types"; | ||
export declare function transformHeaderObjMap(headerMap: Record<string, HeaderObject>, options: { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
}): string; |
@@ -6,3 +6,3 @@ "use strict"; | ||
const schema_1 = require("./schema"); | ||
function transformHeaderObjMap(headerMap, { immutableTypes }) { | ||
function transformHeaderObjMap(headerMap, options) { | ||
let output = ""; | ||
@@ -14,7 +14,5 @@ Object.entries(headerMap).forEach(([k, v]) => { | ||
output += utils_1.comment(v.description); | ||
const readonly = utils_1.tsReadonly(immutableTypes); | ||
const readonly = utils_1.tsReadonly(options.immutableTypes); | ||
const required = v.required ? "" : "?"; | ||
output += ` ${readonly}"${k}"${required}: ${schema_1.transformSchemaObj(v.schema, { | ||
immutableTypes, | ||
})}\n`; | ||
output += ` ${readonly}"${k}"${required}: ${schema_1.transformSchemaObj(v.schema, options)}\n`; | ||
}); | ||
@@ -21,0 +19,0 @@ return output; |
@@ -0,2 +1,4 @@ | ||
import { SchemaFormatter } from "../types"; | ||
interface TransformOptions { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -6,3 +8,3 @@ rawSchema?: boolean; | ||
} | ||
export declare function transformAll(schema: any, { immutableTypes, rawSchema, version }: TransformOptions): string; | ||
export declare function transformAll(schema: any, { formatter, immutableTypes, rawSchema, version }: TransformOptions): string; | ||
export {}; |
@@ -10,3 +10,3 @@ "use strict"; | ||
const schema_1 = require("./schema"); | ||
function transformAll(schema, { immutableTypes, rawSchema, version }) { | ||
function transformAll(schema, { formatter, immutableTypes, rawSchema, version }) { | ||
const readonly = utils_1.tsReadonly(immutableTypes); | ||
@@ -19,2 +19,3 @@ let output = ""; | ||
return `export interface definitions {\n ${schema_1.transformSchemaObjMap(schema, { | ||
formatter, | ||
immutableTypes, | ||
@@ -26,2 +27,3 @@ required: Object.keys(schema), | ||
return `export interface schemas {\n ${schema_1.transformSchemaObjMap(schema, { | ||
formatter, | ||
immutableTypes, | ||
@@ -47,2 +49,3 @@ required: Object.keys(schema), | ||
output += `export interface definitions {\n ${schema_1.transformSchemaObjMap(schema.definitions, { | ||
formatter, | ||
immutableTypes, | ||
@@ -55,2 +58,3 @@ required: Object.keys(schema.definitions), | ||
output += `export interface parameters {\n ${schema_1.transformSchemaObjMap(schema.parameters, { | ||
formatter, | ||
immutableTypes, | ||
@@ -62,2 +66,3 @@ required, | ||
output += `export interface responses {\n ${responses_1.transformResponsesObj(schema.responses, { | ||
formatter, | ||
immutableTypes, | ||
@@ -74,2 +79,3 @@ })}\n }\n\n`; | ||
output += ` ${readonly}schemas: {\n ${schema_1.transformSchemaObjMap(schema.components.schemas, { | ||
formatter, | ||
immutableTypes, | ||
@@ -81,2 +87,3 @@ required, | ||
output += ` ${readonly}responses: {\n ${responses_1.transformResponsesObj(schema.components.responses, { | ||
formatter, | ||
immutableTypes, | ||
@@ -88,2 +95,3 @@ })}\n }\n`; | ||
output += ` ${readonly}parameters: {\n ${schema_1.transformSchemaObjMap(schema.components.parameters, { | ||
formatter, | ||
immutableTypes, | ||
@@ -95,2 +103,3 @@ required, | ||
output += ` ${readonly}requestBodies: {\n ${responses_1.transformRequestBodies(schema.components.requestBodies, { | ||
formatter, | ||
immutableTypes, | ||
@@ -101,2 +110,3 @@ })}\n }\n`; | ||
output += ` ${readonly}headers: {\n ${headers_1.transformHeaderObjMap(schema.components.headers, { | ||
formatter, | ||
immutableTypes, | ||
@@ -103,0 +113,0 @@ })} }\n`; |
@@ -1,3 +0,4 @@ | ||
import { RequestBody } from "../types"; | ||
import { RequestBody, SchemaFormatter } from "../types"; | ||
interface Options { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -4,0 +5,0 @@ } |
@@ -0,2 +1,4 @@ | ||
import { SchemaFormatter } from "types"; | ||
interface TransformSchemaObjMapOptions { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -7,2 +9,3 @@ required?: string[]; | ||
interface Options { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -9,0 +12,0 @@ } |
@@ -13,3 +13,6 @@ "use strict"; | ||
output += `${readonly}"${key}"${required.includes(key) ? "" : "?"}: `; | ||
output += transformSchemaObj(value.schema || value, { immutableTypes: options.immutableTypes }); | ||
output += transformSchemaObj(value.schema || value, { | ||
formatter: options.formatter, | ||
immutableTypes: options.immutableTypes, | ||
}); | ||
output += `;\n`; | ||
@@ -34,74 +37,80 @@ }); | ||
} | ||
switch (utils_1.nodeType(node)) { | ||
case "ref": { | ||
output += utils_1.transformRef(node.$ref); | ||
break; | ||
} | ||
case "string": | ||
case "number": | ||
case "boolean": { | ||
output += utils_1.nodeType(node) || "any"; | ||
break; | ||
} | ||
case "enum": { | ||
const items = []; | ||
node.enum.forEach((item) => { | ||
if (typeof item === "string") | ||
items.push(`'${item.replace(/'/g, "\\'")}'`); | ||
else if (typeof item === "number" || typeof item === "boolean") | ||
items.push(item); | ||
else if (item === null && !node.nullable) | ||
items.push("null"); | ||
}); | ||
output += utils_1.tsUnionOf(items); | ||
break; | ||
} | ||
case "object": { | ||
const isAnyOfOrOneOfOrAllOf = "anyOf" in node || "oneOf" in node || "allOf" in node; | ||
if (!isAnyOfOrOneOfOrAllOf && | ||
(!node.properties || !Object.keys(node.properties).length) && | ||
!node.additionalProperties) { | ||
output += `{ ${readonly}[key: string]: any }`; | ||
const overriddenType = options.formatter && options.formatter(node); | ||
if (overriddenType) { | ||
output += overriddenType; | ||
} | ||
else { | ||
switch (utils_1.nodeType(node)) { | ||
case "ref": { | ||
output += utils_1.transformRef(node.$ref); | ||
break; | ||
} | ||
let properties = transformSchemaObjMap(node.properties || {}, { | ||
immutableTypes: options.immutableTypes, | ||
required: node.required, | ||
}); | ||
let additionalProperties; | ||
if (node.additionalProperties) { | ||
if (node.additionalProperties === true || Object.keys(node.additionalProperties).length === 0) { | ||
additionalProperties = `{ [key: string]: any }`; | ||
case "string": | ||
case "number": | ||
case "boolean": { | ||
output += utils_1.nodeType(node) || "any"; | ||
break; | ||
} | ||
case "enum": { | ||
const items = []; | ||
node.enum.forEach((item) => { | ||
if (typeof item === "string") | ||
items.push(`'${item.replace(/'/g, "\\'")}'`); | ||
else if (typeof item === "number" || typeof item === "boolean") | ||
items.push(item); | ||
else if (item === null && !node.nullable) | ||
items.push("null"); | ||
}); | ||
output += utils_1.tsUnionOf(items); | ||
break; | ||
} | ||
case "object": { | ||
const isAnyOfOrOneOfOrAllOf = "anyOf" in node || "oneOf" in node || "allOf" in node; | ||
if (!isAnyOfOrOneOfOrAllOf && | ||
(!node.properties || !Object.keys(node.properties).length) && | ||
!node.additionalProperties) { | ||
output += `{ ${readonly}[key: string]: any }`; | ||
break; | ||
} | ||
else if (typeof node.additionalProperties === "object") { | ||
const oneOf = node.additionalProperties.oneOf || undefined; | ||
const anyOf = node.additionalProperties.anyOf || undefined; | ||
if (oneOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformOneOf(oneOf, options)}; }`; | ||
let properties = transformSchemaObjMap(node.properties || {}, { | ||
immutableTypes: options.immutableTypes, | ||
required: node.required, | ||
}); | ||
let additionalProperties; | ||
if (node.additionalProperties) { | ||
if (node.additionalProperties === true || Object.keys(node.additionalProperties).length === 0) { | ||
additionalProperties = `{ [key: string]: any }`; | ||
} | ||
else if (anyOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformAnyOf(anyOf, options)}; }`; | ||
else if (typeof node.additionalProperties === "object") { | ||
const oneOf = node.additionalProperties.oneOf || undefined; | ||
const anyOf = node.additionalProperties.anyOf || undefined; | ||
if (oneOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformOneOf(oneOf, options)}; }`; | ||
} | ||
else if (anyOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformAnyOf(anyOf, options)}; }`; | ||
} | ||
else { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformSchemaObj(node.additionalProperties, options) || "any"}; }`; | ||
} | ||
} | ||
else { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformSchemaObj(node.additionalProperties, options) || "any"}; }`; | ||
} | ||
} | ||
output += utils_1.tsIntersectionOf([ | ||
...(node.allOf ? node.allOf.map((node) => transformSchemaObj(node, options)) : []), | ||
...(node.anyOf ? [transformAnyOf(node.anyOf, options)] : []), | ||
...(node.oneOf ? [transformOneOf(node.oneOf, options)] : []), | ||
...(properties ? [`{\n${properties}\n}`] : []), | ||
...(additionalProperties ? [additionalProperties] : []), | ||
]); | ||
break; | ||
} | ||
output += utils_1.tsIntersectionOf([ | ||
...(node.allOf ? node.allOf.map((node) => transformSchemaObj(node, options)) : []), | ||
...(node.anyOf ? [transformAnyOf(node.anyOf, options)] : []), | ||
...(node.oneOf ? [transformOneOf(node.oneOf, options)] : []), | ||
...(properties ? [`{\n${properties}\n}`] : []), | ||
...(additionalProperties ? [additionalProperties] : []), | ||
]); | ||
break; | ||
} | ||
case "array": { | ||
if (Array.isArray(node.items)) { | ||
output += `${readonly}${utils_1.tsTupleOf(node.items.map((node) => transformSchemaObj(node, options)))}`; | ||
case "array": { | ||
if (Array.isArray(node.items)) { | ||
output += `${readonly}${utils_1.tsTupleOf(node.items.map((node) => transformSchemaObj(node, options)))}`; | ||
} | ||
else { | ||
output += `${readonly}${utils_1.tsArrayOf(node.items ? transformSchemaObj(node.items, options) : "any")}`; | ||
} | ||
break; | ||
} | ||
else { | ||
output += `${readonly}${utils_1.tsArrayOf(node.items ? transformSchemaObj(node.items, options) : "any")}`; | ||
} | ||
break; | ||
} | ||
@@ -108,0 +117,0 @@ } |
@@ -102,4 +102,7 @@ export interface OpenAPI2 { | ||
anyOf?: (ReferenceObject | SchemaObject)[]; | ||
format?: string; | ||
} | ||
export declare type SchemaFormatter = (schemaObj: SchemaObject) => string | undefined; | ||
export interface SwaggerToTSOptions { | ||
formatter?: SchemaFormatter; | ||
immutableTypes?: boolean; | ||
@@ -106,0 +109,0 @@ prettierConfig?: string; |
import { OpenAPI2, OpenAPI3, SchemaObject, SwaggerToTSOptions } from "./types"; | ||
export * from "./types"; | ||
export declare const WARNING_MESSAGE = "/**\n* This file was auto-generated by openapi-typescript.\n* Do not make direct changes to the file.\n*/\n\n\n"; | ||
export default function swaggerToTS(schema: OpenAPI2 | OpenAPI3 | Record<string, SchemaObject>, options?: SwaggerToTSOptions): string; | ||
export default function openapiTS(schema: OpenAPI2 | OpenAPI3 | Record<string, SchemaObject>, options?: SwaggerToTSOptions): string; |
@@ -14,6 +14,7 @@ import path from "path"; | ||
`; | ||
export default function swaggerToTS(schema, options) { | ||
export default function openapiTS(schema, options) { | ||
const version = (options && options.version) || swaggerVersion(schema); | ||
let output = `${WARNING_MESSAGE} | ||
${transformAll(schema, { | ||
formatter: options && typeof options.formatter === "function" ? options.formatter : undefined, | ||
immutableTypes: (options && options.immutableTypes) || false, | ||
@@ -20,0 +21,0 @@ rawSchema: options && options.rawSchema, |
@@ -1,4 +0,5 @@ | ||
import { HeaderObject } from "../types"; | ||
export declare function transformHeaderObjMap(headerMap: Record<string, HeaderObject>, { immutableTypes }: { | ||
import { HeaderObject, SchemaFormatter } from "../types"; | ||
export declare function transformHeaderObjMap(headerMap: Record<string, HeaderObject>, options: { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
}): string; |
import { comment, tsReadonly } from "../utils"; | ||
import { transformSchemaObj } from "./schema"; | ||
export function transformHeaderObjMap(headerMap, { immutableTypes }) { | ||
export function transformHeaderObjMap(headerMap, options) { | ||
let output = ""; | ||
@@ -10,7 +10,5 @@ Object.entries(headerMap).forEach(([k, v]) => { | ||
output += comment(v.description); | ||
const readonly = tsReadonly(immutableTypes); | ||
const readonly = tsReadonly(options.immutableTypes); | ||
const required = v.required ? "" : "?"; | ||
output += ` ${readonly}"${k}"${required}: ${transformSchemaObj(v.schema, { | ||
immutableTypes, | ||
})}\n`; | ||
output += ` ${readonly}"${k}"${required}: ${transformSchemaObj(v.schema, options)}\n`; | ||
}); | ||
@@ -17,0 +15,0 @@ return output; |
@@ -0,2 +1,4 @@ | ||
import { SchemaFormatter } from "../types"; | ||
interface TransformOptions { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -6,3 +8,3 @@ rawSchema?: boolean; | ||
} | ||
export declare function transformAll(schema: any, { immutableTypes, rawSchema, version }: TransformOptions): string; | ||
export declare function transformAll(schema: any, { formatter, immutableTypes, rawSchema, version }: TransformOptions): string; | ||
export {}; |
@@ -7,3 +7,3 @@ import { comment, tsReadonly } from "../utils"; | ||
import { transformSchemaObjMap } from "./schema"; | ||
export function transformAll(schema, { immutableTypes, rawSchema, version }) { | ||
export function transformAll(schema, { formatter, immutableTypes, rawSchema, version }) { | ||
const readonly = tsReadonly(immutableTypes); | ||
@@ -16,2 +16,3 @@ let output = ""; | ||
return `export interface definitions {\n ${transformSchemaObjMap(schema, { | ||
formatter, | ||
immutableTypes, | ||
@@ -23,2 +24,3 @@ required: Object.keys(schema), | ||
return `export interface schemas {\n ${transformSchemaObjMap(schema, { | ||
formatter, | ||
immutableTypes, | ||
@@ -44,2 +46,3 @@ required: Object.keys(schema), | ||
output += `export interface definitions {\n ${transformSchemaObjMap(schema.definitions, { | ||
formatter, | ||
immutableTypes, | ||
@@ -52,2 +55,3 @@ required: Object.keys(schema.definitions), | ||
output += `export interface parameters {\n ${transformSchemaObjMap(schema.parameters, { | ||
formatter, | ||
immutableTypes, | ||
@@ -59,2 +63,3 @@ required, | ||
output += `export interface responses {\n ${transformResponsesObj(schema.responses, { | ||
formatter, | ||
immutableTypes, | ||
@@ -71,2 +76,3 @@ })}\n }\n\n`; | ||
output += ` ${readonly}schemas: {\n ${transformSchemaObjMap(schema.components.schemas, { | ||
formatter, | ||
immutableTypes, | ||
@@ -78,2 +84,3 @@ required, | ||
output += ` ${readonly}responses: {\n ${transformResponsesObj(schema.components.responses, { | ||
formatter, | ||
immutableTypes, | ||
@@ -85,2 +92,3 @@ })}\n }\n`; | ||
output += ` ${readonly}parameters: {\n ${transformSchemaObjMap(schema.components.parameters, { | ||
formatter, | ||
immutableTypes, | ||
@@ -92,2 +100,3 @@ required, | ||
output += ` ${readonly}requestBodies: {\n ${transformRequestBodies(schema.components.requestBodies, { | ||
formatter, | ||
immutableTypes, | ||
@@ -98,2 +107,3 @@ })}\n }\n`; | ||
output += ` ${readonly}headers: {\n ${transformHeaderObjMap(schema.components.headers, { | ||
formatter, | ||
immutableTypes, | ||
@@ -100,0 +110,0 @@ })} }\n`; |
@@ -1,3 +0,4 @@ | ||
import { RequestBody } from "../types"; | ||
import { RequestBody, SchemaFormatter } from "../types"; | ||
interface Options { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -4,0 +5,0 @@ } |
@@ -0,2 +1,4 @@ | ||
import { SchemaFormatter } from "types"; | ||
interface TransformSchemaObjMapOptions { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -7,2 +9,3 @@ required?: string[]; | ||
interface Options { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -9,0 +12,0 @@ } |
@@ -10,3 +10,6 @@ import { comment, nodeType, transformRef, tsArrayOf, tsIntersectionOf, tsPartial, tsTupleOf, tsUnionOf, } from "../utils"; | ||
output += `${readonly}"${key}"${required.includes(key) ? "" : "?"}: `; | ||
output += transformSchemaObj(value.schema || value, { immutableTypes: options.immutableTypes }); | ||
output += transformSchemaObj(value.schema || value, { | ||
formatter: options.formatter, | ||
immutableTypes: options.immutableTypes, | ||
}); | ||
output += `;\n`; | ||
@@ -28,74 +31,80 @@ }); | ||
} | ||
switch (nodeType(node)) { | ||
case "ref": { | ||
output += transformRef(node.$ref); | ||
break; | ||
} | ||
case "string": | ||
case "number": | ||
case "boolean": { | ||
output += nodeType(node) || "any"; | ||
break; | ||
} | ||
case "enum": { | ||
const items = []; | ||
node.enum.forEach((item) => { | ||
if (typeof item === "string") | ||
items.push(`'${item.replace(/'/g, "\\'")}'`); | ||
else if (typeof item === "number" || typeof item === "boolean") | ||
items.push(item); | ||
else if (item === null && !node.nullable) | ||
items.push("null"); | ||
}); | ||
output += tsUnionOf(items); | ||
break; | ||
} | ||
case "object": { | ||
const isAnyOfOrOneOfOrAllOf = "anyOf" in node || "oneOf" in node || "allOf" in node; | ||
if (!isAnyOfOrOneOfOrAllOf && | ||
(!node.properties || !Object.keys(node.properties).length) && | ||
!node.additionalProperties) { | ||
output += `{ ${readonly}[key: string]: any }`; | ||
const overriddenType = options.formatter && options.formatter(node); | ||
if (overriddenType) { | ||
output += overriddenType; | ||
} | ||
else { | ||
switch (nodeType(node)) { | ||
case "ref": { | ||
output += transformRef(node.$ref); | ||
break; | ||
} | ||
let properties = transformSchemaObjMap(node.properties || {}, { | ||
immutableTypes: options.immutableTypes, | ||
required: node.required, | ||
}); | ||
let additionalProperties; | ||
if (node.additionalProperties) { | ||
if (node.additionalProperties === true || Object.keys(node.additionalProperties).length === 0) { | ||
additionalProperties = `{ [key: string]: any }`; | ||
case "string": | ||
case "number": | ||
case "boolean": { | ||
output += nodeType(node) || "any"; | ||
break; | ||
} | ||
case "enum": { | ||
const items = []; | ||
node.enum.forEach((item) => { | ||
if (typeof item === "string") | ||
items.push(`'${item.replace(/'/g, "\\'")}'`); | ||
else if (typeof item === "number" || typeof item === "boolean") | ||
items.push(item); | ||
else if (item === null && !node.nullable) | ||
items.push("null"); | ||
}); | ||
output += tsUnionOf(items); | ||
break; | ||
} | ||
case "object": { | ||
const isAnyOfOrOneOfOrAllOf = "anyOf" in node || "oneOf" in node || "allOf" in node; | ||
if (!isAnyOfOrOneOfOrAllOf && | ||
(!node.properties || !Object.keys(node.properties).length) && | ||
!node.additionalProperties) { | ||
output += `{ ${readonly}[key: string]: any }`; | ||
break; | ||
} | ||
else if (typeof node.additionalProperties === "object") { | ||
const oneOf = node.additionalProperties.oneOf || undefined; | ||
const anyOf = node.additionalProperties.anyOf || undefined; | ||
if (oneOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformOneOf(oneOf, options)}; }`; | ||
let properties = transformSchemaObjMap(node.properties || {}, { | ||
immutableTypes: options.immutableTypes, | ||
required: node.required, | ||
}); | ||
let additionalProperties; | ||
if (node.additionalProperties) { | ||
if (node.additionalProperties === true || Object.keys(node.additionalProperties).length === 0) { | ||
additionalProperties = `{ [key: string]: any }`; | ||
} | ||
else if (anyOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformAnyOf(anyOf, options)}; }`; | ||
else if (typeof node.additionalProperties === "object") { | ||
const oneOf = node.additionalProperties.oneOf || undefined; | ||
const anyOf = node.additionalProperties.anyOf || undefined; | ||
if (oneOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformOneOf(oneOf, options)}; }`; | ||
} | ||
else if (anyOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformAnyOf(anyOf, options)}; }`; | ||
} | ||
else { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformSchemaObj(node.additionalProperties, options) || "any"}; }`; | ||
} | ||
} | ||
else { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformSchemaObj(node.additionalProperties, options) || "any"}; }`; | ||
} | ||
} | ||
output += tsIntersectionOf([ | ||
...(node.allOf ? node.allOf.map((node) => transformSchemaObj(node, options)) : []), | ||
...(node.anyOf ? [transformAnyOf(node.anyOf, options)] : []), | ||
...(node.oneOf ? [transformOneOf(node.oneOf, options)] : []), | ||
...(properties ? [`{\n${properties}\n}`] : []), | ||
...(additionalProperties ? [additionalProperties] : []), | ||
]); | ||
break; | ||
} | ||
output += tsIntersectionOf([ | ||
...(node.allOf ? node.allOf.map((node) => transformSchemaObj(node, options)) : []), | ||
...(node.anyOf ? [transformAnyOf(node.anyOf, options)] : []), | ||
...(node.oneOf ? [transformOneOf(node.oneOf, options)] : []), | ||
...(properties ? [`{\n${properties}\n}`] : []), | ||
...(additionalProperties ? [additionalProperties] : []), | ||
]); | ||
break; | ||
} | ||
case "array": { | ||
if (Array.isArray(node.items)) { | ||
output += `${readonly}${tsTupleOf(node.items.map((node) => transformSchemaObj(node, options)))}`; | ||
case "array": { | ||
if (Array.isArray(node.items)) { | ||
output += `${readonly}${tsTupleOf(node.items.map((node) => transformSchemaObj(node, options)))}`; | ||
} | ||
else { | ||
output += `${readonly}${tsArrayOf(node.items ? transformSchemaObj(node.items, options) : "any")}`; | ||
} | ||
break; | ||
} | ||
else { | ||
output += `${readonly}${tsArrayOf(node.items ? transformSchemaObj(node.items, options) : "any")}`; | ||
} | ||
break; | ||
} | ||
@@ -102,0 +111,0 @@ } |
@@ -102,4 +102,7 @@ export interface OpenAPI2 { | ||
anyOf?: (ReferenceObject | SchemaObject)[]; | ||
format?: string; | ||
} | ||
export declare type SchemaFormatter = (schemaObj: SchemaObject) => string | undefined; | ||
export interface SwaggerToTSOptions { | ||
formatter?: SchemaFormatter; | ||
immutableTypes?: boolean; | ||
@@ -106,0 +109,0 @@ prettierConfig?: string; |
{ | ||
"name": "openapi-typescript", | ||
"description": "Generate TypeScript types from Swagger OpenAPI specs", | ||
"version": "3.2.3", | ||
"version": "3.3.0-next.0", | ||
"engines": { | ||
@@ -6,0 +6,0 @@ "node": ">= 10.0.0" |
@@ -24,3 +24,3 @@ [![version(scoped)](https://img.shields.io/npm/v/openapi-typescript.svg)](https://www.npmjs.com/package/openapi-typescript) | ||
### CLI | ||
### 🖥️ CLI | ||
@@ -118,3 +118,3 @@ #### 🗄️ Reading specs from file system | ||
### Node | ||
### 🐢 Node | ||
@@ -127,6 +127,6 @@ ```bash | ||
const { readFileSync } = require("fs"); | ||
const swaggerToTS = require("openapi-typescript").default; | ||
const openapiTS = require("openapi-typescript").default; | ||
const input = JSON.parse(readFileSync("spec.json", "utf8")); // Input can be any JS object (OpenAPI format) | ||
const output = swaggerToTS(input); // Outputs TypeScript defs as a string (to be parsed, or written to a file) | ||
const output = openapiTS(input); // Outputs TypeScript defs as a string (to be parsed, or written to a file) | ||
``` | ||
@@ -141,8 +141,33 @@ | ||
## Migrating from v1 to v2 | ||
#### Custom Formatter | ||
[Migrating from v1 to v2](./docs/migrating-from-v1.md) | ||
If using the Node.js API, you can optionally pass a **formatter** to openapi-typescript. This is useful if you want to override the default types and substitute your own. | ||
## Project Goals | ||
For example, say your schema has the following property: | ||
```yaml | ||
properties: | ||
updated_at: | ||
type: string | ||
format: date-time | ||
``` | ||
By default, this will generate a type `updated_at?: string;`. But we can override this by passing a formatter to the Node API, like so: | ||
```js | ||
const types = openapiTS(mySchema, { | ||
formatter(node: SchemaObject) { | ||
if (node.format === 'date-time') { | ||
return "Date"; // return the TypeScript “Date” type, as a string | ||
} | ||
// for all other schema objects, let openapi-typescript decide (return undefined) | ||
}); | ||
``` | ||
This will generate `updated_at?: Date` instead. Note that you will still have to do the parsing of your data yourself. But this will save you from having to also update all your types. | ||
_Note: you don’t have to use `.format`—this is just an example! You can use any property on a schema object to overwrite its generated type if desired._ | ||
## 🏅 Project Goals | ||
1. Support converting any OpenAPI 3.0 or 2.0 (Swagger) schema to TypeScript types, no matter how complicated | ||
@@ -154,3 +179,3 @@ 1. The generated TypeScript types **must** match your schema as closely as possible (i.e. don’t convert names to | ||
## Contributing | ||
## 🤝 Contributing | ||
@@ -164,2 +189,3 @@ PRs are welcome! Please see our [CONTRIBUTING.md](./CONTRIBUTING.md) guide. Opening an issue beforehand to discuss is | ||
[npm-run-all]: https://www.npmjs.com/package/npm-run-all | ||
[openapi-format]: https://swagger.io/specification/#data-types | ||
[openapi-operationid]: https://swagger.io/specification/#operation-object | ||
@@ -166,0 +192,0 @@ [openapi2]: https://swagger.io/specification/v2/ |
@@ -17,3 +17,3 @@ import path from "path"; | ||
export default function swaggerToTS( | ||
export default function openapiTS( | ||
schema: OpenAPI2 | OpenAPI3 | Record<string, SchemaObject>, | ||
@@ -28,2 +28,3 @@ options?: SwaggerToTSOptions | ||
${transformAll(schema, { | ||
formatter: options && typeof options.formatter === "function" ? options.formatter : undefined, | ||
immutableTypes: (options && options.immutableTypes) || false, | ||
@@ -30,0 +31,0 @@ rawSchema: options && options.rawSchema, |
@@ -1,2 +0,2 @@ | ||
import { HeaderObject } from "../types"; | ||
import { HeaderObject, SchemaFormatter } from "../types"; | ||
import { comment, tsReadonly } from "../utils"; | ||
@@ -7,3 +7,3 @@ import { transformSchemaObj } from "./schema"; | ||
headerMap: Record<string, HeaderObject>, | ||
{ immutableTypes }: { immutableTypes: boolean } | ||
options: { formatter?: SchemaFormatter; immutableTypes: boolean } | ||
): string { | ||
@@ -17,8 +17,6 @@ let output = ""; | ||
const readonly = tsReadonly(immutableTypes); | ||
const readonly = tsReadonly(options.immutableTypes); | ||
const required = v.required ? "" : "?"; | ||
output += ` ${readonly}"${k}"${required}: ${transformSchemaObj(v.schema, { | ||
immutableTypes, | ||
})}\n`; | ||
output += ` ${readonly}"${k}"${required}: ${transformSchemaObj(v.schema, options)}\n`; | ||
}); | ||
@@ -25,0 +23,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { OperationObject, PathItemObject } from "../types"; | ||
import { OperationObject, PathItemObject, SchemaFormatter } from "../types"; | ||
import { comment, tsReadonly } from "../utils"; | ||
@@ -10,2 +10,3 @@ import { transformHeaderObjMap } from "./headers"; | ||
interface TransformOptions { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -16,3 +17,3 @@ rawSchema?: boolean; | ||
export function transformAll(schema: any, { immutableTypes, rawSchema, version }: TransformOptions): string { | ||
export function transformAll(schema: any, { formatter, immutableTypes, rawSchema, version }: TransformOptions): string { | ||
const readonly = tsReadonly(immutableTypes); | ||
@@ -29,2 +30,3 @@ | ||
return `export interface definitions {\n ${transformSchemaObjMap(schema, { | ||
formatter, | ||
immutableTypes, | ||
@@ -36,2 +38,3 @@ required: Object.keys(schema), | ||
return `export interface schemas {\n ${transformSchemaObjMap(schema, { | ||
formatter, | ||
immutableTypes, | ||
@@ -61,2 +64,3 @@ required: Object.keys(schema), | ||
output += `export interface definitions {\n ${transformSchemaObjMap(schema.definitions, { | ||
formatter, | ||
immutableTypes, | ||
@@ -71,2 +75,3 @@ required: Object.keys(schema.definitions), | ||
output += `export interface parameters {\n ${transformSchemaObjMap(schema.parameters, { | ||
formatter, | ||
immutableTypes, | ||
@@ -80,2 +85,3 @@ required, | ||
output += `export interface responses {\n ${transformResponsesObj(schema.responses, { | ||
formatter, | ||
immutableTypes, | ||
@@ -95,2 +101,3 @@ })}\n }\n\n`; | ||
output += ` ${readonly}schemas: {\n ${transformSchemaObjMap(schema.components.schemas, { | ||
formatter, | ||
immutableTypes, | ||
@@ -104,2 +111,3 @@ required, | ||
output += ` ${readonly}responses: {\n ${transformResponsesObj(schema.components.responses, { | ||
formatter, | ||
immutableTypes, | ||
@@ -113,2 +121,3 @@ })}\n }\n`; | ||
output += ` ${readonly}parameters: {\n ${transformSchemaObjMap(schema.components.parameters, { | ||
formatter, | ||
immutableTypes, | ||
@@ -122,2 +131,3 @@ required, | ||
output += ` ${readonly}requestBodies: {\n ${transformRequestBodies(schema.components.requestBodies, { | ||
formatter, | ||
immutableTypes, | ||
@@ -130,2 +140,3 @@ })}\n }\n`; | ||
output += ` ${readonly}headers: {\n ${transformHeaderObjMap(schema.components.headers, { | ||
formatter, | ||
immutableTypes, | ||
@@ -132,0 +143,0 @@ })} }\n`; |
@@ -1,2 +0,2 @@ | ||
import { RequestBody } from "../types"; | ||
import { RequestBody, SchemaFormatter } from "../types"; | ||
import { comment, transformRef, tsReadonly } from "../utils"; | ||
@@ -10,2 +10,3 @@ import { transformHeaderObjMap } from "./headers"; | ||
interface Options { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -12,0 +13,0 @@ } |
@@ -0,1 +1,2 @@ | ||
import { SchemaFormatter } from "types"; | ||
import { | ||
@@ -13,2 +14,3 @@ comment, | ||
interface TransformSchemaObjMapOptions { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -33,3 +35,6 @@ required?: string[]; | ||
// 3. transform | ||
output += transformSchemaObj(value.schema || value, { immutableTypes: options.immutableTypes }); | ||
output += transformSchemaObj(value.schema || value, { | ||
formatter: options.formatter, | ||
immutableTypes: options.immutableTypes, | ||
}); | ||
@@ -44,2 +49,3 @@ // 4. close | ||
interface Options { | ||
formatter?: SchemaFormatter; | ||
immutableTypes: boolean; | ||
@@ -69,81 +75,87 @@ } | ||
// transform core type | ||
switch (nodeType(node)) { | ||
case "ref": { | ||
output += transformRef(node.$ref); | ||
break; | ||
} | ||
case "string": | ||
case "number": | ||
case "boolean": { | ||
output += nodeType(node) || "any"; | ||
break; | ||
} | ||
case "enum": { | ||
const items: Array<string | number | boolean> = []; | ||
(node.enum as unknown[]).forEach((item) => { | ||
if (typeof item === "string") items.push(`'${item.replace(/'/g, "\\'")}'`); | ||
else if (typeof item === "number" || typeof item === "boolean") items.push(item); | ||
else if (item === null && !node.nullable) items.push("null"); | ||
}); | ||
output += tsUnionOf(items); | ||
break; | ||
} | ||
case "object": { | ||
const isAnyOfOrOneOfOrAllOf = "anyOf" in node || "oneOf" in node || "allOf" in node; | ||
// if empty object, then return generic map type | ||
if ( | ||
!isAnyOfOrOneOfOrAllOf && | ||
(!node.properties || !Object.keys(node.properties).length) && | ||
!node.additionalProperties | ||
) { | ||
output += `{ ${readonly}[key: string]: any }`; | ||
// pass in formatter, if specified | ||
const overriddenType = options.formatter && options.formatter(node); | ||
if (overriddenType) { | ||
output += overriddenType; | ||
} else { | ||
// transform core type | ||
switch (nodeType(node)) { | ||
case "ref": { | ||
output += transformRef(node.$ref); | ||
break; | ||
} | ||
case "string": | ||
case "number": | ||
case "boolean": { | ||
output += nodeType(node) || "any"; | ||
break; | ||
} | ||
case "enum": { | ||
const items: Array<string | number | boolean> = []; | ||
(node.enum as unknown[]).forEach((item) => { | ||
if (typeof item === "string") items.push(`'${item.replace(/'/g, "\\'")}'`); | ||
else if (typeof item === "number" || typeof item === "boolean") items.push(item); | ||
else if (item === null && !node.nullable) items.push("null"); | ||
}); | ||
output += tsUnionOf(items); | ||
break; | ||
} | ||
case "object": { | ||
const isAnyOfOrOneOfOrAllOf = "anyOf" in node || "oneOf" in node || "allOf" in node; | ||
let properties = transformSchemaObjMap(node.properties || {}, { | ||
immutableTypes: options.immutableTypes, | ||
required: node.required, | ||
}); | ||
// if empty object, then return generic map type | ||
if ( | ||
!isAnyOfOrOneOfOrAllOf && | ||
(!node.properties || !Object.keys(node.properties).length) && | ||
!node.additionalProperties | ||
) { | ||
output += `{ ${readonly}[key: string]: any }`; | ||
break; | ||
} | ||
// if additional properties, add an intersection with a generic map type | ||
let additionalProperties: string | undefined; | ||
if (node.additionalProperties) { | ||
if (node.additionalProperties === true || Object.keys(node.additionalProperties).length === 0) { | ||
additionalProperties = `{ [key: string]: any }`; | ||
} else if (typeof node.additionalProperties === "object") { | ||
const oneOf: any[] | undefined = (node.additionalProperties as any).oneOf || undefined; // TypeScript does a really bad job at inference here, so we enforce a type | ||
const anyOf: any[] | undefined = (node.additionalProperties as any).anyOf || undefined; // " | ||
if (oneOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformOneOf(oneOf, options)}; }`; | ||
} else if (anyOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformAnyOf(anyOf, options)}; }`; | ||
} else { | ||
additionalProperties = `{ ${readonly}[key: string]: ${ | ||
transformSchemaObj(node.additionalProperties, options) || "any" | ||
}; }`; | ||
let properties = transformSchemaObjMap(node.properties || {}, { | ||
immutableTypes: options.immutableTypes, | ||
required: node.required, | ||
}); | ||
// if additional properties, add an intersection with a generic map type | ||
let additionalProperties: string | undefined; | ||
if (node.additionalProperties) { | ||
if (node.additionalProperties === true || Object.keys(node.additionalProperties).length === 0) { | ||
additionalProperties = `{ [key: string]: any }`; | ||
} else if (typeof node.additionalProperties === "object") { | ||
const oneOf: any[] | undefined = (node.additionalProperties as any).oneOf || undefined; // TypeScript does a really bad job at inference here, so we enforce a type | ||
const anyOf: any[] | undefined = (node.additionalProperties as any).anyOf || undefined; // " | ||
if (oneOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformOneOf(oneOf, options)}; }`; | ||
} else if (anyOf) { | ||
additionalProperties = `{ ${readonly}[key: string]: ${transformAnyOf(anyOf, options)}; }`; | ||
} else { | ||
additionalProperties = `{ ${readonly}[key: string]: ${ | ||
transformSchemaObj(node.additionalProperties, options) || "any" | ||
}; }`; | ||
} | ||
} | ||
} | ||
} | ||
output += tsIntersectionOf([ | ||
// append allOf/anyOf/oneOf first | ||
...(node.allOf ? (node.allOf as any[]).map((node) => transformSchemaObj(node, options)) : []), | ||
...(node.anyOf ? [transformAnyOf(node.anyOf, options)] : []), | ||
...(node.oneOf ? [transformOneOf(node.oneOf, options)] : []), | ||
...(properties ? [`{\n${properties}\n}`] : []), // then properties (line breaks are important!) | ||
...(additionalProperties ? [additionalProperties] : []), // then additional properties | ||
]); | ||
output += tsIntersectionOf([ | ||
// append allOf/anyOf/oneOf first | ||
...(node.allOf ? (node.allOf as any[]).map((node) => transformSchemaObj(node, options)) : []), | ||
...(node.anyOf ? [transformAnyOf(node.anyOf, options)] : []), | ||
...(node.oneOf ? [transformOneOf(node.oneOf, options)] : []), | ||
...(properties ? [`{\n${properties}\n}`] : []), // then properties (line breaks are important!) | ||
...(additionalProperties ? [additionalProperties] : []), // then additional properties | ||
]); | ||
break; | ||
} | ||
break; | ||
} | ||
case "array": { | ||
if (Array.isArray(node.items)) { | ||
output += `${readonly}${tsTupleOf(node.items.map((node: any) => transformSchemaObj(node, options)))}`; | ||
} else { | ||
output += `${readonly}${tsArrayOf(node.items ? transformSchemaObj(node.items as any, options) : "any")}`; | ||
case "array": { | ||
if (Array.isArray(node.items)) { | ||
output += `${readonly}${tsTupleOf(node.items.map((node: any) => transformSchemaObj(node, options)))}`; | ||
} else { | ||
output += `${readonly}${tsArrayOf(node.items ? transformSchemaObj(node.items as any, options) : "any")}`; | ||
} | ||
break; | ||
} | ||
break; | ||
} | ||
@@ -150,0 +162,0 @@ } |
@@ -108,5 +108,10 @@ export interface OpenAPI2 { | ||
anyOf?: (ReferenceObject | SchemaObject)[]; // V3 ONLY | ||
format?: string; // V3 ONLY | ||
} | ||
export type SchemaFormatter = (schemaObj: SchemaObject) => string | undefined; | ||
export interface SwaggerToTSOptions { | ||
/** Specify a formatter */ | ||
formatter?: SchemaFormatter; | ||
/** Generates immutable types (readonly properties and readonly array) */ | ||
@@ -113,0 +118,0 @@ immutableTypes?: boolean; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
176183
2679
250
1