typed-openapi
Version:
183 lines (168 loc) • 6.16 kB
JavaScript
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
};