openapi-typescript
Advanced tools
Comparing version 6.2.0 to 6.2.1
@@ -7,2 +7,3 @@ import { URL } from "node:url"; | ||
import transformParameterObject from "./transform/parameter-object.js"; | ||
import transformParameterObjectArray from "./transform/parameter-object-array.js"; | ||
import transformRequestBodyObject from "./transform/request-body-object.js"; | ||
@@ -33,2 +34,3 @@ import transformResponseObject from "./transform/response-object.js"; | ||
pathParamsAsTypes: options.pathParamsAsTypes ?? false, | ||
parameters: {}, | ||
silent: options.silent ?? false, | ||
@@ -87,3 +89,3 @@ supportArrayLength: options.supportArrayLength ?? false, | ||
let indentLv = 0; | ||
output.push(options.exportType ? "export type external = {" : "export interface external {", ""); | ||
output.push(options.exportType ? "export type external = {" : "export interface external {"); | ||
externalKeys.sort((a, b) => a.localeCompare(b, "en", { numeric: true })); | ||
@@ -127,2 +129,16 @@ indentLv++; | ||
} | ||
case "ParameterObject[]": { | ||
if (typeof subschema.schema === "object" && ("schema" in subschema.schema || "type" in subschema.schema)) { | ||
subschemaOutput = transformSchemaObject(subschema.schema, { path, ctx: { ...ctx, indentLv } }); | ||
} | ||
else { | ||
subschemaOutput += "{\n"; | ||
indentLv++; | ||
subschemaOutput += transformParameterObjectArray(subschema.schema, { path, ctx: { ...ctx, indentLv } }); | ||
subschemaOutput += "\n"; | ||
indentLv--; | ||
subschemaOutput += indent("};", indentLv); | ||
} | ||
break; | ||
} | ||
case "RequestBodyObject": { | ||
@@ -129,0 +145,0 @@ subschemaOutput = transformRequestBodyObject(subschema.schema, { path, ctx: { ...ctx, indentLv } }); |
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import type { Fetch, GlobalContext, Subschema } from "./types"; | ||
import type { Fetch, GlobalContext, ParameterObject, Subschema } from "./types"; | ||
import { Readable } from "node:stream"; | ||
@@ -20,2 +20,3 @@ import { URL } from "node:url"; | ||
fetch: Fetch; | ||
parameters: Record<string, ParameterObject>; | ||
} | ||
@@ -22,0 +23,0 @@ export default function load(schema: URL | Subschema | Readable, options: LoadOptions): Promise<{ |
@@ -199,3 +199,3 @@ import fs from "node:fs"; | ||
for (const subschemaID of Object.keys(options.schemas)) { | ||
walk(options.schemas[subschemaID].schema, (rawNode) => { | ||
walk(options.schemas[subschemaID].schema, (rawNode, nodePath) => { | ||
if (!("$ref" in rawNode) || typeof rawNode.$ref !== "string") | ||
@@ -216,2 +216,10 @@ return; | ||
for (const k of Object.keys(options.schemas)) { | ||
walk(options.schemas[k].schema, (rawNode, nodePath) => { | ||
if (typeof rawNode === "object" && "in" in rawNode) { | ||
const key = k === "." ? makeTSIndex(nodePath) : makeTSIndex(["external", k, ...nodePath]); | ||
options.parameters[key] = rawNode; | ||
} | ||
}); | ||
} | ||
for (const k of Object.keys(options.schemas)) { | ||
if (JSON.stringify(options.schemas[k].schema).includes('"discriminator"')) { | ||
@@ -284,3 +292,3 @@ walk(options.schemas[k].schema, (rawNode, nodePath) => { | ||
case "parameters": | ||
return getHintFromParameterObject(path.slice(1)); | ||
return "ParameterObject[]"; | ||
case "requestBody": | ||
@@ -304,4 +312,8 @@ return getHintFromRequestBodyObject(path.slice(1)); | ||
switch (path[0]) { | ||
case "parameters": | ||
case "parameters": { | ||
if (typeof path[1] === "number") { | ||
return "ParameterObject[]"; | ||
} | ||
return getHintFromParameterObject(path.slice(1)); | ||
} | ||
default: | ||
@@ -308,0 +320,0 @@ return getHintFromOperationObject(path.slice(1)); |
@@ -1,2 +0,2 @@ | ||
import { escObjKey, getEntries, getSchemaObjectComment, indent, makeTSIndex, parseTSIndex, tsIntersectionOf, tsNonNullable, tsOptionalProperty, tsPick, tsReadonly, } from "../utils.js"; | ||
import { escObjKey, getEntries, getSchemaObjectComment, indent, tsOptionalProperty, tsReadonly } from "../utils.js"; | ||
import transformParameterObject from "./parameter-object.js"; | ||
@@ -15,43 +15,28 @@ import transformRequestBodyObject from "./request-body-object.js"; | ||
for (const paramIn of ["query", "header", "path", "cookie"]) { | ||
const inlineOutput = []; | ||
const refs = {}; | ||
const paramInternalOutput = []; | ||
indentLv++; | ||
for (const p of operationObject.parameters) { | ||
if ("in" in p) { | ||
if (p.in !== paramIn) | ||
continue; | ||
let key = escObjKey(p.name); | ||
if (paramIn !== "path" && !p.required) { | ||
key = tsOptionalProperty(key); | ||
} | ||
const c = getSchemaObjectComment(p, indentLv); | ||
if (c) | ||
inlineOutput.push(indent(c, indentLv)); | ||
const parameterType = transformParameterObject(p, { | ||
path: `${path}/parameters/${p.name}`, | ||
for (const param of operationObject.parameters ?? []) { | ||
const node = "$ref" in param ? ctx.parameters[param.$ref] : param; | ||
if (node?.in !== paramIn) | ||
continue; | ||
let key = escObjKey(node.name); | ||
if (paramIn !== "path" && !node.required) | ||
key = tsOptionalProperty(key); | ||
const c = getSchemaObjectComment(param, indentLv); | ||
if (c) | ||
paramInternalOutput.push(indent(c, indentLv)); | ||
const parameterType = "$ref" in param | ||
? param.$ref | ||
: transformParameterObject(param, { | ||
path: `${path}/parameters/${param.name}`, | ||
ctx: { ...ctx, indentLv }, | ||
}); | ||
inlineOutput.push(indent(`${key}: ${parameterType};`, indentLv)); | ||
} | ||
else if (p.$ref) { | ||
const parts = parseTSIndex(p.$ref); | ||
const paramI = parts.indexOf("parameters"); | ||
if (paramI === -1 || parts[paramI + 1] !== paramIn || !parts[paramI + 2]) | ||
continue; | ||
const key = parts.pop(); | ||
const index = makeTSIndex(parts); | ||
if (!refs[index]) | ||
refs[index] = [key]; | ||
else | ||
refs[index].push(key); | ||
} | ||
paramInternalOutput.push(indent(`${key}: ${parameterType};`, indentLv)); | ||
} | ||
indentLv--; | ||
if (!inlineOutput.length && !Object.keys(refs).length) | ||
continue; | ||
const paramType = tsIntersectionOf(...(inlineOutput.length ? [`{\n${inlineOutput.join("\n")}\n${indent("}", indentLv)}`] : []), ...Object.entries(refs).map(([root, keys]) => paramIn === "path" ? tsPick(root, keys) : tsPick(tsNonNullable(root), keys))); | ||
let key = paramIn; | ||
if (ctx.immutableTypes) | ||
key = tsReadonly(key); | ||
parameterOutput.push(indent(`${key}: ${paramType};`, indentLv)); | ||
if (paramInternalOutput.length) { | ||
parameterOutput.push(indent(`${paramIn}: {`, indentLv)); | ||
parameterOutput.push(...paramInternalOutput); | ||
parameterOutput.push(indent(`};`, indentLv)); | ||
} | ||
} | ||
@@ -58,0 +43,0 @@ indentLv--; |
@@ -27,3 +27,7 @@ import { escStr, getSchemaObjectComment, indent } from "../utils.js"; | ||
else { | ||
const operationType = transformOperationObject(operationObject, { path, ctx: { ...ctx, indentLv } }); | ||
const keyedParameters = {}; | ||
for (const parameter of [...(pathItem.parameters ?? []), ...(operationObject.parameters ?? [])]) { | ||
keyedParameters["$ref" in parameter ? parameter.$ref : parameter.name] = parameter; | ||
} | ||
const operationType = transformOperationObject({ ...operationObject, parameters: Object.values(keyedParameters) }, { path, ctx: { ...ctx, indentLv } }); | ||
output.push(indent(`${method}: ${operationType};`, indentLv)); | ||
@@ -30,0 +34,0 @@ } |
@@ -27,3 +27,3 @@ import { escObjKey, escStr, getEntries, getSchemaObjectComment, indent, parseRef, tsArrayOf, tsIntersectionOf, tsOmit, tsOneOf, tsOptionalProperty, tsReadonly, tsTupleOf, tsUnionOf, tsWithRequired, } from "../utils.js"; | ||
} | ||
if (schemaObject.const) { | ||
if (schemaObject.const !== null && schemaObject.const !== undefined) { | ||
let schemaConst = schemaObject.const; | ||
@@ -56,3 +56,3 @@ if ("type" in schemaObject) { | ||
const maybeTypes = schemaObject.oneOf.map((item) => transformSchemaObject(item, { path, ctx })); | ||
if (maybeTypes.some((t) => t.includes("{"))) | ||
if (maybeTypes.some((t) => typeof t === "string" && t.includes("{"))) | ||
return tsOneOf(...maybeTypes); | ||
@@ -59,0 +59,0 @@ return tsUnionOf(...maybeTypes); |
@@ -336,2 +336,5 @@ /// <reference types="node" /> | ||
} | { | ||
hint: "ParameterObject[]"; | ||
schema: (ParameterObject | ReferenceObject)[] | Record<string, ParameterObject | ReferenceObject>; | ||
} | { | ||
hint: "RequestBodyObject"; | ||
@@ -362,2 +365,3 @@ schema: RequestBodyObject; | ||
}>; | ||
parameters: Record<string, ParameterObject>; | ||
pathParamsAsTypes: boolean; | ||
@@ -364,0 +368,0 @@ silent: boolean; |
{ | ||
"name": "openapi-typescript", | ||
"description": "Generate TypeScript types from Swagger OpenAPI specs", | ||
"version": "6.2.0", | ||
"version": "6.2.1", | ||
"author": "drew@pow.rs", | ||
@@ -54,3 +54,3 @@ "license": "MIT", | ||
"supports-color": "^9.3.1", | ||
"undici": "^5.20.0", | ||
"undici": "^5.21.0", | ||
"yargs-parser": "^21.1.1" | ||
@@ -61,17 +61,17 @@ }, | ||
"@types/js-yaml": "^4.0.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", | ||
"@types/node": "^18.15.11", | ||
"@typescript-eslint/eslint-plugin": "^5.57.0", | ||
"@typescript-eslint/parser": "^5.57.0", | ||
"@vitest/coverage-c8": "^0.29.8", | ||
"degit": "^2.8.4", | ||
"del-cli": "^5.0.0", | ||
"eslint": "^8.35.0", | ||
"eslint-config-prettier": "^8.7.0", | ||
"eslint": "^8.37.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"execa": "^6.1.0", | ||
"prettier": "^2.8.4", | ||
"typescript": "^4.9.5", | ||
"vite-node": "^0.29.2", | ||
"vitest": "^0.29.2" | ||
"prettier": "^2.8.7", | ||
"typescript": "^5.0.3", | ||
"vite-node": "^0.29.8", | ||
"vitest": "^0.29.8" | ||
} | ||
} |
@@ -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-65-orange.svg?style=flat-square)](#contributors-) | ||
[![All Contributors](https://img.shields.io/badge/all_contributors-67-orange.svg?style=flat-square)](#contributors-) | ||
<!-- ALL-CONTRIBUTORS-BADGE:END --> | ||
@@ -280,3 +280,3 @@ | ||
if ("format" in schemaObject && schemaObject.format === "date-time") { | ||
return "Date"; | ||
return schemaObject.nullable ? "Date | null" : "Date"; | ||
} | ||
@@ -387,3 +387,3 @@ }, | ||
<td align="center" valign="top" width="14.28%"><a href="https://powell-v2.github.io/"><img src="https://avatars.githubusercontent.com/u/25308326?v=4?s=100" width="100px;" alt="Pavel Yermolin"/><br /><sub><b>Pavel Yermolin</b></sub></a><br /><a href="https://github.com/drwpow/openapi-typescript/commits?author=Powell-v2" title="Code">💻</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=Powell-v2" title="Documentation">📖</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=Powell-v2" title="Tests">⚠️</a></td> | ||
<td align="center" valign="top" width="14.28%"><a href="http://www.duncanbeevers.com"><img src="https://avatars.githubusercontent.com/u/7367?v=4?s=100" width="100px;" alt="Duncan Beevers"/><br /><sub><b>Duncan Beevers</b></sub></a><br /><a href="https://github.com/drwpow/openapi-typescript/commits?author=duncanbeevers" title="Code">💻</a> <a href="https://github.com/drwpow/openapi-typescript/issues?q=author%3Aduncanbeevers" title="Bug reports">🐛</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=duncanbeevers" title="Tests">⚠️</a></td> | ||
<td align="center" valign="top" width="14.28%"><a href="http://www.duncanbeevers.com"><img src="https://avatars.githubusercontent.com/u/7367?v=4?s=100" width="100px;" alt="Duncan Beevers"/><br /><sub><b>Duncan Beevers</b></sub></a><br /><a href="https://github.com/drwpow/openapi-typescript/commits?author=duncanbeevers" title="Code">💻</a> <a href="https://github.com/drwpow/openapi-typescript/issues?q=author%3Aduncanbeevers" title="Bug reports">🐛</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=duncanbeevers" title="Tests">⚠️</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=duncanbeevers" title="Documentation">📖</a></td> | ||
<td align="center" valign="top" width="14.28%"><a href="https://t.me/tkukushkin"><img src="https://avatars.githubusercontent.com/u/1482516?v=4?s=100" width="100px;" alt="Timofey Kukushkin"/><br /><sub><b>Timofey Kukushkin</b></sub></a><br /><a href="https://github.com/drwpow/openapi-typescript/commits?author=tkukushkin" title="Code">💻</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=tkukushkin" title="Tests">⚠️</a> <a href="https://github.com/drwpow/openapi-typescript/issues?q=author%3Atkukushkin" title="Bug reports">🐛</a></td> | ||
@@ -406,2 +406,4 @@ <td align="center" valign="top" width="14.28%"><a href="https://semigradsky.dev/"><img src="https://avatars.githubusercontent.com/u/1198848?v=4?s=100" width="100px;" alt="Dmitry Semigradsky"/><br /><sub><b>Dmitry Semigradsky</b></sub></a><br /><a href="https://github.com/drwpow/openapi-typescript/issues?q=author%3ASemigradsky" title="Bug reports">🐛</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=Semigradsky" title="Tests">⚠️</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=Semigradsky" title="Code">💻</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> | ||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mitchell-merry"><img src="https://avatars.githubusercontent.com/u/8567231?v=4?s=100" width="100px;" alt="Mitchell Merry"/><br /><sub><b>Mitchell Merry</b></sub></a><br /><a href="https://github.com/drwpow/openapi-typescript/commits?author=mitchell-merry" title="Code">💻</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=mitchell-merry" title="Tests">⚠️</a> <a href="https://github.com/drwpow/openapi-typescript/issues?q=author%3Amitchell-merry" title="Bug reports">🐛</a></td> | ||
<td align="center" valign="top" width="14.28%"><a href="http://www.francoisrisoud.com"><img src="https://avatars.githubusercontent.com/u/6012554?v=4?s=100" width="100px;" alt="François Risoud"/><br /><sub><b>François Risoud</b></sub></a><br /><a href="https://github.com/drwpow/openapi-typescript/commits?author=qnp" title="Code">💻</a> <a href="https://github.com/drwpow/openapi-typescript/issues?q=author%3Aqnp" title="Bug reports">🐛</a> <a href="https://github.com/drwpow/openapi-typescript/commits?author=qnp" title="Tests">⚠️</a></td> | ||
</tr> | ||
@@ -408,0 +410,0 @@ </tbody> |
@@ -9,2 +9,3 @@ import type { GlobalContext, OpenAPI3, OpenAPITSOptions, Subschema } from "./types"; | ||
import transformParameterObject from "./transform/parameter-object.js"; | ||
import transformParameterObjectArray from "./transform/parameter-object-array.js"; | ||
import transformRequestBodyObject from "./transform/request-body-object.js"; | ||
@@ -54,2 +55,3 @@ import transformResponseObject from "./transform/response-object.js"; | ||
pathParamsAsTypes: options.pathParamsAsTypes ?? false, | ||
parameters: {}, | ||
silent: options.silent ?? false, | ||
@@ -123,3 +125,3 @@ supportArrayLength: options.supportArrayLength ?? false, | ||
let indentLv = 0; | ||
output.push(options.exportType ? "export type external = {" : "export interface external {", ""); | ||
output.push(options.exportType ? "export type external = {" : "export interface external {"); | ||
externalKeys.sort((a, b) => a.localeCompare(b, "en", { numeric: true })); // sort external keys because they may have resolved in a different order each time | ||
@@ -160,2 +162,17 @@ indentLv++; | ||
} | ||
case "ParameterObject[]": { | ||
// hack: sometimes subschemas contain only a single SchemaObject or ParameterObject and get incorrectly hinted | ||
// currently unknown what the real fix is, but this is a bandaid | ||
if (typeof subschema.schema === "object" && ("schema" in subschema.schema || "type" in subschema.schema)) { | ||
subschemaOutput = transformSchemaObject(subschema.schema as any, { path, ctx: { ...ctx, indentLv } }); | ||
} else { | ||
subschemaOutput += "{\n"; | ||
indentLv++; | ||
subschemaOutput += transformParameterObjectArray(subschema.schema, { path, ctx: { ...ctx, indentLv } }); | ||
subschemaOutput += "\n"; | ||
indentLv--; | ||
subschemaOutput += indent("};", indentLv); | ||
} | ||
break; | ||
} | ||
case "RequestBodyObject": { | ||
@@ -162,0 +179,0 @@ subschemaOutput = transformRequestBodyObject(subschema.schema, { path, ctx: { ...ctx, indentLv } }); |
@@ -7,2 +7,3 @@ import type { | ||
OperationObject, | ||
ParameterObject, | ||
PathItemObject, | ||
@@ -104,2 +105,3 @@ ReferenceObject, | ||
fetch: Fetch; | ||
parameters: Record<string, ParameterObject>; | ||
} | ||
@@ -274,4 +276,5 @@ | ||
for (const subschemaID of Object.keys(options.schemas)) { | ||
walk(options.schemas[subschemaID].schema, (rawNode) => { | ||
walk(options.schemas[subschemaID].schema, (rawNode, nodePath) => { | ||
if (!("$ref" in rawNode) || typeof rawNode.$ref !== "string") return; | ||
const node = rawNode as unknown as ReferenceObject; | ||
@@ -294,4 +297,15 @@ | ||
// 4. scan for discriminators (after everything’s resolved in one file) | ||
// 4. collect parameters (which must be hoisted to the top) | ||
for (const k of Object.keys(options.schemas)) { | ||
walk(options.schemas[k].schema, (rawNode, nodePath) => { | ||
// note: 'in' is a unique required property of parameters. and parameters can live in subschemas (i.e. "parameters" doesn’t have to be part of the traceable path) | ||
if (typeof rawNode === "object" && "in" in rawNode) { | ||
const key = k === "." ? makeTSIndex(nodePath) : makeTSIndex(["external", k, ...nodePath]); | ||
options.parameters[key] = rawNode as any; | ||
} | ||
}); | ||
} | ||
// 5. scan for discriminators (after everything’s resolved in one file) | ||
for (const k of Object.keys(options.schemas)) { | ||
// 4a. lazy stringification check is faster than deep-scanning a giant object for discriminators | ||
@@ -371,3 +385,3 @@ // since most schemas don’t use them | ||
case "parameters": | ||
return getHintFromParameterObject(path.slice(1)); | ||
return "ParameterObject[]"; | ||
case "requestBody": | ||
@@ -391,4 +405,8 @@ return getHintFromRequestBodyObject(path.slice(1)); | ||
switch (path[0] as keyof PathItemObject) { | ||
case "parameters": | ||
case "parameters": { | ||
if (typeof path[1] === "number") { | ||
return "ParameterObject[]"; | ||
} | ||
return getHintFromParameterObject(path.slice(1)); | ||
} | ||
default: | ||
@@ -395,0 +413,0 @@ return getHintFromOperationObject(path.slice(1)); |
import type { GlobalContext, OperationObject, ParameterObject } from "../types"; | ||
import { | ||
escObjKey, | ||
getEntries, | ||
getSchemaObjectComment, | ||
indent, | ||
makeTSIndex, | ||
parseTSIndex, | ||
tsIntersectionOf, | ||
tsNonNullable, | ||
tsOptionalProperty, | ||
tsPick, | ||
tsReadonly, | ||
} from "../utils.js"; | ||
import { escObjKey, getEntries, getSchemaObjectComment, indent, tsOptionalProperty, tsReadonly } from "../utils.js"; | ||
import transformParameterObject from "./parameter-object.js"; | ||
@@ -40,52 +28,29 @@ import transformRequestBodyObject from "./request-body-object.js"; | ||
for (const paramIn of ["query", "header", "path", "cookie"] as ParameterObject["in"][]) { | ||
const inlineOutput: string[] = []; | ||
const refs: Record<string, string[]> = {}; | ||
const paramInternalOutput: string[] = []; | ||
indentLv++; | ||
for (const p of operationObject.parameters) { | ||
// handle inline params | ||
if ("in" in p) { | ||
if (p.in !== paramIn) continue; | ||
let key = escObjKey(p.name); | ||
if (paramIn !== "path" && !p.required) { | ||
key = tsOptionalProperty(key); | ||
} | ||
const c = getSchemaObjectComment(p, indentLv); | ||
if (c) inlineOutput.push(indent(c, indentLv)); | ||
const parameterType = transformParameterObject(p, { | ||
path: `${path}/parameters/${p.name}`, | ||
ctx: { ...ctx, indentLv }, | ||
}); | ||
inlineOutput.push(indent(`${key}: ${parameterType};`, indentLv)); | ||
} | ||
// handle $ref’d params | ||
// note: these can only point to specific parts of the schema, which have already | ||
// been resolved in the initial step and follow a predictable pattern. so we can | ||
// do some clever string magic to link them up properly without needing the | ||
// original object | ||
else if (p.$ref) { | ||
const parts = parseTSIndex(p.$ref); | ||
const paramI = parts.indexOf("parameters"); | ||
if (paramI === -1 || parts[paramI + 1] !== paramIn || !parts[paramI + 2]) continue; | ||
const key = parts.pop()!; | ||
const index = makeTSIndex(parts); | ||
if (!refs[index]) refs[index] = [key]; | ||
else refs[index].push(key); | ||
} | ||
for (const param of operationObject.parameters ?? []) { | ||
const node: ParameterObject | undefined = "$ref" in param ? ctx.parameters[param.$ref] : param; | ||
if (node?.in !== paramIn) continue; | ||
let key = escObjKey(node.name); | ||
if (paramIn !== "path" && !node.required) key = tsOptionalProperty(key); | ||
const c = getSchemaObjectComment(param, indentLv); | ||
if (c) paramInternalOutput.push(indent(c, indentLv)); | ||
const parameterType = | ||
"$ref" in param | ||
? param.$ref | ||
: transformParameterObject(param, { | ||
path: `${path}/parameters/${param.name}`, | ||
ctx: { ...ctx, indentLv }, | ||
}); | ||
paramInternalOutput.push(indent(`${key}: ${parameterType};`, indentLv)); | ||
} | ||
indentLv--; | ||
// nothing here? skip | ||
if (!inlineOutput.length && !Object.keys(refs).length) continue; | ||
const paramType = tsIntersectionOf( | ||
...(inlineOutput.length ? [`{\n${inlineOutput.join("\n")}\n${indent("}", indentLv)}`] : []), | ||
...Object.entries(refs).map(([root, keys]) => | ||
paramIn === "path" ? tsPick(root, keys) : tsPick(tsNonNullable(root), keys) | ||
) | ||
); | ||
let key: string = paramIn; | ||
if (ctx.immutableTypes) key = tsReadonly(key); | ||
parameterOutput.push(indent(`${key}: ${paramType};`, indentLv)); | ||
if (paramInternalOutput.length) { | ||
parameterOutput.push(indent(`${paramIn}: {`, indentLv)); | ||
parameterOutput.push(...paramInternalOutput); | ||
parameterOutput.push(indent(`};`, indentLv)); | ||
} | ||
} | ||
indentLv--; | ||
if (parameterOutput.length) { | ||
@@ -92,0 +57,0 @@ output.push(indent(`parameters: {`, indentLv)); |
@@ -1,2 +0,2 @@ | ||
import type { GlobalContext, PathItemObject } from "../types"; | ||
import type { GlobalContext, ParameterObject, PathItemObject, ReferenceObject } from "../types"; | ||
import { escStr, getSchemaObjectComment, indent } from "../utils.js"; | ||
@@ -39,3 +39,13 @@ import transformOperationObject from "./operation-object.js"; | ||
} else { | ||
const operationType = transformOperationObject(operationObject, { path, ctx: { ...ctx, indentLv } }); | ||
// fold top-level PathItem parameters into method-level, with the latter overriding the former | ||
const keyedParameters: Record<string, ParameterObject | ReferenceObject> = {}; | ||
// important: OperationObject parameters come last, and will override any conflicts with PathItem parameters | ||
for (const parameter of [...(pathItem.parameters ?? []), ...(operationObject.parameters ?? [])]) { | ||
// note: the actual key doesn’t matter here, as long as it can match between PathItem and OperationObject | ||
keyedParameters["$ref" in parameter ? parameter.$ref : parameter.name] = parameter; | ||
} | ||
const operationType = transformOperationObject( | ||
{ ...operationObject, parameters: Object.values(keyedParameters) }, | ||
{ path, ctx: { ...ctx, indentLv } } | ||
); | ||
output.push(indent(`${method}: ${operationType};`, indentLv)); | ||
@@ -42,0 +52,0 @@ } |
@@ -65,3 +65,3 @@ import type { GlobalContext, ReferenceObject, SchemaObject } from "../types"; | ||
// const (valid for any type) | ||
if (schemaObject.const) { | ||
if (schemaObject.const !== null && schemaObject.const !== undefined) { | ||
let schemaConst = schemaObject.const as any; | ||
@@ -102,3 +102,3 @@ if ("type" in schemaObject) { | ||
const maybeTypes = schemaObject.oneOf.map((item) => transformSchemaObject(item, { path, ctx })); | ||
if (maybeTypes.some((t) => t.includes("{"))) return tsOneOf(...maybeTypes); // OneOf<> helper needed if any objects present ("{") | ||
if (maybeTypes.some((t) => typeof t === "string" && t.includes("{"))) return tsOneOf(...maybeTypes); // OneOf<> helper needed if any objects present ("{") | ||
return tsUnionOf(...maybeTypes); // otherwise, TS union works for primitives | ||
@@ -105,0 +105,0 @@ } |
@@ -633,2 +633,6 @@ import type { URL } from "node:url"; | ||
| { hint: "ParameterObject"; schema: ParameterObject } | ||
| { | ||
hint: "ParameterObject[]"; | ||
schema: (ParameterObject | ReferenceObject)[] | Record<string, ParameterObject | ReferenceObject>; | ||
} | ||
| { hint: "RequestBodyObject"; schema: RequestBodyObject } | ||
@@ -656,2 +660,3 @@ | { hint: "ResponseObject"; schema: ResponseObject } | ||
>; | ||
parameters: Record<string, ParameterObject>; | ||
pathParamsAsTypes: boolean; | ||
@@ -658,0 +663,0 @@ silent: 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
311053
72
4539
416
Updatedundici@^5.21.0