-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
meta.ts
139 lines (124 loc) · 3.75 KB
/
meta.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import type { IApi } from '@/types';
import path from 'path';
import type { IRoute } from 'umi';
import { Mustache, winPath } from 'umi/plugin-utils';
import { isTabRouteFile } from './tabs';
export const TABS_META_PATH = 'dumi/meta/tabs.ts';
export const ATOMS_META_PATH = 'dumi/meta/atoms.ts';
export default (api: IApi) => {
const metaFiles: { index: number; file: string; id: string }[] = [];
api.register({
key: 'modifyRoutes',
// make sure it is called last
stage: Infinity,
fn: (routes: Record<string, IRoute>) => {
// reset for re-generate files
metaFiles.length = 0;
// collect all markdown route files for combine demos & page meta
Object.values(routes).forEach((route) => {
if (
!route.isLayout &&
!/\*|:/.test(route.path) &&
route.file &&
!isTabRouteFile(route.file)
) {
metaFiles.push({
index: metaFiles.length,
file: winPath(route.file),
id: route.id,
});
}
});
return routes;
},
});
api.onGenerateFiles(async () => {
// generate empty atoms, then fill it by ./parser.ts
api.writeTmpFile({
noPluginDir: true,
path: ATOMS_META_PATH,
content: 'export const components = null;',
});
// generate meta entry
api.writeTmpFile({
noPluginDir: true,
path: 'dumi/meta/index.ts',
content: Mustache.render(
`{{#metaFiles}}
import { demos as dm{{{index}}}, frontmatter as fm{{{index}}}, toc as toc{{{index}}}, texts as txt{{{index}}} } from '{{{file}}}?type=meta';
{{/metaFiles}}
export { components } from './atoms';
export { tabs } from './tabs';
export const filesMeta = {
{{#metaFiles}}
'{{{id}}}': {
frontmatter: fm{{{index}}},
toc: toc{{{index}}},
texts: txt{{{index}}},
demos: dm{{{index}}},
{{#tabs}}
tabs: {{{tabs}}},
{{/tabs}}
},
{{/metaFiles}}
}
// generate demos data in runtime, for reuse route.id to reduce bundle size
export const demos = Object.entries(filesMeta).reduce((acc, [id, meta]) => {
// append route id to demo
Object.values(meta.demos).forEach((demo) => {
demo.routeId = id;
});
// merge demos
Object.assign(acc, meta.demos);
// remove demos from meta, to avoid deep clone demos in umi routes/children compatible logic
delete meta.demos;
return acc;
}, {});
`,
{
metaFiles: await api.applyPlugins({
type: api.ApplyPluginsType.modify,
key: 'dumi.modifyMetaFiles',
initialValue: metaFiles,
}),
},
),
});
// generate runtime plugin, to append page meta to route object
api.writeTmpFile({
noPluginDir: true,
path: 'dumi/meta/runtime.ts',
content: `import { filesMeta, tabs } from '.';
import deepmerge from '${winPath(
path.dirname(require.resolve('deepmerge/package')),
)}';
export const patchRoutes = ({ routes }) => {
Object.values(routes).forEach((route) => {
if (filesMeta[route.id]) {
if (process.env.NODE_ENV === 'production' && (route.meta?.frontmatter?.debug || filesMeta[route.id].frontmatter.debug)) {
// hide route in production which set hide frontmatter
delete routes[route.id];
} else {
// merge meta to route object
route.meta = deepmerge(route.meta, filesMeta[route.id]);
// apply real tab data from id
route.meta.tabs = route.meta.tabs?.map((id) => {
const meta = {
frontmatter: { title: tabs[id].title },
toc: [],
texts: [],
}
return {
...tabs[id],
meta: filesMeta[id] || meta,
}
});
}
}
});
}
`,
});
});
api.addRuntimePlugin(() => '@@/dumi/meta/runtime.ts');
};