@sugarat/theme 0.4.13 → 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 (36) hide show
  1. package/node.d.ts +13 -1
  2. package/node.js +144 -51
  3. package/node.mjs +753 -0
  4. package/package.json +9 -6
  5. package/src/components/BlogAlert.vue +10 -9
  6. package/src/components/BlogArticleAnalyze.vue +8 -9
  7. package/src/components/BlogAuthor.vue +6 -5
  8. package/src/components/BlogBackToTop.vue +8 -10
  9. package/src/components/BlogButtonAfterArticle.vue +5 -14
  10. package/src/components/BlogCommentWrapper.vue +11 -28
  11. package/src/components/BlogDocCover.vue +3 -3
  12. package/src/components/BlogFooter.vue +3 -1
  13. package/src/components/BlogFriendLink.vue +4 -3
  14. package/src/components/BlogHomeBanner.vue +7 -6
  15. package/src/components/BlogHomeHeaderAvatar.vue +3 -3
  16. package/src/components/BlogHomeOverview.vue +3 -3
  17. package/src/components/BlogHomeTags.vue +5 -5
  18. package/src/components/BlogHotArticle.vue +4 -7
  19. package/src/components/BlogItem.vue +1 -1
  20. package/src/components/BlogList.vue +7 -6
  21. package/src/components/BlogRecommendArticle.vue +11 -14
  22. package/src/components/BlogSidebar.vue +9 -15
  23. package/src/components/CommentArtalk.vue +7 -5
  24. package/src/components/CommentGiscus.vue +7 -8
  25. package/src/components/Icon.vue +33 -0
  26. package/src/composables/config/blog.ts +203 -87
  27. package/src/composables/config/index.ts +9 -0
  28. package/src/hooks/useOml2d.ts +15 -8
  29. package/src/index.ts +3 -0
  30. package/src/node.ts +5 -1
  31. package/src/styles/index.scss +6 -6
  32. package/src/utils/node/hot-reload-plugin.ts +31 -1
  33. package/src/utils/node/index.ts +0 -2
  34. package/src/utils/node/mdPlugins.ts +4 -0
  35. package/src/utils/node/theme.ts +15 -18
  36. package/src/utils/node/vitePlugins.ts +117 -33
package/node.d.ts CHANGED
@@ -321,6 +321,9 @@ declare namespace Theme {
321
321
  type ThemeColor = 'vp-default' | 'vp-green' | 'vp-yellow' | 'vp-red' | 'el-blue' | 'el-yellow' | 'el-green' | 'el-red';
322
322
  interface BlogConfig {
323
323
  blog?: false;
324
+ locales?: Record<string, Omit<BlogConfig, 'locales' | 'pagesData' | 'search' | 'popover' | 'RSS'> & {
325
+ pagesData?: PageData[];
326
+ }>;
324
327
  /**
325
328
  * 展示日期格式化
326
329
  */
@@ -332,6 +335,11 @@ declare namespace Theme {
332
335
  */
333
336
  themeColor?: ThemeColor;
334
337
  pagesData: PageData[];
338
+ /**
339
+ * @deprecated 请使用 VitePress 官方 srcDir 替代
340
+ *
341
+ * @doc https://vitepress.dev/zh/reference/site-config#srcdir
342
+ */
335
343
  srcDir?: string;
336
344
  author?: string;
337
345
  hotArticle?: HotArticle | false;
@@ -430,6 +438,9 @@ declare namespace Theme {
430
438
  * 渲染时替换图片地址
431
439
  */
432
440
  imageStyle?: ImageStyleConfig;
441
+ groupIcon?: {
442
+ customIcon: Record<string, string>;
443
+ };
433
444
  }
434
445
  type FormatShowDate = {
435
446
  /**
@@ -593,7 +604,8 @@ declare function getThemeConfig(cfg?: Partial<Theme.BlogConfig>): any;
593
604
  * defineConfig Helper
594
605
  */
595
606
  declare function defineConfig(config: UserConfig<Theme.Config>): any;
607
+ declare function defineLocaleConfig(cfg: Omit<Theme.BlogConfig, 'locales' | 'pagesData'>): Omit<Theme.BlogConfig, "locales" | "pagesData">;
596
608
 
597
609
  declare function footerHTML(footerData: Theme.FooterItem | Theme.FooterItem[]): string;
598
610
 
599
- 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,15 +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");
467
460
  var import_vitepress_plugin_announcement = require("vitepress-plugin-announcement");
461
+ var import_vitepress_plugin_group_icons2 = require("vitepress-plugin-group-icons");
468
462
 
469
463
  // src/utils/node/hot-reload-plugin.ts
464
+ var import_fs = __toESM(require("fs"));
465
+ var import_theme_shared3 = require("@sugarat/theme-shared");
470
466
  function themeReloadPlugin() {
471
467
  let blogConfig;
472
468
  let vitepressConfig;
@@ -481,26 +477,51 @@ function themeReloadPlugin() {
481
477
  const restart = debounce(() => {
482
478
  server.restart();
483
479
  }, 500);
484
- server.watcher.on("add", async (path4) => {
485
- const route = generateRoute(path4);
486
- 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);
487
483
  blogConfig.pagesData.push({
488
484
  route,
489
485
  meta
490
486
  });
491
487
  restart();
492
488
  });
493
- server.watcher.on("change", async (path4) => {
494
- const route = generateRoute(path4);
495
- 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);
496
496
  const matched = blogConfig.pagesData.find((v) => v.route === route);
497
- 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))) {
498
519
  matched.meta = meta;
499
520
  restart();
500
521
  }
501
522
  });
502
- server.watcher.on("unlink", (path4) => {
503
- const route = generateRoute(path4);
523
+ server.watcher.on("unlink", (path3) => {
524
+ const route = generateRoute(path3);
504
525
  const idx = blogConfig.pagesData.findIndex((v) => v.route === route);
505
526
  if (idx >= 0) {
506
527
  blogConfig.pagesData.splice(idx, 1);
@@ -545,6 +566,7 @@ function getVitePlugins(cfg = {}) {
545
566
  if (cfg?.popover) {
546
567
  plugins.push((0, import_vitepress_plugin_announcement.AnnouncementPlugin)(cfg.popover));
547
568
  }
569
+ plugins.push((0, import_vitepress_plugin_group_icons2.groupIconVitePlugin)(cfg?.groupIcon));
548
570
  return plugins;
549
571
  }
550
572
  function registerVitePlugins(vpCfg, plugins) {
@@ -568,40 +590,85 @@ function coverImgTransform() {
568
590
  let blogConfig;
569
591
  let vitepressConfig;
570
592
  let assetsDir;
593
+ const relativeMetaName = ["cover"];
594
+ const relativeMeta = [];
595
+ const relativeMetaMap = {};
596
+ const viteAssetsMap = {};
597
+ const relativePathMap = {};
571
598
  return {
572
599
  name: "@sugarat/theme-plugin-cover-transform",
573
600
  apply: "build",
574
- enforce: "pre",
601
+ // enforce: 'pre',
575
602
  configResolved(config) {
576
603
  vitepressConfig = config.vitepress;
577
604
  assetsDir = vitepressConfig.assetsDir;
578
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
+ });
579
626
  },
580
- async generateBundle(_, bundle) {
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
+ });
649
+ },
650
+ generateBundle(_, bundle) {
581
651
  const assetsMap = Object.entries(bundle).filter(([key]) => {
582
652
  return key.startsWith(assetsDir);
583
653
  }).map(([_2, value]) => {
584
654
  return value;
585
- });
586
- for (const page of blogConfig.pagesData) {
587
- const { cover } = page.meta;
588
- if (!cover?.startsWith?.("/")) {
589
- continue;
590
- }
591
- try {
592
- const realPath = import_node_path3.default.join(vitepressConfig.root, cover);
593
- if (!(0, import_node_fs2.existsSync)(realPath)) {
594
- 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;
595
664
  }
596
- const fileBuffer = (0, import_node_fs2.readFileSync)(realPath);
597
- 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);
598
667
  if (matchAsset) {
599
- page.meta.cover = (0, import_theme_shared3.joinPath)("/", matchAsset.fileName);
668
+ Object.assign(meta, { [k]: (0, import_theme_shared4.joinPath)("/", matchAsset.fileName) });
600
669
  }
601
- } catch (e) {
602
- vitepressConfig.logger.warn(e?.message);
603
- }
604
- }
670
+ });
671
+ });
605
672
  }
606
673
  };
607
674
  }
@@ -609,8 +676,29 @@ function providePageData(cfg) {
609
676
  return {
610
677
  name: "@sugarat/theme-plugin-provide-page-data",
611
678
  async config(config) {
612
- const pagesData = await getArticles(cfg, config.vitepress);
613
- 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;
614
702
  }
615
703
  };
616
704
  }
@@ -663,6 +751,7 @@ function getThemeConfig(cfg = {}) {
663
751
  themeConfig: {
664
752
  blog: {
665
753
  pagesData,
754
+ // 插件里补全
666
755
  ...cfg
667
756
  },
668
757
  // 补充一些额外的配置用于继承
@@ -674,6 +763,9 @@ function getThemeConfig(cfg = {}) {
674
763
  function defineConfig(config) {
675
764
  return config;
676
765
  }
766
+ function defineLocaleConfig(cfg) {
767
+ return cfg;
768
+ }
677
769
  function footerHTML(footerData) {
678
770
  const data = [footerData || []].flat();
679
771
  return data.map((d) => {
@@ -687,6 +779,7 @@ function footerHTML(footerData) {
687
779
  // Annotate the CommonJS export names for ESM import in node:
688
780
  0 && (module.exports = {
689
781
  defineConfig,
782
+ defineLocaleConfig,
690
783
  footerHTML,
691
784
  getThemeConfig,
692
785
  tabsMarkdownPlugin