@sugarat/theme 0.5.5 → 0.5.7

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
@@ -181,6 +181,11 @@ declare namespace Theme {
181
181
  * 首页数据分析卡片
182
182
  */
183
183
  analysis?: HomeAnalysis;
184
+ /**
185
+ * 首页博客信息卡片是否在移动端折叠展示
186
+ * @default false
187
+ */
188
+ blogInfoCollapsible?: boolean;
184
189
  }
185
190
  interface ArticleConfig {
186
191
  /**
@@ -377,7 +382,7 @@ declare namespace Theme {
377
382
  works?: UserWorks;
378
383
  /**
379
384
  * https://mermaid.js.org/config/setup/modules/mermaidAPI.html#mermaidapi-configuration-defaults for options
380
- * @default true
385
+ * @default false
381
386
  */
382
387
  mermaid?: any;
383
388
  /**
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.5",
3
+ "version": "0.5.7",
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-pagefind": "0.4.14",
56
+ "vitepress-plugin-pagefind": "0.4.15",
58
57
  "vitepress-plugin-announcement": "0.1.5",
59
- "vitepress-plugin-rss": "0.3.1"
58
+ "vitepress-plugin-rss": "0.3.2",
59
+ "@sugarat/theme-shared": "0.0.6"
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
  },
@@ -3,7 +3,7 @@ import Theme from 'vitepress/theme'
3
3
  import { useData } from 'vitepress'
4
4
  import { computed } from 'vue'
5
5
  import { useDarkTransition } from '../hooks/useDarkTransition'
6
- import { useBlogThemeMode, useDarkTransitionConfig } from '../composables/config/blog'
6
+ import { useBlogInfoCollapsible, useBlogThemeMode, useDarkTransitionConfig } from '../composables/config/blog'
7
7
  import BlogHomeInfo from './BlogHomeInfo.vue'
8
8
  import BlogHomeBanner from './BlogHomeBanner.vue'
9
9
  import BlogList from './BlogList.vue'
@@ -26,6 +26,8 @@ const layout = computed(() => frontmatter.value.layout)
26
26
  const isBlogTheme = useBlogThemeMode()
27
27
  const { Layout } = Theme
28
28
 
29
+ const blogInfoCollapsible = useBlogInfoCollapsible()
30
+
29
31
  // 切换深色模式过渡
30
32
  // https://vitepress.dev/zh/guide/extending-default-theme#on-appearance-toggle
31
33
  useDarkTransition()
@@ -67,7 +69,11 @@ const openTransition = useDarkTransitionConfig()
67
69
  <div class="blog-list-wrapper">
68
70
  <BlogList />
69
71
  </div>
70
- <div class="blog-info-wrapper">
72
+ <div
73
+ :class="{
74
+ 'normal-mode': blogInfoCollapsible,
75
+ }" class="blog-info-wrapper"
76
+ >
71
77
  <BlogHomeInfo />
72
78
  </div>
73
79
  </div>
@@ -108,6 +114,9 @@ const openTransition = useDarkTransitionConfig()
108
114
  <slot name="nav-screen-content-before" />
109
115
  </template>
110
116
  <template #nav-screen-content-after>
117
+ <div v-if="blogInfoCollapsible" class="minify-mode blog-info-wrapper">
118
+ <BlogHomeInfo />
119
+ </div>
111
120
  <slot name="nav-screen-content-after" />
112
121
  </template>
113
122
 
@@ -226,6 +235,24 @@ const openTransition = useDarkTransitionConfig()
226
235
  margin: 20px 0;
227
236
  width: 100%;
228
237
  }
238
+
239
+ .normal-mode {
240
+ display: none;
241
+ }
242
+
243
+ .minify-mode {
244
+ display: block;
245
+ }
246
+ }
247
+
248
+ @media screen and (min-width: 768px) {
249
+ .minify-mode {
250
+ display: none;
251
+ }
252
+
253
+ .normal-mode {
254
+ display: block;
255
+ }
229
256
  }
230
257
  </style>
231
258
 
@@ -131,24 +131,25 @@ const timeTitle = computed(() =>
131
131
  <template>
132
132
  <div v-if="showAnalyze && readingTimePosition === 'top'" class="doc-analyze" data-pagefind-ignore="all">
133
133
  <span>
134
- <ElIcon><EditPen /></ElIcon>
134
+ <ElIcon>
135
+ <EditPen />
136
+ </ElIcon>
135
137
  {{ topWordCount }}
136
138
  </span>
137
139
  <span>
138
- <ElIcon><AlarmClock /></ElIcon>
140
+ <ElIcon>
141
+ <AlarmClock />
142
+ </ElIcon>
139
143
  {{ topReadTime }}
140
144
  </span>
141
145
  </div>
142
146
  <div id="hack-article-des" ref="$des" class="meta-des">
143
147
  <!-- TODO:是否需要原创?转载等标签,理论上可以添加标签解决,可以参考 charles7c -->
144
148
  <span v-if="author && !hiddenAuthor" class="author" :title="authorTitle">
145
- <ElIcon><UserFilled /></ElIcon>
146
- <a
147
- v-if="currentAuthorInfo"
148
- class="link"
149
- :href="currentAuthorInfo.url"
150
- :title="currentAuthorInfo.des"
151
- >
149
+ <ElIcon>
150
+ <UserFilled />
151
+ </ElIcon>
152
+ <a v-if="currentAuthorInfo" class="link" :href="currentAuthorInfo.url" :title="currentAuthorInfo.des">
152
153
  {{ currentAuthorInfo.nickname }}
153
154
  </a>
154
155
  <template v-else>
@@ -156,36 +157,48 @@ const timeTitle = computed(() =>
156
157
  </template>
157
158
  </span>
158
159
  <span v-if="publishDate && !hiddenTime" class="publishDate" :title="timeTitle + hoverDate">
159
- <ElIcon><Clock /></ElIcon>
160
+ <ElIcon>
161
+ <Clock />
162
+ </ElIcon>
160
163
  {{ publishDate }}
161
164
  </span>
162
- <span v-if="tags.length" class="tags" :title="tagTitle">
163
- <ElIcon><CollectionTag /></ElIcon>
164
- <a v-for="tag in tags" :key="tag" class="link" :href="`/?tag=${tag}`">{{ tag }}
165
- </a>
166
- </span>
167
165
  <template v-if="readingTimePosition === 'inline' && showAnalyze">
168
166
  <span :title="wordCountTitle">
169
- <ElIcon><EditPen /></ElIcon>
167
+ <ElIcon>
168
+ <EditPen />
169
+ </ElIcon>
170
170
  {{ inlineWordCount }}
171
171
  </span>
172
172
  <span :title="readTimeTitle">
173
- <ElIcon><AlarmClock /></ElIcon>
173
+ <ElIcon>
174
+ <AlarmClock />
175
+ </ElIcon>
174
176
  {{ inlineReadTime }}
175
177
  </span>
176
178
  </template>
177
179
  <template v-if="readingTimePosition === 'newLine' && showAnalyze">
178
180
  <div style="width: 100%;" class="new-line-meta-des">
179
181
  <span :title="wordCountTitle">
180
- <ElIcon><EditPen /></ElIcon>
182
+ <ElIcon>
183
+ <EditPen />
184
+ </ElIcon>
181
185
  {{ inlineWordCount }}
182
186
  </span>
183
187
  <span :title="readTimeTitle">
184
- <ElIcon><AlarmClock /></ElIcon>
188
+ <ElIcon>
189
+ <AlarmClock />
190
+ </ElIcon>
185
191
  {{ inlineReadTime }}
186
192
  </span>
187
193
  </div>
188
194
  </template>
195
+ <span v-if="tags.length" class="tags" :title="tagTitle">
196
+ <ElIcon>
197
+ <CollectionTag />
198
+ </ElIcon>
199
+ <a v-for="tag in tags" :key="tag" class="link" :href="`/?tag=${tag}`">{{ tag }}
200
+ </a>
201
+ </span>
189
202
  <!-- 封面展示 -->
190
203
  <ClientOnly>
191
204
  <BlogDocCover />
@@ -200,26 +213,33 @@ const timeTitle = computed(() =>
200
213
  margin-bottom: 20px;
201
214
  display: flex;
202
215
  justify-content: center;
216
+
203
217
  span {
204
218
  margin-right: 16px;
205
219
  display: flex;
206
220
  align-items: center;
221
+
207
222
  .el-icon {
208
223
  margin-right: 4px;
209
224
  }
210
225
  }
211
226
  }
212
- .meta-des,.new-line-meta-des {
227
+
228
+ .meta-des,
229
+ .new-line-meta-des {
213
230
  text-align: left;
214
231
  color: var(--vp-c-text-2);
215
232
  font-size: 14px;
216
233
  margin-top: 6px;
217
234
  display: flex;
218
235
  flex-wrap: wrap;
236
+
219
237
  >span {
220
238
  margin-right: 16px;
221
239
  display: flex;
222
240
  align-items: center;
241
+ flex-wrap: wrap;
242
+
223
243
  .el-icon {
224
244
  margin-right: 4px;
225
245
  }
@@ -227,12 +247,14 @@ const timeTitle = computed(() =>
227
247
 
228
248
  .link {
229
249
  color: var(--vp-c-text-2);
250
+
230
251
  &:hover {
231
252
  color: var(--vp-c-brand-1);
232
253
  cursor: pointer;
233
254
  }
234
255
  }
235
256
  }
257
+
236
258
  .tags {
237
259
  a.link:not(:last-child) {
238
260
  &::after {
@@ -186,6 +186,10 @@ export function useBlogThemeMode() {
186
186
  return inject(configSymbol)!.value?.blog?.blog ?? true
187
187
  }
188
188
 
189
+ export function useBlogInfoCollapsible() {
190
+ return inject(configSymbol)!.value?.blog?.home?.blogInfoCollapsible ?? false
191
+ }
192
+
189
193
  export function useArticles() {
190
194
  const blogConfig = useConfig()
191
195
  const { localeIndex, site } = useData()
@@ -357,7 +361,7 @@ export function useHomeAnalysis() {
357
361
  }
358
362
 
359
363
  export function useAnalyzeTitles(wordCount: Ref<number>, readTime: ComputedRef<number>) {
360
- const article = computed(() => useConfig()?.value.blog?.article)
364
+ const article = useArticleConfig()
361
365
 
362
366
  const topWordCount = computed(() =>
363
367
  replaceValue(article.value?.analyzeTitles?.topWordCount || '字数:{{value}} 个字', wordCount.value)
@@ -186,6 +186,11 @@ export namespace Theme {
186
186
  * 首页数据分析卡片
187
187
  */
188
188
  analysis?: HomeAnalysis
189
+ /**
190
+ * 首页博客信息卡片是否在移动端折叠展示
191
+ * @default false
192
+ */
193
+ blogInfoCollapsible?: boolean
189
194
  }
190
195
 
191
196
  export interface ArticleConfig {
@@ -403,7 +408,7 @@ export namespace Theme {
403
408
  works?: UserWorks
404
409
  /**
405
410
  * https://mermaid.js.org/config/setup/modules/mermaidAPI.html#mermaidapi-configuration-defaults for options
406
- * @default true
411
+ * @default false
407
412
  */
408
413
  mermaid?: any
409
414
  /**
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
  ) {