UNPKG

typed-openapi

Version:
183 lines (168 loc) 6.16 kB
import { DEFAULT_ERROR_STATUS_CODES, DEFAULT_SUCCESS_STATUS_CODES, allowedRuntimes, generateFile, generateTanstackQueryFile, mapOpenApiEndpoints } from "./chunk-ZM3UP6UJ.js"; import { prettify } from "./chunk-KAEXXJ7X.js"; // src/generate-client-files.ts import SwaggerParser from "@apidevtools/swagger-parser"; import { basename, join, dirname, isAbsolute } from "pathe"; import { type } from "arktype"; import { mkdir, writeFile } from "fs/promises"; // src/default-fetcher.generator.ts var generateDefaultFetcher = (options) => { const { envApiBaseUrl = "API_BASE_URL", clientPath = "./openapi.client.ts", fetcherName = "defaultFetcher", apiName = "api" } = options; return `/** * Generic API Client for typed-openapi generated code * * This is a simple, production-ready wrapper that you can copy and customize. * It handles: * - Path parameter replacement * - Query parameter serialization * - JSON request/response handling * - Basic error handling * * Usage: * 1. Replace './${clientPath}' with your actual generated file path * 2. Set your ${envApiBaseUrl} * 3. Customize error handling and headers as needed */ import { type Fetcher, createApiClient } from "${clientPath}"; // Basic configuration const ${envApiBaseUrl} = process.env["${envApiBaseUrl}"] || "https://api.example.com"; /** * Simple fetcher implementation without external dependencies */ const ${fetcherName}: Fetcher["fetch"] = async (input) => { const headers = new Headers(); // Handle query parameters if (input.urlSearchParams) { input.url.search = input.urlSearchParams.toString(); } // Handle request body for mutation methods const body = ["post", "put", "patch", "delete"].includes(input.method.toLowerCase()) ? JSON.stringify(input.parameters?.body) : undefined; if (body) { headers.set("Content-Type", "application/json"); } // Add custom headers if (input.parameters?.header) { Object.entries(input.parameters.header).forEach(([key, value]) => { if (value != null) { headers.set(key, String(value)); } }); } const response = await fetch(input.url, { method: input.method.toUpperCase(), ...(body && { body }), headers, ...input.overrides, }); return response; }; export const ${apiName} = createApiClient({ fetch: ${fetcherName} }, API_BASE_URL);`; }; // src/generate-client-files.ts var cwd = process.cwd(); var now = /* @__PURE__ */ new Date(); async function ensureDir(dirPath) { try { await mkdir(dirPath, { recursive: true }); } catch (error) { console.error(`Error ensuring directory: ${error.message}`); } } var optionsSchema = type({ "output?": "string", runtime: allowedRuntimes, tanstack: "boolean | string", "defaultFetcher?": type({ "envApiBaseUrl?": "string", "clientPath?": "string", "fetcherName?": "string", "apiName?": "string" }), schemasOnly: "boolean", "includeClient?": "boolean | 'true' | 'false'", "successStatusCodes?": "string", "errorStatusCodes?": "string" }); async function generateClientFiles(input, options) { const openApiDoc = await SwaggerParser.bundle(input); const ctx = mapOpenApiEndpoints(openApiDoc, options); console.log(`Found ${ctx.endpointList.length} endpoints`); const successStatusCodes = options.successStatusCodes ? options.successStatusCodes.split(",").map((code) => parseInt(code.trim(), 10)) : void 0; const errorStatusCodes = options.errorStatusCodes ? options.errorStatusCodes.split(",").map((code) => parseInt(code.trim(), 10)) : void 0; const includeClient = options.includeClient === "false" ? false : options.includeClient === "true" ? true : options.includeClient; const generatorOptions = { ...ctx, runtime: options.runtime, schemasOnly: options.schemasOnly, nameTransform: options.nameTransform, includeClient: includeClient ?? true, successStatusCodes: successStatusCodes ?? DEFAULT_SUCCESS_STATUS_CODES, errorStatusCodes: errorStatusCodes ?? DEFAULT_ERROR_STATUS_CODES }; const content = await prettify(generateFile(generatorOptions)); const outputPath = join( cwd, options.output ?? input + `.${options.runtime === "none" ? "client" : options.runtime}.ts` ); console.log("Generating client...", outputPath); await ensureDir(dirname(outputPath)); await writeFile(outputPath, content); if (options.tanstack) { const tanstackContent = await generateTanstackQueryFile({ ...generatorOptions, relativeApiClientPath: "./" + basename(outputPath) }); let tanstackOutputPath; if (typeof options.tanstack === "string" && isAbsolute(options.tanstack)) { tanstackOutputPath = options.tanstack; } else { tanstackOutputPath = join( dirname(outputPath), typeof options.tanstack === "string" ? options.tanstack : `tanstack.client.ts` ); } console.log("Generating tanstack client...", tanstackOutputPath); await ensureDir(dirname(tanstackOutputPath)); await writeFile(tanstackOutputPath, tanstackContent); } if (options.defaultFetcher) { const defaultFetcherContent = generateDefaultFetcher({ envApiBaseUrl: options.defaultFetcher.envApiBaseUrl, clientPath: options.defaultFetcher.clientPath ?? join(dirname(outputPath), basename(outputPath)), fetcherName: options.defaultFetcher.fetcherName, apiName: options.defaultFetcher.apiName }); let defaultFetcherOutputPath; if (typeof options.defaultFetcher === "string" && isAbsolute(options.defaultFetcher)) { defaultFetcherOutputPath = options.defaultFetcher; } else { defaultFetcherOutputPath = join( dirname(outputPath), typeof options.defaultFetcher === "string" ? options.defaultFetcher : `api.client.ts` ); } console.log("Generating default fetcher...", defaultFetcherOutputPath); await ensureDir(dirname(defaultFetcherOutputPath)); await writeFile(defaultFetcherOutputPath, defaultFetcherContent); } console.log(`Done in ${(/* @__PURE__ */ new Date()).getTime() - now.getTime()}ms !`); } export { generateClientFiles };