Files
opsxapi/packages/core/src/importers/markdown.ts
T
logan dec66db5aa feat: OpsxAPI v1.0 全协议接口工作台
- 六协议调试:HTTP/GraphQL/WebSocket/SSE/Socket.IO/MQTT
- 集合树/多标签/多环境变量/历史记录,localStorage 持久化
- 导入:OpenAPI 2/3/3.1、Postman、Apifox、Apipost、cURL、HAR、
  Insomnia、Hoppscotch、Markdown(自动识别,21 个单测覆盖)
- 导出:OpenAPI 3.0 JSON/YAML、Postman v2.1、Markdown 文档、
  全量备份;单请求生成 cURL/fetch/Python/Go 代码
- Gin 服务:HTTP 代理、NDJSON 流式代理(SSE 透传)、
  WebSocket 隧道(浏览器自定义握手 Header)
- 内置产品官网 /home 与用户文档站 /docs(SSR,随二进制交付)
- lucide 图标体系 + 品牌标识(Logo/favicon),明暗双主题
- 环境变量:彩色徽标切换器、管理弹窗、{{ 自动补全
2026-09-18 00:16:59 +08:00

87 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Markdown API 文档导入器:提取 curl 代码块(及可选 JSON 请求体示例)。
*/
import type { Collection } from "../model";
import { createCollection, createFolder } from "../factory";
import type { ImportResult } from "../model";
import { importCurl } from "./curl";
export function looksLikeMarkdown(text: string): boolean {
const t = text.trimStart();
if (!t.startsWith("#") && !t.includes("```")) return false;
return /^#{1,6}\s/m.test(text) || /```/.test(text);
}
export function importMarkdown(text: string): ImportResult {
const warnings: string[] = [];
const blocks: { heading: string; code: string }[] = [];
const lines = text.split(/\r?\n/);
let heading = "";
let inBlock = false;
let lang = "";
let buf: string[] = [];
for (const line of lines) {
const fence = line.match(/^\s*```(\w*)\s*$/);
if (fence) {
if (!inBlock) {
inBlock = true;
lang = fence[1].toLowerCase();
buf = [];
} else {
inBlock = false;
const code = buf.join("\n");
if (["curl", "bash", "sh", "shell", ""].includes(lang) && code.trim().startsWith("curl")) {
blocks.push({ heading: heading || `curl ${blocks.length + 1}`, code });
}
}
continue;
}
if (inBlock) {
buf.push(line);
continue;
}
const h = line.match(/^#{1,6}\s+(.*)$/);
if (h) heading = h[1].trim();
}
if (blocks.length === 0) {
warnings.push("未在 Markdown 中找到 curl 代码块(也支持直接粘贴 cURL 导入)");
}
const folders = new Map<string, ReturnType<typeof createFolder>>();
const rootChildren = [];
for (const block of blocks) {
try {
const temp = importCurl(block.code, "temp");
const node = temp.children[0];
if (!node) continue;
// 一级标题做目录,二级及以下做请求名
const parts = block.heading.split(/[::]/);
const folderName = parts[0]?.trim();
if (folderName && folders.has(folderName)) {
folders.get(folderName)!.children.push(node);
} else if (folderName) {
const f = createFolder(folderName, [node]);
folders.set(folderName, f);
} else {
rootChildren.push(node);
}
} catch (e) {
warnings.push(`解析代码块失败:${e instanceof Error ? e.message : String(e)}`);
}
}
const collection = createCollection(
firstHeading(text) || "Markdown 导入",
[...folders.values(), ...rootChildren],
);
return { collections: [collection], environmentVariables: [], warnings };
}
function firstHeading(text: string): string {
const m = text.match(/^#\s+(.+)$/m);
return m ? m[1].trim().slice(0, 60) : "";
}