dumi 2.2.7 → 2.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/client/pages/Demo/index.js +3 -4
  2. package/dist/client/theme-api/DumiDemo/DemoErrorBoundary.d.ts +5 -0
  3. package/dist/client/theme-api/DumiDemo/DemoErrorBoundary.js +16 -0
  4. package/dist/client/theme-api/{DumiDemo.d.ts → DumiDemo/index.d.ts} +1 -1
  5. package/dist/client/theme-api/DumiDemo/index.js +37 -0
  6. package/dist/client/theme-api/DumiDemoGrid.js +4 -1
  7. package/dist/client/theme-api/{context.d.ts → context/index.d.ts} +11 -7
  8. package/dist/client/theme-api/context/index.js +50 -0
  9. package/dist/client/theme-api/context/use.d.ts +10 -0
  10. package/dist/client/theme-api/context/use.js +25 -0
  11. package/dist/client/theme-api/index.d.ts +2 -2
  12. package/dist/client/theme-api/index.js +2 -2
  13. package/dist/client/theme-api/useRouteMeta.js +27 -26
  14. package/dist/client/theme-api/useSiteSearch/index.js +31 -13
  15. package/dist/client/theme-api/useSiteSearch/useSearchData.d.ts +9 -0
  16. package/dist/client/theme-api/useSiteSearch/useSearchData.js +76 -0
  17. package/dist/constants.d.ts +1 -0
  18. package/dist/constants.js +4 -0
  19. package/dist/features/compile/index.js +13 -1
  20. package/dist/features/exports.js +3 -1
  21. package/dist/features/meta.js +41 -76
  22. package/dist/features/theme/index.js +19 -55
  23. package/dist/loaders/markdown/index.d.ts +15 -3
  24. package/dist/loaders/markdown/index.js +117 -38
  25. package/dist/templates/ContextWrapper.ts.tpl +92 -0
  26. package/dist/templates/meta-demos.ts.tpl +34 -0
  27. package/dist/templates/meta-frontmatter.ts.tpl +9 -0
  28. package/dist/templates/meta-route.ts.tpl +43 -0
  29. package/dist/templates/meta-runtime.ts.tpl +48 -0
  30. package/dist/templates/meta-search.ts.tpl +43 -0
  31. package/index.d.ts +3 -0
  32. package/package.json +2 -1
  33. package/theme-default/locales/en-US.json +1 -0
  34. package/theme-default/locales/zh-CN.json +1 -0
  35. package/theme-default/slots/ContentTabs/index.js +0 -1
  36. package/theme-default/slots/SearchBar/index.js +3 -3
  37. package/theme-default/slots/SearchResult/index.js +34 -22
  38. package/dist/client/theme-api/DumiDemo.js +0 -46
  39. package/dist/client/theme-api/context.js +0 -16
@@ -34,6 +34,7 @@ __export(meta_exports, {
34
34
  default: () => meta_default
35
35
  });
36
36
  module.exports = __toCommonJS(meta_exports);
37
+ var import_constants = require("../constants");
37
38
  var import_path = __toESM(require("path"));
38
39
  var import_plugin_utils = require("umi/plugin-utils");
39
40
  var import_tabs = require("./tabs");
@@ -65,89 +66,53 @@ var meta_default = (api) => {
65
66
  path: ATOMS_META_PATH,
66
67
  content: "export const components = null;"
67
68
  });
69
+ const parsedMetaFiles = await api.applyPlugins({
70
+ type: api.ApplyPluginsType.modify,
71
+ key: "dumi.modifyMetaFiles",
72
+ initialValue: JSON.parse(JSON.stringify(metaFiles))
73
+ });
68
74
  api.writeTmpFile({
69
75
  noPluginDir: true,
70
- path: "dumi/meta/index.ts",
71
- content: import_plugin_utils.Mustache.render(
72
- `{{#metaFiles}}
73
- import { demos as dm{{{index}}}, frontmatter as fm{{{index}}}, toc as toc{{{index}}}, texts as txt{{{index}}} } from '{{{file}}}?type=meta';
74
- {{/metaFiles}}
75
-
76
- export { components } from './atoms';
77
- export { tabs } from './tabs';
78
-
79
- export const filesMeta = {
80
- {{#metaFiles}}
81
- '{{{id}}}': {
82
- frontmatter: fm{{{index}}},
83
- toc: toc{{{index}}},
84
- texts: txt{{{index}}},
85
- demos: dm{{{index}}},
86
- {{#tabs}}
87
- tabs: {{{tabs}}},
88
- {{/tabs}}
89
- },
90
- {{/metaFiles}}
91
- }
92
-
93
- // generate demos data in runtime, for reuse route.id to reduce bundle size
94
- export const demos = Object.entries(filesMeta).reduce((acc, [id, meta]) => {
95
- // append route id to demo
96
- Object.values(meta.demos).forEach((demo) => {
97
- demo.routeId = id;
98
- });
99
- // merge demos
100
- Object.assign(acc, meta.demos);
101
-
102
- // remove demos from meta, to avoid deep clone demos in umi routes/children compatible logic
103
- delete meta.demos;
104
-
105
- return acc;
106
- }, {});
107
- `,
108
- {
109
- metaFiles: await api.applyPlugins({
110
- type: api.ApplyPluginsType.modify,
111
- key: "dumi.modifyMetaFiles",
112
- initialValue: metaFiles
113
- })
114
- }
115
- )
76
+ path: "dumi/meta/search.ts",
77
+ tplPath: (0, import_plugin_utils.winPath)((0, import_path.join)(import_constants.TEMPLATES_DIR, "meta-search.ts.tpl")),
78
+ context: {
79
+ metaFiles: parsedMetaFiles
80
+ }
81
+ });
82
+ api.writeTmpFile({
83
+ noPluginDir: true,
84
+ path: "dumi/meta/frontmatter.ts",
85
+ tplPath: (0, import_plugin_utils.winPath)((0, import_path.join)(import_constants.TEMPLATES_DIR, "meta-frontmatter.ts.tpl")),
86
+ context: {
87
+ metaFiles: parsedMetaFiles
88
+ }
89
+ });
90
+ const mdFiles = parsedMetaFiles.filter(
91
+ (metaFile) => metaFile.file.endsWith(".md")
92
+ );
93
+ api.writeTmpFile({
94
+ noPluginDir: true,
95
+ path: "dumi/meta/demos.ts",
96
+ tplPath: (0, import_plugin_utils.winPath)((0, import_path.join)(import_constants.TEMPLATES_DIR, "meta-demos.ts.tpl")),
97
+ context: {
98
+ metaFiles: mdFiles
99
+ }
100
+ });
101
+ api.writeTmpFile({
102
+ noPluginDir: true,
103
+ path: "dumi/meta/route-meta.ts",
104
+ tplPath: (0, import_plugin_utils.winPath)((0, import_path.join)(import_constants.TEMPLATES_DIR, "meta-route.ts.tpl")),
105
+ context: {
106
+ metaFiles: mdFiles
107
+ }
116
108
  });
117
109
  api.writeTmpFile({
118
110
  noPluginDir: true,
119
111
  path: "dumi/meta/runtime.ts",
120
- content: `import { filesMeta, tabs } from '.';
121
- import deepmerge from '${(0, import_plugin_utils.winPath)(
122
- import_path.default.dirname(require.resolve("deepmerge/package"))
123
- )}';
124
- export const patchRoutes = ({ routes }) => {
125
- Object.values(routes).forEach((route) => {
126
- if (filesMeta[route.id]) {
127
- if (process.env.NODE_ENV === 'production' && (route.meta?.frontmatter?.debug || filesMeta[route.id].frontmatter.debug)) {
128
- // hide route in production which set hide frontmatter
129
- delete routes[route.id];
130
- } else {
131
- // merge meta to route object
132
- route.meta = deepmerge(route.meta, filesMeta[route.id]);
133
-
134
- // apply real tab data from id
135
- route.meta.tabs = route.meta.tabs?.map((id) => {
136
- const meta = {
137
- frontmatter: { title: tabs[id].title },
138
- toc: [],
139
- texts: [],
140
- }
141
- return {
142
- ...tabs[id],
143
- meta: filesMeta[id] || meta,
144
- }
145
- });
112
+ tplPath: (0, import_plugin_utils.winPath)((0, import_path.join)(import_constants.TEMPLATES_DIR, "meta-runtime.ts.tpl")),
113
+ context: {
114
+ deepmerge: (0, import_plugin_utils.winPath)(import_path.default.dirname(require.resolve("deepmerge/package")))
146
115
  }
147
- }
148
- });
149
- }
150
- `
151
116
  });
152
117
  });
153
118
  api.addRuntimePlugin(() => "@@/dumi/meta/runtime.ts");
@@ -279,63 +279,27 @@ export default DumiLoading;
279
279
  api.writeTmpFile({
280
280
  noPluginDir: true,
281
281
  path: "dumi/theme/ContextWrapper.tsx",
282
- content: `import React, { useState, useEffect, useRef } from 'react';
283
- import { useOutlet, history } from 'dumi';
284
- import { SiteContext } from '${(0, import_plugin_utils.winPath)(
285
- require.resolve("../../client/theme-api/context")
286
- )}';
287
- import { demos, components } from '../meta';
288
- import { locales } from '../locales/config';${hasDefaultExport ? `
289
- import entryDefaultExport from '${(0, import_plugin_utils.winPath)(entryFile)}';` : ""}${hasNamedExport ? `
290
- import * as entryMemberExports from '${(0, import_plugin_utils.winPath)(entryFile)}';` : ""}
291
-
292
- const entryExports = {
293
- ${hasDefaultExport ? "default: entryDefaultExport," : ""}
294
- ${hasNamedExport ? "...entryMemberExports," : ""}
295
- };
296
-
297
- export default function DumiContextWrapper() {
298
- const outlet = useOutlet();
299
- const [loading, setLoading] = useState(false);
300
- const prev = useRef(history.location.pathname);
301
-
302
- useEffect(() => {
303
- return history.listen((next) => {
304
- if (next.location.pathname !== prev.current) {
305
- prev.current = next.location.pathname;
306
-
307
- // scroll to top when route changed
308
- document.documentElement.scrollTo(0, 0);
282
+ tplPath: (0, import_plugin_utils.winPath)((0, import_path.join)(import_constants.TEMPLATES_DIR, "ContextWrapper.ts.tpl")),
283
+ context: {
284
+ contextPath: (0, import_plugin_utils.winPath)(require.resolve("../../client/theme-api/context")),
285
+ defaultExport: hasDefaultExport ? `import entryDefaultExport from '${(0, import_plugin_utils.winPath)(entryFile)}';` : "",
286
+ namedExport: hasNamedExport ? `import * as entryMemberExports from '${(0, import_plugin_utils.winPath)(entryFile)}';` : "",
287
+ hasDefaultExport,
288
+ hasNamedExport,
289
+ pkg: JSON.stringify(
290
+ import_plugin_utils.lodash.pick(api.pkg, ...Object.keys(import_constants.PICKED_PKG_FIELDS))
291
+ ),
292
+ historyType: ((_a = api.config.history) == null ? void 0 : _a.type) || "browser",
293
+ hostname: String(JSON.stringify((_b = api.config.sitemap) == null ? void 0 : _b.hostname)),
294
+ themeConfig: JSON.stringify(
295
+ Object.assign(
296
+ import_plugin_utils.lodash.pick(api.config, "logo", "description", "title"),
297
+ api.config.themeConfig
298
+ )
299
+ ),
300
+ _2_level_nav_available: api.appData._2LevelNavAvailable
309
301
  }
310
302
  });
311
- }, []);
312
-
313
- return (
314
- <SiteContext.Provider value={{
315
- pkg: ${JSON.stringify(
316
- import_plugin_utils.lodash.pick(api.pkg, ...Object.keys(import_constants.PICKED_PKG_FIELDS))
317
- )},
318
- historyType: "${((_a = api.config.history) == null ? void 0 : _a.type) || "browser"}",
319
- entryExports,
320
- demos,
321
- components,
322
- locales,
323
- loading,
324
- setLoading,
325
- hostname: ${JSON.stringify((_b = api.config.sitemap) == null ? void 0 : _b.hostname)},
326
- themeConfig: ${JSON.stringify(
327
- Object.assign(
328
- import_plugin_utils.lodash.pick(api.config, "logo", "description", "title"),
329
- api.config.themeConfig
330
- )
331
- )},
332
- _2_level_nav_available: ${api.appData._2LevelNavAvailable},
333
- }}>
334
- {outlet}
335
- </SiteContext.Provider>
336
- );
337
- }`
338
- });
339
303
  const primaryColor = typeof ((_c = api.config) == null ? void 0 : _c.theme) === "object" ? (_e = (_d = api.config) == null ? void 0 : _d.theme) == null ? void 0 : _e["@c-primary"] : "#1677ff";
340
304
  api.writeTmpFile({
341
305
  noPluginDir: true,
@@ -3,12 +3,24 @@ import { type IMdTransformerOptions, type IMdTransformerResult } from './transfo
3
3
  interface IMdLoaderDefaultModeOptions extends Omit<IMdTransformerOptions, 'fileAbsPath'> {
4
4
  mode?: 'markdown';
5
5
  builtins: IThemeLoadResult['builtins'];
6
+ onResolveDemos?: (demos: NonNullable<IMdTransformerResult['meta']['demos']>) => void;
7
+ onResolveAtomMeta?: (atomId: string, meta: IMdTransformerResult['meta']['frontmatter']) => void;
6
8
  }
7
9
  interface IMdLoaderDemosModeOptions extends Omit<IMdLoaderDefaultModeOptions, 'builtins' | 'mode'> {
8
10
  mode: 'meta';
9
- onResolveDemos?: (demos: NonNullable<IMdTransformerResult['meta']['demos']>) => void;
10
- onResolveAtomMeta?: (atomId: string, meta: IMdTransformerResult['meta']['frontmatter']) => void;
11
11
  }
12
- export type IMdLoaderOptions = IMdLoaderDefaultModeOptions | IMdLoaderDemosModeOptions;
12
+ interface IMdLoaderDemoModeOptions extends Omit<IMdLoaderDefaultModeOptions, 'builtins' | 'mode'> {
13
+ mode: 'demo';
14
+ }
15
+ interface IMdLoaderDemoIndexModeOptions extends Omit<IMdLoaderDefaultModeOptions, 'builtins' | 'mode'> {
16
+ mode: 'demo-index';
17
+ }
18
+ interface IMdLoaderFrontmatterModeOptions extends Omit<IMdLoaderDefaultModeOptions, 'builtins' | 'mode'> {
19
+ mode: 'frontmatter';
20
+ }
21
+ interface IMdLoaderTextModeOptions extends Omit<IMdLoaderDefaultModeOptions, 'builtins' | 'mode'> {
22
+ mode: 'text';
23
+ }
24
+ export type IMdLoaderOptions = IMdLoaderDefaultModeOptions | IMdLoaderDemosModeOptions | IMdLoaderDemoModeOptions | IMdLoaderFrontmatterModeOptions | IMdLoaderTextModeOptions | IMdLoaderDemoIndexModeOptions;
13
25
  export default function mdLoader(this: any, content: string): void;
14
26
  export {};
@@ -46,20 +46,10 @@ function getDemoSourceFiles(demos = []) {
46
46
  return ret;
47
47
  }, []);
48
48
  }
49
- function emit(opts, ret) {
50
- const { demos, embeds } = ret.meta;
51
- embeds.forEach((file) => this.addDependency(file));
52
- getDemoSourceFiles(demos).forEach((file) => this.addDependency(file));
53
- if (opts.mode === "meta") {
54
- const { frontmatter, toc, texts } = ret.meta;
55
- if (demos && opts.onResolveDemos) {
56
- opts.onResolveDemos(demos);
57
- }
58
- if (frontmatter.atomId && opts.onResolveAtomMeta) {
59
- opts.onResolveAtomMeta(frontmatter.atomId, frontmatter);
60
- }
61
- return import_plugin_utils.Mustache.render(
62
- `import React from 'react';
49
+ function emitMeta(opts, ret) {
50
+ const { frontmatter, toc, texts, demos } = ret.meta;
51
+ return import_plugin_utils.Mustache.render(
52
+ `import React from 'react';
63
53
 
64
54
  export const demos = {
65
55
  {{#demos}}
@@ -73,40 +63,129 @@ export const frontmatter = {{{frontmatter}}};
73
63
  export const toc = {{{toc}}};
74
64
  export const texts = {{{texts}}};
75
65
  `,
76
- {
77
- demos,
78
- frontmatter: JSON.stringify(frontmatter),
79
- toc: JSON.stringify(toc),
80
- texts: JSON.stringify(texts),
81
- renderAsset: function renderAsset() {
82
- if (!("asset" in this))
83
- return "null";
84
- let { asset } = this;
85
- const { sources } = this;
86
- Object.keys(this.sources).forEach((file) => {
87
- if (!asset.dependencies[file])
88
- return;
89
- asset = import_plugin_utils.lodash.cloneDeep(asset);
90
- asset.dependencies[file].value = `{{{require('-!${sources[file]}?dumi-raw').default}}}`;
91
- });
92
- return JSON.stringify(asset, null, 2).replace(/"{{{|}}}"/g, "");
93
- }
66
+ {
67
+ demos,
68
+ frontmatter: JSON.stringify(frontmatter),
69
+ toc: JSON.stringify(toc),
70
+ texts: JSON.stringify(texts),
71
+ renderAsset: function renderAsset() {
72
+ if (!("asset" in this))
73
+ return "null";
74
+ let { asset } = this;
75
+ const { sources } = this;
76
+ Object.keys(this.sources).forEach((file) => {
77
+ if (!asset.dependencies[file])
78
+ return;
79
+ asset = import_plugin_utils.lodash.cloneDeep(asset);
80
+ asset.dependencies[file].value = `{{{require('-!${sources[file]}?dumi-raw').default}}}`;
81
+ });
82
+ return JSON.stringify(asset, null, 2).replace(/"{{{|}}}"/g, "");
94
83
  }
95
- );
96
- } else {
97
- const isTabContent = (0, import_tabs.isTabRouteFile)(this.resourcePath);
98
- return `${Object.values(opts.builtins).map((item) => `import ${item.specifier} from '${item.source}';`).join("\n")}
84
+ }
85
+ );
86
+ }
87
+ function emitDefault(opts, ret) {
88
+ const { frontmatter, demos } = ret.meta;
89
+ const isTabContent = (0, import_tabs.isTabRouteFile)(this.resourcePath);
90
+ if (demos && opts.onResolveDemos) {
91
+ opts.onResolveDemos(demos);
92
+ }
93
+ if (frontmatter.atomId && opts.onResolveAtomMeta) {
94
+ opts.onResolveAtomMeta(frontmatter.atomId, frontmatter);
95
+ }
96
+ return `${Object.values(opts.builtins).map((item) => `import ${item.specifier} from '${item.source}';`).join("\n")}
99
97
  import React from 'react';
100
- ${isTabContent ? `import { useTabMeta } from 'dumi';` : `import { DumiPage, useRouteMeta } from 'dumi';`}
98
+ ${isTabContent ? `` : `import { DumiPage } from 'dumi';`}
99
+ import { texts as ${import_rehypeText.CONTENT_TEXTS_OBJ_NAME} } from '${(0, import_plugin_utils.winPath)(
100
+ this.resourcePath
101
+ )}?type=text';
101
102
 
102
103
  // export named function for fastRefresh
103
104
  // ref: https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#edits-always-lead-to-full-reload
104
105
  function DumiMarkdownContent() {
105
- const { texts: ${import_rehypeText.CONTENT_TEXTS_OBJ_NAME} } = use${isTabContent ? "TabMeta" : "RouteMeta"}();
106
106
  return ${isTabContent ? ret.content : `<DumiPage>${ret.content}</DumiPage>`};
107
107
  }
108
108
 
109
109
  export default DumiMarkdownContent;`;
110
+ }
111
+ function emitDemo(opts, ret) {
112
+ const { demos } = ret.meta;
113
+ return import_plugin_utils.Mustache.render(
114
+ `import React from 'react';
115
+
116
+ export const demos = {
117
+ {{#demos}}
118
+ '{{{id}}}': {
119
+ component: {{{component}}},
120
+ asset: {{{renderAsset}}}
121
+ },
122
+ {{/demos}}
123
+ };`,
124
+ {
125
+ demos,
126
+ renderAsset: function renderAsset() {
127
+ if (!("asset" in this))
128
+ return "null";
129
+ let { asset } = this;
130
+ const { sources } = this;
131
+ Object.keys(this.sources).forEach((file) => {
132
+ if (!asset.dependencies[file])
133
+ return;
134
+ asset = import_plugin_utils.lodash.cloneDeep(asset);
135
+ asset.dependencies[file].value = `{{{require('-!${sources[file]}?dumi-raw').default}}}`;
136
+ });
137
+ return JSON.stringify(asset, null, 2).replace(/"{{{|}}}"/g, "");
138
+ }
139
+ }
140
+ );
141
+ }
142
+ function emitDemoIndex(opts, ret) {
143
+ const { demos } = ret.meta;
144
+ return import_plugin_utils.Mustache.render(
145
+ `export const demoIndex = {
146
+ ids: {{{ids}}},
147
+ getter: {{{getter}}}
148
+ };`,
149
+ {
150
+ ids: JSON.stringify(demos == null ? void 0 : demos.map((demo) => demo.id)),
151
+ getter: `() => import('${(0, import_plugin_utils.winPath)(this.resourcePath)}?type=demo')`
152
+ }
153
+ );
154
+ }
155
+ function emitFrontmatter(opts, ret) {
156
+ const { frontmatter } = ret.meta;
157
+ return import_plugin_utils.Mustache.render(`export const frontmatter = {{{frontmatter}}};`, {
158
+ frontmatter: JSON.stringify(frontmatter)
159
+ });
160
+ }
161
+ function emitText(opts, ret) {
162
+ const { texts, toc } = ret.meta;
163
+ return import_plugin_utils.Mustache.render(
164
+ `export const toc = {{{toc}}};
165
+ export const texts = {{{texts}}};`,
166
+ {
167
+ toc: JSON.stringify(toc),
168
+ texts: JSON.stringify(texts)
169
+ }
170
+ );
171
+ }
172
+ function emit(opts, ret) {
173
+ const { demos, embeds } = ret.meta;
174
+ embeds.forEach((file) => this.addDependency(file));
175
+ getDemoSourceFiles(demos).forEach((file) => this.addDependency(file));
176
+ switch (opts.mode) {
177
+ case "meta":
178
+ return emitMeta.call(this, opts, ret);
179
+ case "demo":
180
+ return emitDemo.call(this, opts, ret);
181
+ case "demo-index":
182
+ return emitDemoIndex.call(this, opts, ret);
183
+ case "frontmatter":
184
+ return emitFrontmatter.call(this, opts, ret);
185
+ case "text":
186
+ return emitText.call(this, opts, ret);
187
+ default:
188
+ return emitDefault.call(this, opts, ret);
110
189
  }
111
190
  }
112
191
  function getDepsCacheKey(deps = []) {
@@ -0,0 +1,92 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { useOutlet, history } from 'dumi';
3
+ import { warning } from 'rc-util';
4
+ import { SiteContext, type ISiteContext } from '{{{contextPath}}}';
5
+ import { components } from '../meta/atoms';
6
+ import { tabs } from '../meta/tabs';
7
+ import { getDemoById } from '../meta/demos';
8
+ import { locales } from '../locales/config';
9
+ {{{defaultExport}}}
10
+ {{{namedExport}}}
11
+
12
+ const entryExports = {
13
+ {{#hasDefaultExport}}
14
+ default: entryDefaultExport,
15
+ {{/hasDefaultExport}}
16
+ {{#hasNamedExport}}
17
+ ...entryMemberExports,
18
+ {{/hasNamedExport}}
19
+ };
20
+
21
+ // Static content
22
+ const pkg = {{{pkg}}};
23
+ const historyType = "{{{historyType}}}";
24
+ const hostname = {{{hostname}}};
25
+ const themeConfig = {{{themeConfig}}};
26
+ const _2_level_nav_available = {{{_2_level_nav_available}}};
27
+
28
+ export default function DumiContextWrapper() {
29
+ const outlet = useOutlet();
30
+ const [loading, setLoading] = useState(false);
31
+ const prev = useRef(history.location.pathname);
32
+
33
+ useEffect(() => {
34
+ return history.listen((next) => {
35
+ if (next.location.pathname !== prev.current) {
36
+ prev.current = next.location.pathname;
37
+
38
+ // scroll to top when route changed
39
+ document.documentElement.scrollTo(0, 0);
40
+ }
41
+ });
42
+ }, []);
43
+
44
+ const context: ISiteContext = React.useMemo(() => {
45
+ const ctx = {
46
+ pkg,
47
+ historyType,
48
+ entryExports,
49
+ demos: null,
50
+ components,
51
+ tabs,
52
+ locales,
53
+ loading,
54
+ setLoading,
55
+ hostname,
56
+ themeConfig,
57
+ _2_level_nav_available,
58
+ getDemoById,
59
+ };
60
+
61
+ // Proxy do not warning since `Object.keys` will get nothing to loop
62
+ Object.defineProperty(ctx, 'demos', {
63
+ get: () => {
64
+ warning(false, '`demos` return empty in latest version.');
65
+ return {};
66
+ },
67
+ });
68
+
69
+ return ctx;
70
+ }, [
71
+ pkg,
72
+ historyType,
73
+ entryExports,
74
+ components,
75
+ tabs,
76
+ locales,
77
+ loading,
78
+ setLoading,
79
+ hostname,
80
+ themeConfig,
81
+ _2_level_nav_available,
82
+ getDemoById,
83
+ ]);
84
+
85
+
86
+
87
+ return (
88
+ <SiteContext.Provider value={context}>
89
+ {outlet}
90
+ </SiteContext.Provider>
91
+ );
92
+ }
@@ -0,0 +1,34 @@
1
+ {{#metaFiles}}
2
+ import { demoIndex as dmi{{{index}}} } from '{{{file}}}?type=demo-index';
3
+ {{/metaFiles}}
4
+
5
+ const demoIndexes: Record<string, { ids: string[], getter: () => Promise<any> }> = {
6
+ {{#metaFiles}}
7
+ '{{{id}}}': dmi{{{index}}},
8
+ {{/metaFiles}}
9
+ };
10
+
11
+ // Convert the demoIndex to a key-value pairs: <demoId, getter>
12
+ const demoIdMap = Object.keys(demoIndexes).reduce((total, current) => {
13
+ const demoIndex = demoIndexes[current];
14
+ const { ids, getter } = demoIndex;
15
+
16
+ ids.forEach((id) => {
17
+ total[id] = getter;
18
+ });
19
+
20
+ return total;
21
+ }, {});
22
+
23
+ /** Async to load demo by id */
24
+ export const getDemoById = async (id: string) => {
25
+ const getter = demoIdMap[id];
26
+
27
+ if (!getter) {
28
+ return null;
29
+ }
30
+
31
+ const { demos }: any = await getter() || {};
32
+
33
+ return demos?.[id];
34
+ };
@@ -0,0 +1,9 @@
1
+ {{#metaFiles}}
2
+ import { frontmatter as fm{{{index}}} } from '{{{file}}}?type=frontmatter';
3
+ {{/metaFiles}}
4
+
5
+ export const filesFrontmatter = {
6
+ {{#metaFiles}}
7
+ '{{{id}}}': fm{{{index}}},
8
+ {{/metaFiles}}
9
+ }
@@ -0,0 +1,43 @@
1
+ import { tabs } from './tabs';
2
+ import { filesFrontmatter } from './frontmatter';
3
+
4
+ const files = {
5
+ {{#metaFiles}}
6
+ '{{{id}}}': {
7
+ textGetter: () => import('{{{file}}}?type=text'),
8
+ {{#tabs}}
9
+ tabs: {{{tabs}}},
10
+ {{/tabs}}
11
+ },
12
+ {{/metaFiles}}
13
+ };
14
+
15
+ export const getRouteMetaById = async (id: string) => {
16
+ const file = files[id];
17
+
18
+ if (!file) {
19
+ return null;
20
+ }
21
+
22
+ const text = await file.textGetter();
23
+ const frontmatter = filesFrontmatter[id];
24
+
25
+ const tabsMeta = file.tabs && await Promise.all(file.tabs.map(async (tab) => {
26
+ const meta = await getRouteMetaById(tab) ?? {
27
+ frontmatter: { title: tabs[tab].title },
28
+ toc: [],
29
+ texts: [],
30
+ };
31
+ return {
32
+ ...tabs[tab],
33
+ meta,
34
+ }
35
+ }));
36
+
37
+ return {
38
+ texts: text?.texts,
39
+ toc: text?.toc,
40
+ frontmatter,
41
+ tabs: tabsMeta,
42
+ };
43
+ }
@@ -0,0 +1,48 @@
1
+ import { warning } from 'rc-util';
2
+ import { tabs } from './tabs';
3
+ import { filesFrontmatter } from './frontmatter';
4
+ import deepmerge from '{{{deepmerge}}}';
5
+
6
+ // Proxy do not warning since `Object.keys` will get nothing to loop
7
+ function wrapEmpty(meta, fieldName, defaultValue) {
8
+ Object.defineProperty(meta, fieldName, {
9
+ get: () => {
10
+ warning(false, `'${fieldName}' return empty in latest version.`);
11
+ return defaultValue;
12
+ },
13
+ });
14
+ }
15
+
16
+ export const patchRoutes = ({ routes }) => {
17
+ Object.values(routes).forEach((route) => {
18
+ if (filesFrontmatter[route.id]) {
19
+ if (process.env.NODE_ENV === 'production' && (route.meta?.frontmatter?.debug || filesFrontmatter[route.id].debug)) {
20
+ // hide route in production which set hide frontmatter
21
+ delete routes[route.id];
22
+ } else {
23
+ // merge meta to route object
24
+ route.meta = deepmerge(route.meta, { frontmatter: filesFrontmatter[route.id] });
25
+
26
+ wrapEmpty(route.meta, 'demos', {});
27
+ wrapEmpty(route.meta, 'texts', []);
28
+
29
+ // apply real tab data from id
30
+ route.meta.tabs = route.meta.tabs?.map((id) => {
31
+ const meta = {
32
+ frontmatter: filesFrontmatter[id] || { title: tabs[id].title },
33
+ toc: [],
34
+ texts: [],
35
+ }
36
+
37
+ wrapEmpty(meta, 'demos', {});
38
+ wrapEmpty(meta, 'texts', []);
39
+
40
+ return {
41
+ ...tabs[id],
42
+ meta,
43
+ }
44
+ });
45
+ }
46
+ }
47
+ });
48
+ }