@sugarat/theme 0.4.12 → 0.5.0

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 (38) hide show
  1. package/node.d.ts +16 -74
  2. package/node.js +148 -51
  3. package/node.mjs +753 -0
  4. package/package.json +10 -6
  5. package/src/components/BlogAlert.vue +10 -9
  6. package/src/components/BlogApp.vue +0 -2
  7. package/src/components/BlogArticleAnalyze.vue +8 -9
  8. package/src/components/BlogAuthor.vue +6 -5
  9. package/src/components/BlogBackToTop.vue +8 -10
  10. package/src/components/BlogButtonAfterArticle.vue +5 -14
  11. package/src/components/BlogCommentWrapper.vue +11 -28
  12. package/src/components/BlogDocCover.vue +3 -3
  13. package/src/components/BlogFooter.vue +3 -1
  14. package/src/components/BlogFriendLink.vue +4 -3
  15. package/src/components/BlogHomeBanner.vue +7 -6
  16. package/src/components/BlogHomeHeaderAvatar.vue +3 -3
  17. package/src/components/BlogHomeOverview.vue +3 -3
  18. package/src/components/BlogHomeTags.vue +5 -5
  19. package/src/components/BlogHotArticle.vue +4 -7
  20. package/src/components/BlogItem.vue +1 -1
  21. package/src/components/BlogList.vue +7 -6
  22. package/src/components/BlogRecommendArticle.vue +11 -14
  23. package/src/components/BlogSidebar.vue +9 -15
  24. package/src/components/CommentArtalk.vue +7 -5
  25. package/src/components/CommentGiscus.vue +7 -8
  26. package/src/components/Icon.vue +33 -0
  27. package/src/composables/config/blog.ts +203 -87
  28. package/src/composables/config/index.ts +12 -79
  29. package/src/hooks/useOml2d.ts +15 -8
  30. package/src/index.ts +3 -0
  31. package/src/node.ts +5 -1
  32. package/src/styles/index.scss +6 -6
  33. package/src/utils/node/hot-reload-plugin.ts +31 -1
  34. package/src/utils/node/index.ts +0 -2
  35. package/src/utils/node/mdPlugins.ts +4 -0
  36. package/src/utils/node/theme.ts +15 -18
  37. package/src/utils/node/vitePlugins.ts +122 -33
  38. package/src/components/BlogPopover.vue +0 -290
package/node.d.ts CHANGED
@@ -1,38 +1,12 @@
1
- import { Route, DefaultTheme, UserConfig } from 'vitepress';
2
- import { ElButton } from 'element-plus';
1
+ import { DefaultTheme, UserConfig } from 'vitepress';
3
2
  import { RSSOptions } from 'vitepress-plugin-rss';
4
3
  import { Repo, Mapping } from '@giscus/vue';
5
4
  import { Options } from 'oh-my-live2d';
6
- import { Ref } from 'vue';
7
5
  import { PagefindConfig } from 'vitepress-plugin-pagefind';
6
+ import { AnnouncementOptions } from 'vitepress-plugin-announcement';
8
7
  export { tabsMarkdownPlugin } from 'vitepress-plugin-tabs';
9
8
 
10
9
  type RSSPluginOptions = RSSOptions;
11
- declare namespace BlogPopover {
12
- interface Title {
13
- type: 'title';
14
- content: string;
15
- style?: string;
16
- }
17
- interface Text {
18
- type: 'text';
19
- content: string;
20
- style?: string;
21
- }
22
- interface Image {
23
- type: 'image';
24
- src: string;
25
- style?: string;
26
- }
27
- interface Button {
28
- type: 'button';
29
- link: string;
30
- content: string;
31
- style?: string;
32
- props?: InstanceType<typeof ElButton>['$props'];
33
- }
34
- type Value = Title | Text | Image | Button;
35
- }
36
10
  type ThemeableImage = string | {
37
11
  src: string;
38
12
  alt?: string;
@@ -279,50 +253,6 @@ declare namespace Theme {
279
253
  showIcon?: boolean;
280
254
  html?: string;
281
255
  }
282
- /**
283
- * 公告
284
- */
285
- interface Popover {
286
- title: string;
287
- /**
288
- * 细粒度的时间控制
289
- * 默认展示时间,-1 只展示1次,其它数字为每次都展示,一定时间后自动消失,0为不自动消失
290
- * 配置改变时,会重新触发展示
291
- */
292
- duration: number;
293
- /**
294
- * 移动端自动最小化
295
- * @default false
296
- */
297
- mobileMinify?: boolean;
298
- body?: BlogPopover.Value[];
299
- footer?: BlogPopover.Value[];
300
- /**
301
- * 手动重新打开
302
- * @default true
303
- */
304
- reopen?: boolean;
305
- /**
306
- * 是否打开闪烁提示,通常需要和 reopen 搭配使用
307
- * @default true
308
- */
309
- twinkle?: boolean;
310
- /**
311
- * 设置展示图标,svg
312
- * @recommend https://iconbuddy.app/search?q=fire
313
- */
314
- icon?: string;
315
- /**
316
- * 设置关闭图标,svg
317
- * @recommend https://iconbuddy.app/search?q=fire
318
- */
319
- closeIcon?: string;
320
- /**
321
- * 自定义展示策略
322
- * @param to 切换到的目标路由
323
- */
324
- onRouteChanged?: (to: Route, show: Ref<boolean>) => void;
325
- }
326
256
  interface FriendLink {
327
257
  nickname: string;
328
258
  des: string;
@@ -391,6 +321,9 @@ declare namespace Theme {
391
321
  type ThemeColor = 'vp-default' | 'vp-green' | 'vp-yellow' | 'vp-red' | 'el-blue' | 'el-yellow' | 'el-green' | 'el-red';
392
322
  interface BlogConfig {
393
323
  blog?: false;
324
+ locales?: Record<string, Omit<BlogConfig, 'locales' | 'pagesData' | 'search' | 'popover' | 'RSS'> & {
325
+ pagesData?: PageData[];
326
+ }>;
394
327
  /**
395
328
  * 展示日期格式化
396
329
  */
@@ -402,6 +335,11 @@ declare namespace Theme {
402
335
  */
403
336
  themeColor?: ThemeColor;
404
337
  pagesData: PageData[];
338
+ /**
339
+ * @deprecated 请使用 VitePress 官方 srcDir 替代
340
+ *
341
+ * @doc https://vitepress.dev/zh/reference/site-config#srcdir
342
+ */
405
343
  srcDir?: string;
406
344
  author?: string;
407
345
  hotArticle?: HotArticle | false;
@@ -428,7 +366,7 @@ declare namespace Theme {
428
366
  * el-alert
429
367
  */
430
368
  alert?: Alert;
431
- popover?: Popover;
369
+ popover?: AnnouncementOptions;
432
370
  friend?: FriendLink[] | FriendConfig;
433
371
  authorList?: Omit<FriendLink, 'avatar'>[];
434
372
  /**
@@ -500,6 +438,9 @@ declare namespace Theme {
500
438
  * 渲染时替换图片地址
501
439
  */
502
440
  imageStyle?: ImageStyleConfig;
441
+ groupIcon?: {
442
+ customIcon: Record<string, string>;
443
+ };
503
444
  }
504
445
  type FormatShowDate = {
505
446
  /**
@@ -663,7 +604,8 @@ declare function getThemeConfig(cfg?: Partial<Theme.BlogConfig>): any;
663
604
  * defineConfig Helper
664
605
  */
665
606
  declare function defineConfig(config: UserConfig<Theme.Config>): any;
607
+ declare function defineLocaleConfig(cfg: Omit<Theme.BlogConfig, 'locales' | 'pagesData'>): Omit<Theme.BlogConfig, "locales" | "pagesData">;
666
608
 
667
609
  declare function footerHTML(footerData: Theme.FooterItem | Theme.FooterItem[]): string;
668
610
 
669
- export { defineConfig, footerHTML, getThemeConfig };
611
+ export { defineConfig, defineLocaleConfig, footerHTML, getThemeConfig };
package/node.js CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var node_exports = {};
32
32
  __export(node_exports, {
33
33
  defineConfig: () => defineConfig,
34
+ defineLocaleConfig: () => defineLocaleConfig,
34
35
  footerHTML: () => footerHTML,
35
36
  getThemeConfig: () => getThemeConfig,
36
37
  tabsMarkdownPlugin: () => tabsPlugin
@@ -40,7 +41,7 @@ module.exports = __toCommonJS(node_exports);
40
41
  // src/utils/node/mdPlugins.ts
41
42
  var import_module = require("module");
42
43
 
43
- // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.3.4_@algolia+client-search@4.19.1_@types+node@20.6.3__ovjzvjgjmyeesvgttjdmi2b3dm/node_modules/vitepress-plugin-tabs/dist/index.js
44
+ // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.4.1_@algolia+client-search@4.19.1_@types+node@20.6.3__5k3dgasc4a7imgevutmoj2zgtm/node_modules/vitepress-plugin-tabs/dist/index.js
44
45
  var tabsMarker = "=tabs";
45
46
  var tabsMarkerLen = tabsMarker.length;
46
47
  var ruleBlockTabs = (state, startLine, endLine, silent) => {
@@ -206,6 +207,7 @@ var tabsPlugin = (md) => {
206
207
 
207
208
  // src/utils/node/mdPlugins.ts
208
209
  var import_vitepress_markdown_timeline = __toESM(require("vitepress-markdown-timeline"));
210
+ var import_vitepress_plugin_group_icons = require("vitepress-plugin-group-icons");
209
211
 
210
212
  // src/utils/node/index.ts
211
213
  var import_node_path = __toESM(require("path"));
@@ -287,6 +289,7 @@ function getMarkdownPlugins(cfg) {
287
289
  if (cfg?.timeline !== false) {
288
290
  markdownPlugin.push(import_vitepress_markdown_timeline.default);
289
291
  }
292
+ markdownPlugin.push(import_vitepress_plugin_group_icons.groupIconMdPlugin);
290
293
  return markdownPlugin;
291
294
  }
292
295
  function taskCheckboxPlugin(ops) {
@@ -330,8 +333,6 @@ function patchOptimizeDeps(config) {
330
333
  // src/utils/node/theme.ts
331
334
  var import_node_fs = __toESM(require("fs"));
332
335
  var import_node_path2 = __toESM(require("path"));
333
- var import_node_process = __toESM(require("process"));
334
- var import_fast_glob = __toESM(require("fast-glob"));
335
336
  var import_theme_shared2 = require("@sugarat/theme-shared");
336
337
 
337
338
  // src/utils/client/index.ts
@@ -382,13 +383,9 @@ function patchDefaultThemeSideBar(cfg) {
382
383
  ]
383
384
  } : void 0;
384
385
  }
385
- function getPageRoute(filepath, srcDir) {
386
- const route = (0, import_theme_shared2.normalizePath)(import_node_path2.default.relative(srcDir, filepath)).replace(/\.md$/, "");
387
- return `/${route}`;
388
- }
389
386
  var defaultTimeZoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() / -60;
390
- async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset) {
391
- const fileContent = await import_node_fs.default.promises.readFile(filepath, "utf-8");
387
+ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset, baseContent) {
388
+ const fileContent = baseContent || await import_node_fs.default.promises.readFile(filepath, "utf-8");
392
389
  const { data: frontmatter, excerpt, content } = (0, import_theme_shared2.grayMatter)(fileContent, {
393
390
  excerpt: true
394
391
  });
@@ -415,20 +412,19 @@ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset)
415
412
  return meta;
416
413
  }
417
414
  async function getArticles(cfg, vpConfig) {
418
- const srcDir = cfg?.srcDir || vpConfig.srcDir.replace(vpConfig.root, "").replace(/^\//, "") || import_node_process.default.argv.slice(2)?.[1] || ".";
419
- const files = import_fast_glob.default.sync(`${srcDir}/**/*.md`, { ignore: ["node_modules"], absolute: true });
420
- const metaResults = files.reduce((prev, curr) => {
421
- const route = getPageRoute(curr, vpConfig.srcDir);
422
- const metaPromise = getArticleMeta(curr, route, cfg?.timeZone);
423
- prev[curr] = {
415
+ const pages = (0, import_theme_shared2.getVitePressPages)(vpConfig);
416
+ const metaResults = pages.reduce((prev, value) => {
417
+ const { page, route, originRoute, filepath, isDynamic, dynamicRoute } = value;
418
+ const metaPromise = isDynamic && dynamicRoute ? getArticleMeta(filepath, originRoute, cfg?.timeZone, (0, import_theme_shared2.renderDynamicMarkdown)(filepath, dynamicRoute.params, dynamicRoute.content)) : getArticleMeta(filepath, originRoute, cfg?.timeZone);
419
+ prev[page] = {
424
420
  route,
425
421
  metaPromise
426
422
  };
427
423
  return prev;
428
424
  }, {});
429
425
  const pageData = [];
430
- for (const file of files) {
431
- const { route, metaPromise } = metaResults[file];
426
+ for (const page of pages) {
427
+ const { route, metaPromise } = metaResults[page.page];
432
428
  const meta = await metaPromise;
433
429
  if (meta.layout === "home") {
434
430
  continue;
@@ -458,14 +454,15 @@ function checkConfig(cfg) {
458
454
  }
459
455
 
460
456
  // src/utils/node/vitePlugins.ts
461
- var import_node_path3 = __toESM(require("path"));
462
- var import_node_fs2 = require("fs");
463
- var import_node_buffer = require("buffer");
464
457
  var import_vitepress_plugin_pagefind = require("vitepress-plugin-pagefind");
465
458
  var import_vitepress_plugin_rss = require("vitepress-plugin-rss");
466
- var import_theme_shared3 = require("@sugarat/theme-shared");
459
+ var import_theme_shared4 = require("@sugarat/theme-shared");
460
+ var import_vitepress_plugin_announcement = require("vitepress-plugin-announcement");
461
+ var import_vitepress_plugin_group_icons2 = require("vitepress-plugin-group-icons");
467
462
 
468
463
  // src/utils/node/hot-reload-plugin.ts
464
+ var import_fs = __toESM(require("fs"));
465
+ var import_theme_shared3 = require("@sugarat/theme-shared");
469
466
  function themeReloadPlugin() {
470
467
  let blogConfig;
471
468
  let vitepressConfig;
@@ -480,26 +477,51 @@ function themeReloadPlugin() {
480
477
  const restart = debounce(() => {
481
478
  server.restart();
482
479
  }, 500);
483
- server.watcher.on("add", async (path4) => {
484
- const route = generateRoute(path4);
485
- const meta = await getArticleMeta(path4, route, blogConfig?.timeZone);
480
+ server.watcher.on("add", async (path3) => {
481
+ const route = generateRoute(path3);
482
+ const meta = await getArticleMeta(path3, route, blogConfig?.timeZone);
486
483
  blogConfig.pagesData.push({
487
484
  route,
488
485
  meta
489
486
  });
490
487
  restart();
491
488
  });
492
- server.watcher.on("change", async (path4) => {
493
- const route = generateRoute(path4);
494
- const meta = await getArticleMeta(path4, route, blogConfig?.timeZone);
489
+ server.watcher.on("change", async (path3) => {
490
+ const route = generateRoute(path3);
491
+ const fileContent = await import_fs.default.promises.readFile(path3, "utf-8");
492
+ const { data: frontmatter } = (0, import_theme_shared3.grayMatter)(fileContent, {
493
+ excerpt: true
494
+ });
495
+ const meta = await getArticleMeta(path3, route, blogConfig?.timeZone);
495
496
  const matched = blogConfig.pagesData.find((v) => v.route === route);
496
- if (matched && !isEqual(matched.meta, meta, ["date", "description"])) {
497
+ const excludeKeys = ["date", "description"].filter((key) => !frontmatter[key]);
498
+ const inlineKeys = [
499
+ // vitepress 默认主题 https://vitepress.dev/zh/reference/frontmatter-config
500
+ "lang",
501
+ "outline",
502
+ "head",
503
+ "layout",
504
+ "hero",
505
+ "features",
506
+ "navbar",
507
+ "sidebar",
508
+ "aside",
509
+ "lastUpdated",
510
+ "editLink",
511
+ "footer",
512
+ "pageClass",
513
+ // 本主题扩展 https://theme.sugarat.top/config/frontmatter.html
514
+ "hiddenCover",
515
+ "readingTime",
516
+ "buttonAfterArticle"
517
+ ];
518
+ if (matched && !isEqual(matched.meta, meta, inlineKeys.concat(excludeKeys))) {
497
519
  matched.meta = meta;
498
520
  restart();
499
521
  }
500
522
  });
501
- server.watcher.on("unlink", (path4) => {
502
- const route = generateRoute(path4);
523
+ server.watcher.on("unlink", (path3) => {
524
+ const route = generateRoute(path3);
503
525
  const idx = blogConfig.pagesData.findIndex((v) => v.route === route);
504
526
  if (idx >= 0) {
505
527
  blogConfig.pagesData.splice(idx, 1);
@@ -541,6 +563,10 @@ function getVitePlugins(cfg = {}) {
541
563
  ;
542
564
  [cfg?.RSS].flat().forEach((rssConfig) => plugins.push((0, import_vitepress_plugin_rss.RssPlugin)(rssConfig)));
543
565
  }
566
+ if (cfg?.popover) {
567
+ plugins.push((0, import_vitepress_plugin_announcement.AnnouncementPlugin)(cfg.popover));
568
+ }
569
+ plugins.push((0, import_vitepress_plugin_group_icons2.groupIconVitePlugin)(cfg?.groupIcon));
544
570
  return plugins;
545
571
  }
546
572
  function registerVitePlugins(vpCfg, plugins) {
@@ -564,40 +590,85 @@ function coverImgTransform() {
564
590
  let blogConfig;
565
591
  let vitepressConfig;
566
592
  let assetsDir;
593
+ const relativeMetaName = ["cover"];
594
+ const relativeMeta = [];
595
+ const relativeMetaMap = {};
596
+ const viteAssetsMap = {};
597
+ const relativePathMap = {};
567
598
  return {
568
599
  name: "@sugarat/theme-plugin-cover-transform",
569
600
  apply: "build",
570
- enforce: "pre",
601
+ // enforce: 'pre',
571
602
  configResolved(config) {
572
603
  vitepressConfig = config.vitepress;
573
604
  assetsDir = vitepressConfig.assetsDir;
574
605
  blogConfig = config.vitepress.site.themeConfig.blog;
606
+ const pagesData = [...blogConfig.pagesData];
607
+ if (vitepressConfig.site.locales && Object.keys(vitepressConfig.site.locales).length > 1 && blogConfig?.locales) {
608
+ Object.values(blogConfig?.locales).map((v) => v.pagesData).filter((v) => !!v).forEach((v) => pagesData.push(...v));
609
+ }
610
+ pagesData.forEach((v) => {
611
+ relativeMetaName.forEach((k) => {
612
+ const value = v.meta[k];
613
+ if (value && typeof value === "string" && value.startsWith("/")) {
614
+ const absolutePath = `${vitepressConfig.srcDir}${value}`;
615
+ if (relativeMetaMap[absolutePath]) {
616
+ Object.assign(v.meta, { [k]: relativeMetaMap[absolutePath][k] });
617
+ return;
618
+ }
619
+ relativePathMap[value] = absolutePath;
620
+ relativePathMap[absolutePath] = value;
621
+ relativeMeta.push(v.meta);
622
+ relativeMetaMap[absolutePath] = v.meta;
623
+ }
624
+ });
625
+ });
626
+ },
627
+ moduleParsed(info) {
628
+ if (!relativePathMap[info.id]) {
629
+ return;
630
+ }
631
+ const asset = info.code?.match(/export default "(.*)"/)?.[1];
632
+ if (!asset) {
633
+ return;
634
+ }
635
+ viteAssetsMap[info.id] = asset;
636
+ viteAssetsMap[asset] = info.id;
637
+ relativeMeta.forEach((meta) => {
638
+ relativeMetaName.forEach((k) => {
639
+ const value = meta[k];
640
+ if (!value || !relativePathMap[value]) {
641
+ return;
642
+ }
643
+ const viteAsset = viteAssetsMap[relativePathMap[value]];
644
+ if (viteAsset) {
645
+ Object.assign(meta, { [k]: viteAsset });
646
+ }
647
+ });
648
+ });
575
649
  },
576
- async generateBundle(_, bundle) {
650
+ generateBundle(_, bundle) {
577
651
  const assetsMap = Object.entries(bundle).filter(([key]) => {
578
652
  return key.startsWith(assetsDir);
579
653
  }).map(([_2, value]) => {
580
654
  return value;
581
- });
582
- for (const page of blogConfig.pagesData) {
583
- const { cover } = page.meta;
584
- if (!cover?.startsWith?.("/")) {
585
- continue;
586
- }
587
- try {
588
- const realPath = import_node_path3.default.join(vitepressConfig.root, cover);
589
- if (!(0, import_node_fs2.existsSync)(realPath)) {
590
- continue;
655
+ }).filter((v) => v.type === "asset");
656
+ if (!assetsMap.length) {
657
+ return;
658
+ }
659
+ relativeMeta.forEach((meta) => {
660
+ relativeMetaName.forEach((k) => {
661
+ const value = meta[k];
662
+ if (!value || !viteAssetsMap[value]) {
663
+ return;
591
664
  }
592
- const fileBuffer = (0, import_node_fs2.readFileSync)(realPath);
593
- const matchAsset = assetsMap.find((v) => import_node_buffer.Buffer.compare(fileBuffer, v.source) === 0);
665
+ const absolutePath = viteAssetsMap[value];
666
+ const matchAsset = assetsMap.find((v) => (0, import_theme_shared4.joinPath)(`${vitepressConfig.srcDir}/`, v.originalFileName) === absolutePath);
594
667
  if (matchAsset) {
595
- page.meta.cover = (0, import_theme_shared3.joinPath)("/", matchAsset.fileName);
668
+ Object.assign(meta, { [k]: (0, import_theme_shared4.joinPath)("/", matchAsset.fileName) });
596
669
  }
597
- } catch (e) {
598
- vitepressConfig.logger.warn(e?.message);
599
- }
600
- }
670
+ });
671
+ });
601
672
  }
602
673
  };
603
674
  }
@@ -605,8 +676,29 @@ function providePageData(cfg) {
605
676
  return {
606
677
  name: "@sugarat/theme-plugin-provide-page-data",
607
678
  async config(config) {
608
- const pagesData = await getArticles(cfg, config.vitepress);
609
- config.vitepress.site.themeConfig.blog.pagesData = pagesData;
679
+ const vitepressConfig = config.vitepress;
680
+ const pagesData = await getArticles(cfg, vitepressConfig);
681
+ if (vitepressConfig.site.locales && Object.keys(vitepressConfig.site.locales).length > 1) {
682
+ if (!vitepressConfig.site.themeConfig.blog.locales) {
683
+ vitepressConfig.site.themeConfig.blog.locales = {};
684
+ }
685
+ const localeKeys = Object.keys(vitepressConfig.site.locales);
686
+ localeKeys.forEach((localeKey) => {
687
+ if (!vitepressConfig.site.themeConfig.blog.locales[localeKey]) {
688
+ vitepressConfig.site.themeConfig.blog.locales[localeKey] = {};
689
+ }
690
+ vitepressConfig.site.themeConfig.blog.locales[localeKey].pagesData = pagesData.filter((v) => {
691
+ const { route } = v;
692
+ const isRoot = localeKey === "root";
693
+ if (isRoot) {
694
+ return !localeKeys.filter((v2) => v2 !== "root").some((k) => route.startsWith(`/${k}`));
695
+ }
696
+ return route.startsWith(`/${localeKey}`);
697
+ });
698
+ });
699
+ return;
700
+ }
701
+ vitepressConfig.site.themeConfig.blog.pagesData = pagesData;
610
702
  }
611
703
  };
612
704
  }
@@ -659,6 +751,7 @@ function getThemeConfig(cfg = {}) {
659
751
  themeConfig: {
660
752
  blog: {
661
753
  pagesData,
754
+ // 插件里补全
662
755
  ...cfg
663
756
  },
664
757
  // 补充一些额外的配置用于继承
@@ -670,6 +763,9 @@ function getThemeConfig(cfg = {}) {
670
763
  function defineConfig(config) {
671
764
  return config;
672
765
  }
766
+ function defineLocaleConfig(cfg) {
767
+ return cfg;
768
+ }
673
769
  function footerHTML(footerData) {
674
770
  const data = [footerData || []].flat();
675
771
  return data.map((d) => {
@@ -683,6 +779,7 @@ function footerHTML(footerData) {
683
779
  // Annotate the CommonJS export names for ESM import in node:
684
780
  0 && (module.exports = {
685
781
  defineConfig,
782
+ defineLocaleConfig,
686
783
  footerHTML,
687
784
  getThemeConfig,
688
785
  tabsMarkdownPlugin