@lonu/stc
Version:
A tool for converting OpenApi/Swagger/Apifox into code.
138 lines (137 loc) • 4.88 kB
JavaScript
import Logs from "./console.js";
import { PluginManager } from "./plugins/index.js";
import { getApiPath, getDefinition } from "./core.js";
import { parseSpec } from "./parser.js";
import { createDiffFile, createFile, readFile, removeFile } from "./utils.js";
import { getT } from "./i18n/index.js";
const LOCK_FILE = ".stc.lock";
const formatErrorMessage = (error) => {
if (error instanceof Error)
return error.message;
if (typeof error === "string")
return error;
try {
return JSON.stringify(error, null, 2);
}
catch {
return String(error);
}
};
/**
* 初始化插件管理器
*/
const initPluginManager = (context) => {
const pluginManager = new PluginManager();
// 注册插件
pluginManager.register(context.options.plugins ?? []);
// 启动所有插件
pluginManager.setupAll(context);
};
/**
* 创建上下文
* @param options
*/
const createContext = (context) => {
// 初始化插件管理器
initPluginManager(context);
return context;
};
/**
* 获取 Swagger 数据
* @param urlOrPath - 远程地址或本地
* @returns
*/
const getData = async (urlOrPath, _outDir) => {
try {
// const lockFile = await readFile(`${outDir}/${LOCK_FILE}`);
let data;
// 从本地文件获取 Swagger 数据
if (!/^http(s?):\/\//.test(urlOrPath)) {
const content = await readFile(urlOrPath);
data = parseSpec(content, urlOrPath);
}
else {
// 从远程地址获取 Swagger 数据
const res = await fetch(urlOrPath);
if (!res.ok) {
throw new Error(`${res.status} ${res.statusText}`);
}
const content = await res.text();
data = parseSpec(content, urlOrPath);
}
// 对比 path 和 definitions/schemas 数据是否有变化,有变化则使用新数据
// if (lockFile) {
// const oldData = JSON.parse(lockFile) as unknown as ISwaggerResult;
// createFile(
// `${outDir}/.stc_new.lock`,
// JSON.stringify(data, null, 2),
// {
// banner: false,
// },
// );
// const isChange = diff["diffLines"](
// JSON.stringify(oldData, null, 2),
// JSON.stringify(data, null, 2),
// );
// createFile(
// `${outDir}/.stc_diff.lock`,
// JSON.stringify(isChange, null, 2),
// {
// banner: false,
// },
// );
// }
return data;
}
catch (error) {
throw new Error(getT("$t(app.apiJsonFileError)", {
error: formatErrorMessage(error),
}));
}
};
/**
* 启动
* @param options - 配置
*/
export const start = async (options) => {
// 创建上下文
const context = createContext({ options });
const data = await getData(options.url, options.outDir);
// 触发插件 onload 事件
context.onLoad?.(data, context.options);
// 处理类型定义。v2 版本中,通过 `definitions` 属性获取。而 v3 版本,则通过 `components.schemas` 属性获取。
const defData = getDefinition(data.definitions || data.components?.schemas || {});
// 触发插件 onDefinition 事件
context.onDefinition?.(defData, context.options);
const actionData = getApiPath(data.paths, options, data.definitions || data.components?.schemas || {});
// 触发插件 onAction 事件
context.onAction?.(actionData, context.options);
if (options.clean) {
const exclude = [`${options.outDir}/${LOCK_FILE}`];
if (!options.shared) {
exclude.push(`${options.outDir}/shared/**/*`);
}
await removeFile(`${options.outDir}/**/*.*`, {
exclude,
});
}
// 触发插件 onTransform 事件
const transformData = await context.onTransform?.(defData, actionData, context.options);
// 写入类型定义文件
const definition = transformData?.definition;
if (definition?.content) {
const name = `${options.outDir}/${definition.filename}`;
const content = definition.content;
await createDiffFile(name, content, options.clean, definition.banner ?? true);
}
// 写入 API 文件
if (transformData?.action) {
await Promise.all(Array.from(transformData.action).map(([filename, content]) => createDiffFile(`${options.outDir}/${filename}`, content, options.clean)));
}
// 保存数据
await createFile(`${options.outDir}/${LOCK_FILE}`, JSON.stringify(data, null, 2), { banner: false });
console.log("\n");
Logs.success(`${getT("$t(app.generateFileDone)", { total: actionData.size })}\n\t${options.outDir}\n`);
// 触发插件 onEnd 事件
await context.onEnd?.(context.options);
};