dumi 2.2.0 → 2.2.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.
@@ -20,7 +20,7 @@ var DemoErrorBoundary = function DemoErrorBoundary(props) {
20
20
  }, props.children);
21
21
  };
22
22
 
23
- export var DumiDemo = function DumiDemo(props) {
23
+ export var DumiDemo = /*#__PURE__*/React.memo(function (props) {
24
24
  var _useSiteData = useSiteData(),
25
25
  demos = _useSiteData.demos,
26
26
  historyType = _useSiteData.historyType;
@@ -45,4 +45,7 @@ export var DumiDemo = function DumiDemo(props) {
45
45
  props.previewerProps.demoUrl || // when use hash route, browser can automatically handle relative paths starting with #
46
46
  "".concat(isHashRoute ? "#" : '').concat(basename).concat(SP_ROUTE_PREFIX, "demos/").concat(props.demo.id)
47
47
  }, props.previewerProps), props.previewerProps.iframe ? null : /*#__PURE__*/React.createElement(DemoErrorBoundary, null, /*#__PURE__*/createElement(component)));
48
- };
48
+ }, function (prev, next) {
49
+ // compare length for performance
50
+ return JSON.stringify(prev).length === JSON.stringify(next).length;
51
+ });
@@ -14,6 +14,7 @@ interface ISiteContext {
14
14
  components: Record<string, AtomComponentAsset>;
15
15
  locales: ILocalesConfig;
16
16
  themeConfig: IThemeConfig;
17
+ hostname?: string;
17
18
  loading: boolean;
18
19
  setLoading: (status: boolean) => void;
19
20
  /**
@@ -11,7 +11,8 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
11
11
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
12
12
 
13
13
  import { getParameters } from 'codesandbox/lib/api/define';
14
- import { genReactRenderCode } from "./utils";
14
+ import { ApplyPluginsType } from 'dumi';
15
+ import { genReactRenderCode, pluginManager } from "./utils";
15
16
  var CSB_API_ENDPOINT = 'https://codesandbox.io/api/v1/sandboxes/define';
16
17
  /**
17
18
  * get serialized data that use to submit to codesandbox.io
@@ -78,9 +79,15 @@ function getCSBData(opts) {
78
79
  content: genReactRenderCode(deps.react),
79
80
  isBinary: false
80
81
  };
81
- return getParameters({
82
- files: files
82
+ var csbOpts = pluginManager.applyPlugins({
83
+ type: ApplyPluginsType.modify,
84
+ key: 'modifyCodeSandboxData',
85
+ initialValue: {
86
+ files: files
87
+ },
88
+ args: opts
83
89
  });
90
+ return getParameters(csbOpts);
84
91
  }
85
92
  /**
86
93
  * use CodeSandbox.io
@@ -11,7 +11,8 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
11
11
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
12
12
 
13
13
  import sdk from '@stackblitz/sdk';
14
- import { genReactRenderCode } from "./utils";
14
+ import { ApplyPluginsType } from 'dumi';
15
+ import { genReactRenderCode, pluginManager } from "./utils";
15
16
  export var openStackBlitz = function openStackBlitz(data) {
16
17
  var _data$asset$dependenc, _react, _deps$_react, _reactDom, _deps$_reactDom;
17
18
 
@@ -56,5 +57,11 @@ export var openStackBlitz = function openStackBlitz(data) {
56
57
  }, null, 2);
57
58
  files[entryFileName] = genReactRenderCode(deps.react);
58
59
  config.files = files;
59
- sdk.openProject(config);
60
+ var stbOpts = pluginManager.applyPlugins({
61
+ type: ApplyPluginsType.modify,
62
+ key: 'modifyStackBlitzData',
63
+ initialValue: config,
64
+ args: data
65
+ });
66
+ sdk.openProject(stbOpts);
60
67
  };
@@ -203,8 +203,8 @@ export interface IThemeConfig {
203
203
  switch: boolean;
204
204
  };
205
205
  nprogress?: boolean;
206
- socialLinks: {
207
- [key in SocialTypes]: string;
206
+ socialLinks?: {
207
+ [key in SocialTypes]?: string;
208
208
  };
209
209
  [key: string]: any;
210
210
  }
@@ -1,5 +1,11 @@
1
+ import { PluginManager } from 'dumi';
1
2
  import { useLayoutEffect } from 'react';
2
3
  import type { ILocale, INav, INavItem, IRouteMeta, IRoutesById, IUserNavValue } from './types';
4
+ /**
5
+ * private instance, do not use it in your code
6
+ */
7
+ export declare let pluginManager: PluginManager;
8
+ export declare const setPluginManager: (pm: PluginManager) => void;
3
9
  export declare const useLocaleDocRoutes: () => IRoutesById;
4
10
  /**
5
11
  * 在 react 18 中需要新的 render 方式,这个函数用来处理不同的 jsx 模式。
@@ -15,6 +15,14 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
15
15
  import { useAppData, useIntl, useSiteData } from 'dumi';
16
16
  import { useCallback, useEffect, useLayoutEffect, useState } from 'react';
17
17
  import { useLocale } from "./useLocale";
18
+ /**
19
+ * private instance, do not use it in your code
20
+ */
21
+
22
+ export var pluginManager;
23
+ export var setPluginManager = function setPluginManager(pm) {
24
+ pluginManager = pm;
25
+ };
18
26
  export var useLocaleDocRoutes = function useLocaleDocRoutes() {
19
27
  var intl = useIntl();
20
28
 
@@ -16,3 +16,4 @@ export declare const PICKED_PKG_FIELDS: {
16
16
  };
17
17
  export declare const USELESS_TMP_FILES: string[];
18
18
  export declare const VERSION_2_LEVEL_NAV = "^2.2.0";
19
+ export declare const VERSION_2_DEPRECATE_SOFT_BREAKS = "^2.2.0";
package/dist/constants.js CHANGED
@@ -28,6 +28,7 @@ __export(constants_exports, {
28
28
  SP_ROUTE_PREFIX: () => SP_ROUTE_PREFIX,
29
29
  THEME_PREFIX: () => THEME_PREFIX,
30
30
  USELESS_TMP_FILES: () => USELESS_TMP_FILES,
31
+ VERSION_2_DEPRECATE_SOFT_BREAKS: () => VERSION_2_DEPRECATE_SOFT_BREAKS,
31
32
  VERSION_2_LEVEL_NAV: () => VERSION_2_LEVEL_NAV
32
33
  });
33
34
  module.exports = __toCommonJS(constants_exports);
@@ -49,6 +50,7 @@ var PICKED_PKG_FIELDS = {
49
50
  };
50
51
  var USELESS_TMP_FILES = ["tsconfig.json", "typings.d.ts"];
51
52
  var VERSION_2_LEVEL_NAV = "^2.2.0";
53
+ var VERSION_2_DEPRECATE_SOFT_BREAKS = "^2.2.0";
52
54
  // Annotate the CommonJS export names for ESM import in node:
53
55
  0 && (module.exports = {
54
56
  LOCAL_DUMI_DIR,
@@ -60,5 +62,6 @@ var VERSION_2_LEVEL_NAV = "^2.2.0";
60
62
  SP_ROUTE_PREFIX,
61
63
  THEME_PREFIX,
62
64
  USELESS_TMP_FILES,
65
+ VERSION_2_DEPRECATE_SOFT_BREAKS,
63
66
  VERSION_2_LEVEL_NAV
64
67
  });
@@ -55,7 +55,8 @@ var compile_default = (api) => {
55
55
  resolve: api.config.resolve,
56
56
  extraRemarkPlugins: api.config.extraRemarkPlugins,
57
57
  extraRehypePlugins: api.config.extraRehypePlugins,
58
- routes: api.appData.routes
58
+ routes: api.appData.routes,
59
+ pkg: api.pkg
59
60
  };
60
61
  memo.module.rule("dumi-md").type("javascript/auto").test(/\.md$/).oneOf("md-meta").resourceQuery(/meta$/).use("babel-loader").loader(babelInUmi.loader).options(babelInUmi.options).end().use("md-meta-loader").loader(loaderPath).options({
61
62
  ...loaderBaseOpts,
@@ -50,7 +50,7 @@ var sitemap_default = (api) => {
50
50
  const writeStream = import_fs.default.createWriteStream(import_path.default.join(api.paths.absOutputPath, "sitemap.xml"));
51
51
  smis.pipe(writeStream);
52
52
  Object.values(api.appData.routes).forEach((route) => {
53
- if (!exclude.includes(route.path) && ![":", "*"].some((char) => route.path.includes(char))) {
53
+ if (!exclude.includes(route.path) && ![":", "*"].some((char) => route.path.includes(char)) && route.path !== "" && !route.isLayout) {
54
54
  smis.write({ url: route.path });
55
55
  }
56
56
  });
@@ -34,12 +34,19 @@ var import_derivative = require("../derivative");
34
34
  var import_loader = __toESM(require("./loader"));
35
35
  var DEFAULT_THEME_PATH = import_path.default.join(__dirname, "../../../theme-default");
36
36
  function getPkgThemeName(api) {
37
+ if (process.env.DUMI_THEME) {
38
+ const envThemePkgPath = require.resolve(import_path.default.join(process.env.DUMI_THEME, "package.json"), { paths: [api.cwd] });
39
+ return require(envThemePkgPath).name;
40
+ }
37
41
  const validDeps = Object.assign({}, api.pkg.dependencies, api.pkg.devDependencies);
38
42
  const pkgThemeName = Object.keys(validDeps).find((pkg) => pkg.split("/").pop().startsWith(import_constants.THEME_PREFIX));
39
43
  return pkgThemeName;
40
44
  }
41
45
  function getPkgThemePath(api) {
42
46
  const pkgThemeName = getPkgThemeName(api);
47
+ if (process.env.DUMI_THEME) {
48
+ return import_path.default.resolve(api.cwd, process.env.DUMI_THEME);
49
+ }
43
50
  return pkgThemeName && import_path.default.dirname(import_plugin_utils.resolve.sync(`${pkgThemeName}/package.json`, {
44
51
  basedir: api.cwd,
45
52
  preserveSymlinks: true
@@ -151,7 +158,7 @@ var theme_default = (api) => {
151
158
  return memo;
152
159
  });
153
160
  api.onGenerateFiles(() => {
154
- var _a, _b, _c, _d;
161
+ var _a, _b, _c, _d, _e;
155
162
  themeMapKeys.forEach((key) => {
156
163
  Object.values(originalThemeData[key] || {}).forEach((item) => {
157
164
  if (item.source === "dumi")
@@ -228,6 +235,7 @@ export default function DumiContextWrapper() {
228
235
  locales,
229
236
  loading,
230
237
  setLoading,
238
+ hostname: ${JSON.stringify((_b = api.config.sitemap) == null ? void 0 : _b.hostname)},
231
239
  themeConfig: ${JSON.stringify(Object.assign(import_plugin_utils.lodash.pick(api.config, "logo", "description", "title"), api.config.themeConfig))},
232
240
  _2_level_nav_available: ${api.appData._2LevelNavAvailable},
233
241
  }}>
@@ -236,7 +244,7 @@ export default function DumiContextWrapper() {
236
244
  );
237
245
  }`
238
246
  });
239
- const primaryColor = typeof ((_b = api.config) == null ? void 0 : _b.theme) === "object" ? (_d = (_c = api.config) == null ? void 0 : _c.theme) == null ? void 0 : _d["@c-primary"] : "#1677ff";
247
+ 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";
240
248
  api.writeTmpFile({
241
249
  noPluginDir: true,
242
250
  path: "dumi/theme/nprogress.css",
@@ -321,6 +329,21 @@ export default function DumiContextWrapper() {
321
329
  );
322
330
  })();`;
323
331
  });
332
+ api.addEntryImportsAhead(() => [
333
+ {
334
+ specifier: "{ getPluginManager as getDumiPluginManager }",
335
+ source: "./core/plugin"
336
+ },
337
+ {
338
+ specifier: "{ setPluginManager as setDumiPluginManager }",
339
+ source: (0, import_plugin_utils.winPath)(require.resolve("../../client/theme-api/utils"))
340
+ }
341
+ ]);
342
+ api.addEntryCode(() => "setDumiPluginManager(getDumiPluginManager());");
343
+ api.addRuntimePluginKey(() => [
344
+ "modifyCodeSandboxData",
345
+ "modifyStackBlitzData"
346
+ ]);
324
347
  if (require("@umijs/core/package").__npminstall_done && import_fs.default.existsSync(localThemePath) && import_fs.default.lstatSync(localThemePath).isSymbolicLink()) {
325
348
  api.chainWebpack((memo) => {
326
349
  const devThemeNodeModules = import_path.default.join(api.cwd, "../node_modules");
@@ -1,6 +1,6 @@
1
1
  import type { IParsedBlockAsset } from "../../../assetParsers/block";
2
2
  import type { IRouteMeta } from "../../../client/theme-api/types";
3
- import type { IDumiConfig, IDumiTechStack } from "../../../types";
3
+ import type { IApi, IDumiConfig, IDumiTechStack } from "../../../types";
4
4
  import type { IRoute } from 'umi';
5
5
  import type { Data } from 'vfile';
6
6
  declare module 'hast' {
@@ -42,6 +42,7 @@ export interface IMdTransformerOptions {
42
42
  extraRemarkPlugins?: IDumiConfig['extraRemarkPlugins'];
43
43
  extraRehypePlugins?: IDumiConfig['extraRehypePlugins'];
44
44
  routes: Record<string, IRoute>;
45
+ pkg: IApi['pkg'];
45
46
  }
46
47
  export interface IMdTransformerResult {
47
48
  content: string;
@@ -25,7 +25,9 @@ __export(transformer_exports, {
25
25
  default: () => transformer_default
26
26
  });
27
27
  module.exports = __toCommonJS(transformer_exports);
28
+ var import_constants = require("../../../constants");
28
29
  var import_enhanced_resolve = __toESM(require("enhanced-resolve"));
30
+ var import_plugin_utils = require("umi/plugin-utils");
29
31
  var import_rehypeDemo = __toESM(require("./rehypeDemo"));
30
32
  var import_rehypeDesc = __toESM(require("./rehypeDesc"));
31
33
  var import_rehypeEnhancedTag = __toESM(require("./rehypeEnhancedTag"));
@@ -38,9 +40,17 @@ var import_rehypeRaw = __toESM(require("./rehypeRaw"));
38
40
  var import_rehypeSlug = __toESM(require("./rehypeSlug"));
39
41
  var import_rehypeStrip = __toESM(require("./rehypeStrip"));
40
42
  var import_rehypeText = __toESM(require("./rehypeText"));
43
+ var import_remarkBreaks = __toESM(require("./remarkBreaks"));
41
44
  var import_remarkContainer = __toESM(require("./remarkContainer"));
42
45
  var import_remarkEmbed = __toESM(require("./remarkEmbed"));
43
46
  var import_remarkMeta = __toESM(require("./remarkMeta"));
47
+ function keepSoftBreak(pkg) {
48
+ var _a, _b, _c;
49
+ if (((_a = pkg == null ? void 0 : pkg.name) == null ? void 0 : _a.startsWith("@examples/")) || (pkg == null ? void 0 : pkg.name) === "dumi")
50
+ return false;
51
+ const ver = ((_b = pkg == null ? void 0 : pkg.devDependencies) == null ? void 0 : _b.dumi) ?? ((_c = pkg == null ? void 0 : pkg.dependencies) == null ? void 0 : _c.dumi) ?? "^2.0.0";
52
+ return !import_plugin_utils.semver.subset(ver, import_constants.VERSION_2_DEPRECATE_SOFT_BREAKS);
53
+ }
44
54
  function applyUnifiedPlugin(opts) {
45
55
  const [plugin, options] = Array.isArray(opts.plugin) ? opts.plugin : [opts.plugin];
46
56
  const mod = typeof plugin === "function" ? plugin : require(require.resolve(plugin, { paths: [opts.cwd] }));
@@ -53,7 +63,6 @@ var transformer_default = async (raw, opts) => {
53
63
  const { default: remarkParse } = await import("remark-parse");
54
64
  const { default: remarkFrontmatter } = await import("remark-frontmatter");
55
65
  const { default: remarkDirective } = await import("remark-directive");
56
- const { default: remarkBreaks } = await import("remark-breaks");
57
66
  const { default: remarkGfm } = await import("remark-gfm");
58
67
  const { default: remarkRehype } = await import("remark-rehype");
59
68
  const { default: rehypeAutolinkHeadings } = await import("rehype-autolink-headings");
@@ -66,7 +75,10 @@ var transformer_default = async (raw, opts) => {
66
75
  cwd: opts.cwd,
67
76
  fileAbsPath: opts.fileAbsPath,
68
77
  resolve: opts.resolve
69
- }).use(remarkDirective).use(import_remarkContainer.default).use(remarkBreaks).use(remarkGfm);
78
+ }).use(remarkDirective).use(import_remarkContainer.default).use(remarkGfm);
79
+ if (keepSoftBreak(opts.pkg)) {
80
+ processor.use(import_remarkBreaks.default, { fileAbsPath: opts.fileAbsPath });
81
+ }
70
82
  (_a = opts.extraRemarkPlugins) == null ? void 0 : _a.forEach((plugin) => applyUnifiedPlugin({
71
83
  plugin,
72
84
  processor,
@@ -172,7 +172,7 @@ function rehypeDemo(opts) {
172
172
  parseOpts.fileAbsPath = (0, import_plugin_utils.winPath)(codeNode.properties.src);
173
173
  let localId = ((_b = codeNode.properties) == null ? void 0 : _b.id) ?? import_path.default.parse(parseOpts.fileAbsPath.replace(/\/index\.(j|t)sx?$/, "")).name;
174
174
  parseOpts.id = getCodeId(opts.cwd, opts.fileAbsPath, localId, vFile.data.frontmatter.atomId);
175
- component = `React.lazy(() => import( /* webpackChunkName: "${chunkName}" */ '${(0, import_plugin_utils.winPath)(parseOpts.fileAbsPath)}?techStack=${techStack.name}'))`;
175
+ component = `React.memo(React.lazy(() => import( /* webpackChunkName: "${chunkName}" */ '${(0, import_plugin_utils.winPath)(parseOpts.fileAbsPath)}?techStack=${techStack.name}')))`;
176
176
  if (codeValue)
177
177
  codeNode.properties.title = codeValue;
178
178
  (_c = codeNode.properties).filename ?? (_c.filename = (0, import_plugin_utils.winPath)(import_path.default.relative(opts.cwd, parseOpts.fileAbsPath)));
@@ -0,0 +1,3 @@
1
+ import type { IMdTransformerOptions } from "./index";
2
+ import type { Root } from 'mdast';
3
+ export default function remarkBreaks(opts: Pick<IMdTransformerOptions, 'fileAbsPath'>): (tree: Root) => void;
@@ -0,0 +1,54 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/loaders/markdown/transformer/remarkBreaks.ts
23
+ var remarkBreaks_exports = {};
24
+ __export(remarkBreaks_exports, {
25
+ default: () => remarkBreaks
26
+ });
27
+ module.exports = __toCommonJS(remarkBreaks_exports);
28
+ var import_plugin_utils = require("umi/plugin-utils");
29
+ var findAndReplace;
30
+ (async () => {
31
+ ({ findAndReplace } = await import("mdast-util-find-and-replace"));
32
+ })();
33
+ var warningLock = /* @__PURE__ */ new Map();
34
+ function logDeprecationWarning(fileAbsPath) {
35
+ if (warningLock.get(fileAbsPath))
36
+ return;
37
+ warningLock.set(fileAbsPath, true);
38
+ import_plugin_utils.logger.warn("Detected that you are using soft breaks, dumi will transform them into spaces after the declaration", "version greater than or equal to `2.2.0`, however, they are still being transformed as line breaks now, please", "migrate them to hard breaks before upgrading the declaration version for dumi.\n", import_plugin_utils.chalk.grey(` at ${fileAbsPath}
39
+ `), import_plugin_utils.chalk.grey(" see also: https://github.com/umijs/dumi/issues/1683\n"));
40
+ }
41
+ function remarkBreaks(opts) {
42
+ const replace = (_, match) => {
43
+ if (match.input === "\n" || match.input === "\r" || match.input === "\r\n") {
44
+ return false;
45
+ }
46
+ logDeprecationWarning(opts.fileAbsPath);
47
+ return { type: "break" };
48
+ };
49
+ return (tree) => {
50
+ findAndReplace(tree, /\r?\n|\r/g, replace);
51
+ };
52
+ }
53
+ // Annotate the CommonJS export names for ESM import in node:
54
+ 0 && (module.exports = {});
@@ -58,9 +58,9 @@ var ReactTechStack = class {
58
58
  type: "es6"
59
59
  }
60
60
  });
61
- return `React.lazy(async () => {
61
+ return `React.memo(React.lazy(async () => {
62
62
  ${code}
63
- })`;
63
+ }))`;
64
64
  }
65
65
  return raw;
66
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dumi",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "📖 Documentation Generator of React Component",
5
5
  "keywords": [
6
6
  "generator",
@@ -106,6 +106,7 @@
106
106
  "html2sketch": "^1.0.1",
107
107
  "js-yaml": "^4.1.0",
108
108
  "lodash.throttle": "^4.1.1",
109
+ "mdast-util-find-and-replace": "^2.2.2",
109
110
  "mdast-util-to-string": "^3.1.0",
110
111
  "nprogress": "^0.2.0",
111
112
  "pluralize": "^8.0.0",
@@ -122,7 +123,6 @@
122
123
  "rehype-autolink-headings": "^6.1.1",
123
124
  "rehype-remove-comments": "^5.0.0",
124
125
  "rehype-stringify": "^9.0.3",
125
- "remark-breaks": "^3.0.2",
126
126
  "remark-directive": "^2.0.1",
127
127
  "remark-frontmatter": "^4.0.1",
128
128
  "remark-gfm": "^3.0.1",
@@ -29,10 +29,12 @@ var DocLayout = function DocLayout() {
29
29
  var sidebar = useSidebarData();
30
30
 
31
31
  var _useLocation = useLocation(),
32
- hash = _useLocation.hash;
32
+ hash = _useLocation.hash,
33
+ pathname = _useLocation.pathname;
33
34
 
34
35
  var _useSiteData = useSiteData(),
35
- loading = _useSiteData.loading;
36
+ loading = _useSiteData.loading,
37
+ hostname = _useSiteData.hostname;
36
38
 
37
39
  var _useState = useState(false),
38
40
  _useState2 = _slicedToArray(_useState, 2),
@@ -80,9 +82,15 @@ var DocLayout = function DocLayout() {
80
82
  }), fm.keywords && /*#__PURE__*/React.createElement("meta", {
81
83
  name: "keywords",
82
84
  content: fm.keywords.join(',')
83
- }), fm.keywords && /*#__PURE__*/React.createElement("meta", {
84
- property: "og:keywords",
85
- content: fm.keywords.join(',')
85
+ }), fm.keywords && fm.keywords.map(function (keyword) {
86
+ return /*#__PURE__*/React.createElement("meta", {
87
+ key: keyword,
88
+ property: "article:tag",
89
+ content: keyword
90
+ });
91
+ }), hostname && /*#__PURE__*/React.createElement("link", {
92
+ rel: "canonical",
93
+ href: hostname + pathname
86
94
  })), /*#__PURE__*/React.createElement(Header, null), /*#__PURE__*/React.createElement(Hero, null), /*#__PURE__*/React.createElement(Features, null), showSidebar && /*#__PURE__*/React.createElement("div", {
87
95
  className: "dumi-default-doc-layout-mobile-bar"
88
96
  }, /*#__PURE__*/React.createElement("button", {
@@ -10,11 +10,11 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
10
10
 
11
11
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
12
12
 
13
- import HeaderExtra from "../HeadeExtra";
14
13
  import { ReactComponent as IconClose } from '@ant-design/icons-svg/inline-svg/outlined/close.svg';
15
14
  import { ReactComponent as IconMenu } from '@ant-design/icons-svg/inline-svg/outlined/menu.svg';
16
15
  import { useRouteMeta, useSiteData } from 'dumi';
17
16
  import ColorSwitch from 'dumi/theme/slots/ColorSwitch';
17
+ import HeaderExtra from 'dumi/theme/slots/HeaderExtra';
18
18
  import LangSwitch from 'dumi/theme/slots/LangSwitch';
19
19
  import Logo from 'dumi/theme/slots/Logo';
20
20
  import Navbar from 'dumi/theme/slots/Navbar';
@@ -36,6 +36,7 @@
36
36
  line-height: 1;
37
37
  border: 0;
38
38
  background-color: transparent;
39
+ cursor: pointer;
39
40
 
40
41
  @{dark-selector} & {
41
42
  color: @c-text-secondary-dark;
@@ -75,7 +75,7 @@ var NavbarContent = function NavbarContent(_ref2) {
75
75
  var data = _ref2.data;
76
76
  return /*#__PURE__*/React.createElement(React.Fragment, null, data.map(function (item) {
77
77
  return /*#__PURE__*/React.createElement("li", {
78
- key: item.activePath
78
+ key: item.activePath || item.link || item.title
79
79
  }, item.link && /^(\w+:)\/\/|^(mailto|tel):/.test(item.link) ? /*#__PURE__*/React.createElement("a", {
80
80
  href: item.link,
81
81
  target: "_blank",
@@ -1,11 +1,12 @@
1
1
  import { type IPreviewerProps } from 'dumi';
2
- import { type FC } from 'react';
2
+ import { type FC, type ReactNode } from 'react';
3
3
  import './index.less';
4
4
  export interface IPreviewerActionsProps extends IPreviewerProps {
5
5
  /**
6
6
  * disabled actions
7
7
  */
8
8
  disabledActions?: ('CSB' | 'STACKBLITZ' | 'EXTERNAL' | 'HTML2SKETCH')[];
9
+ extra?: ReactNode;
9
10
  forceShowCode?: boolean;
10
11
  demoContainer: HTMLDivElement | HTMLIFrameElement;
11
12
  }
@@ -159,7 +159,7 @@ var PreviewerActions = function PreviewerActions(props) {
159
159
  "data-dumi-tooltip": intl.formatMessage({
160
160
  id: 'previewer.actions.separate'
161
161
  })
162
- }, /*#__PURE__*/React.createElement(IconExternalLink, null)), /*#__PURE__*/React.createElement(PreviewerActionsExtra, props), !props.forceShowCode && /*#__PURE__*/React.createElement("button", {
162
+ }, /*#__PURE__*/React.createElement(IconExternalLink, null)), props.extra, /*#__PURE__*/React.createElement(PreviewerActionsExtra, props), !props.forceShowCode && /*#__PURE__*/React.createElement("button", {
163
163
  className: "dumi-default-previewer-action-btn",
164
164
  type: "button",
165
165
  onClick: function onClick() {