|
1 | 1 | import glob from 'fast-glob';
|
2 |
| -import { join, dirname } from 'path'; |
| 2 | +import { dirname, join } from 'path'; |
3 | 3 | import { mdParser } from './parser';
|
4 | 4 | import { formatter } from './formatter';
|
5 | 5 | import { genWebTypes } from './web-types';
|
6 |
| -import { readFileSync, outputFileSync } from 'fs-extra'; |
| 6 | +import { outputFileSync, readFileSync } from 'fs-extra'; |
7 | 7 | import type { Options, VueTag } from './type';
|
8 |
| -import { normalizePath, getComponentName } from './utils'; |
9 |
| -import { genVeturTags, genVeturAttributes } from './vetur'; |
| 8 | +import { getComponentName, normalizePath, toKebabCase } from './utils'; |
| 9 | +import { genVeturAttributes, genVeturTags } from './vetur'; |
10 | 10 |
|
11 |
| -async function readMarkdown(options: Options) { |
12 |
| - // const mds = await glob(normalizePath(`${options.path}/**/*.md`)) |
13 |
| - const mds = await glob(normalizePath(`${options.path}/**/*.md`)); |
14 |
| - return mds |
| 11 | +async function readMarkdown(options: Options): Promise<Map<String, VueTag>> { |
| 12 | + const mdPaths = await glob(normalizePath(`${options.path}/**/*.md`)); |
| 13 | + const data = mdPaths |
15 | 14 | .filter(md => options.test.test(md))
|
16 | 15 | .map(path => {
|
17 | 16 | const docPath = dirname(path);
|
18 |
| - const componentName = docPath.substring(docPath.lastIndexOf('/') + 1); |
19 |
| - return { |
20 |
| - componentName: getComponentName(componentName || ''), |
21 |
| - md: readFileSync(path, 'utf-8'), |
22 |
| - }; |
23 |
| - }); |
| 17 | + const kebabComponentName = |
| 18 | + options.tagPrefix + docPath.substring(docPath.lastIndexOf('/') + 1) || ''; |
| 19 | + const componentName = getComponentName(docPath.substring(docPath.lastIndexOf('/') + 1) || ''); |
| 20 | + const fileContent = readFileSync(path, 'utf-8'); |
| 21 | + return formatter(mdParser(fileContent), componentName, kebabComponentName, options.tagPrefix); |
| 22 | + }) |
| 23 | + .filter(item => item) as VueTag[][]; |
| 24 | + const tags: Map<String, VueTag> = new Map(); |
| 25 | + data.flatMap(item => item).forEach(mergedTag => mergeTag(tags, mergedTag)); |
| 26 | + return tags; |
24 | 27 | }
|
25 | 28 |
|
26 |
| -export async function parseAndWrite(options: Options) { |
27 |
| - if (!options.outputDir) { |
28 |
| - throw new Error('outputDir can not be empty.'); |
| 29 | +function readTypings(options: Options): Map<String, VueTag> { |
| 30 | + const tags: Map<String, VueTag> = new Map(); |
| 31 | + const fileContent = readFileSync(options.typingsPath, 'utf-8'); |
| 32 | + fileContent |
| 33 | + .split('\n') |
| 34 | + .filter(line => line && line.includes('typeof')) |
| 35 | + .map(line => { |
| 36 | + const l = line.trim(); |
| 37 | + return toKebabCase(l.substring(0, l.indexOf(':'))); |
| 38 | + }) |
| 39 | + .forEach(tagName => |
| 40 | + tags.set(tagName, { |
| 41 | + name: tagName, |
| 42 | + slots: [], |
| 43 | + events: [], |
| 44 | + attributes: [], |
| 45 | + }), |
| 46 | + ); |
| 47 | + return tags; |
| 48 | +} |
| 49 | + |
| 50 | +function mergeTag(tags: Map<String, VueTag>, mergedTag: VueTag) { |
| 51 | + const tagName = mergedTag.name; |
| 52 | + const vueTag = tags.get(tagName); |
| 53 | + if (vueTag) { |
| 54 | + vueTag.slots = [...vueTag.slots, ...mergedTag.slots]; |
| 55 | + vueTag.events = [...vueTag.events, ...mergedTag.events]; |
| 56 | + vueTag.attributes = [...vueTag.attributes, ...mergedTag.attributes]; |
| 57 | + } else { |
| 58 | + tags.set(tagName, mergedTag); |
29 | 59 | }
|
| 60 | +} |
30 | 61 |
|
31 |
| - const docs = await readMarkdown(options); |
32 |
| - const datas = docs |
33 |
| - .map(doc => formatter(mdParser(doc.md), doc.componentName, options.tagPrefix)) |
34 |
| - .filter(item => item) as VueTag[][]; |
35 |
| - const tags: VueTag[] = []; |
36 |
| - datas.forEach(arr => { |
37 |
| - tags.push(...arr); |
| 62 | +function mergeTags(mergedTagsArr: Map<String, VueTag>[]): VueTag[] { |
| 63 | + if (mergedTagsArr.length === 1) return [...mergedTagsArr[0].values()]; |
| 64 | + const tags: Map<String, VueTag> = new Map(); |
| 65 | + if (mergedTagsArr.length === 0) return []; |
| 66 | + mergedTagsArr.forEach(mergedTags => { |
| 67 | + mergedTags.forEach(mergedTag => mergeTag(tags, mergedTag)); |
38 | 68 | });
|
| 69 | + return [...tags.values()]; |
| 70 | +} |
39 | 71 |
|
| 72 | +export async function parseAndWrite(options: Options): Promise<Number> { |
| 73 | + if (!options.outputDir) { |
| 74 | + throw new Error('outputDir can not be empty.'); |
| 75 | + } |
| 76 | + const tagsFromMarkdown = await readMarkdown(options); |
| 77 | + const tagsFromTypings = await readTypings(options); |
| 78 | + const tags = mergeTags([tagsFromMarkdown, tagsFromTypings]); |
40 | 79 | const webTypes = genWebTypes(tags, options);
|
41 | 80 | const veturTags = genVeturTags(tags);
|
42 | 81 | const veturAttributes = genVeturAttributes(tags);
|
43 |
| - |
44 | 82 | outputFileSync(join(options.outputDir, 'tags.json'), JSON.stringify(veturTags, null, 2));
|
45 | 83 | outputFileSync(
|
46 | 84 | join(options.outputDir, 'attributes.json'),
|
47 | 85 | JSON.stringify(veturAttributes, null, 2),
|
48 | 86 | );
|
49 | 87 | outputFileSync(join(options.outputDir, 'web-types.json'), JSON.stringify(webTypes, null, 2));
|
| 88 | + return tags.length; |
50 | 89 | }
|
51 | 90 |
|
52 | 91 | export default { parseAndWrite };
|
0 commit comments