dumi 2.4.35 → 2.4.36

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.
@@ -13,7 +13,7 @@ export interface IParsedBlockAsset {
13
13
  resolveMap: Record<string, string>;
14
14
  frontmatter: ReturnType<typeof parseCodeFrontmatter>['frontmatter'];
15
15
  }
16
- declare function parseBlockAsset(opts: {
16
+ interface IParseBlockAssetOptions {
17
17
  fileAbsPath: string;
18
18
  fileLocale?: string;
19
19
  id: string;
@@ -21,5 +21,7 @@ declare function parseBlockAsset(opts: {
21
21
  entryPointCode?: string;
22
22
  resolver: typeof sync;
23
23
  techStack: IDumiTechStack;
24
- }): Promise<IParsedBlockAsset>;
24
+ cacheable?: boolean;
25
+ }
26
+ declare function parseBlockAsset(opts: IParseBlockAssetOptions): Promise<IParsedBlockAsset>;
25
27
  export default parseBlockAsset;
@@ -39,7 +39,71 @@ var import_fs = __toESM(require("fs"));
39
39
  var import_path = __toESM(require("path"));
40
40
  var import_plugin_utils = require("umi/plugin-utils");
41
41
  var import_constants = require("../constants");
42
+ var MAX_BLOCK_ASSET_CACHE_SIZE = 512;
43
+ var blockAssetCache = /* @__PURE__ */ new Map();
44
+ function cloneParsedBlockAsset(ret) {
45
+ return {
46
+ asset: JSON.parse(JSON.stringify(ret.asset)),
47
+ resolveMap: { ...ret.resolveMap },
48
+ frontmatter: ret.frontmatter ? JSON.parse(JSON.stringify(ret.frontmatter)) : ret.frontmatter
49
+ };
50
+ }
51
+ function getContentHashFromFile(file) {
52
+ try {
53
+ return (0, import_utils.getContentHash)(import_fs.default.readFileSync(file, "utf-8"));
54
+ } catch {
55
+ return "missing";
56
+ }
57
+ }
58
+ function getDepsKey(deps) {
59
+ return JSON.stringify(
60
+ Array.from(new Set(deps)).sort().map((file) => `${(0, import_plugin_utils.winPath)(file)}:${getContentHashFromFile(file)}`)
61
+ );
62
+ }
63
+ function getParseCacheKey(opts) {
64
+ const entryContent = opts.entryPointCode ?? import_fs.default.readFileSync(opts.fileAbsPath, "utf-8");
65
+ return JSON.stringify({
66
+ file: (0, import_plugin_utils.winPath)(opts.fileAbsPath),
67
+ fileLocale: opts.fileLocale,
68
+ id: opts.id,
69
+ refAtomIds: opts.refAtomIds,
70
+ entryHash: (0, import_utils.getContentHash)(entryContent),
71
+ techStack: opts.techStack.name,
72
+ runtimeOpts: opts.techStack.runtimeOpts,
73
+ hasOnBlockLoad: Boolean(opts.techStack.onBlockLoad),
74
+ hasGenerateMetadata: Boolean(opts.techStack.generateMetadata),
75
+ hasGenerateSources: Boolean(opts.techStack.generateSources)
76
+ });
77
+ }
78
+ function getParseDeps(ret, entryFile) {
79
+ return Object.values(ret.resolveMap).filter(
80
+ (file) => import_path.default.isAbsolute(file) && file !== entryFile
81
+ );
82
+ }
83
+ function getCachedBlockAsset(cacheKey) {
84
+ const cached = blockAssetCache.get(cacheKey);
85
+ if (cached && cached.depsKey === getDepsKey(cached.deps)) {
86
+ return cloneParsedBlockAsset(cached.result);
87
+ }
88
+ }
89
+ function setCachedBlockAsset(cacheKey, ret, deps) {
90
+ if (blockAssetCache.size >= MAX_BLOCK_ASSET_CACHE_SIZE) {
91
+ const firstKey = blockAssetCache.keys().next().value;
92
+ if (firstKey)
93
+ blockAssetCache.delete(firstKey);
94
+ }
95
+ blockAssetCache.set(cacheKey, {
96
+ deps,
97
+ depsKey: getDepsKey(deps),
98
+ result: cloneParsedBlockAsset(ret)
99
+ });
100
+ }
42
101
  async function parseBlockAsset(opts) {
102
+ const cacheKey = opts.cacheable ? getParseCacheKey(opts) : "";
103
+ const cached = cacheKey ? getCachedBlockAsset(cacheKey) : void 0;
104
+ if (cached) {
105
+ return cached;
106
+ }
43
107
  const asset = {
44
108
  type: "BLOCK",
45
109
  id: opts.id,
@@ -163,9 +227,18 @@ async function parseBlockAsset(opts) {
163
227
  }
164
228
  ]
165
229
  });
230
+ let hasError = false;
166
231
  try {
167
232
  await deferrer;
168
233
  } catch {
234
+ hasError = true;
235
+ }
236
+ if (!hasError && cacheKey) {
237
+ setCachedBlockAsset(
238
+ cacheKey,
239
+ result,
240
+ getParseDeps(result, opts.fileAbsPath)
241
+ );
169
242
  }
170
243
  return result;
171
244
  }
@@ -1,10 +1,16 @@
1
1
  import { type FC } from 'react';
2
- import type { IPreviewerProps } from '../types';
2
+ import type { IDemoData, IPreviewerProps } from '../types';
3
+ type DemoGetter = () => Promise<{
4
+ demos: Record<string, IDemoData>;
5
+ }>;
3
6
  export interface IDumiDemoProps {
4
7
  demo: {
5
8
  id: string;
6
9
  inline?: boolean;
10
+ loader?: DemoGetter;
11
+ version?: string;
7
12
  };
8
13
  previewerProps: Omit<IPreviewerProps, 'asset' | 'children'>;
9
14
  }
10
15
  export declare const DumiDemo: FC<IDumiDemoProps>;
16
+ export {};
@@ -10,8 +10,11 @@ var InternalDumiDemo = function InternalDumiDemo(props) {
10
10
  historyType = _useSiteData.historyType;
11
11
  var _useAppData = useAppData(),
12
12
  basename = _useAppData.basename;
13
- var id = props.demo.id;
14
- var demo = useDemo(id);
13
+ var _props$demo = props.demo,
14
+ id = _props$demo.id,
15
+ loader = _props$demo.loader,
16
+ version = _props$demo.version;
17
+ var demo = useDemo(id, loader, version);
15
18
  var component = demo.component,
16
19
  asset = demo.asset,
17
20
  renderOpts = demo.renderOpts;
@@ -73,6 +73,7 @@ var meta_default = (api) => {
73
73
  });
74
74
  parsedMetaFiles.forEach((metaFile) => {
75
75
  metaFile.isMarkdown = metaFile.file.endsWith(".md");
76
+ metaFile.loadDemoIndex = metaFile.isMarkdown && !(api.env === "development" && Boolean(api.config.utoopack));
76
77
  });
77
78
  api.writeTmpFile({
78
79
  noPluginDir: true,
@@ -320,6 +320,7 @@ function mdLoader(content) {
320
320
  var _a, _b, _c;
321
321
  let opts = this.getOptions();
322
322
  const loaderContextPath = opts[import_utoopackLoaders.UTOOPACK_LOADER_CTX_KEY];
323
+ const useUtoopackDemoHMR = process.env.NODE_ENV === "development" && Boolean(loaderContextPath);
323
324
  if (loaderContextPath) {
324
325
  const ctx = require(loaderContextPath);
325
326
  if (!((_a = opts.techStacks) == null ? void 0 : _a.length)) {
@@ -346,6 +347,7 @@ function mdLoader(content) {
346
347
  const baseCacheKey = [
347
348
  this.resourcePath,
348
349
  (0, import_utils.getContentHash)(content),
350
+ useUtoopackDemoHMR,
349
351
  JSON.stringify(import_plugin_utils.lodash.omit(opts, ["mode", "builtins", "onResolveDemos"]))
350
352
  ].join(":");
351
353
  const cacheKey = [
@@ -364,7 +366,8 @@ function mdLoader(content) {
364
366
  }
365
367
  deferrer[cacheKey] = (0, import_transformer.default)(content, {
366
368
  ...import_plugin_utils.lodash.omit(opts, ["mode", "builtins", "onResolveDemos"]),
367
- fileAbsPath: (0, import_plugin_utils.winPath)(this.resourcePath)
369
+ fileAbsPath: (0, import_plugin_utils.winPath)(this.resourcePath),
370
+ useUtoopackDemoHMR
368
371
  });
369
372
  deferrer[cacheKey].then((ret) => {
370
373
  depsMapping[this.resourcePath] = ret.meta.embeds.concat(
@@ -48,6 +48,7 @@ declare module 'vfile' {
48
48
  export interface IMdTransformerOptions {
49
49
  cwd: string;
50
50
  fileAbsPath: string;
51
+ useUtoopackDemoHMR?: boolean;
51
52
  alias: ResolveOptions['alias'];
52
53
  parentAbsPath?: string;
53
54
  techStacks: IDumiTechStack[];
@@ -113,6 +113,7 @@ var transformer_default = async (raw, opts) => {
113
113
  fileAbsPath: opts.fileAbsPath,
114
114
  fileLocaleLessPath,
115
115
  fileLocale,
116
+ useUtoopackDemoHMR: opts.useUtoopackDemoHMR,
116
117
  resolve: opts.resolve,
117
118
  resolver
118
119
  }).use(import_rehypeSlug.default).use(import_rehypeLink.default, {
@@ -10,6 +10,7 @@ type IRehypeDemoOptions = Pick<IMdTransformerOptions, 'techStacks' | 'cwd' | 'fi
10
10
  resolver: typeof sync;
11
11
  fileLocaleLessPath: string;
12
12
  fileLocale?: string;
13
+ useUtoopackDemoHMR?: boolean;
13
14
  };
14
15
  export default function rehypeDemo(opts: IRehypeDemoOptions): Transformer<Root>;
15
16
  export {};
@@ -47,6 +47,8 @@ var toString;
47
47
  var isElement;
48
48
  var DEMO_NODE_CONTAINER = "$demo-container";
49
49
  var DEMO_PROP_VALUE_KEY = "$demo-prop-value-key";
50
+ var DEMO_LOADER_PLACEHOLDER = "__DUMI_DEMO_LOADER__";
51
+ var DEMO_LOADER_PLACEHOLDER_VALUE = DEMO_LOADER_PLACEHOLDER;
50
52
  var DUMI_DEMO_TAG = "DumiDemo";
51
53
  var DUMI_DEMO_GRID_TAG = "DumiDemoGrid";
52
54
  var SKIP_DEMO_PARSE = "pure";
@@ -249,9 +251,15 @@ function rehypeDemo(opts) {
249
251
  });
250
252
  }
251
253
  const propDemo = { id: parseOpts.id };
254
+ if (opts.useUtoopackDemoHMR) {
255
+ propDemo.loader = DEMO_LOADER_PLACEHOLDER_VALUE;
256
+ }
252
257
  demoIds.push(parseOpts.id);
253
258
  deferrers.push(
254
- (0, import_block.default)(parseOpts).then(
259
+ (0, import_block.default)({
260
+ ...parseOpts,
261
+ cacheable: opts.useUtoopackDemoHMR
262
+ }).then(
255
263
  async ({ asset, resolveMap, frontmatter }) => {
256
264
  var _a2, _b2, _c2;
257
265
  if (demoIds.indexOf(parseOpts.id) !== demoIds.lastIndexOf(parseOpts.id)) {
@@ -287,6 +295,11 @@ function rehypeDemo(opts) {
287
295
  if (originalProps[key])
288
296
  asset[key] = originalProps[key];
289
297
  });
298
+ if (opts.useUtoopackDemoHMR) {
299
+ propDemo.version = (0, import_utils.getContentHash)(
300
+ JSON.stringify(asset.dependencies)
301
+ );
302
+ }
290
303
  if (/ inline/.test(String((_b2 = codeNode.data) == null ? void 0 : _b2.meta)) || originalProps.inline) {
291
304
  propDemo.inline = true;
292
305
  return {
@@ -368,7 +381,16 @@ function rehypeDemo(opts) {
368
381
  await Promise.all(deferrers).then((demos) => {
369
382
  vFile.data.demos = demos;
370
383
  replaceNodes.forEach((node) => {
371
- const value = JSON.stringify(node.data[DEMO_PROP_VALUE_KEY]);
384
+ const demoLoader = `() => import('${(0, import_plugin_utils.winPath)(
385
+ opts.fileAbsPath
386
+ )}?type=demo')`;
387
+ let value = JSON.stringify(node.data[DEMO_PROP_VALUE_KEY]);
388
+ if (opts.useUtoopackDemoHMR) {
389
+ value = value.replace(
390
+ new RegExp(`"${DEMO_LOADER_PLACEHOLDER}"`, "g"),
391
+ demoLoader
392
+ );
393
+ }
372
394
  if (node.JSXAttributes[0].type === "JSXAttribute") {
373
395
  node.JSXAttributes[0].value = value;
374
396
  } else {
@@ -46,6 +46,8 @@ const demoIdMap = Object.keys(filesMeta).reduce((total, current) => {
46
46
  return total;
47
47
  }, {});
48
48
 
49
+ type DemoGetter = () => Promise<{ demos: Record<string, IDemoData> }>;
50
+
49
51
  const demosCache = new Map<string, Promise<IDemoData | undefined>>();
50
52
 
51
53
  /**
@@ -68,19 +70,31 @@ function expandDemoContext(context?: IDemoData['context']) {
68
70
  /**
69
71
  * use demo data by id
70
72
  */
71
- export function useDemo(id: string): IDemoData | undefined {
72
- if (!demosCache.get(id)) {
73
+ export function useDemo(
74
+ id: string,
75
+ loader?: DemoGetter,
76
+ version?: string,
77
+ ): IDemoData | undefined {
78
+ const cacheKey = version ? `${id}:${version}` : id;
79
+ const getter = loader ?? demoIdMap[id];
80
+
81
+ if (!demosCache.get(cacheKey)) {
82
+ if (!getter) return undefined;
83
+
73
84
  demosCache.set(
74
- id,
75
- demoIdMap[id]?.().then(({ demos }) => {
85
+ cacheKey,
86
+ getter().then(({ demos }) => {
76
87
  // expand context for omit ext
77
88
  expandDemoContext(demos[id].context);
78
89
  return demos[id];
79
90
  }),
80
91
  );
92
+
93
+ // Reuse local demo data for consumers that still call useDemo(id), such as useLiveDemo.
94
+ demosCache.set(id, demosCache.get(cacheKey)!);
81
95
  }
82
96
 
83
- return use(demosCache.get(id)!);
97
+ return use(demosCache.get(cacheKey)!);
84
98
  }
85
99
 
86
100
  /**
@@ -1,10 +1,10 @@
1
1
  {{#metaFiles}}
2
- {{#isMarkdown}}
2
+ {{#loadDemoIndex}}
3
3
  import { frontmatter as fm{{{index}}}, toc as t{{{index}}}, demoIndex as dmi{{{index}}} } from '{{{file}}}?type=frontmatter';
4
- {{/isMarkdown}}
5
- {{^isMarkdown}}
4
+ {{/loadDemoIndex}}
5
+ {{^loadDemoIndex}}
6
6
  import { frontmatter as fm{{{index}}}, toc as t{{{index}}} } from '{{{file}}}?type=frontmatter';
7
- {{/isMarkdown}}
7
+ {{/loadDemoIndex}}
8
8
  {{/metaFiles}}
9
9
 
10
10
  export const filesMeta = {
@@ -12,9 +12,9 @@ export const filesMeta = {
12
12
  '{{{id}}}': {
13
13
  frontmatter: fm{{{index}}},
14
14
  toc: t{{{index}}},
15
- {{#isMarkdown}}
15
+ {{#loadDemoIndex}}
16
16
  demoIndex: dmi{{{index}}},
17
- {{/isMarkdown}}
17
+ {{/loadDemoIndex}}
18
18
  {{#tabs}}
19
19
  tabs: {{{tabs}}},
20
20
  {{/tabs}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dumi",
3
- "version": "2.4.35",
3
+ "version": "2.4.36",
4
4
  "description": "📖 Documentation Generator of React Component",
5
5
  "keywords": [
6
6
  "generator",