@sugarat/theme 0.4.3 → 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,59 +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
- var import_node_fs = __toESM(require("fs"));
214
- function getDefaultTitle(content) {
215
- const match = content.match(/^(#+)\s+(.+)/m);
216
- return match?.[2] || "";
217
- }
218
- var cache = /* @__PURE__ */ new Map();
219
- function getFileBirthTime(url) {
220
- const cached = cache.get(url);
221
- if (cached) {
222
- return cached;
223
- }
224
- return new Promise((resolve) => {
225
- const child = (0, import_node_child_process.spawn)("git", ["log", "-1", '--pretty="%ai"', url]);
226
- let output = "";
227
- child.stdout.on("data", (d) => output += String(d));
228
- child.on("close", async () => {
229
- let date;
230
- if (output.trim()) {
231
- date = new Date(output);
232
- } else {
233
- date = await getFileBirthTimeByFs(url);
234
- }
235
- cache.set(url, date);
236
- resolve(date);
237
- });
238
- child.on("error", async () => {
239
- const fsDate = await getFileBirthTimeByFs(url);
240
- resolve(fsDate);
241
- });
242
- });
243
- }
244
- async function getFileBirthTimeByFs(url) {
245
- try {
246
- const fsStat = await import_node_fs.default.promises.stat(url);
247
- return fsStat.birthtime;
248
- } catch {
249
- return void 0;
250
- }
251
- }
252
- function getTextSummary(text, count = 100) {
253
- 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);
254
- }
212
+ var import_theme_shared = require("@sugarat/theme-shared");
255
213
  function aliasObjectToArray(obj) {
256
214
  return Object.entries(obj).map(([find, replacement]) => ({
257
215
  find,
258
216
  replacement
259
217
  }));
260
218
  }
261
- function joinPath(base, path4) {
262
- return `${base}${path4}`.replace(/\/+/g, "/");
263
- }
264
219
  function isBase64ImageURL(url) {
265
220
  const regex = /^data:image\/[a-z]+;base64,/;
266
221
  return regex.test(url);
@@ -275,10 +230,10 @@ function getFirstImagURLFromMD(content, route) {
275
230
  if (isHTTPSource || isBase64ImageURL(url)) {
276
231
  return url;
277
232
  }
278
- const paths = joinPath("/", route).split("/");
233
+ const paths = (0, import_theme_shared.joinPath)("/", route).split("/");
279
234
  paths.splice(paths.length - 1, 1);
280
235
  const relativePath = url.startsWith("/") ? url : import_node_path.default.join(paths.join("/") || "", url);
281
- return joinPath("/", relativePath);
236
+ return (0, import_theme_shared.joinPath)("/", relativePath);
282
237
  }
283
238
  function debounce(func, delay = 1e3) {
284
239
  let timeoutId;
@@ -373,115 +328,11 @@ function patchOptimizeDeps(config) {
373
328
  }
374
329
 
375
330
  // src/utils/node/theme.ts
376
- var import_node_fs2 = __toESM(require("fs"));
331
+ var import_node_fs = __toESM(require("fs"));
377
332
  var import_node_path2 = __toESM(require("path"));
378
333
  var import_node_process = __toESM(require("process"));
379
- var import_node_os = __toESM(require("os"));
380
334
  var import_fast_glob = __toESM(require("fast-glob"));
381
- var import_gray_matter = __toESM(require("gray-matter"));
382
-
383
- // ../../node_modules/.pnpm/yocto-queue@1.0.0/node_modules/yocto-queue/index.js
384
- var Node = class {
385
- value;
386
- next;
387
- constructor(value) {
388
- this.value = value;
389
- }
390
- };
391
- var Queue = class {
392
- #head;
393
- #tail;
394
- #size;
395
- constructor() {
396
- this.clear();
397
- }
398
- enqueue(value) {
399
- const node = new Node(value);
400
- if (this.#head) {
401
- this.#tail.next = node;
402
- this.#tail = node;
403
- } else {
404
- this.#head = node;
405
- this.#tail = node;
406
- }
407
- this.#size++;
408
- }
409
- dequeue() {
410
- const current = this.#head;
411
- if (!current) {
412
- return;
413
- }
414
- this.#head = this.#head.next;
415
- this.#size--;
416
- return current.value;
417
- }
418
- clear() {
419
- this.#head = void 0;
420
- this.#tail = void 0;
421
- this.#size = 0;
422
- }
423
- get size() {
424
- return this.#size;
425
- }
426
- *[Symbol.iterator]() {
427
- let current = this.#head;
428
- while (current) {
429
- yield current.value;
430
- current = current.next;
431
- }
432
- }
433
- };
434
-
435
- // ../../node_modules/.pnpm/p-limit@4.0.0/node_modules/p-limit/index.js
436
- function pLimit(concurrency) {
437
- if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
438
- throw new TypeError("Expected `concurrency` to be a number from 1 and up");
439
- }
440
- const queue = new Queue();
441
- let activeCount = 0;
442
- const next = () => {
443
- activeCount--;
444
- if (queue.size > 0) {
445
- queue.dequeue()();
446
- }
447
- };
448
- const run = async (fn, resolve, args) => {
449
- activeCount++;
450
- const result = (async () => fn(...args))();
451
- resolve(result);
452
- try {
453
- await result;
454
- } catch {
455
- }
456
- next();
457
- };
458
- const enqueue = (fn, resolve, args) => {
459
- queue.enqueue(run.bind(void 0, fn, resolve, args));
460
- (async () => {
461
- await Promise.resolve();
462
- if (activeCount < concurrency && queue.size > 0) {
463
- queue.dequeue()();
464
- }
465
- })();
466
- };
467
- const generator = (fn, ...args) => new Promise((resolve) => {
468
- enqueue(fn, resolve, args);
469
- });
470
- Object.defineProperties(generator, {
471
- activeCount: {
472
- get: () => activeCount
473
- },
474
- pendingCount: {
475
- get: () => queue.size
476
- },
477
- clearQueue: {
478
- value: () => {
479
- queue.clear();
480
- }
481
- }
482
- });
483
- return generator;
484
- }
335
+ var import_theme_shared2 = require("@sugarat/theme-shared");
485
336
 
486
337
  // src/utils/client/index.ts
487
338
  function formatDate(d, fmt = "yyyy-MM-dd hh:mm:ss") {
@@ -532,44 +383,29 @@ function patchDefaultThemeSideBar(cfg) {
532
383
  } : void 0;
533
384
  }
534
385
  function getPageRoute(filepath, srcDir) {
535
- let route = filepath.replace(".md", "");
536
- if (route.startsWith("./")) {
537
- route = route.replace(
538
- new RegExp(
539
- `^\\.\\/${import_node_path2.default.join(srcDir, "/").replace(new RegExp(`\\${import_node_path2.default.sep}`, "g"), "/")}`
540
- ),
541
- ""
542
- );
543
- } else {
544
- route = route.replace(
545
- new RegExp(
546
- `^${import_node_path2.default.join(srcDir, "/").replace(new RegExp(`\\${import_node_path2.default.sep}`, "g"), "/")}`
547
- ),
548
- ""
549
- );
550
- }
386
+ const route = (0, import_theme_shared2.normalizePath)(import_node_path2.default.relative(srcDir, filepath)).replace(/\.md$/, "");
551
387
  return `/${route}`;
552
388
  }
553
389
  var defaultTimeZoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() / -60;
554
390
  async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset) {
555
- const fileContent = await import_node_fs2.default.promises.readFile(filepath, "utf-8");
556
- const { data: frontmatter, excerpt, content } = (0, import_gray_matter.default)(fileContent, {
391
+ const fileContent = await import_node_fs.default.promises.readFile(filepath, "utf-8");
392
+ const { data: frontmatter, excerpt, content } = (0, import_theme_shared2.grayMatter)(fileContent, {
557
393
  excerpt: true
558
394
  });
559
395
  const meta = {
560
396
  ...frontmatter
561
397
  };
562
398
  if (!meta.title) {
563
- meta.title = getDefaultTitle(content);
399
+ meta.title = (0, import_theme_shared2.getDefaultTitle)(content);
564
400
  }
565
- const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}+${timeZone}`) || getFileBirthTime(filepath));
401
+ const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}+${timeZone}`) || (0, import_theme_shared2.getFileLastModifyTime)(filepath));
566
402
  meta.date = formatDate(date || /* @__PURE__ */ new Date());
567
403
  meta.categories = typeof meta.categories === "string" ? [meta.categories] : meta.categories;
568
404
  meta.tags = typeof meta.tags === "string" ? [meta.tags] : meta.tags;
569
405
  meta.tag = [meta.tag || []].flat().concat([
570
406
  .../* @__PURE__ */ new Set([...meta.categories || [], ...meta.tags || []])
571
407
  ]);
572
- meta.description = meta.description || getTextSummary(content, 100) || excerpt;
408
+ meta.description = meta.description || (0, import_theme_shared2.getTextSummary)(content, 100) || excerpt;
573
409
  meta.cover = meta.cover ?? getFirstImagURLFromMD(fileContent, route);
574
410
  if (meta.publish === false) {
575
411
  meta.hidden = true;
@@ -577,13 +413,12 @@ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset)
577
413
  }
578
414
  return meta;
579
415
  }
580
- async function getArticles(cfg) {
581
- const srcDir = cfg?.srcDir || import_node_process.default.argv.slice(2)?.[1] || ".";
582
- const files = import_fast_glob.default.sync(`${srcDir}/**/*.md`, { ignore: ["node_modules"] });
583
- 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 });
584
419
  const metaResults = files.reduce((prev, curr) => {
585
- const route = getPageRoute(curr, srcDir);
586
- const metaPromise = limit(() => getArticleMeta(curr, route, cfg?.timeZone));
420
+ const route = getPageRoute(curr, vpConfig.srcDir);
421
+ const metaPromise = getArticleMeta(curr, route, cfg?.timeZone);
587
422
  prev[curr] = {
588
423
  route,
589
424
  metaPromise
@@ -623,10 +458,11 @@ function checkConfig(cfg) {
623
458
 
624
459
  // src/utils/node/vitePlugins.ts
625
460
  var import_node_path3 = __toESM(require("path"));
626
- var import_node_fs3 = require("fs");
461
+ var import_node_fs2 = require("fs");
627
462
  var import_node_buffer = require("buffer");
628
463
  var import_vitepress_plugin_pagefind = require("vitepress-plugin-pagefind");
629
464
  var import_vitepress_plugin_rss = require("vitepress-plugin-rss");
465
+ var import_theme_shared3 = require("@sugarat/theme-shared");
630
466
 
631
467
  // src/utils/node/hot-reload-plugin.ts
632
468
  function themeReloadPlugin() {
@@ -679,7 +515,7 @@ function themeReloadPlugin() {
679
515
  }
680
516
 
681
517
  // src/utils/node/vitePlugins.ts
682
- function getVitePlugins(cfg) {
518
+ function getVitePlugins(cfg = {}) {
683
519
  const plugins = [];
684
520
  plugins.push(coverImgTransform());
685
521
  plugins.push(themeReloadPlugin());
@@ -688,11 +524,7 @@ function getVitePlugins(cfg) {
688
524
  const ops = cfg.search instanceof Object ? cfg.search : {};
689
525
  plugins.push(
690
526
  (0, import_vitepress_plugin_pagefind.pagefindPlugin)({
691
- ...ops,
692
- customSearchQuery: import_vitepress_plugin_pagefind.chineseSearchOptimize,
693
- filter(searchItem) {
694
- return searchItem.meta.publish !== false;
695
- }
527
+ ...ops
696
528
  })
697
529
  );
698
530
  }
@@ -702,7 +534,8 @@ function getVitePlugins(cfg) {
702
534
  plugins.push(MermaidPlugin(cfg?.mermaid === true ? {} : cfg?.mermaid ?? {}));
703
535
  }
704
536
  if (cfg?.RSS) {
705
- 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)));
706
539
  }
707
540
  return plugins;
708
541
  }
@@ -749,13 +582,13 @@ function coverImgTransform() {
749
582
  }
750
583
  try {
751
584
  const realPath = import_node_path3.default.join(vitepressConfig.root, cover);
752
- if (!(0, import_node_fs3.existsSync)(realPath)) {
585
+ if (!(0, import_node_fs2.existsSync)(realPath)) {
753
586
  continue;
754
587
  }
755
- const fileBuffer = (0, import_node_fs3.readFileSync)(realPath);
588
+ const fileBuffer = (0, import_node_fs2.readFileSync)(realPath);
756
589
  const matchAsset = assetsMap.find((v) => import_node_buffer.Buffer.compare(fileBuffer, v.source) === 0);
757
590
  if (matchAsset) {
758
- page.meta.cover = joinPath("/", matchAsset.fileName);
591
+ page.meta.cover = (0, import_theme_shared3.joinPath)("/", matchAsset.fileName);
759
592
  }
760
593
  } catch (e) {
761
594
  vitepressConfig.logger.warn(e?.message);
@@ -768,7 +601,7 @@ function providePageData(cfg) {
768
601
  return {
769
602
  name: "@sugarat/theme-plugin-provide-page-data",
770
603
  async config(config) {
771
- const pagesData = await getArticles(cfg);
604
+ const pagesData = await getArticles(cfg, config.vitepress);
772
605
  config.vitepress.site.themeConfig.blog.pagesData = pagesData;
773
606
  }
774
607
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.4.3",
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",
@@ -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,118 +1,5 @@
1
- /* eslint-disable global-require */
2
- /* eslint-disable prefer-rest-params */
3
- import { spawn } from 'node:child_process'
4
1
  import path from 'node:path'
5
- import fs from 'node:fs'
6
-
7
- export function clearMatterContent(content: string) {
8
- let first___: unknown
9
- let second___: unknown
10
-
11
- const lines = content.split('\n').reduce<string[]>((pre, line) => {
12
- // 移除开头的空白行
13
- if (!line.trim() && pre.length === 0) {
14
- return pre
15
- }
16
- if (line.trim() === '---') {
17
- if (first___ === undefined) {
18
- first___ = pre.length
19
- }
20
- else if (second___ === undefined) {
21
- second___ = pre.length
22
- }
23
- }
24
- pre.push(line)
25
- return pre
26
- }, [])
27
- return (
28
- lines
29
- // 剔除---之间的内容
30
- .slice((second___ as number) || 0)
31
- .join('\n')
32
- )
33
- }
34
-
35
- export function getDefaultTitle(content: string) {
36
- const match = content.match(/^(#+)\s+(.+)/m)
37
- return match?.[2] || ''
38
- }
39
-
40
- const cache = new Map<string, Date | undefined>()
41
- export function getFileBirthTime(url: string): Promise<Date | undefined> | Date {
42
- const cached = cache.get(url)
43
- if (cached) {
44
- return cached
45
- }
46
-
47
- return new Promise((resolve) => {
48
- // 使用异步回调
49
- const child = spawn('git', ['log', '-1', '--pretty="%ai"', url])
50
- let output = ''
51
- child.stdout.on('data', d => (output += String(d)))
52
- child.on('close', async () => {
53
- let date: Date | undefined
54
- if (output.trim()) {
55
- date = new Date(output)
56
- }
57
- else {
58
- date = await getFileBirthTimeByFs(url)
59
- }
60
- cache.set(url, date)
61
- resolve(date)
62
- })
63
- child.on('error', async () => {
64
- const fsDate = await getFileBirthTimeByFs(url)
65
- resolve(fsDate)
66
- })
67
- })
68
- }
69
-
70
- export async function getFileBirthTimeByFs(url: string) {
71
- try {
72
- const fsStat = await fs.promises.stat(url)
73
- return fsStat.birthtime
74
- }
75
- catch {
76
- return undefined
77
- }
78
- }
79
-
80
- export function getGitTimestamp(file: string) {
81
- return new Promise((resolve, reject) => {
82
- const child = spawn('git', ['log', '-1', '--pretty="%ci"', file])
83
- let output = ''
84
- child.stdout.on('data', (d) => {
85
- output += String(d)
86
- })
87
- child.on('close', () => {
88
- resolve(+new Date(output))
89
- })
90
- child.on('error', reject)
91
- })
92
- }
93
-
94
- export function getTextSummary(text: string, count = 100) {
95
- return (
96
- text
97
- // 首个标题
98
- ?.replace(/^#+\s+.*/, '')
99
- // 除去标题
100
- ?.replace(/#/g, '')
101
- // 除去图片
102
- ?.replace(/!\[.*?\]\(.*?\)/g, '')
103
- // 除去链接
104
- ?.replace(/\[(.*?)\]\(.*?\)/g, '$1')
105
- // 除去加粗
106
- ?.replace(/\*\*(.*?)\*\*/g, '$1')
107
- ?.split('\n')
108
- ?.filter(v => !!v)
109
- ?.join('\n')
110
- ?.replace(/>(.*)/, '')
111
- ?.replace(/</g, '&lt;').replace(/>/g, '&gt;')
112
- ?.trim()
113
- ?.slice(0, count)
114
- )
115
- }
2
+ import { joinPath } from '@sugarat/theme-shared'
116
3
 
117
4
  export function aliasObjectToArray(obj: Record<string, string>) {
118
5
  return Object.entries(obj).map(([find, replacement]) => ({
@@ -121,21 +8,6 @@ export function aliasObjectToArray(obj: Record<string, string>) {
121
8
  }))
122
9
  }
123
10
 
124
- export const EXTERNAL_URL_RE = /^[a-z]+:/i
125
-
126
- /**
127
- * Join two paths by resolving the slash collision.
128
- */
129
- export function joinPath(base: string, path: string): string {
130
- return `${base}${path}`.replace(/\/+/g, '/')
131
- }
132
-
133
- export function withBase(base: string, path: string) {
134
- return EXTERNAL_URL_RE.test(path) || path.startsWith('.')
135
- ? path
136
- : joinPath(base, path)
137
- }
138
-
139
11
  function isBase64ImageURL(url: string) {
140
12
  // Base64 图片链接的格式为 data:image/[image format];base64,[Base64 编码的数据]
141
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,7 +46,7 @@ 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
51
  // 无法获取时兜底当前时间
74
52
  meta.date = formatDate(date || new Date())
@@ -102,14 +80,16 @@ export async function getArticleMeta(filepath: string, route: string, timeZone =
102
80
  }
103
81
  return meta as Theme.PageMeta
104
82
  }
105
- export async function getArticles(cfg?: Partial<Theme.BlogConfig>) {
106
- const srcDir = cfg?.srcDir || process.argv.slice(2)?.[1] || '.'
107
- const files = glob.sync(`${srcDir}/**/*.md`, { ignore: ['node_modules'] })
108
- 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 })
109
89
 
110
90
  const metaResults = files.reduce((prev, curr) => {
111
- const route = getPageRoute(curr, srcDir)
112
- const metaPromise = limit(() => getArticleMeta(curr, route, cfg?.timeZone))
91
+ const route = getPageRoute(curr, vpConfig.srcDir)
92
+ const metaPromise = getArticleMeta(curr, route, cfg?.timeZone)
113
93
 
114
94
  // 提前获取,有缓存取缓存
115
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