vite-bolt/plugin/initialize.ts
2023-02-01 17:52:53 +08:00

194 lines
3.6 KiB
TypeScript

import path from "path";
import fs from "fs";
import TsToJson from "./ts2json";
import toc from "markdown-toc";
import objHash from "object-hash";
const hash = (str: string) => "" + objHash(str).substring(0, 6);
/**
* props 注释元信息
*/
interface IPropMeta {
/**
* 声明属性是否必须 对应 ts ?
*/
required?: boolean;
/**
* @name 参数名称
*/
name?: string;
/**
* @type 类型
*/
type?: string;
/**
* @description 说明
*/
description?: string;
/**
* @default 默认值
*/
default?: string;
/**
* @version 版本
*/
version?: string | number;
}
interface IProp2jsonItem {
[key: string]: {
props: IPropMeta[];
};
}
interface IRoute {
active: false;
path: string;
name: string;
}
export interface ITree {
/**
* 组件🎄
*/
compoTree: ICompoTree;
/**
* 文件🎄,最小粒度更新
*/
fileTree?: {};
/**
* 路由🎄,根据 menuTree 生成
*/
routerList: IRoute[];
/**
* 插件配置
*/
config?: {};
}
interface ITocItem {
/**
* 锚点的 hash
*/
anchor?: string;
/**
* mardkown h标签 innerHTML
*/
content: string;
i: 0;
lvl: 1;
seen: 0;
/**
* 小写 innerHTML
*/
slug: "hi";
}
/**
* ts interface 转 json
*/
const tsParser = new TsToJson();
/**
* 组件开发目录
*/
const root = path.resolve(process.cwd(), "src/components");
/**
* 文件映射,最小粒度更新 For HMR
*/
const fileTree = {};
/**
* ICompoTree Item
*/
interface ICompoMetadata {
/**
* 组件 props 信息
*/
props: IPropMeta[] | [];
/**
* markdown 文件目录 table of content
*/
toc: ITocItem[];
}
/**
* 组件🎄
*/
interface ICompoTree {
[key: string]: ICompoMetadata;
}
/**
* 解析 markdown 内容生成目录 table of content
* @param {string} content markdown 内容
*/
const md2toc = (content: string) => {
if (!content) return [];
return toc(content).json;
};
/**
* 生成路由
* @param {ICompoTree} compoTree 菜单
*/
const processRouter = (compoTree: ICompoTree): IRoute[] => {
return Object.keys(compoTree).map((t) => ({
active: false,
path: t.toLowerCase(),
name: t,
}));
};
/**
* props.ts 转换成 json 对象
* @param {string} path 组件 props.ts 文件路径
* @returns
*/
const parsePropsTs = (path: string) => {
const prop2json: IProp2jsonItem = fs.existsSync(path)
? tsParser.parse(path)
: {};
return Object.values(prop2json)?.[0]?.props ?? [];
};
/**
* 解析 mdx 文件中的目录
* @param {string} path 组件 mdx 文件路径
*/
const parseToc = (path: string) => {
const isFile = fs.statSync(path).isFile();
const mdxContent = isFile ? fs.readFileSync(path).toString() : "";
// 注入锚点 hash
const toc = md2toc(mdxContent).map((i) => ({
...i,
anchor: `anchor-${hash(i.content)}`,
}));
return toc;
};
const walkCompoMetadata = (dirs: string[]): ICompoTree => {
const compoTree: ICompoTree = {};
dirs.forEach((dir) => {
const mdxFile = path.resolve(root, dir, "index.mdx");
const propFile = path.resolve(root, dir, "props.ts");
fileTree[mdxFile] = dir;
fileTree[propFile] = dir;
const toc = parseToc(mdxFile);
const props = parsePropsTs(propFile);
compoTree[dir] = { toc, props };
});
return compoTree;
};
/**
* 初始化
* @returns menuTree: 目录树, 包含markdownHeadings | propsTree: 组件 api 树
*/
export const initialize = (): ITree => {
const dirs = fs.existsSync(root) ? fs.readdirSync(root) : [];
const compoTree = walkCompoMetadata(dirs);
const routerList = processRouter(compoTree);
return { compoTree, fileTree, routerList };
};