@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 +3 -12
- package/node.js +26 -193
- package/package.json +4 -6
- package/src/components/BlogItem.vue +1 -1
- package/src/composables/config/index.ts +4 -14
- package/src/utils/node/index.ts +1 -129
- package/src/utils/node/theme.ts +15 -35
- package/src/utils/node/vitePlugins.ts +5 -11
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 =
|
|
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
|
|
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, "<").replace(/>/g, ">")?.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
|
|
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
|
|
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
|
-
|
|
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
|
|
556
|
-
const { data: frontmatter, excerpt, content } = (0,
|
|
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}`) ||
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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,
|
|
585
|
+
if (!(0, import_node_fs2.existsSync)(realPath)) {
|
|
753
586
|
continue;
|
|
754
587
|
}
|
|
755
|
-
const fileBuffer = (0,
|
|
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
|
+
"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-
|
|
56
|
-
"vitepress-plugin-
|
|
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-
|
|
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
|
-
|
|
|
351
|
-
|
|
|
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
|
*/
|
package/src/utils/node/index.ts
CHANGED
|
@@ -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
|
|
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, '<').replace(/>/g, '>')
|
|
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,/
|
package/src/utils/node/theme.ts
CHANGED
|
@@ -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
|
|
8
|
-
import
|
|
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 {
|
|
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
|
-
|
|
28
|
-
|
|
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 } =
|
|
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
|
-
||
|
|
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
|
|
106
|
-
const srcDir
|
|
107
|
-
|
|
108
|
-
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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
|