@kubb/plugin-oas
Version:
OpenAPI Specification (OAS) plugin for Kubb, providing core functionality for parsing and processing OpenAPI/Swagger schemas for code generation.
311 lines (307 loc) • 11.2 kB
JavaScript
import { n as createReactGenerator$1, r as createGenerator$1, t as jsonGenerator } from "./generators-DP42tXgJ.js";
import { a as pLimit, i as buildSchema, n as buildOperation, r as buildOperations, t as SchemaGenerator } from "./SchemaGenerator-CUJJ1Cb-.js";
import "./getFooter-DVB-hawK.js";
import { n as schemaKeywords, t as isKeyword } from "./SchemaMapper-Ck7sFVaF.js";
import "./getSchemas-B0dk_Cbz.js";
import { BaseGenerator, definePlugin, getMode } from "@kubb/core";
import transformers, { camelCase } from "@kubb/core/transformers";
import path from "node:path";
import { parseFromConfig } from "@kubb/oas";
//#region src/OperationGenerator.ts
var OperationGenerator = class extends BaseGenerator {
#getOptions(operation, method) {
const { override = [] } = this.context;
const operationId = operation.getOperationId({ friendlyCase: true });
const contentType = operation.getContentType();
return override.find(({ pattern, type }) => {
switch (type) {
case "tag": return operation.getTags().some((tag) => tag.name.match(pattern));
case "operationId": return !!operationId.match(pattern);
case "path": return !!operation.path.match(pattern);
case "method": return !!method.match(pattern);
case "contentType": return !!contentType.match(pattern);
default: return false;
}
})?.options || {};
}
#isExcluded(operation, method) {
const { exclude = [] } = this.context;
const operationId = operation.getOperationId({ friendlyCase: true });
const contentType = operation.getContentType();
return exclude.some(({ pattern, type }) => {
switch (type) {
case "tag": return operation.getTags().some((tag) => tag.name.match(pattern));
case "operationId": return !!operationId.match(pattern);
case "path": return !!operation.path.match(pattern);
case "method": return !!method.match(pattern);
case "contentType": return !!contentType.match(pattern);
default: return false;
}
});
}
#isIncluded(operation, method) {
const { include = [] } = this.context;
const operationId = operation.getOperationId({ friendlyCase: true });
const contentType = operation.getContentType();
return include.some(({ pattern, type }) => {
switch (type) {
case "tag": return operation.getTags().some((tag) => tag.name.match(pattern));
case "operationId": return !!operationId.match(pattern);
case "path": return !!operation.path.match(pattern);
case "method": return !!method.match(pattern);
case "contentType": return !!contentType.match(pattern);
default: return false;
}
});
}
getSchemas(operation, { resolveName = (name) => name } = {}) {
const operationId = operation.getOperationId({ friendlyCase: true });
const method = operation.method;
const operationName = transformers.pascalCase(operationId);
const resolveKeys = (schema) => schema?.properties ? Object.keys(schema.properties) : void 0;
const pathParamsSchema = this.context.oas.getParametersSchema(operation, "path");
const queryParamsSchema = this.context.oas.getParametersSchema(operation, "query");
const headerParamsSchema = this.context.oas.getParametersSchema(operation, "header");
const requestSchema = this.context.oas.getRequestSchema(operation);
const statusCodes = operation.getResponseStatusCodes().map((statusCode) => {
const name = statusCode === "default" ? "error" : statusCode;
const schema = this.context.oas.getResponseSchema(operation, statusCode);
const keys = resolveKeys(schema);
return {
name: resolveName(transformers.pascalCase(`${operationId} ${name}`)),
description: operation.getResponseByStatusCode(statusCode)?.description,
schema,
operation,
operationName,
statusCode: name === "error" ? void 0 : Number(statusCode),
keys,
keysToOmit: keys?.filter((key) => (schema?.properties?.[key])?.writeOnly)
};
});
const successful = statusCodes.filter((item) => item.statusCode?.toString().startsWith("2"));
const errors = statusCodes.filter((item) => item.statusCode?.toString().startsWith("4") || item.statusCode?.toString().startsWith("5"));
return {
pathParams: pathParamsSchema ? {
name: resolveName(transformers.pascalCase(`${operationId} PathParams`)),
operation,
operationName,
schema: pathParamsSchema,
keys: resolveKeys(pathParamsSchema)
} : void 0,
queryParams: queryParamsSchema ? {
name: resolveName(transformers.pascalCase(`${operationId} QueryParams`)),
operation,
operationName,
schema: queryParamsSchema,
keys: resolveKeys(queryParamsSchema) || []
} : void 0,
headerParams: headerParamsSchema ? {
name: resolveName(transformers.pascalCase(`${operationId} HeaderParams`)),
operation,
operationName,
schema: headerParamsSchema,
keys: resolveKeys(headerParamsSchema)
} : void 0,
request: requestSchema ? {
name: resolveName(transformers.pascalCase(`${operationId} ${method === "get" ? "queryRequest" : "mutationRequest"}`)),
description: operation.schema.requestBody?.description,
operation,
operationName,
schema: requestSchema,
keys: resolveKeys(requestSchema),
keysToOmit: resolveKeys(requestSchema)?.filter((key) => (requestSchema.properties?.[key])?.readOnly)
} : void 0,
response: {
name: resolveName(transformers.pascalCase(`${operationId} ${method === "get" ? "queryResponse" : "mutationResponse"}`)),
operation,
operationName,
schema: { oneOf: successful.map((item) => ({
...item.schema,
$ref: item.name
})) || void 0 }
},
responses: successful,
errors,
statusCodes
};
}
async getOperations() {
const { oas } = this.context;
const paths = oas.getPaths();
return Object.entries(paths).flatMap(([path$1, methods]) => Object.entries(methods).map((values) => {
const [method, operation] = values;
if (this.#isExcluded(operation, method)) return null;
if (this.context.include && !this.#isIncluded(operation, method)) return null;
return operation ? {
path: path$1,
method,
operation
} : null;
}).filter(Boolean));
}
async build(...generators) {
const operations = await this.getOperations();
const generatorLimit = pLimit(1);
const operationLimit = pLimit(10);
const writeTasks = generators.map((generator) => generatorLimit(async () => {
const operationTasks = operations.map(({ operation, method }) => operationLimit(async () => {
const options = this.#getOptions(operation, method);
if (generator.type === "react") {
await buildOperation(operation, {
config: this.context.pluginManager.config,
fabric: this.context.fabric,
Component: generator.Operation,
generator: this,
plugin: {
...this.context.plugin,
options: {
...this.options,
...options
}
}
});
return [];
}
return await generator.operation?.({
generator: this,
config: this.context.pluginManager.config,
operation,
plugin: {
...this.context.plugin,
options: {
...this.options,
...options
}
}
}) ?? [];
}));
const opResultsFlat = (await Promise.all(operationTasks)).flat();
if (generator.type === "react") {
await buildOperations(operations.map((op) => op.operation), {
fabric: this.context.fabric,
config: this.context.pluginManager.config,
Component: generator.Operations,
generator: this,
plugin: this.context.plugin
});
return [];
}
const operationsResult = await generator.operations?.({
generator: this,
config: this.context.pluginManager.config,
operations: operations.map((op) => op.operation),
plugin: this.context.plugin
});
return [...opResultsFlat, ...operationsResult ?? []];
}));
return (await Promise.all(writeTasks)).flat();
}
};
//#endregion
//#region src/plugin.ts
const pluginOasName = "plugin-oas";
const pluginOas = definePlugin((options) => {
const { output = { path: "schemas" }, group, validate = true, generators = [jsonGenerator], serverIndex, contentType, oasClass, discriminator = "strict" } = options;
const getOas = async ({ config }) => {
const oas = await parseFromConfig(config, oasClass);
oas.setOptions({
contentType,
discriminator
});
try {
if (validate) await oas.valdiate();
} catch (e) {
const error = e;
console.warn(error?.message);
}
return oas;
};
return {
name: pluginOasName,
options: {
output,
validate,
discriminator,
...options
},
inject() {
const config = this.config;
let oas;
return {
async getOas() {
if (!oas) oas = await getOas({ config });
return oas;
},
async getBaseURL() {
const oas$1 = await getOas({ config });
if (serverIndex) return oas$1.api.servers?.at(serverIndex)?.url;
}
};
},
resolvePath(baseName, pathMode, options$1) {
const root = path.resolve(this.config.root, this.config.output.path);
if ((pathMode ?? getMode(path.resolve(root, output.path))) === "single")
/**
* when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
* Other plugins then need to call addOrAppend instead of just add from the fileManager class
*/
return path.resolve(root, output.path);
if (group && (options$1?.group?.path || options$1?.group?.tag)) {
const groupName = group?.name ? group.name : (ctx) => {
if (group?.type === "path") return `${ctx.group.split("/")[1]}`;
return `${camelCase(ctx.group)}Controller`;
};
return path.resolve(root, output.path, groupName({ group: group.type === "path" ? options$1.group.path : options$1.group.tag }), baseName);
}
return path.resolve(root, output.path, baseName);
},
async install() {
if (!output) return;
const oas = await this.getOas();
await oas.dereference();
const schemaFiles = await new SchemaGenerator({
unknownType: "unknown",
emptySchemaType: "unknown",
dateType: "date",
transformers: {},
...this.plugin.options
}, {
fabric: this.fabric,
oas,
pluginManager: this.pluginManager,
plugin: this.plugin,
contentType,
include: void 0,
override: void 0,
mode: "split",
output: output.path
}).build(...generators);
await this.upsertFile(...schemaFiles);
const operationFiles = await new OperationGenerator(this.plugin.options, {
fabric: this.fabric,
oas,
pluginManager: this.pluginManager,
plugin: this.plugin,
contentType,
exclude: void 0,
include: void 0,
override: void 0,
mode: "split"
}).build(...generators);
await this.upsertFile(...operationFiles);
}
};
});
//#endregion
//#region src/index.ts
/**
* @deprecated use `import { createGenerator } from '@kubb/plugin-oas/generators'`
*/
const createGenerator = createGenerator$1;
/**
* @deprecated use `import { createReactGenerator } from '@kubb/plugin-oas/generators'`
*/
const createReactGenerator = createReactGenerator$1;
//#endregion
export { OperationGenerator, SchemaGenerator, buildOperation, buildOperations, buildSchema, createGenerator, createReactGenerator, isKeyword, pluginOas, pluginOasName, schemaKeywords };
//# sourceMappingURL=index.js.map