refactor(markdown-vetur): support generate webstorm types (#5900)

This commit is contained in:
neverland
2020-03-24 20:43:28 +08:00
committed by GitHub
parent 7ae3a4a1b1
commit c74fbf58a4
12 changed files with 422 additions and 226 deletions
+32 -126
View File
@@ -1,140 +1,46 @@
import glob from 'fast-glob';
import { join } from 'path';
import { mdParser } from './md-parser';
import { codegen, Tag, Attribute } from './codegen';
import {
PathLike,
statSync,
mkdirSync,
existsSync,
readdirSync,
readFileSync,
writeFileSync,
} from 'fs';
import { mdParser } from './parser';
import { formatter } from './formatter';
import { genWebTypes } from './web-types';
import { readFileSync, outputFileSync } from 'fs-extra';
import { Options, VueTag } from './type';
import { normalizePath } from './utils';
import { genVeturTags, genVeturAttributes } from './vetur';
export function parseText(input: string) {
const ast = mdParser(input);
return codegen(ast);
async function readMarkdown(options: Options) {
const mds = await glob(normalizePath(`${options.path}/**/*.md`));
return mds
.filter(md => options.test.test(md))
.map(path => readFileSync(path, 'utf-8'));
}
export type Options = {
// 需要解析的文件夹路径
path: PathLike;
// 文件匹配正则
test: RegExp;
// 输出目录
outputDir?: string;
// 递归的目录最大深度
maxDeep?: number;
// 解析出来的组件名前缀
tagPrefix?: string;
};
export type ParseResult = {
tags: Record<
string,
{
description: string;
attributes: string[];
}
>;
attributes: Record<string, Attribute>;
};
const defaultOptions = {
maxDeep: Infinity,
tagPrefix: '',
};
export function parse(options: Options) {
options = {
...defaultOptions,
...options,
};
const result: ParseResult = {
tags: {},
attributes: {},
};
function putResult(componentName: string, component: Tag) {
componentName = options.tagPrefix + componentName;
const attributes = Object.keys(component.attributes);
const tag = {
description: component.description,
attributes,
};
result.tags[componentName] = tag;
attributes.forEach(key => {
result.attributes[`${componentName}/${key}`] = component.attributes[key];
});
}
function recursiveParse(options: Options, deep: number) {
if (options.maxDeep && deep > options.maxDeep) {
return;
}
deep++;
const files = readdirSync(options.path);
files.forEach(item => {
const currentPath = join(options.path.toString(), item);
const stats = statSync(currentPath);
if (stats.isDirectory()) {
recursiveParse(
{
...options,
path: currentPath,
},
deep
);
} else if (stats.isFile() && options.test.test(item)) {
const file = readFileSync(currentPath);
const tags = parseText(file.toString());
if (tags.default) {
// one tag
putResult(currentPath.split('/').slice(-2)[0], tags.default);
} else {
Object.keys(tags).forEach(key => {
putResult(key, tags[key]);
});
}
}
});
}
recursiveParse(options, 0);
return result;
}
export function parseAndWrite(options: Options) {
const { tags, attributes } = parse(options);
export async function parseAndWrite(options: Options) {
if (!options.outputDir) {
return;
throw new Error('outputDir can not be empty.');
}
const isExist = existsSync(options.outputDir);
if (!isExist) {
mkdirSync(options.outputDir);
}
const mds = await readMarkdown(options);
const datas = mds
.map(md => formatter(mdParser(md), options.tagPrefix))
.filter(item => !!item) as VueTag[];
writeFileSync(
const webTypes = genWebTypes(datas, options);
const veturTags = genVeturTags(datas);
const veturAttributes = genVeturAttributes(datas);
outputFileSync(
join(options.outputDir, 'tags.json'),
JSON.stringify(tags, null, 2)
JSON.stringify(veturTags, null, 2)
);
writeFileSync(
outputFileSync(
join(options.outputDir, 'attributes.json'),
JSON.stringify(attributes, null, 2)
JSON.stringify(veturAttributes, null, 2)
);
outputFileSync(
join(options.outputDir, 'web-types.json'),
JSON.stringify(webTypes, null, 2)
);
}
export default {
parse,
parseText,
parseAndWrite,
};
export default { parseAndWrite };