openapi-typescript
Advanced tools
Comparing version 6.1.1 to 6.2.0
@@ -25,2 +25,3 @@ #!/usr/bin/env node | ||
--additional-properties (optional) Allow arbitrary properties for all schema objects without "additionalProperties: false" | ||
--empty-objects-unknown (optional) Allow arbitrary properties for schema objects with no specified properties, and no specified "additionalProperties" | ||
--default-non-nullable (optional) If a schema object has a default value set, don’t mark it as nullable | ||
@@ -50,2 +51,3 @@ --support-array-length (optional) Generate tuples using array minItems / maxItems | ||
"defaultNonNullable", | ||
"emptyObjectsUnknown", | ||
"immutableTypes", | ||
@@ -94,2 +96,3 @@ "contentNever", | ||
additionalProperties: flags.additionalProperties, | ||
emptyObjectsUnknown: flags.emptyObjectsUnknown, | ||
auth: flags.auth, | ||
@@ -96,0 +99,0 @@ defaultNonNullable: flags.defaultNonNullable, |
@@ -10,3 +10,3 @@ import { URL } from "node:url"; | ||
import transformSchemaObject from "./transform/schema-object.js"; | ||
import { error, escObjKey, getDefaultFetch, getEntries, indent } from "./utils.js"; | ||
import { error, escObjKey, getDefaultFetch, getEntries, getSchemaObjectComment, indent } from "./utils.js"; | ||
export * from "./types.js"; | ||
@@ -22,16 +22,17 @@ const EMPTY_OBJECT_RE = /^\s*\{?\s*\}?\s*$/; | ||
const ctx = { | ||
additionalProperties: options.additionalProperties || false, | ||
alphabetize: options.alphabetize || false, | ||
defaultNonNullable: options.defaultNonNullable || false, | ||
additionalProperties: options.additionalProperties ?? false, | ||
alphabetize: options.alphabetize ?? false, | ||
defaultNonNullable: options.defaultNonNullable ?? false, | ||
discriminators: {}, | ||
transform: options && typeof options.transform === "function" ? options.transform : undefined, | ||
postTransform: options && typeof options.postTransform === "function" ? options.postTransform : undefined, | ||
immutableTypes: options.immutableTypes || false, | ||
transform: typeof options.transform === "function" ? options.transform : undefined, | ||
postTransform: typeof options.postTransform === "function" ? options.postTransform : undefined, | ||
immutableTypes: options.immutableTypes ?? false, | ||
emptyObjectsUnknown: options.emptyObjectsUnknown ?? false, | ||
indentLv: 0, | ||
operations: {}, | ||
pathParamsAsTypes: options.pathParamsAsTypes || false, | ||
silent: options.silent || false, | ||
supportArrayLength: options.supportArrayLength || false, | ||
pathParamsAsTypes: options.pathParamsAsTypes ?? false, | ||
silent: options.silent ?? false, | ||
supportArrayLength: options.supportArrayLength ?? false, | ||
}; | ||
const isInlineSchema = typeof schema !== "string" && schema instanceof URL == false; | ||
const isInlineSchema = typeof schema !== "string" && schema instanceof URL === false; | ||
const allSchemas = {}; | ||
@@ -63,3 +64,3 @@ const schemaURL = typeof schema === "string" ? resolveSchema(schema) : schema; | ||
const output = []; | ||
if (options && "commentHeader" in options) { | ||
if ("commentHeader" in options) { | ||
if (options.commentHeader) | ||
@@ -95,2 +96,3 @@ output.push(options.commentHeader); | ||
let subschemaOutput = ""; | ||
let comment; | ||
switch (subschema.hint) { | ||
@@ -118,2 +120,3 @@ case "OpenAPI3": { | ||
case "OperationObject": { | ||
comment = getSchemaObjectComment(subschema.schema, indentLv); | ||
subschemaOutput = transformOperationObject(subschema.schema, { path, ctx: { ...ctx, indentLv } }); | ||
@@ -144,2 +147,4 @@ break; | ||
if (subschemaOutput && !EMPTY_OBJECT_RE.test(subschemaOutput)) { | ||
if (comment) | ||
output.push(indent(comment, indentLv)); | ||
output.push(indent(`${key}: ${subschemaOutput}`, indentLv)); | ||
@@ -157,4 +162,6 @@ } | ||
output.push(options.exportType ? "export type operations = {" : "export interface operations {", ""); | ||
for (const k of Object.keys(ctx.operations)) { | ||
output.push(indent(`${escObjKey(k)}: ${ctx.operations[k]};`, 1)); | ||
for (const [key, { operationType, comment }] of Object.entries(ctx.operations)) { | ||
if (comment) | ||
output.push(indent(comment, 1)); | ||
output.push(indent(`${escObjKey(key)}: ${operationType};`, 1)); | ||
} | ||
@@ -161,0 +168,0 @@ output.push(`}${options.exportType ? ";" : ""}`, ""); |
@@ -6,5 +6,5 @@ /// <reference types="node" /> | ||
import { URL } from "node:url"; | ||
type SchemaMap = { | ||
interface SchemaMap { | ||
[id: string]: Subschema; | ||
}; | ||
} | ||
export declare const VIRTUAL_JSON_URL = "file:///_json"; | ||
@@ -11,0 +11,0 @@ export declare function resolveSchema(filename: string): URL; |
@@ -63,3 +63,3 @@ import fs from "node:fs"; | ||
if (schema instanceof URL) { | ||
const hint = options.hint || "OpenAPI3"; | ||
const hint = options.hint ?? "OpenAPI3"; | ||
if (schema.href !== options.rootURL.href) | ||
@@ -86,3 +86,3 @@ schemaID = relativePath(options.rootURL, schema); | ||
const contentType = res.headers.get("content-type"); | ||
if (ext === ".json" || (contentType && contentType.includes("json"))) { | ||
if (ext === ".json" || contentType?.includes("json")) { | ||
options.schemas[schemaID] = { | ||
@@ -93,3 +93,3 @@ hint, | ||
} | ||
else if (ext === ".yaml" || ext === ".yml" || (contentType && contentType.includes("yaml"))) { | ||
else if (ext === ".yaml" || ext === ".yml" || contentType?.includes("yaml")) { | ||
options.schemas[schemaID] = { | ||
@@ -130,3 +130,3 @@ hint, | ||
hint: "OpenAPI3", | ||
schema: contents.charAt(0) === "{" ? parseJSON(contents) : parseYAML(contents), | ||
schema: contents.startsWith("{") ? parseJSON(contents) : parseYAML(contents), | ||
}; | ||
@@ -154,2 +154,4 @@ } | ||
rawNode[k] = rawNode[k].filter((o) => { | ||
if (!o || typeof o !== "object" || Array.isArray(o)) | ||
throw new Error(`${nodePath}.${k}: Expected array of objects. Is your schema valid?`); | ||
if (!("$ref" in o) || typeof o.$ref !== "string") | ||
@@ -162,2 +164,4 @@ return true; | ||
} | ||
if (!rawNode || typeof rawNode !== "object" || Array.isArray(rawNode)) | ||
throw new Error(`${nodePath}: Expected object, got ${Array.isArray(rawNode) ? "array" : typeof rawNode}. Is your schema valid?`); | ||
if (!("$ref" in rawNode) || typeof rawNode.$ref !== "string") | ||
@@ -164,0 +168,0 @@ return; |
@@ -10,5 +10,2 @@ import { escObjKey, getEntries, getSchemaObjectComment, indent, makeTSIndex, parseTSIndex, tsIntersectionOf, tsNonNullable, tsOptionalProperty, tsPick, tsReadonly, } from "../utils.js"; | ||
indentLv++; | ||
const c = getSchemaObjectComment(operationObject, indentLv); | ||
if (c) | ||
output.push(indent(c, indentLv)); | ||
{ | ||
@@ -32,3 +29,3 @@ if (operationObject.parameters) { | ||
if (c) | ||
parameterOutput.push(indent(c, indentLv)); | ||
inlineOutput.push(indent(c, indentLv)); | ||
const parameterType = transformParameterObject(p, { | ||
@@ -35,0 +32,0 @@ path: `${path}/parameters/${p.name}`, |
@@ -20,3 +20,6 @@ import { escStr, getSchemaObjectComment, indent } from "../utils.js"; | ||
const operationType = transformOperationObject(operationObject, { path, ctx: { ...ctx, indentLv: 1 } }); | ||
ctx.operations[operationObject.operationId] = operationType; | ||
ctx.operations[operationObject.operationId] = { | ||
operationType, | ||
comment: getSchemaObjectComment(operationObject, 1), | ||
}; | ||
output.push(indent(`${method}: operations[${escStr(operationObject.operationId)}];`, indentLv)); | ||
@@ -29,3 +32,3 @@ } | ||
} | ||
if (pathItem.parameters && pathItem.parameters.length) { | ||
if (pathItem.parameters?.length) { | ||
output.push(indent(transformOperationObject({ parameters: pathItem.parameters }, { path, ctx, wrapObject: false }).trim(), indentLv)); | ||
@@ -32,0 +35,0 @@ } |
@@ -87,3 +87,3 @@ import { escObjKey, escStr, getEntries, getSchemaObjectComment, indent, parseRef, tsArrayOf, tsIntersectionOf, tsOmit, tsOneOf, tsOptionalProperty, tsReadonly, tsTupleOf, tsUnionOf, tsWithRequired, } from "../utils.js"; | ||
else { | ||
return tsUnionOf(...Array.from({ length: (maxItems || 0) - minItems + 1 }) | ||
return tsUnionOf(...Array.from({ length: (maxItems ?? 0) - minItems + 1 }) | ||
.map((_, i) => i + minItems) | ||
@@ -97,5 +97,4 @@ .map((n) => { | ||
itemType = tsArrayOf(itemType); | ||
if (schemaObject.nullable) | ||
itemType = tsUnionOf(itemType, "null"); | ||
return ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(itemType) : itemType; | ||
itemType = ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(itemType) : itemType; | ||
return schemaObject.nullable ? tsUnionOf(itemType, "null") : itemType; | ||
} | ||
@@ -107,3 +106,3 @@ } | ||
indentLv++; | ||
for (const [k, v] of getEntries(schemaObject.properties || {}, ctx.alphabetize)) { | ||
for (const [k, v] of getEntries(schemaObject.properties ?? {}, ctx.alphabetize)) { | ||
const c = getSchemaObjectComment(v, indentLv); | ||
@@ -147,3 +146,3 @@ if (c) | ||
if (discriminator.mapping) { | ||
const matchedValue = Object.entries(discriminator.mapping).find(([_, v]) => (v[0] !== "#" && v === value) || (v[0] === "#" && parseRef(v).path.pop() === value)); | ||
const matchedValue = Object.entries(discriminator.mapping).find(([_, v]) => (!v.startsWith("#") && v === value) || (v.startsWith("#") && parseRef(v).path.pop() === value)); | ||
if (matchedValue) | ||
@@ -187,4 +186,6 @@ value = matchedValue[0]; | ||
finalType = tsUnionOf(finalType || "Record<string, unknown>", "null"); | ||
return finalType || "Record<string, never>"; | ||
if (finalType) | ||
return finalType; | ||
return ctx.emptyObjectsUnknown ? "Record<string, unknown>" : "Record<string, never>"; | ||
} | ||
//# sourceMappingURL=schema-object.js.map |
@@ -65,8 +65,8 @@ /// <reference types="node" /> | ||
} | ||
export type PathsObject = { | ||
export interface PathsObject { | ||
[pathname: string]: PathItemObject; | ||
}; | ||
export type WebhooksObject = { | ||
} | ||
export interface WebhooksObject { | ||
[name: string]: PathItemObject; | ||
}; | ||
} | ||
export interface PathItemObject extends Extensable { | ||
@@ -301,6 +301,7 @@ get?: OperationObject | ReferenceObject; | ||
auth?: string; | ||
emptyObjectsUnknown?: boolean; | ||
cwd?: URL; | ||
defaultNonNullable?: boolean; | ||
transform?: (schemaObject: SchemaObject, options: TransformSchemaObjectOptions) => string | undefined | void; | ||
postTransform?: (type: string, options: TransformSchemaObjectOptions) => string | undefined | void; | ||
transform?: (schemaObject: SchemaObject, options: TransformSchemaObjectOptions) => string | undefined; | ||
postTransform?: (type: string, options: TransformSchemaObjectOptions) => string | undefined; | ||
immutableTypes?: boolean; | ||
@@ -349,2 +350,3 @@ silent?: boolean; | ||
alphabetize: boolean; | ||
emptyObjectsUnknown: boolean; | ||
defaultNonNullable: boolean; | ||
@@ -358,3 +360,6 @@ discriminators: { | ||
indentLv: number; | ||
operations: Record<string, string>; | ||
operations: Record<string, { | ||
comment?: string; | ||
operationType: string; | ||
}>; | ||
pathParamsAsTypes: boolean; | ||
@@ -361,0 +366,0 @@ silent: boolean; |
import c from "ansi-colors"; | ||
import { Fetch } from "types"; | ||
export { c }; | ||
type CommentObject = { | ||
interface CommentObject { | ||
const?: unknown; | ||
@@ -16,3 +16,3 @@ default?: unknown; | ||
type?: string | string[]; | ||
}; | ||
} | ||
export declare const LB_RE: RegExp; | ||
@@ -42,3 +42,3 @@ export declare const DOUBLE_QUOTE_RE: RegExp; | ||
export declare function tsTupleOf(...types: string[]): string; | ||
export declare function tsUnionOf(...types: Array<string | number | boolean>): string; | ||
export declare function tsUnionOf(...types: (string | number | boolean)[]): string; | ||
export declare function escStr(input: any): string; | ||
@@ -45,0 +45,0 @@ export declare function escObjKey(input: string): string; |
@@ -43,4 +43,3 @@ import c from "ansi-colors"; | ||
const supportedJsDocTags = ["description", "default", "example"]; | ||
for (let index = 0; index < supportedJsDocTags.length; index++) { | ||
const field = supportedJsDocTags[index]; | ||
for (const field of supportedJsDocTags) { | ||
const allowEmptyString = field === "default" || field === "example"; | ||
@@ -70,6 +69,6 @@ if (v[field] === undefined) { | ||
const commentText = text.trim().replace(COMMENT_RE, "*\\/"); | ||
if (commentText.indexOf("\n") === -1) | ||
if (!commentText.includes("\n")) | ||
return `/** ${commentText} */`; | ||
const ln = indent(" * ", indentLv || 0); | ||
return ["/**", `${ln}${commentText.replace(LB_RE, `\n${ln}`)}`, indent(" */", indentLv || 0)].join("\n"); | ||
const ln = indent(" * ", indentLv ?? 0); | ||
return ["/**", `${ln}${commentText.replace(LB_RE, `\n${ln}`)}`, indent(" */", indentLv ?? 0)].join("\n"); | ||
} | ||
@@ -76,0 +75,0 @@ export function parseRef(ref) { |
{ | ||
"name": "openapi-typescript", | ||
"description": "Generate TypeScript types from Swagger OpenAPI specs", | ||
"version": "6.1.1", | ||
"version": "6.2.0", | ||
"author": "drew@pow.rs", | ||
@@ -60,10 +60,10 @@ "license": "MIT", | ||
"@types/js-yaml": "^4.0.5", | ||
"@types/node": "^18.14.1", | ||
"@typescript-eslint/eslint-plugin": "^5.53.0", | ||
"@typescript-eslint/parser": "^5.53.0", | ||
"@vitest/coverage-c8": "^0.28.5", | ||
"@types/node": "^18.15.0", | ||
"@typescript-eslint/eslint-plugin": "^5.54.1", | ||
"@typescript-eslint/parser": "^5.54.1", | ||
"@vitest/coverage-c8": "^0.29.2", | ||
"degit": "^2.8.4", | ||
"del-cli": "^5.0.0", | ||
"eslint": "^8.34.0", | ||
"eslint-config-prettier": "^8.6.0", | ||
"eslint": "^8.35.0", | ||
"eslint-config-prettier": "^8.7.0", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
@@ -73,5 +73,5 @@ "execa": "^6.1.0", | ||
"typescript": "^4.9.5", | ||
"vite-node": "^0.28.5", | ||
"vitest": "^0.28.5" | ||
"vite-node": "^0.29.2", | ||
"vitest": "^0.29.2" | ||
} | ||
} |
@@ -6,3 +6,3 @@ [![version(scoped)](https://img.shields.io/npm/v/openapi-typescript.svg)](https://www.npmjs.com/package/openapi-typescript) | ||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> | ||
[![All Contributors](https://img.shields.io/badge/all_contributors-64-orange.svg?style=flat-square)](#contributors-) | ||
[![All Contributors](https://img.shields.io/badge/all_contributors-65-orange.svg?style=flat-square)](#contributors-) | ||
<!-- ALL-CONTRIBUTORS-BADGE:END --> | ||
@@ -153,2 +153,3 @@ | ||
| `--additional-properties` | | `false` | Allow arbitrary properties for all schema objects without `additionalProperties: false` | | ||
| `--empty-objects-unknown` | | `false` | Allow arbitrary properties for schema objects with no specified properties, and no specified `additionalProperties` | | ||
| `--default-non-nullable` | | `false` | Treat schema objects with default values as non-nullable | | ||
@@ -403,2 +404,3 @@ | `--export-type` | `-t` | `false` | Export `type` instead of `interface` | | ||
<td align="center" valign="top" width="14.28%"><a href="https://trutoo.com/people/erik-hughes"><img src="https://avatars.githubusercontent.com/u/455178?v=4?s=100" width="100px;" alt="Erik Hughes"/><br /><sub><b>Erik Hughes</b></sub></a><br /><a href="https://github.com/drwpow/openapi-typescript/commits?author=Swiftwork" title="Code">💻</a> <a href="https://github.com/drwpow/openapi-typescript/issues?q=author%3ASwiftwork" title="Bug reports">🐛</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=Swiftwork" title="Tests">⚠️</a></td> | ||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mtth"><img src="https://avatars.githubusercontent.com/u/1216372?v=4?s=100" width="100px;" alt="Matthieu Monsch"/><br /><sub><b>Matthieu Monsch</b></sub></a><br /><a href="https://github.com/drwpow/openapi-typescript/commits?author=mtth" title="Code">💻</a> <a href="https://github.com/drwpow/openapi-typescript/issues?q=author%3Amtth" title="Bug reports">🐛</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=mtth" title="Tests">⚠️</a></td> | ||
</tr> | ||
@@ -405,0 +407,0 @@ </tbody> |
@@ -12,3 +12,3 @@ import type { GlobalContext, OpenAPI3, OpenAPITSOptions, Subschema } from "./types"; | ||
import transformSchemaObject from "./transform/schema-object.js"; | ||
import { error, escObjKey, getDefaultFetch, getEntries, indent } from "./utils.js"; | ||
import { error, escObjKey, getDefaultFetch, getEntries, getSchemaObjectComment, indent } from "./utils.js"; | ||
export * from "./types.js"; // expose all types to consumers | ||
@@ -43,18 +43,19 @@ | ||
const ctx: GlobalContext = { | ||
additionalProperties: options.additionalProperties || false, | ||
alphabetize: options.alphabetize || false, | ||
defaultNonNullable: options.defaultNonNullable || false, | ||
additionalProperties: options.additionalProperties ?? false, | ||
alphabetize: options.alphabetize ?? false, | ||
defaultNonNullable: options.defaultNonNullable ?? false, | ||
discriminators: {}, | ||
transform: options && typeof options.transform === "function" ? options.transform : undefined, | ||
postTransform: options && typeof options.postTransform === "function" ? options.postTransform : undefined, | ||
immutableTypes: options.immutableTypes || false, | ||
transform: typeof options.transform === "function" ? options.transform : undefined, | ||
postTransform: typeof options.postTransform === "function" ? options.postTransform : undefined, | ||
immutableTypes: options.immutableTypes ?? false, | ||
emptyObjectsUnknown: options.emptyObjectsUnknown ?? false, | ||
indentLv: 0, | ||
operations: {}, | ||
pathParamsAsTypes: options.pathParamsAsTypes || false, | ||
silent: options.silent || false, | ||
supportArrayLength: options.supportArrayLength || false, | ||
pathParamsAsTypes: options.pathParamsAsTypes ?? false, | ||
silent: options.silent ?? false, | ||
supportArrayLength: options.supportArrayLength ?? false, | ||
}; | ||
// note: we may be loading many large schemas into memory at once; take care to reuse references without cloning | ||
const isInlineSchema = typeof schema !== "string" && schema instanceof URL == false; | ||
const isInlineSchema = typeof schema !== "string" && schema instanceof URL === false; // eslint-disable-line @typescript-eslint/no-unnecessary-boolean-literal-compare | ||
@@ -94,3 +95,3 @@ // 1. load schema (and subschemas) | ||
// 2a. Start file, inject custom code (if any) | ||
if (options && "commentHeader" in options) { | ||
if ("commentHeader" in options) { | ||
if (options.commentHeader) output.push(options.commentHeader); | ||
@@ -131,2 +132,3 @@ } else { | ||
let subschemaOutput = ""; | ||
let comment: string | undefined; | ||
switch (subschema.hint) { | ||
@@ -151,2 +153,3 @@ case "OpenAPI3": { | ||
case "OperationObject": { | ||
comment = getSchemaObjectComment(subschema.schema, indentLv); | ||
subschemaOutput = transformOperationObject(subschema.schema, { path, ctx: { ...ctx, indentLv } }); | ||
@@ -177,2 +180,3 @@ break; | ||
if (subschemaOutput && !EMPTY_OBJECT_RE.test(subschemaOutput)) { | ||
if (comment) output.push(indent(comment, indentLv)); | ||
output.push(indent(`${key}: ${subschemaOutput}`, indentLv)); | ||
@@ -191,4 +195,5 @@ } | ||
output.push(options.exportType ? "export type operations = {" : "export interface operations {", ""); | ||
for (const k of Object.keys(ctx.operations)) { | ||
output.push(indent(`${escObjKey(k)}: ${ctx.operations[k]};`, 1)); | ||
for (const [key, { operationType, comment }] of Object.entries(ctx.operations)) { | ||
if (comment) output.push(indent(comment, 1)); | ||
output.push(indent(`${escObjKey(key)}: ${operationType};`, 1)); | ||
} | ||
@@ -195,0 +200,0 @@ output.push(`}${options.exportType ? ";" : ""}`, ""); |
@@ -22,3 +22,5 @@ import type { | ||
type SchemaMap = { [id: string]: Subschema }; | ||
interface SchemaMap { | ||
[id: string]: Subschema; | ||
} | ||
@@ -113,3 +115,3 @@ export const VIRTUAL_JSON_URL = `file:///_json`; // fake URL reserved for dynamic JSON | ||
if (schema instanceof URL) { | ||
const hint = options.hint || "OpenAPI3"; | ||
const hint = options.hint ?? "OpenAPI3"; | ||
@@ -141,3 +143,3 @@ // normalize ID | ||
const contentType = res.headers.get("content-type"); | ||
if (ext === ".json" || (contentType && contentType.includes("json"))) { | ||
if (ext === ".json" || contentType?.includes("json")) { | ||
options.schemas[schemaID] = { | ||
@@ -147,3 +149,3 @@ hint, | ||
}; | ||
} else if (ext === ".yaml" || ext === ".yml" || (contentType && contentType.includes("yaml"))) { | ||
} else if (ext === ".yaml" || ext === ".yml" || contentType?.includes("yaml")) { | ||
options.schemas[schemaID] = { | ||
@@ -187,3 +189,3 @@ hint, | ||
hint: "OpenAPI3", | ||
schema: contents.charAt(0) === "{" ? parseJSON(contents) : parseYAML(contents), | ||
schema: contents.startsWith("{") ? parseJSON(contents) : parseYAML(contents), | ||
}; | ||
@@ -219,2 +221,4 @@ } | ||
rawNode[k] = (rawNode as any)[k].filter((o: SchemaObject | ReferenceObject) => { | ||
if (!o || typeof o !== "object" || Array.isArray(o)) | ||
throw new Error(`${nodePath}.${k}: Expected array of objects. Is your schema valid?`); | ||
if (!("$ref" in o) || typeof o.$ref !== "string") return true; | ||
@@ -226,3 +230,6 @@ const ref = parseRef(o.$ref); | ||
} | ||
if (!rawNode || typeof rawNode !== "object" || Array.isArray(rawNode)) | ||
throw new Error( | ||
`${nodePath}: Expected object, got ${Array.isArray(rawNode) ? "array" : typeof rawNode}. Is your schema valid?` | ||
); | ||
if (!("$ref" in rawNode) || typeof rawNode.$ref !== "string") return; | ||
@@ -229,0 +236,0 @@ const node = rawNode as unknown as ReferenceObject; |
@@ -33,4 +33,2 @@ import type { GlobalContext, OperationObject, ParameterObject } from "../types"; | ||
indentLv++; | ||
const c = getSchemaObjectComment(operationObject, indentLv); | ||
if (c) output.push(indent(c, indentLv)); | ||
@@ -55,3 +53,3 @@ // parameters | ||
const c = getSchemaObjectComment(p, indentLv); | ||
if (c) parameterOutput.push(indent(c, indentLv)); | ||
if (c) inlineOutput.push(indent(c, indentLv)); | ||
const parameterType = transformParameterObject(p, { | ||
@@ -72,3 +70,3 @@ path: `${path}/parameters/${p.name}`, | ||
if (paramI === -1 || parts[paramI + 1] !== paramIn || !parts[paramI + 2]) continue; | ||
const key = parts.pop() as string; | ||
const key = parts.pop()!; | ||
const index = makeTSIndex(parts); | ||
@@ -75,0 +73,0 @@ if (!refs[index]) refs[index] = [key]; |
@@ -33,3 +33,6 @@ import type { GlobalContext, PathItemObject } from "../types"; | ||
const operationType = transformOperationObject(operationObject, { path, ctx: { ...ctx, indentLv: 1 } }); | ||
ctx.operations[operationObject.operationId] = operationType; | ||
ctx.operations[operationObject.operationId] = { | ||
operationType, | ||
comment: getSchemaObjectComment(operationObject, 1), | ||
}; | ||
output.push(indent(`${method}: operations[${escStr(operationObject.operationId)}];`, indentLv)); | ||
@@ -43,3 +46,3 @@ } else { | ||
// parameters | ||
if (pathItem.parameters && pathItem.parameters.length) { | ||
if (pathItem.parameters?.length) { | ||
output.push( | ||
@@ -46,0 +49,0 @@ indent( |
@@ -147,3 +147,3 @@ import type { GlobalContext, ReferenceObject, SchemaObject } from "../types"; | ||
return tsUnionOf( | ||
...Array.from({ length: (maxItems || 0) - minItems + 1 }) | ||
...Array.from({ length: (maxItems ?? 0) - minItems + 1 }) | ||
.map((_, i) => i + minItems) | ||
@@ -158,4 +158,4 @@ .map((n) => { | ||
itemType = tsArrayOf(itemType); | ||
if (schemaObject.nullable) itemType = tsUnionOf(itemType, "null"); | ||
return ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(itemType) : itemType; | ||
itemType = ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(itemType) : itemType; | ||
return schemaObject.nullable ? tsUnionOf(itemType, "null") : itemType; | ||
} | ||
@@ -174,3 +174,3 @@ } | ||
indentLv++; | ||
for (const [k, v] of getEntries(schemaObject.properties || {}, ctx.alphabetize)) { | ||
for (const [k, v] of getEntries(schemaObject.properties ?? {}, ctx.alphabetize)) { | ||
const c = getSchemaObjectComment(v, indentLv); | ||
@@ -210,7 +210,7 @@ if (c) coreType.push(indent(c, indentLv)); | ||
const discriminator = ctx.discriminators[discriminatorRef.$ref]; | ||
let value = parseRef(path).path.pop() as string; | ||
let value = parseRef(path).path.pop()!; | ||
if (discriminator.mapping) { | ||
// Mapping value can either be a fully-qualified ref (#/components/schemas/XYZ) or a schema name (XYZ) | ||
const matchedValue = Object.entries(discriminator.mapping).find( | ||
([_, v]) => (v[0] !== "#" && v === value) || (v[0] === "#" && parseRef(v).path.pop() === value) | ||
([_, v]) => (!v.startsWith("#") && v === value) || (v.startsWith("#") && parseRef(v).path.pop() === value) | ||
); | ||
@@ -262,3 +262,6 @@ if (matchedValue) value = matchedValue[0]; // why was this designed backwards!? | ||
return finalType || "Record<string, never>"; // if no type could be generated, fall back to “empty object” type | ||
if (finalType) return finalType; | ||
// if no type could be generated, fall back to “empty object” type | ||
return ctx.emptyObjectsUnknown ? "Record<string, unknown>" : "Record<string, never>"; | ||
} |
@@ -140,3 +140,5 @@ import type { URL } from "node:url"; | ||
*/ | ||
export type PathsObject = { [pathname: string]: PathItemObject }; | ||
export interface PathsObject { | ||
[pathname: string]: PathItemObject; | ||
} | ||
@@ -147,3 +149,5 @@ /** | ||
*/ | ||
export type WebhooksObject = { [name: string]: PathItemObject }; | ||
export interface WebhooksObject { | ||
[name: string]: PathItemObject; | ||
} | ||
@@ -572,2 +576,4 @@ /** | ||
auth?: string; | ||
/** Allow schema objects with no specified properties to have additional properties if not expressly forbidden? (default: false) */ | ||
emptyObjectsUnknown?: boolean; | ||
/** Specify current working directory (cwd) to resolve remote schemas on disk (not needed for remote URL schemas) */ | ||
@@ -578,5 +584,5 @@ cwd?: URL; | ||
/** Manually transform certain Schema Objects with a custom TypeScript type */ | ||
transform?: (schemaObject: SchemaObject, options: TransformSchemaObjectOptions) => string | undefined | void; | ||
transform?: (schemaObject: SchemaObject, options: TransformSchemaObjectOptions) => string | undefined; | ||
/** Modify TypeScript types built from Schema Objects */ | ||
postTransform?: (type: string, options: TransformSchemaObjectOptions) => string | undefined | void; | ||
postTransform?: (type: string, options: TransformSchemaObjectOptions) => string | undefined; | ||
/** Add readonly properties and readonly arrays? (default: false) */ | ||
@@ -639,2 +645,3 @@ immutableTypes?: boolean; | ||
alphabetize: boolean; | ||
emptyObjectsUnknown: boolean; | ||
defaultNonNullable: boolean; | ||
@@ -646,3 +653,9 @@ discriminators: { [$ref: string]: DiscriminatorObject }; | ||
indentLv: number; | ||
operations: Record<string, string>; | ||
operations: Record< | ||
string, | ||
{ | ||
comment?: string; | ||
operationType: string; | ||
} | ||
>; | ||
pathParamsAsTypes: boolean; | ||
@@ -649,0 +662,0 @@ silent: boolean; |
@@ -7,2 +7,3 @@ import c from "ansi-colors"; | ||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare | ||
if (!supportsColor.stdout || supportsColor.stdout.hasBasic === false) c.enabled = false; | ||
@@ -12,3 +13,3 @@ | ||
type CommentObject = { | ||
interface CommentObject { | ||
const?: unknown; // jsdoc without value | ||
@@ -25,3 +26,3 @@ default?: unknown; // jsdoc with value | ||
type?: string | string[]; // Type of node | ||
}; | ||
} | ||
@@ -73,5 +74,4 @@ const COMMENT_RE = /\*\//g; | ||
// * JSDOC tags with value | ||
const supportedJsDocTags: Array<keyof CommentObject> = ["description", "default", "example"]; | ||
for (let index = 0; index < supportedJsDocTags.length; index++) { | ||
const field = supportedJsDocTags[index]; | ||
const supportedJsDocTags: (keyof CommentObject)[] = ["description", "default", "example"]; | ||
for (const field of supportedJsDocTags) { | ||
const allowEmptyString = field === "default" || field === "example"; | ||
@@ -107,7 +107,7 @@ if (v[field] === undefined) { | ||
// if single-line comment | ||
if (commentText.indexOf("\n") === -1) return `/** ${commentText} */`; | ||
if (!commentText.includes("\n")) return `/** ${commentText} */`; | ||
// if multi-line comment | ||
const ln = indent(" * ", indentLv || 0); | ||
return ["/**", `${ln}${commentText.replace(LB_RE, `\n${ln}`)}`, indent(" */", indentLv || 0)].join("\n"); | ||
const ln = indent(" * ", indentLv ?? 0); | ||
return ["/**", `${ln}${commentText.replace(LB_RE, `\n${ln}`)}`, indent(" */", indentLv ?? 0)].join("\n"); | ||
} | ||
@@ -211,3 +211,3 @@ | ||
/** X | Y | Z */ | ||
export function tsUnionOf(...types: Array<string | number | boolean>): string { | ||
export function tsUnionOf(...types: (string | number | boolean)[]): string { | ||
if (types.length === 1) return String(types[0]); // don’t add parentheses around one thing | ||
@@ -214,0 +214,0 @@ return types.map((t) => (TS_UNION_INTERSECTION_RE.test(String(t)) ? `(${t})` : t)).join(" | "); |
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
302391
4441
414