@sugarat/theme 0.4.2 → 0.4.4

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
@@ -4,6 +4,7 @@ import { RSSOptions } from 'vitepress-plugin-rss';
4
4
  import { Repo, Mapping } from '@giscus/vue';
5
5
  import { Options } from 'oh-my-live2d';
6
6
  import { Ref } from 'vue';
7
+ import { PagefindConfig } from 'vitepress-plugin-pagefind';
7
8
  export { tabsMarkdownPlugin } from 'vitepress-plugin-tabs';
8
9
 
9
10
  type RSSPluginOptions = RSSOptions;
@@ -324,17 +325,7 @@ declare namespace Theme {
324
325
  tags?: string[];
325
326
  top?: number;
326
327
  }
327
- type SearchConfig = boolean | 'pagefind' | {
328
- btnPlaceholder?: string;
329
- placeholder?: string;
330
- emptyText?: string;
331
- /**
332
- * @example
333
- * 'Total: {{searchResult}} search results.'
334
- */
335
- heading?: string;
336
- mode?: boolean | 'pagefind';
337
- };
328
+ type SearchConfig = false | PagefindConfig;
338
329
  interface UserWorks {
339
330
  title: string;
340
331
  description?: string;
@@ -400,7 +391,7 @@ declare namespace Theme {
400
391
  /**
401
392
  * 启用RSS配置
402
393
  */
403
- RSS?: RSSOptions;
394
+ RSS?: RSSOptions | RSSOptions[];
404
395
  /**
405
396
  * 首页页脚
406
397
  */
package/node.js CHANGED
@@ -208,44 +208,14 @@ var tabsPlugin = (md) => {
208
208
  var import_vitepress_markdown_timeline = __toESM(require("vitepress-markdown-timeline"));
209
209
 
210
210
  // src/utils/node/index.ts
211
- var import_node_child_process = require("child_process");
212
211
  var import_node_path = __toESM(require("path"));
213
- function getDefaultTitle(content) {
214
- const match = content.match(/^(#+)\s+(.+)/m);
215
- return match?.[2] || "";
216
- }
217
- var cache = /* @__PURE__ */ new Map();
218
- function getFileBirthTime(url) {
219
- const cached = cache.get(url);
220
- if (cached) {
221
- return cached;
222
- }
223
- return new Promise((resolve) => {
224
- const child = (0, import_node_child_process.spawn)("git", ["log", "-1", '--pretty="%ai"', url]);
225
- let output = "";
226
- child.stdout.on("data", (d) => output += String(d));
227
- child.on("close", () => {
228
- const date = new Date(output);
229
- cache.set(url, date);
230
- resolve(date);
231
- });
232
- child.on("error", () => {
233
- resolve(void 0);
234
- });
235
- });
236
- }
237
- function getTextSummary(text, count = 100) {
238
- return text?.replace(/^#+\s+.*/, "")?.replace(/#/g, "")?.replace(/!\[.*?\]\(.*?\)/g, "")?.replace(/\[(.*?)\]\(.*?\)/g, "$1")?.replace(/\*\*(.*?)\*\*/g, "$1")?.split("\n")?.filter((v) => !!v)?.join("\n")?.replace(/>(.*)/, "")?.replace(/</g, "&lt;").replace(/>/g, "&gt;")?.trim()?.slice(0, count);
239
- }
212
+ var import_theme_shared = require("@sugarat/theme-shared");
240
213
  function aliasObjectToArray(obj) {
241
214
  return Object.entries(obj).map(([find, replacement]) => ({
242
215
  find,
243
216
  replacement
244
217
  }));
245
218
  }
246
- function joinPath(base, path4) {
247
- return `${base}${path4}`.replace(/\/+/g, "/");
248
- }
249
219
  function isBase64ImageURL(url) {
250
220
  const regex = /^data:image\/[a-z]+;base64,/;
251
221
  return regex.test(url);
@@ -260,10 +230,10 @@ function getFirstImagURLFromMD(content, route) {
260
230
  if (isHTTPSource || isBase64ImageURL(url)) {
261
231
  return url;
262
232
  }
263
- const paths = joinPath("/", route).split("/");
233
+ const paths = (0, import_theme_shared.joinPath)("/", route).split("/");
264
234
  paths.splice(paths.length - 1, 1);
265
235
  const relativePath = url.startsWith("/") ? url : import_node_path.default.join(paths.join("/") || "", url);
266
- return joinPath("/", relativePath);
236
+ return (0, import_theme_shared.joinPath)("/", relativePath);
267
237
  }
268
238
  function debounce(func, delay = 1e3) {
269
239
  let timeoutId;
@@ -361,112 +331,8 @@ function patchOptimizeDeps(config) {
361
331
  var import_node_fs = __toESM(require("fs"));
362
332
  var import_node_path2 = __toESM(require("path"));
363
333
  var import_node_process = __toESM(require("process"));
364
- var import_node_os = __toESM(require("os"));
365
334
  var import_fast_glob = __toESM(require("fast-glob"));
366
- var import_gray_matter = __toESM(require("gray-matter"));
367
-
368
- // ../../node_modules/.pnpm/yocto-queue@1.0.0/node_modules/yocto-queue/index.js
369
- var Node = class {
370
- value;
371
- next;
372
- constructor(value) {
373
- this.value = value;
374
- }
375
- };
376
- var Queue = class {
377
- #head;
378
- #tail;
379
- #size;
380
- constructor() {
381
- this.clear();
382
- }
383
- enqueue(value) {
384
- const node = new Node(value);
385
- if (this.#head) {
386
- this.#tail.next = node;
387
- this.#tail = node;
388
- } else {
389
- this.#head = node;
390
- this.#tail = node;
391
- }
392
- this.#size++;
393
- }
394
- dequeue() {
395
- const current = this.#head;
396
- if (!current) {
397
- return;
398
- }
399
- this.#head = this.#head.next;
400
- this.#size--;
401
- return current.value;
402
- }
403
- clear() {
404
- this.#head = void 0;
405
- this.#tail = void 0;
406
- this.#size = 0;
407
- }
408
- get size() {
409
- return this.#size;
410
- }
411
- *[Symbol.iterator]() {
412
- let current = this.#head;
413
- while (current) {
414
- yield current.value;
415
- current = current.next;
416
- }
417
- }
418
- };
419
-
420
- // ../../node_modules/.pnpm/p-limit@4.0.0/node_modules/p-limit/index.js
421
- function pLimit(concurrency) {
422
- if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
423
- throw new TypeError("Expected `concurrency` to be a number from 1 and up");
424
- }
425
- const queue = new Queue();
426
- let activeCount = 0;
427
- const next = () => {
428
- activeCount--;
429
- if (queue.size > 0) {
430
- queue.dequeue()();
431
- }
432
- };
433
- const run = async (fn, resolve, args) => {
434
- activeCount++;
435
- const result = (async () => fn(...args))();
436
- resolve(result);
437
- try {
438
- await result;
439
- } catch {
440
- }
441
- next();
442
- };
443
- const enqueue = (fn, resolve, args) => {
444
- queue.enqueue(run.bind(void 0, fn, resolve, args));
445
- (async () => {
446
- await Promise.resolve();
447
- if (activeCount < concurrency && queue.size > 0) {
448
- queue.dequeue()();
449
- }
450
- })();
451
- };
452
- const generator = (fn, ...args) => new Promise((resolve) => {
453
- enqueue(fn, resolve, args);
454
- });
455
- Object.defineProperties(generator, {
456
- activeCount: {
457
- get: () => activeCount
458
- },
459
- pendingCount: {
460
- get: () => queue.size
461
- },
462
- clearQueue: {
463
- value: () => {
464
- queue.clear();
465
- }
466
- }
467
- });
468
- return generator;
469
- }
335
+ var import_theme_shared2 = require("@sugarat/theme-shared");
470
336
 
471
337
  // src/utils/client/index.ts
472
338
  function formatDate(d, fmt = "yyyy-MM-dd hh:mm:ss") {
@@ -517,46 +383,29 @@ function patchDefaultThemeSideBar(cfg) {
517
383
  } : void 0;
518
384
  }
519
385
  function getPageRoute(filepath, srcDir) {
520
- let route = filepath.replace(".md", "");
521
- if (route.startsWith("./")) {
522
- route = route.replace(
523
- new RegExp(
524
- `^\\.\\/${import_node_path2.default.join(srcDir, "/").replace(new RegExp(`\\${import_node_path2.default.sep}`, "g"), "/")}`
525
- ),
526
- ""
527
- );
528
- } else {
529
- route = route.replace(
530
- new RegExp(
531
- `^${import_node_path2.default.join(srcDir, "/").replace(new RegExp(`\\${import_node_path2.default.sep}`, "g"), "/")}`
532
- ),
533
- ""
534
- );
535
- }
386
+ const route = (0, import_theme_shared2.normalizePath)(import_node_path2.default.relative(srcDir, filepath)).replace(/\.md$/, "");
536
387
  return `/${route}`;
537
388
  }
538
389
  var defaultTimeZoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() / -60;
539
390
  async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset) {
540
391
  const fileContent = await import_node_fs.default.promises.readFile(filepath, "utf-8");
541
- const { data: frontmatter, excerpt, content } = (0, import_gray_matter.default)(fileContent, {
392
+ const { data: frontmatter, excerpt, content } = (0, import_theme_shared2.grayMatter)(fileContent, {
542
393
  excerpt: true
543
394
  });
544
395
  const meta = {
545
396
  ...frontmatter
546
397
  };
547
398
  if (!meta.title) {
548
- meta.title = getDefaultTitle(content);
549
- }
550
- const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}+${timeZone}`) || getFileBirthTime(filepath));
551
- if (date) {
552
- meta.date = formatDate(date);
399
+ meta.title = (0, import_theme_shared2.getDefaultTitle)(content);
553
400
  }
401
+ const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}+${timeZone}`) || (0, import_theme_shared2.getFileLastModifyTime)(filepath));
402
+ meta.date = formatDate(date || /* @__PURE__ */ new Date());
554
403
  meta.categories = typeof meta.categories === "string" ? [meta.categories] : meta.categories;
555
404
  meta.tags = typeof meta.tags === "string" ? [meta.tags] : meta.tags;
556
405
  meta.tag = [meta.tag || []].flat().concat([
557
406
  .../* @__PURE__ */ new Set([...meta.categories || [], ...meta.tags || []])
558
407
  ]);
559
- meta.description = meta.description || getTextSummary(content, 100) || excerpt;
408
+ meta.description = meta.description || (0, import_theme_shared2.getTextSummary)(content, 100) || excerpt;
560
409
  meta.cover = meta.cover ?? getFirstImagURLFromMD(fileContent, route);
561
410
  if (meta.publish === false) {
562
411
  meta.hidden = true;
@@ -564,13 +413,12 @@ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset)
564
413
  }
565
414
  return meta;
566
415
  }
567
- async function getArticles(cfg) {
568
- const srcDir = cfg?.srcDir || import_node_process.default.argv.slice(2)?.[1] || ".";
569
- const files = import_fast_glob.default.sync(`${srcDir}/**/*.md`, { ignore: ["node_modules"] });
570
- const limit = pLimit(+(import_node_process.default.env.P_LIMT_MAX || import_node_os.default.cpus().length));
416
+ async function getArticles(cfg, vpConfig) {
417
+ const srcDir = cfg?.srcDir || vpConfig.srcDir.replace(vpConfig.root, "").replace(/^\//, "") || import_node_process.default.argv.slice(2)?.[1] || ".";
418
+ const files = import_fast_glob.default.sync(`${srcDir}/**/*.md`, { ignore: ["node_modules"], absolute: true });
571
419
  const metaResults = files.reduce((prev, curr) => {
572
- const route = getPageRoute(curr, srcDir);
573
- const metaPromise = limit(() => getArticleMeta(curr, route, cfg?.timeZone));
420
+ const route = getPageRoute(curr, vpConfig.srcDir);
421
+ const metaPromise = getArticleMeta(curr, route, cfg?.timeZone);
574
422
  prev[curr] = {
575
423
  route,
576
424
  metaPromise
@@ -614,6 +462,7 @@ var import_node_fs2 = require("fs");
614
462
  var import_node_buffer = require("buffer");
615
463
  var import_vitepress_plugin_pagefind = require("vitepress-plugin-pagefind");
616
464
  var import_vitepress_plugin_rss = require("vitepress-plugin-rss");
465
+ var import_theme_shared3 = require("@sugarat/theme-shared");
617
466
 
618
467
  // src/utils/node/hot-reload-plugin.ts
619
468
  function themeReloadPlugin() {
@@ -666,7 +515,7 @@ function themeReloadPlugin() {
666
515
  }
667
516
 
668
517
  // src/utils/node/vitePlugins.ts
669
- function getVitePlugins(cfg) {
518
+ function getVitePlugins(cfg = {}) {
670
519
  const plugins = [];
671
520
  plugins.push(coverImgTransform());
672
521
  plugins.push(themeReloadPlugin());
@@ -675,11 +524,7 @@ function getVitePlugins(cfg) {
675
524
  const ops = cfg.search instanceof Object ? cfg.search : {};
676
525
  plugins.push(
677
526
  (0, import_vitepress_plugin_pagefind.pagefindPlugin)({
678
- ...ops,
679
- customSearchQuery: import_vitepress_plugin_pagefind.chineseSearchOptimize,
680
- filter(searchItem) {
681
- return searchItem.meta.publish !== false;
682
- }
527
+ ...ops
683
528
  })
684
529
  );
685
530
  }
@@ -689,7 +534,8 @@ function getVitePlugins(cfg) {
689
534
  plugins.push(MermaidPlugin(cfg?.mermaid === true ? {} : cfg?.mermaid ?? {}));
690
535
  }
691
536
  if (cfg?.RSS) {
692
- plugins.push((0, import_vitepress_plugin_rss.RssPlugin)(cfg.RSS));
537
+ ;
538
+ [cfg?.RSS].flat().forEach((rssConfig) => plugins.push((0, import_vitepress_plugin_rss.RssPlugin)(rssConfig)));
693
539
  }
694
540
  return plugins;
695
541
  }
@@ -742,7 +588,7 @@ function coverImgTransform() {
742
588
  const fileBuffer = (0, import_node_fs2.readFileSync)(realPath);
743
589
  const matchAsset = assetsMap.find((v) => import_node_buffer.Buffer.compare(fileBuffer, v.source) === 0);
744
590
  if (matchAsset) {
745
- page.meta.cover = joinPath("/", matchAsset.fileName);
591
+ page.meta.cover = (0, import_theme_shared3.joinPath)("/", matchAsset.fileName);
746
592
  }
747
593
  } catch (e) {
748
594
  vitepressConfig.logger.warn(e?.message);
@@ -755,7 +601,7 @@ function providePageData(cfg) {
755
601
  return {
756
602
  name: "@sugarat/theme-plugin-provide-page-data",
757
603
  async config(config) {
758
- const pagesData = await getArticles(cfg);
604
+ const pagesData = await getArticles(cfg, config.vitepress);
759
605
  config.vitepress.site.themeConfig.blog.pagesData = pagesData;
760
606
  }
761
607
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
5
5
  "author": "sugar",
6
6
  "license": "MIT",
@@ -42,9 +42,7 @@
42
42
  "@mermaid-js/mermaid-mindmap": "^9.3.0",
43
43
  "@vue/shared": "^3.4.26",
44
44
  "@vueuse/core": "^9.13.0",
45
- "cross-spawn": "^7.0.3",
46
45
  "fast-glob": "^3.3.2",
47
- "gray-matter": "^4.0.3",
48
46
  "markdown-it-task-checkbox": "^1.0.6",
49
47
  "mermaid": "^10.9.0",
50
48
  "oh-my-live2d": "^0.19.3",
@@ -52,14 +50,14 @@
52
50
  "vitepress-markdown-timeline": "^1.2.1",
53
51
  "vitepress-plugin-mermaid": "2.0.13",
54
52
  "vitepress-plugin-tabs": "0.2.0",
55
- "vitepress-plugin-pagefind": "0.4.1",
56
- "vitepress-plugin-rss": "0.2.6"
53
+ "vitepress-plugin-rss": "0.2.7",
54
+ "vitepress-plugin-pagefind": "0.4.2",
55
+ "@sugarat/theme-shared": "0.0.1"
57
56
  },
58
57
  "devDependencies": {
59
58
  "@element-plus/icons-vue": "^2.3.1",
60
59
  "artalk": "^2.8.5",
61
60
  "element-plus": "^2.7.2",
62
- "p-limit": "4",
63
61
  "pagefind": "^1.1.0",
64
62
  "sass": "^1.76.0",
65
63
  "typescript": "^5.4.5",
@@ -12,7 +12,7 @@ import {
12
12
  UserFilled
13
13
  } from '@element-plus/icons-vue'
14
14
  import { useBlogConfig, useCurrentArticle, useDocMetaInsertPosition, useDocMetaInsertSelector } from '../composables/config/blog'
15
- import countWord, { formatShowDate } from '../utils/client'
15
+ import countWord, { formatDate, formatShowDate } from '../utils/client'
16
16
  import type { Theme } from '../composables/config'
17
17
  import BlogDocCover from './BlogDocCover.vue'
18
18
 
@@ -108,6 +108,10 @@ const publishDate = computed(() => {
108
108
  return formatShowDate(currentArticle.value?.meta?.date || '')
109
109
  })
110
110
 
111
+ const hoverDate = computed(() => {
112
+ return currentArticle.value?.meta?.date ? `: ${formatDate(currentArticle.value?.meta?.date)}` : ''
113
+ })
114
+
111
115
  const timeTitle = computed(() =>
112
116
  frontmatter.value.date ? '发布时间' : '最近修改时间'
113
117
  )
@@ -164,7 +168,7 @@ watch(
164
168
  {{ author }}
165
169
  </template>
166
170
  </span>
167
- <span v-if="publishDate && !hiddenTime" class="publishDate" :title="timeTitle">
171
+ <span v-if="publishDate && !hiddenTime" class="publishDate" :title="timeTitle + hoverDate">
168
172
  <ElIcon><Clock /></ElIcon>
169
173
  {{ publishDate }}
170
174
  </span>
@@ -102,7 +102,7 @@ const resultCover = computed(() => {
102
102
  <div class="badge-list mobile-visible">
103
103
  <span v-show="author" class="split">{{ author }}</span>
104
104
  <span class="split">{{ showTime }}</span>
105
- <span v-show="tag?.length" class="split">{{ tag?.join(' · ') }}</span>
105
+ <span v-if="tag?.length" class="split">{{ tag?.join(' · ') }}</span>
106
106
  </div>
107
107
  </a>
108
108
  </template>
@@ -5,6 +5,7 @@ import type { RSSOptions } from 'vitepress-plugin-rss'
5
5
  import type { Mapping, Repo } from '@giscus/vue'
6
6
  import type { Options as Oml2dOptions } from 'oh-my-live2d'
7
7
  import type { Ref } from 'vue'
8
+ import type { PagefindConfig } from 'vitepress-plugin-pagefind'
8
9
 
9
10
  type RSSPluginOptions = RSSOptions
10
11
 
@@ -347,19 +348,8 @@ export namespace Theme {
347
348
  top?: number
348
349
  }
349
350
  export type SearchConfig =
350
- | boolean
351
- | 'pagefind'
352
- | {
353
- btnPlaceholder?: string
354
- placeholder?: string
355
- emptyText?: string
356
- /**
357
- * @example
358
- * 'Total: {{searchResult}} search results.'
359
- */
360
- heading?: string
361
- mode?: boolean | 'pagefind'
362
- }
351
+ | false
352
+ | PagefindConfig
363
353
 
364
354
  export interface UserWorks {
365
355
  title: string
@@ -434,7 +424,7 @@ export namespace Theme {
434
424
  /**
435
425
  * 启用RSS配置
436
426
  */
437
- RSS?: RSSOptions
427
+ RSS?: RSSOptions | RSSOptions[]
438
428
  /**
439
429
  * 首页页脚
440
430
  */
@@ -1,100 +1,5 @@
1
- /* eslint-disable global-require */
2
- /* eslint-disable prefer-rest-params */
3
- import { spawn, spawnSync } from 'node:child_process'
4
1
  import path from 'node:path'
5
-
6
- export function clearMatterContent(content: string) {
7
- let first___: unknown
8
- let second___: unknown
9
-
10
- const lines = content.split('\n').reduce<string[]>((pre, line) => {
11
- // 移除开头的空白行
12
- if (!line.trim() && pre.length === 0) {
13
- return pre
14
- }
15
- if (line.trim() === '---') {
16
- if (first___ === undefined) {
17
- first___ = pre.length
18
- }
19
- else if (second___ === undefined) {
20
- second___ = pre.length
21
- }
22
- }
23
- pre.push(line)
24
- return pre
25
- }, [])
26
- return (
27
- lines
28
- // 剔除---之间的内容
29
- .slice((second___ as number) || 0)
30
- .join('\n')
31
- )
32
- }
33
-
34
- export function getDefaultTitle(content: string) {
35
- const match = content.match(/^(#+)\s+(.+)/m)
36
- return match?.[2] || ''
37
- }
38
-
39
- const cache = new Map<string, Date>()
40
- export function getFileBirthTime(url: string): Promise<Date | undefined> | Date {
41
- const cached = cache.get(url)
42
- if (cached) {
43
- return cached
44
- }
45
-
46
- return new Promise((resolve) => {
47
- // 使用异步回调
48
- const child = spawn('git', ['log', '-1', '--pretty="%ai"', url])
49
- let output = ''
50
- child.stdout.on('data', d => (output += String(d)))
51
- child.on('close', () => {
52
- const date = new Date(output)
53
- cache.set(url, date)
54
- resolve(date)
55
- })
56
- child.on('error', () => {
57
- resolve(undefined)
58
- })
59
- })
60
- }
61
-
62
- export function getGitTimestamp(file: string) {
63
- return new Promise((resolve, reject) => {
64
- const child = spawn('git', ['log', '-1', '--pretty="%ci"', file])
65
- let output = ''
66
- child.stdout.on('data', (d) => {
67
- output += String(d)
68
- })
69
- child.on('close', () => {
70
- resolve(+new Date(output))
71
- })
72
- child.on('error', reject)
73
- })
74
- }
75
-
76
- export function getTextSummary(text: string, count = 100) {
77
- return (
78
- text
79
- // 首个标题
80
- ?.replace(/^#+\s+.*/, '')
81
- // 除去标题
82
- ?.replace(/#/g, '')
83
- // 除去图片
84
- ?.replace(/!\[.*?\]\(.*?\)/g, '')
85
- // 除去链接
86
- ?.replace(/\[(.*?)\]\(.*?\)/g, '$1')
87
- // 除去加粗
88
- ?.replace(/\*\*(.*?)\*\*/g, '$1')
89
- ?.split('\n')
90
- ?.filter(v => !!v)
91
- ?.join('\n')
92
- ?.replace(/>(.*)/, '')
93
- ?.replace(/</g, '&lt;').replace(/>/g, '&gt;')
94
- ?.trim()
95
- ?.slice(0, count)
96
- )
97
- }
2
+ import { joinPath } from '@sugarat/theme-shared'
98
3
 
99
4
  export function aliasObjectToArray(obj: Record<string, string>) {
100
5
  return Object.entries(obj).map(([find, replacement]) => ({
@@ -103,21 +8,6 @@ export function aliasObjectToArray(obj: Record<string, string>) {
103
8
  }))
104
9
  }
105
10
 
106
- export const EXTERNAL_URL_RE = /^[a-z]+:/i
107
-
108
- /**
109
- * Join two paths by resolving the slash collision.
110
- */
111
- export function joinPath(base: string, path: string): string {
112
- return `${base}${path}`.replace(/\/+/g, '/')
113
- }
114
-
115
- export function withBase(base: string, path: string) {
116
- return EXTERNAL_URL_RE.test(path) || path.startsWith('.')
117
- ? path
118
- : joinPath(base, path)
119
- }
120
-
121
11
  function isBase64ImageURL(url: string) {
122
12
  // Base64 图片链接的格式为 data:image/[image format];base64,[Base64 编码的数据]
123
13
  const regex = /^data:image\/[a-z]+;base64,/
@@ -2,13 +2,12 @@
2
2
  import fs from 'node:fs'
3
3
  import path from 'node:path'
4
4
  import process from 'node:process'
5
- import os from 'node:os'
6
5
  import glob from 'fast-glob'
7
- import matter from 'gray-matter'
8
- import pLimit from 'p-limit'
6
+ import { getDefaultTitle, getFileLastModifyTime, getTextSummary, grayMatter, normalizePath } from '@sugarat/theme-shared'
7
+ import type { SiteConfig } from 'vitepress'
9
8
  import type { Theme } from '../../composables/config/index'
10
9
  import { formatDate } from '../client'
11
- import { getDefaultTitle, getFileBirthTime, getFirstImagURLFromMD, getTextSummary } from './index'
10
+ import { getFirstImagURLFromMD } from './index'
12
11
 
13
12
  export function patchDefaultThemeSideBar(cfg?: Partial<Theme.BlogConfig>) {
14
13
  return cfg?.blog !== false && cfg?.recommend !== false
@@ -24,29 +23,8 @@ export function patchDefaultThemeSideBar(cfg?: Partial<Theme.BlogConfig>) {
24
23
  }
25
24
 
26
25
  export function getPageRoute(filepath: string, srcDir: string) {
27
- let route = filepath.replace('.md', '')
28
- // 去除 srcDir 处理目录名
29
- // TODO:优化 路径处理,同VitePress 内部一致
30
- if (route.startsWith('./')) {
31
- route = route.replace(
32
- new RegExp(
33
- `^\\.\\/${path
34
- .join(srcDir, '/')
35
- .replace(new RegExp(`\\${path.sep}`, 'g'), '/')}`
36
- ),
37
- ''
38
- )
39
- }
40
- else {
41
- route = route.replace(
42
- new RegExp(
43
- `^${path
44
- .join(srcDir, '/')
45
- .replace(new RegExp(`\\${path.sep}`, 'g'), '/')}`
46
- ),
47
- ''
48
- )
49
- }
26
+ const route = normalizePath(path.relative(srcDir, filepath))
27
+ .replace(/\.md$/, '')
50
28
  return `/${route}`
51
29
  }
52
30
 
@@ -54,7 +32,7 @@ const defaultTimeZoneOffset = new Date().getTimezoneOffset() / -60
54
32
  export async function getArticleMeta(filepath: string, route: string, timeZone = defaultTimeZoneOffset) {
55
33
  const fileContent = await fs.promises.readFile(filepath, 'utf-8')
56
34
 
57
- const { data: frontmatter, excerpt, content } = matter(fileContent, {
35
+ const { data: frontmatter, excerpt, content } = grayMatter(fileContent, {
58
36
  excerpt: true,
59
37
  })
60
38
 
@@ -68,11 +46,10 @@ export async function getArticleMeta(filepath: string, route: string, timeZone =
68
46
  const date = await (
69
47
  (meta.date
70
48
  && new Date(`${new Date(meta.date).toUTCString()}+${timeZone}`))
71
- || getFileBirthTime(filepath)
49
+ || getFileLastModifyTime(filepath)
72
50
  )
73
- if (date) {
74
- meta.date = formatDate(date)
75
- }
51
+ // 无法获取时兜底当前时间
52
+ meta.date = formatDate(date || new Date())
76
53
 
77
54
  // 处理tags和categories,兼容历史文章
78
55
  meta.categories
@@ -103,14 +80,16 @@ export async function getArticleMeta(filepath: string, route: string, timeZone =
103
80
  }
104
81
  return meta as Theme.PageMeta
105
82
  }
106
- export async function getArticles(cfg?: Partial<Theme.BlogConfig>) {
107
- const srcDir = cfg?.srcDir || process.argv.slice(2)?.[1] || '.'
108
- const files = glob.sync(`${srcDir}/**/*.md`, { ignore: ['node_modules'] })
109
- const limit = pLimit(+(process.env.P_LIMT_MAX || os.cpus().length))
83
+ export async function getArticles(cfg: Partial<Theme.BlogConfig>, vpConfig: SiteConfig) {
84
+ const srcDir
85
+ = cfg?.srcDir || vpConfig.srcDir.replace(vpConfig.root, '').replace(/^\//, '')
86
+ || process.argv.slice(2)?.[1]
87
+ || '.'
88
+ const files = glob.sync(`${srcDir}/**/*.md`, { ignore: ['node_modules'], absolute: true })
110
89
 
111
90
  const metaResults = files.reduce((prev, curr) => {
112
- const route = getPageRoute(curr, srcDir)
113
- const metaPromise = limit(() => getArticleMeta(curr, route, cfg?.timeZone))
91
+ const route = getPageRoute(curr, vpConfig.srcDir)
92
+ const metaPromise = getArticleMeta(curr, route, cfg?.timeZone)
114
93
 
115
94
  // 提前获取,有缓存取缓存
116
95
  prev[curr] = {
@@ -2,20 +2,18 @@ import path from 'node:path'
2
2
  import { existsSync, readFileSync } from 'node:fs'
3
3
  import { Buffer } from 'node:buffer'
4
4
  import type { SiteConfig } from 'vitepress'
5
-
6
5
  import {
7
- chineseSearchOptimize,
8
6
  pagefindPlugin
9
7
  } from 'vitepress-plugin-pagefind'
10
8
  import { RssPlugin } from 'vitepress-plugin-rss'
11
9
  import type { PluginOption } from 'vite'
10
+ import { joinPath } from '@sugarat/theme-shared'
12
11
  import type { Theme } from '../../composables/config/index'
13
12
  import { _require } from './mdPlugins'
14
13
  import { themeReloadPlugin } from './hot-reload-plugin'
15
14
  import { getArticles } from './theme'
16
- import { joinPath } from './index'
17
15
 
18
- export function getVitePlugins(cfg?: Partial<Theme.BlogConfig>) {
16
+ export function getVitePlugins(cfg: Partial<Theme.BlogConfig> = {}) {
19
17
  const plugins: any[] = []
20
18
 
21
19
  // const buildEndFn: any[] = []
@@ -37,10 +35,6 @@ export function getVitePlugins(cfg?: Partial<Theme.BlogConfig>) {
37
35
  plugins.push(
38
36
  pagefindPlugin({
39
37
  ...ops,
40
- customSearchQuery: chineseSearchOptimize,
41
- filter(searchItem) {
42
- return searchItem.meta.publish !== false
43
- }
44
38
  })
45
39
  )
46
40
  }
@@ -54,7 +48,7 @@ export function getVitePlugins(cfg?: Partial<Theme.BlogConfig>) {
54
48
 
55
49
  // 内置支持RSS
56
50
  if (cfg?.RSS) {
57
- plugins.push(RssPlugin(cfg.RSS))
51
+ ;[cfg?.RSS].flat().forEach(rssConfig => plugins.push(RssPlugin(rssConfig)))
58
52
  }
59
53
  return plugins
60
54
  }
@@ -153,11 +147,11 @@ export function coverImgTransform() {
153
147
  }
154
148
  }
155
149
 
156
- export function providePageData(cfg?: Partial<Theme.BlogConfig>) {
150
+ export function providePageData(cfg: Partial<Theme.BlogConfig>) {
157
151
  return {
158
152
  name: '@sugarat/theme-plugin-provide-page-data',
159
153
  async config(config: any) {
160
- const pagesData = await getArticles(cfg)
154
+ const pagesData = await getArticles(cfg, config.vitepress)
161
155
  config.vitepress.site.themeConfig.blog.pagesData = pagesData
162
156
  },
163
157
  } as PluginOption