@sugarat/theme 0.5.6 → 0.5.8

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.
package/node.d.ts CHANGED
@@ -186,6 +186,27 @@ declare namespace Theme {
186
186
  * @default false
187
187
  */
188
188
  blogInfoCollapsible?: boolean;
189
+ /**
190
+ * 旋转的配置
191
+ */
192
+ hoverSpin?: boolean | HoverSpinConfig;
193
+ }
194
+ interface HoverSpinConfig {
195
+ /**
196
+ * 旋转的加速度
197
+ * @default 180
198
+ */
199
+ accel?: number;
200
+ /**
201
+ * 旋转的最大角速度
202
+ * @default 2160
203
+ */
204
+ maxVel?: number;
205
+ /**
206
+ * 缓停时长
207
+ * @default 1500
208
+ */
209
+ decelDuration?: number;
189
210
  }
190
211
  interface ArticleConfig {
191
212
  /**
@@ -382,7 +403,7 @@ declare namespace Theme {
382
403
  works?: UserWorks;
383
404
  /**
384
405
  * https://mermaid.js.org/config/setup/modules/mermaidAPI.html#mermaidapi-configuration-defaults for options
385
- * @default true
406
+ * @default false
386
407
  */
387
408
  mermaid?: any;
388
409
  /**
package/node.js CHANGED
@@ -41,7 +41,7 @@ module.exports = __toCommonJS(node_exports);
41
41
  // src/utils/node/mdPlugins.ts
42
42
  var import_module = require("module");
43
43
 
44
- // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.6.3_@algolia+client-search@5.20.0_@types+node@2_89d33a1149fc4afc3a4b1eeb07b93bba/node_modules/vitepress-plugin-tabs/dist/index.js
44
+ // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@https+++pkg.pr.new+vitepress@3d61619_@types+node@_fc84fc5ced1bdf672a18932976f96d0a/node_modules/vitepress-plugin-tabs/dist/index.js
45
45
  var tabsMarker = "=tabs";
46
46
  var tabsMarkerLen = tabsMarker.length;
47
47
  var ruleBlockTabs = (state, startLine, endLine, silent) => {
@@ -386,8 +386,9 @@ function patchDefaultThemeSideBar(cfg) {
386
386
  } : void 0;
387
387
  }
388
388
  var defaultTimeZoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() / -60;
389
- async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset, baseContent) {
390
- const fileContent = baseContent || await import_node_fs.default.promises.readFile(filepath, "utf-8");
389
+ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset, ops) {
390
+ const fileContent = ops?.baseContent || await import_node_fs.default.promises.readFile(filepath, "utf-8");
391
+ const cacheDir = ops?.cacheDir;
391
392
  const { data: frontmatter, excerpt, content } = (0, import_theme_shared2.grayMatter)(fileContent, {
392
393
  excerpt: true
393
394
  });
@@ -398,7 +399,7 @@ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset,
398
399
  meta.title = (0, import_theme_shared2.getDefaultTitle)(content);
399
400
  }
400
401
  const utcValue = timeZone >= 0 ? `+${timeZone}` : `${timeZone}`;
401
- const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}${utcValue}`) || (0, import_theme_shared2.getFileLastModifyTime)(filepath));
402
+ const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}${utcValue}`) || (0, import_theme_shared2.getFileLastModifyTime)(filepath, cacheDir));
402
403
  meta.date = formatDate(date || /* @__PURE__ */ new Date(), "yyyy/MM/dd hh:mm:ss");
403
404
  meta.categories = typeof meta.categories === "string" ? [meta.categories] : meta.categories;
404
405
  meta.tags = typeof meta.tags === "string" ? [meta.tags] : meta.tags;
@@ -417,7 +418,12 @@ async function getArticles(cfg, vpConfig) {
417
418
  const pages = (0, import_theme_shared2.getVitePressPages)(vpConfig);
418
419
  const metaResults = pages.reduce((prev, value) => {
419
420
  const { page, route, originRoute, filepath, isDynamic, dynamicRoute } = value;
420
- const metaPromise = isDynamic && dynamicRoute ? getArticleMeta(filepath, originRoute, cfg?.timeZone, (0, import_theme_shared2.renderDynamicMarkdown)(filepath, dynamicRoute.params, dynamicRoute.content)) : getArticleMeta(filepath, originRoute, cfg?.timeZone);
421
+ const metaPromise = isDynamic && dynamicRoute ? getArticleMeta(filepath, originRoute, cfg?.timeZone, {
422
+ baseContent: (0, import_theme_shared2.renderDynamicMarkdown)(filepath, dynamicRoute.params, dynamicRoute.content),
423
+ cacheDir: vpConfig.cacheDir
424
+ }) : getArticleMeta(filepath, originRoute, cfg?.timeZone, {
425
+ cacheDir: vpConfig.cacheDir
426
+ });
421
427
  prev[page] = {
422
428
  route,
423
429
  metaPromise
@@ -542,6 +548,7 @@ function themeReloadPlugin() {
542
548
  // src/utils/node/vitePlugins.ts
543
549
  function getVitePlugins(cfg = {}) {
544
550
  const plugins = [];
551
+ plugins.push(cacheAllGitTimestampsPlugin());
545
552
  plugins.push(coverImgTransform());
546
553
  if (cfg.themeColor) {
547
554
  plugins.push(setThemeScript(cfg.themeColor));
@@ -723,6 +730,15 @@ function providePageData(cfg) {
723
730
  }
724
731
  };
725
732
  }
733
+ function cacheAllGitTimestampsPlugin() {
734
+ return {
735
+ name: "@sugarat/theme-plugin-cache-all-git-timestamps",
736
+ async config(config) {
737
+ const { srcDir } = config.vitepress;
738
+ await (0, import_theme_shared4.cacheAllGitTimestamps)(srcDir);
739
+ }
740
+ };
741
+ }
726
742
  function setThemeScript(themeColor) {
727
743
  let resolveConfig;
728
744
  const pluginOps = {
@@ -759,13 +775,13 @@ function getThemeConfig(cfg = {}) {
759
775
  const extraVPConfig = {
760
776
  vite: {
761
777
  // see https://sass-lang.com/documentation/breaking-changes/legacy-js-api/
762
- css: {
763
- preprocessorOptions: {
764
- scss: {
765
- api: "modern"
766
- }
767
- }
768
- },
778
+ // css: {
779
+ // preprocessorOptions: {
780
+ // scss: {
781
+ // api: 'modern',
782
+ // },
783
+ // },
784
+ // },
769
785
  build: {
770
786
  // https://vite.dev/config/build-options.html#build-chunksizewarninglimit
771
787
  chunkSizeWarningLimit: 2048
package/node.mjs CHANGED
@@ -9,7 +9,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
9
  // src/utils/node/mdPlugins.ts
10
10
  import { createRequire } from "module";
11
11
 
12
- // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.6.3_@algolia+client-search@5.20.0_@types+node@2_89d33a1149fc4afc3a4b1eeb07b93bba/node_modules/vitepress-plugin-tabs/dist/index.js
12
+ // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@https+++pkg.pr.new+vitepress@3d61619_@types+node@_fc84fc5ced1bdf672a18932976f96d0a/node_modules/vitepress-plugin-tabs/dist/index.js
13
13
  var tabsMarker = "=tabs";
14
14
  var tabsMarkerLen = tabsMarker.length;
15
15
  var ruleBlockTabs = (state, startLine, endLine, silent) => {
@@ -352,8 +352,9 @@ function patchDefaultThemeSideBar(cfg) {
352
352
  } : void 0;
353
353
  }
354
354
  var defaultTimeZoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() / -60;
355
- async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset, baseContent) {
356
- const fileContent = baseContent || await fs.promises.readFile(filepath, "utf-8");
355
+ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset, ops) {
356
+ const fileContent = ops?.baseContent || await fs.promises.readFile(filepath, "utf-8");
357
+ const cacheDir = ops?.cacheDir;
357
358
  const { data: frontmatter, excerpt, content } = grayMatter(fileContent, {
358
359
  excerpt: true
359
360
  });
@@ -364,7 +365,7 @@ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset,
364
365
  meta.title = getDefaultTitle(content);
365
366
  }
366
367
  const utcValue = timeZone >= 0 ? `+${timeZone}` : `${timeZone}`;
367
- const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}${utcValue}`) || getFileLastModifyTime(filepath));
368
+ const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}${utcValue}`) || getFileLastModifyTime(filepath, cacheDir));
368
369
  meta.date = formatDate(date || /* @__PURE__ */ new Date(), "yyyy/MM/dd hh:mm:ss");
369
370
  meta.categories = typeof meta.categories === "string" ? [meta.categories] : meta.categories;
370
371
  meta.tags = typeof meta.tags === "string" ? [meta.tags] : meta.tags;
@@ -383,7 +384,12 @@ async function getArticles(cfg, vpConfig) {
383
384
  const pages = getVitePressPages(vpConfig);
384
385
  const metaResults = pages.reduce((prev, value) => {
385
386
  const { page, route, originRoute, filepath, isDynamic, dynamicRoute } = value;
386
- const metaPromise = isDynamic && dynamicRoute ? getArticleMeta(filepath, originRoute, cfg?.timeZone, renderDynamicMarkdown(filepath, dynamicRoute.params, dynamicRoute.content)) : getArticleMeta(filepath, originRoute, cfg?.timeZone);
387
+ const metaPromise = isDynamic && dynamicRoute ? getArticleMeta(filepath, originRoute, cfg?.timeZone, {
388
+ baseContent: renderDynamicMarkdown(filepath, dynamicRoute.params, dynamicRoute.content),
389
+ cacheDir: vpConfig.cacheDir
390
+ }) : getArticleMeta(filepath, originRoute, cfg?.timeZone, {
391
+ cacheDir: vpConfig.cacheDir
392
+ });
387
393
  prev[page] = {
388
394
  route,
389
395
  metaPromise
@@ -426,7 +432,7 @@ import {
426
432
  pagefindPlugin
427
433
  } from "vitepress-plugin-pagefind";
428
434
  import { RssPlugin } from "vitepress-plugin-rss";
429
- import { joinPath as joinPath2 } from "@sugarat/theme-shared";
435
+ import { cacheAllGitTimestamps, joinPath as joinPath2 } from "@sugarat/theme-shared";
430
436
  import { AnnouncementPlugin } from "vitepress-plugin-announcement";
431
437
  import { groupIconVitePlugin } from "vitepress-plugin-group-icons";
432
438
 
@@ -510,6 +516,7 @@ function themeReloadPlugin() {
510
516
  // src/utils/node/vitePlugins.ts
511
517
  function getVitePlugins(cfg = {}) {
512
518
  const plugins = [];
519
+ plugins.push(cacheAllGitTimestampsPlugin());
513
520
  plugins.push(coverImgTransform());
514
521
  if (cfg.themeColor) {
515
522
  plugins.push(setThemeScript(cfg.themeColor));
@@ -691,6 +698,15 @@ function providePageData(cfg) {
691
698
  }
692
699
  };
693
700
  }
701
+ function cacheAllGitTimestampsPlugin() {
702
+ return {
703
+ name: "@sugarat/theme-plugin-cache-all-git-timestamps",
704
+ async config(config) {
705
+ const { srcDir } = config.vitepress;
706
+ await cacheAllGitTimestamps(srcDir);
707
+ }
708
+ };
709
+ }
694
710
  function setThemeScript(themeColor) {
695
711
  let resolveConfig;
696
712
  const pluginOps = {
@@ -727,13 +743,13 @@ function getThemeConfig(cfg = {}) {
727
743
  const extraVPConfig = {
728
744
  vite: {
729
745
  // see https://sass-lang.com/documentation/breaking-changes/legacy-js-api/
730
- css: {
731
- preprocessorOptions: {
732
- scss: {
733
- api: "modern"
734
- }
735
- }
736
- },
746
+ // css: {
747
+ // preprocessorOptions: {
748
+ // scss: {
749
+ // api: 'modern',
750
+ // },
751
+ // },
752
+ // },
737
753
  build: {
738
754
  // https://vite.dev/config/build-options.html#build-chunksizewarninglimit
739
755
  chunkSizeWarningLimit: 2048
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
5
5
  "author": "sugar",
6
6
  "license": "MIT",
@@ -53,10 +53,10 @@
53
53
  "vitepress-plugin-group-icons": "1.2.4",
54
54
  "vitepress-plugin-mermaid": "2.0.13",
55
55
  "vitepress-plugin-tabs": "0.2.0",
56
- "@sugarat/theme-shared": "0.0.5",
57
- "vitepress-plugin-announcement": "0.1.5",
58
- "vitepress-plugin-pagefind": "0.4.14",
59
- "vitepress-plugin-rss": "0.3.1"
56
+ "vitepress-plugin-rss": "0.3.3",
57
+ "@sugarat/theme-shared": "0.0.6",
58
+ "vitepress-plugin-pagefind": "0.4.16",
59
+ "vitepress-plugin-announcement": "0.1.5"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@element-plus/icons-vue": "^2.3.1",
@@ -66,7 +66,7 @@
66
66
  "sass": "^1.80.6",
67
67
  "typescript": "^5.4.5",
68
68
  "vite": "^5.4.9",
69
- "vitepress": "1.6.3",
69
+ "vitepress": "https://pkg.pr.new/vitepress@3d61619",
70
70
  "vue": "^3.5.12",
71
71
  "vitepress-plugin-51la": "0.1.0"
72
72
  },
@@ -1,7 +1,8 @@
1
1
  <script setup>
2
2
  import { useData, withBase } from 'vitepress'
3
- import { computed } from 'vue'
4
- import { useBlogConfig, useGlobalAuthor, useHomeConfig } from '../composables/config/blog'
3
+ import { computed, ref } from 'vue'
4
+ import { useGlobalAuthor, useHomeConfig } from '../composables/config/blog'
5
+ import { useHoverSpin } from '../hooks/useHoverSpin'
5
6
 
6
7
  const home = useHomeConfig()
7
8
  const { frontmatter, site } = useData()
@@ -20,11 +21,15 @@ const logo = computed(() =>
20
21
  ?? '/logo.png'
21
22
  )
22
23
  const show = computed(() => author.value || logo.value)
24
+
25
+ const imgRef = ref(null)
26
+
27
+ useHoverSpin(imgRef, home?.value?.hoverSpin)
23
28
  </script>
24
29
 
25
30
  <template>
26
31
  <div v-if="show" class="blog-author">
27
- <img v-if="logo" :src="withBase(logo)" alt="avatar">
32
+ <img v-if="logo" ref="imgRef" :src="withBase(logo)" alt="avatar">
28
33
  <p v-if="author">
29
34
  {{ author }}
30
35
  </p>
@@ -42,12 +47,7 @@ const show = computed(() => author.value || logo.value)
42
47
  height: 100px;
43
48
  border-radius: 50%;
44
49
  background-color: rgba(var(--bg-gradient-home));
45
- }
46
-
47
- img:hover {
48
- transform: rotate(666turn);
49
- transition-duration: 59s;
50
- transition-timing-function: cubic-bezier(.34, 0, .84, 1)
50
+ cursor: pointer;
51
51
  }
52
52
 
53
53
  p {
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import { useData, withBase } from 'vitepress'
3
- import { computed } from 'vue'
3
+ import { computed, ref } from 'vue'
4
4
  import { useHomeConfig } from '../composables/config/blog'
5
+ import { useHoverSpin } from '../hooks/useHoverSpin'
5
6
 
6
7
  const home = useHomeConfig()
7
8
  const { frontmatter, site } = useData()
@@ -13,11 +14,14 @@ const logo = computed(() =>
13
14
  ?? '/logo.png'
14
15
  )
15
16
  const alwaysHide = computed(() => frontmatter.value.blog?.minScreenAvatar === false)
17
+
18
+ const imgRef = ref(null)
19
+ useHoverSpin(imgRef, home?.value?.hoverSpin)
16
20
  </script>
17
21
 
18
22
  <template>
19
23
  <div v-show="!alwaysHide" class="blog-home-header-avatar">
20
- <img :src="withBase(logo)" alt="avatar">
24
+ <img ref="imgRef" :src="withBase(logo)" alt="avatar">
21
25
  </div>
22
26
  </template>
23
27
 
@@ -36,13 +40,14 @@ const alwaysHide = computed(() => frontmatter.value.blog?.minScreenAvatar === fa
36
40
  background-color: transparent;
37
41
  border: 5px solid rgba(var(--bg-gradient-home));
38
42
  box-sizing: border-box;
43
+ cursor: pointer;
39
44
  }
40
45
 
41
- img:hover {
42
- transform: rotate(666turn);
43
- transition-duration: 59s;
44
- transition-timing-function: cubic-bezier(.34, 0, .84, 1)
45
- }
46
+ // img:hover {
47
+ // transform: rotate(666turn);
48
+ // transition-duration: 59s;
49
+ // transition-timing-function: cubic-bezier(.34, 0, .84, 1)
50
+ // }
46
51
  }
47
52
 
48
53
  @media screen and (min-width: 768px) {
@@ -191,6 +191,28 @@ export namespace Theme {
191
191
  * @default false
192
192
  */
193
193
  blogInfoCollapsible?: boolean
194
+ /**
195
+ * 旋转的配置
196
+ */
197
+ hoverSpin?: boolean | HoverSpinConfig
198
+ }
199
+
200
+ export interface HoverSpinConfig {
201
+ /**
202
+ * 旋转的加速度
203
+ * @default 180
204
+ */
205
+ accel?: number
206
+ /**
207
+ * 旋转的最大角速度
208
+ * @default 2160
209
+ */
210
+ maxVel?: number
211
+ /**
212
+ * 缓停时长
213
+ * @default 1500
214
+ */
215
+ decelDuration?: number
194
216
  }
195
217
 
196
218
  export interface ArticleConfig {
@@ -408,7 +430,7 @@ export namespace Theme {
408
430
  works?: UserWorks
409
431
  /**
410
432
  * https://mermaid.js.org/config/setup/modules/mermaidAPI.html#mermaidapi-configuration-defaults for options
411
- * @default true
433
+ * @default false
412
434
  */
413
435
  mermaid?: any
414
436
  /**
@@ -0,0 +1,114 @@
1
+ import { type Ref, onMounted, onUnmounted } from 'vue'
2
+ import type { Theme } from '../composables/config/index'
3
+
4
+ /**
5
+ * 元素悬停旋转
6
+ * @param elRef 元素引用
7
+ * @param accel 加速度
8
+ * @param maxVel 最大角速度
9
+ * @param decelDuration 缓停时长
10
+ */
11
+ export function useHoverSpin(elRef: Ref<HTMLElement | null>, hoverSpinConfig?: Theme.HoverSpinConfig | boolean) {
12
+ const { accel = 180, maxVel = 2160, decelDuration = 1500 } = hoverSpinConfig === true ? {} : hoverSpinConfig || {}
13
+ let rafId = 0
14
+ let lastTs = 0
15
+ let rotation = 0
16
+ let velocity = 0
17
+ let decelStart = 0
18
+ let decelStartRotation = 0
19
+ let decelTargetRotation = 0
20
+ let decelBias = 0
21
+ let isDecel = false
22
+ let hovering = false
23
+
24
+ function animate(ts: number) {
25
+ if (!lastTs)
26
+ lastTs = ts
27
+ const dt = (ts - lastTs) / 1000
28
+ lastTs = ts
29
+
30
+ if (hovering) {
31
+ isDecel = false
32
+ decelStart = 0
33
+ velocity = Math.min(velocity + accel * dt, maxVel)
34
+ rotation += velocity * dt
35
+ }
36
+ else if (isDecel) {
37
+ const t = Math.min((ts - decelStart) / decelDuration, 1)
38
+ const durSec = decelDuration / 1000
39
+ const base = velocity * durSec * (t - t * t / 2)
40
+ const smooth = 3 * t * t - 2 * t * t * t
41
+ rotation = decelStartRotation + base + decelBias * smooth
42
+ if (t >= 1) {
43
+ decelTargetRotation = 0
44
+ decelStartRotation = 0
45
+ decelBias = 0
46
+ rotation = decelTargetRotation
47
+ velocity = 0
48
+ isDecel = false
49
+ if (rafId) {
50
+ cancelAnimationFrame(rafId)
51
+ rafId = 0
52
+ }
53
+ const el2 = elRef.value as HTMLElement | null
54
+ if (el2)
55
+ el2.style.transform = `rotate(${rotation}deg)`
56
+ return
57
+ }
58
+ }
59
+
60
+ const el = elRef.value as HTMLElement | null
61
+ if (el)
62
+ el.style.transform = `rotate(${rotation}deg)`
63
+ if (hovering || isDecel) {
64
+ rafId = requestAnimationFrame(animate)
65
+ }
66
+ else if (rafId) {
67
+ cancelAnimationFrame(rafId)
68
+ rafId = 0
69
+ }
70
+ }
71
+
72
+ function onEnter() {
73
+ hovering = true
74
+ if (!rafId) {
75
+ lastTs = 0
76
+ rafId = requestAnimationFrame(animate)
77
+ }
78
+ }
79
+
80
+ function onLeave() {
81
+ hovering = false
82
+ isDecel = true
83
+ decelStart = performance.now()
84
+ decelStartRotation = rotation
85
+ const durSec = decelDuration / 1000
86
+ const S = velocity * durSec / 2
87
+ const minTarget = decelStartRotation + S
88
+ decelTargetRotation = Math.ceil(minTarget / 360) * 360
89
+ decelBias = (decelTargetRotation - decelStartRotation) - S
90
+ lastTs = 0
91
+ }
92
+
93
+ onMounted(() => {
94
+ if (hoverSpinConfig === false)
95
+ return
96
+ const el = elRef.value
97
+ if (!el)
98
+ return
99
+ el.addEventListener('mouseenter', onEnter)
100
+ el.addEventListener('mouseleave', onLeave)
101
+ })
102
+
103
+ onUnmounted(() => {
104
+ if (hoverSpinConfig === false)
105
+ return
106
+ const el = elRef.value
107
+ if (el) {
108
+ el.removeEventListener('mouseenter', onEnter)
109
+ el.removeEventListener('mouseleave', onLeave)
110
+ }
111
+ if (rafId)
112
+ cancelAnimationFrame(rafId)
113
+ })
114
+ }
package/src/node.ts CHANGED
@@ -25,13 +25,13 @@ export function getThemeConfig(cfg: Partial<Theme.BlogConfig> = {}) {
25
25
  const extraVPConfig: any = {
26
26
  vite: {
27
27
  // see https://sass-lang.com/documentation/breaking-changes/legacy-js-api/
28
- css: {
29
- preprocessorOptions: {
30
- scss: {
31
- api: 'modern',
32
- },
33
- },
34
- },
28
+ // css: {
29
+ // preprocessorOptions: {
30
+ // scss: {
31
+ // api: 'modern',
32
+ // },
33
+ // },
34
+ // },
35
35
  build: {
36
36
  // https://vite.dev/config/build-options.html#build-chunksizewarninglimit
37
37
  chunkSizeWarningLimit: 2048
@@ -26,9 +26,12 @@ export function getPageRoute(filepath: string, srcDir: string) {
26
26
  }
27
27
 
28
28
  const defaultTimeZoneOffset = new Date().getTimezoneOffset() / -60
29
- export async function getArticleMeta(filepath: string, route: string, timeZone = defaultTimeZoneOffset, baseContent?: string) {
30
- const fileContent = baseContent || await fs.promises.readFile(filepath, 'utf-8')
31
-
29
+ export async function getArticleMeta(filepath: string, route: string, timeZone = defaultTimeZoneOffset, ops?: {
30
+ baseContent?: string
31
+ cacheDir?: string
32
+ }) {
33
+ const fileContent = ops?.baseContent || await fs.promises.readFile(filepath, 'utf-8')
34
+ const cacheDir = ops?.cacheDir
32
35
  const { data: frontmatter, excerpt, content } = grayMatter(fileContent, {
33
36
  excerpt: true,
34
37
  })
@@ -44,7 +47,7 @@ export async function getArticleMeta(filepath: string, route: string, timeZone =
44
47
  const date = await (
45
48
  (meta.date
46
49
  && new Date(`${new Date(meta.date).toUTCString()}${utcValue}`))
47
- || getFileLastModifyTime(filepath)
50
+ || getFileLastModifyTime(filepath, cacheDir)
48
51
  )
49
52
  // 无法获取时兜底当前时间
50
53
  meta.date = formatDate(date || new Date(), 'yyyy/MM/dd hh:mm:ss')
@@ -86,8 +89,13 @@ export async function getArticles(cfg: Partial<Theme.BlogConfig>, vpConfig: Site
86
89
  const { page, route, originRoute, filepath, isDynamic, dynamicRoute } = value
87
90
 
88
91
  const metaPromise = (isDynamic && dynamicRoute)
89
- ? getArticleMeta(filepath, originRoute, cfg?.timeZone, renderDynamicMarkdown(filepath, dynamicRoute.params, dynamicRoute.content))
90
- : getArticleMeta(filepath, originRoute, cfg?.timeZone)
92
+ ? getArticleMeta(filepath, originRoute, cfg?.timeZone, {
93
+ baseContent: renderDynamicMarkdown(filepath, dynamicRoute.params, dynamicRoute.content),
94
+ cacheDir: vpConfig.cacheDir
95
+ })
96
+ : getArticleMeta(filepath, originRoute, cfg?.timeZone, {
97
+ cacheDir: vpConfig.cacheDir
98
+ })
91
99
 
92
100
  // 提前获取,有缓存取缓存
93
101
  prev[page] = {
@@ -4,7 +4,7 @@ import {
4
4
  } from 'vitepress-plugin-pagefind'
5
5
  import { RssPlugin } from 'vitepress-plugin-rss'
6
6
  import type { PluginOption } from 'vite'
7
- import { joinPath } from '@sugarat/theme-shared'
7
+ import { cacheAllGitTimestamps, joinPath } from '@sugarat/theme-shared'
8
8
  import { AnnouncementPlugin } from 'vitepress-plugin-announcement'
9
9
  import { groupIconVitePlugin } from 'vitepress-plugin-group-icons'
10
10
  import type { Theme } from '../../composables/config/index'
@@ -15,6 +15,9 @@ import { getArticles } from './theme'
15
15
  export function getVitePlugins(cfg: Partial<Theme.BlogConfig> = {}) {
16
16
  const plugins: any[] = []
17
17
 
18
+ // 缓存所有文章的 git 提交时间
19
+ plugins.push(cacheAllGitTimestampsPlugin())
20
+
18
21
  // 处理 cover image 的路径(暂只支持自动识别的文章首图)
19
22
  plugins.push(coverImgTransform())
20
23
 
@@ -266,6 +269,16 @@ export function providePageData(cfg: Partial<Theme.BlogConfig>) {
266
269
  } as PluginOption
267
270
  }
268
271
 
272
+ export function cacheAllGitTimestampsPlugin() {
273
+ return {
274
+ name: '@sugarat/theme-plugin-cache-all-git-timestamps',
275
+ async config(config: any) {
276
+ const { srcDir } = config.vitepress
277
+ await cacheAllGitTimestamps(srcDir)
278
+ },
279
+ } as PluginOption
280
+ }
281
+
269
282
  export function setThemeScript(
270
283
  themeColor: Theme.ThemeColor
271
284
  ) {