oceanpress 1.0.12 → 1.0.13
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/CLAUDE.md +4 -0
- package/dist/assets/{index-SyBTrsYP.js → index-C2pqUSpO.js} +19 -19
- package/dist/assets/steps-D7njNjRB.js +2191 -0
- package/dist/index.html +1 -1
- package/dist-cli/cli.js +598 -415
- package/dist-cli/cli.js.map +1 -1
- package/package.json +17 -1
- package/dist/assets/steps-Cpyl02WI.js +0 -2030
package/dist-cli/cli.js
CHANGED
|
@@ -77,8 +77,22 @@ import { computed, reactive, watch } from "vue";
|
|
|
77
77
|
// package.json
|
|
78
78
|
var package_default = {
|
|
79
79
|
name: "oceanpress",
|
|
80
|
-
version: "1.0.
|
|
80
|
+
version: "1.0.13",
|
|
81
81
|
type: "module",
|
|
82
|
+
description: "\u4ECE\u601D\u6E90\u7B14\u8BB0\u672C\u751F\u6210\u9759\u6001\u7AD9\u70B9\u7684\u5DE5\u5177",
|
|
83
|
+
author: "siyuan-note",
|
|
84
|
+
license: "MIT",
|
|
85
|
+
keywords: [
|
|
86
|
+
"siyuan",
|
|
87
|
+
"static-site-generator",
|
|
88
|
+
"markdown",
|
|
89
|
+
"blog",
|
|
90
|
+
"ssg"
|
|
91
|
+
],
|
|
92
|
+
homepage: "https://github.com/siyuan-note/oceanpress#readme",
|
|
93
|
+
bugs: {
|
|
94
|
+
url: "https://github.com/siyuan-note/oceanpress/issues"
|
|
95
|
+
},
|
|
82
96
|
scripts: {
|
|
83
97
|
dev: "vite",
|
|
84
98
|
cli: "tsx ./src/cli.ts",
|
|
@@ -105,6 +119,10 @@ var package_default = {
|
|
|
105
119
|
"dist-cli",
|
|
106
120
|
"*.md"
|
|
107
121
|
],
|
|
122
|
+
repository: {
|
|
123
|
+
type: "git",
|
|
124
|
+
url: "https://github.com/siyuan-note/oceanpress.git"
|
|
125
|
+
},
|
|
108
126
|
dependencies: {
|
|
109
127
|
"@aws-sdk/client-s3": "^3.873.0",
|
|
110
128
|
"@hono/node-server": "^1.19.0",
|
|
@@ -119,12 +137,14 @@ var package_default = {
|
|
|
119
137
|
octokit: "^5.0.3",
|
|
120
138
|
superjson: "^2.2.2",
|
|
121
139
|
tsx: "^4.20.4",
|
|
140
|
+
turndown: "^7.2.2",
|
|
122
141
|
vditor: "^3.11.1",
|
|
123
142
|
vue: "^3.5.19",
|
|
124
143
|
"vue-router": "^4.5.1",
|
|
125
144
|
"zstd-codec": "^0.1.5"
|
|
126
145
|
},
|
|
127
146
|
devDependencies: {
|
|
147
|
+
"@types/turndown": "^5.0.6",
|
|
128
148
|
"@vitejs/plugin-vue": "^6.0.1",
|
|
129
149
|
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
|
130
150
|
"dependency-cruiser": "^17.0.1",
|
|
@@ -248,6 +268,17 @@ var defaultConfig = {
|
|
|
248
268
|
apiKey: "",
|
|
249
269
|
indexName: ""
|
|
250
270
|
},
|
|
271
|
+
/** Markdown 镜像导出配置 */
|
|
272
|
+
markdownMirror: {
|
|
273
|
+
enable: false,
|
|
274
|
+
outputDir: "",
|
|
275
|
+
includeAssets: false,
|
|
276
|
+
watchMode: false,
|
|
277
|
+
/** 定时同步间隔(毫秒) */
|
|
278
|
+
watchInterval: 6e4,
|
|
279
|
+
/** 是否移除头部和底部(侧边栏、导航、footer 等) */
|
|
280
|
+
removeTemplate: false
|
|
281
|
+
},
|
|
251
282
|
/** html模板嵌入代码块,会将此处配置中的代码嵌入到生成的html所对应的位置 */
|
|
252
283
|
embedCode: {
|
|
253
284
|
head: "",
|
|
@@ -518,6 +549,237 @@ var s3_uploads = async function* (tree, config) {
|
|
|
518
549
|
}
|
|
519
550
|
};
|
|
520
551
|
|
|
552
|
+
// src/plugins/markdown_mirror/plugin.ts
|
|
553
|
+
import TurndownService from "turndown";
|
|
554
|
+
var MarkdownMirrorPlugin = class {
|
|
555
|
+
constructor(config) {
|
|
556
|
+
this.config = config;
|
|
557
|
+
__publicField(this, "name", "MarkdownMirrorPlugin");
|
|
558
|
+
/** 拦截 build,添加 Markdown 导出回调 */
|
|
559
|
+
__publicField(this, "build", function([config, otherConfig], next) {
|
|
560
|
+
return next(config, {
|
|
561
|
+
...otherConfig,
|
|
562
|
+
beforeFileTree: async (tree, effectApi) => {
|
|
563
|
+
if (otherConfig?.beforeFileTree) {
|
|
564
|
+
await otherConfig.beforeFileTree(tree, effectApi);
|
|
565
|
+
}
|
|
566
|
+
if (config.markdownMirror?.enable) {
|
|
567
|
+
effectApi.log("\n=== \u5F00\u59CB Markdown \u955C\u50CF\u5BFC\u51FA ===");
|
|
568
|
+
try {
|
|
569
|
+
convertHtmlToMarkdown(
|
|
570
|
+
tree,
|
|
571
|
+
config.markdownMirror.includeAssets
|
|
572
|
+
);
|
|
573
|
+
effectApi.log("=== Markdown \u955C\u50CF\u5BFC\u51FA\u5B8C\u6210 ===\n");
|
|
574
|
+
} catch (error) {
|
|
575
|
+
effectApi.log("\u274C Markdown \u955C\u50CF\u5BFC\u51FA\u5931\u8D25: " + error);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
},
|
|
579
|
+
onFileTree: async (tree, effectApi) => {
|
|
580
|
+
if (otherConfig?.onFileTree) {
|
|
581
|
+
await otherConfig.onFileTree(tree, effectApi);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
function buildIdToHeadingMap(htmlContent) {
|
|
589
|
+
const idToHeading = /* @__PURE__ */ new Map();
|
|
590
|
+
const headingRegex = /<h([1-6])\s+id="([^"]+)"[^>]*>(.*?)<\/h\1>/gi;
|
|
591
|
+
let match;
|
|
592
|
+
while ((match = headingRegex.exec(htmlContent)) !== null) {
|
|
593
|
+
const id = match[2];
|
|
594
|
+
const headingText = match[3].replace(/<[^>]+>/g, "").replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").trim();
|
|
595
|
+
idToHeading.set(id, headingText);
|
|
596
|
+
}
|
|
597
|
+
return idToHeading;
|
|
598
|
+
}
|
|
599
|
+
function headingToAnchor(headingText) {
|
|
600
|
+
return headingText.toLowerCase().replace(/[^\p{L}\p{N}\s-]/gu, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, "");
|
|
601
|
+
}
|
|
602
|
+
function normalizePath(relativePath) {
|
|
603
|
+
let normalized = relativePath.replace(/^\.\//, "");
|
|
604
|
+
while (normalized.startsWith("../")) {
|
|
605
|
+
normalized = normalized.slice(3);
|
|
606
|
+
}
|
|
607
|
+
return normalized;
|
|
608
|
+
}
|
|
609
|
+
function convertHtmlToMarkdown(tree, includeAssets = false) {
|
|
610
|
+
const allFiles = Object.keys(tree);
|
|
611
|
+
console.log(`\u{1F4C2} \u6587\u4EF6\u6811\u4E2D\u5171\u6709 ${allFiles.length} \u4E2A\u6587\u4EF6`);
|
|
612
|
+
if (allFiles.length > 0) {
|
|
613
|
+
console.log("\u{1F4C2} \u524D 10 \u4E2A\u6587\u4EF6:", allFiles.slice(0, 10));
|
|
614
|
+
}
|
|
615
|
+
const globalIdToHeading = /* @__PURE__ */ new Map();
|
|
616
|
+
const htmlFiles = Object.entries(tree).filter(([filePath]) => filePath.endsWith(".html"));
|
|
617
|
+
console.log("\u{1F4D6} \u6B63\u5728\u626B\u63CF HTML \u6587\u4EF6\u4EE5\u5EFA\u7ACB\u6807\u9898\u6620\u5C04...");
|
|
618
|
+
for (const [htmlPath, htmlContent] of htmlFiles) {
|
|
619
|
+
const htmlStr = htmlContent.toString();
|
|
620
|
+
const idToHeading = buildIdToHeadingMap(htmlStr);
|
|
621
|
+
for (const [id, heading] of idToHeading.entries()) {
|
|
622
|
+
const mdPath = htmlPath.replace(/\.html$/, ".md");
|
|
623
|
+
globalIdToHeading.set(`${mdPath}:${id}`, heading);
|
|
624
|
+
globalIdToHeading.set(`${htmlPath}:${id}`, heading);
|
|
625
|
+
globalIdToHeading.set(`./${mdPath}:${id}`, heading);
|
|
626
|
+
globalIdToHeading.set(`./${htmlPath}:${id}`, heading);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
console.log(`\u2705 \u5EFA\u7ACB\u4E86 ${globalIdToHeading.size} \u4E2A\u6807\u9898\u6620\u5C04
|
|
630
|
+
`);
|
|
631
|
+
const turndownService = new TurndownService({
|
|
632
|
+
headingStyle: "atx",
|
|
633
|
+
codeBlockStyle: "fenced",
|
|
634
|
+
bulletListMarker: "-"
|
|
635
|
+
});
|
|
636
|
+
turndownService.addRule("remove-sidebars", {
|
|
637
|
+
filter: (node) => {
|
|
638
|
+
return node.id === "oceanpress-left-sidebar" || node.id === "oceanpress-right-sidebar";
|
|
639
|
+
},
|
|
640
|
+
replacement: () => ""
|
|
641
|
+
});
|
|
642
|
+
turndownService.addRule("remove-html-metadata", {
|
|
643
|
+
filter: (node) => {
|
|
644
|
+
return ["head", "style", "script", "noscript", "link", "meta", "title"].includes(
|
|
645
|
+
node.nodeName?.toLowerCase()
|
|
646
|
+
);
|
|
647
|
+
},
|
|
648
|
+
replacement: () => ""
|
|
649
|
+
});
|
|
650
|
+
turndownService.addRule("remove-json-ld", {
|
|
651
|
+
filter: (node) => {
|
|
652
|
+
return node.nodeName === "SCRIPT" && node.getAttribute("type") === "application/ld+json";
|
|
653
|
+
},
|
|
654
|
+
replacement: () => ""
|
|
655
|
+
});
|
|
656
|
+
turndownService.addRule("remove-siyuan-config", {
|
|
657
|
+
filter: (node) => {
|
|
658
|
+
return node.nodeName === "SCRIPT" && node.textContent?.includes("window.siyuan");
|
|
659
|
+
},
|
|
660
|
+
replacement: () => ""
|
|
661
|
+
});
|
|
662
|
+
turndownService.addRule("remove-footer", {
|
|
663
|
+
filter: (node) => {
|
|
664
|
+
return node.nodeName === "FOOTER" || node.nodeName === "DIV" && node.classList?.contains("theme-aware-footer");
|
|
665
|
+
},
|
|
666
|
+
replacement: () => ""
|
|
667
|
+
});
|
|
668
|
+
turndownService.addRule("remove-home-link", {
|
|
669
|
+
filter: (node) => {
|
|
670
|
+
return node.nodeName === "A" && node.getAttribute("href") === "/";
|
|
671
|
+
},
|
|
672
|
+
replacement: () => ""
|
|
673
|
+
});
|
|
674
|
+
turndownService.addRule("convert-internal-links", {
|
|
675
|
+
filter: (node) => {
|
|
676
|
+
return node.nodeName === "A" && node.getAttribute("href")?.includes(".html");
|
|
677
|
+
},
|
|
678
|
+
replacement: (content, node) => {
|
|
679
|
+
const href = node.getAttribute("href");
|
|
680
|
+
if (!href) return content;
|
|
681
|
+
const linkMatch = href.match(/^(.+?\.html)(#.+)?$/);
|
|
682
|
+
if (!linkMatch) return `[${content}](${href})`;
|
|
683
|
+
const htmlPath = linkMatch[1];
|
|
684
|
+
const anchor = linkMatch[2] || "";
|
|
685
|
+
const normalizedHtmlPath = normalizePath(htmlPath);
|
|
686
|
+
const mdPath = normalizedHtmlPath.replace(/\.html$/, ".md");
|
|
687
|
+
if (anchor) {
|
|
688
|
+
const anchorId = anchor.slice(1);
|
|
689
|
+
let headingText;
|
|
690
|
+
const possiblePaths = [
|
|
691
|
+
`/${mdPath}:${anchorId}`,
|
|
692
|
+
// /工具/blender.md:xxx
|
|
693
|
+
`${mdPath}:${anchorId}`,
|
|
694
|
+
// 工具/blender.md:xxx
|
|
695
|
+
`/${normalizedHtmlPath}:${anchorId}`,
|
|
696
|
+
`${normalizedHtmlPath}:${anchorId}`
|
|
697
|
+
];
|
|
698
|
+
for (const possiblePath of possiblePaths) {
|
|
699
|
+
if (globalIdToHeading.has(possiblePath)) {
|
|
700
|
+
headingText = globalIdToHeading.get(possiblePath);
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (headingText) {
|
|
705
|
+
const headingAnchor = headingToAnchor(headingText);
|
|
706
|
+
const prefix2 = href.startsWith("../") ? "../" : href.startsWith("./") ? "./" : "";
|
|
707
|
+
return `[${content}](${prefix2}${mdPath}#${headingAnchor})`;
|
|
708
|
+
} else {
|
|
709
|
+
if (Math.random() < 0.05) {
|
|
710
|
+
console.warn(`\u26A0\uFE0F \u672A\u627E\u5230\u6807\u9898\u6620\u5C04: ${href}`);
|
|
711
|
+
console.warn(` \u5C1D\u8BD5\u7684\u8DEF\u5F84:`, possiblePaths.slice(0, 2));
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
const prefix = href.startsWith("../") ? "../" : href.startsWith("./") ? "./" : "";
|
|
716
|
+
return `[${content}](${prefix}${mdPath})`;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
turndownService.addRule("convert-code-blocks", {
|
|
720
|
+
filter: (node) => {
|
|
721
|
+
return node.nodeName === "DIV" && node.classList?.contains("hljs");
|
|
722
|
+
},
|
|
723
|
+
replacement: (_content, node) => {
|
|
724
|
+
const code = node.textContent || "";
|
|
725
|
+
let language = "";
|
|
726
|
+
const previousSibling = node.previousSibling;
|
|
727
|
+
if (previousSibling && previousSibling.nodeName === "DIV") {
|
|
728
|
+
const actionDiv = previousSibling.querySelector(".protyle-action--first");
|
|
729
|
+
if (actionDiv) {
|
|
730
|
+
const langText = actionDiv.textContent?.trim();
|
|
731
|
+
if (langText && langText !== "Copy") {
|
|
732
|
+
language = langText.toLowerCase();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (!language) {
|
|
737
|
+
if (code.includes("function ") || code.includes("const ") || code.includes("let ") || code.includes("=>") || code.includes("interface ")) {
|
|
738
|
+
language = "typescript";
|
|
739
|
+
} else if (code.includes("def ") || code.includes("import ") || code.includes("class ")) {
|
|
740
|
+
language = "python";
|
|
741
|
+
} else if (code.includes("SELECT ") || code.includes("INSERT ") || code.includes("UPDATE ")) {
|
|
742
|
+
language = "sql";
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return `
|
|
746
|
+
\`\`\`${language}
|
|
747
|
+
${code.trim()}
|
|
748
|
+
\`\`\`
|
|
749
|
+
`;
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
console.log(`\u{1F4C4} \u627E\u5230 ${htmlFiles.length} \u4E2A HTML \u6587\u4EF6
|
|
753
|
+
`);
|
|
754
|
+
let convertedCount = 0;
|
|
755
|
+
for (const [htmlPath, htmlContent] of htmlFiles) {
|
|
756
|
+
try {
|
|
757
|
+
const mdPath = htmlPath.replace(/\.html$/, ".md");
|
|
758
|
+
const htmlStr = htmlContent.toString();
|
|
759
|
+
let markdown = turndownService.turndown(htmlStr);
|
|
760
|
+
markdown = markdown.trimStart();
|
|
761
|
+
markdown = markdown.replace(/\n([a-z]+)\n\n(```[a-z]+\n)/g, "\n$2");
|
|
762
|
+
tree[mdPath] = markdown;
|
|
763
|
+
delete tree[htmlPath];
|
|
764
|
+
convertedCount++;
|
|
765
|
+
console.log(`\u2705 [${convertedCount}] ${htmlPath} \u2192 ${mdPath}`);
|
|
766
|
+
} catch (error) {
|
|
767
|
+
console.error(`\u274C \u8F6C\u6362\u5931\u8D25: ${htmlPath}`, error);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
console.log(`
|
|
771
|
+
\u{1F389} \u6210\u529F\u8F6C\u6362 ${convertedCount} \u4E2A HTML \u6587\u4EF6\u4E3A Markdown`);
|
|
772
|
+
if (!includeAssets) {
|
|
773
|
+
const assetPaths = Object.keys(tree).filter((path) => path.startsWith("assets/"));
|
|
774
|
+
assetPaths.forEach((assetPath) => {
|
|
775
|
+
delete tree[assetPath];
|
|
776
|
+
});
|
|
777
|
+
if (assetPaths.length > 0) {
|
|
778
|
+
console.log(`\u{1F5D1}\uFE0F \u5DF2\u4ECE\u6587\u4EF6\u6811\u4E2D\u79FB\u9664 ${assetPaths.length} \u4E2A\u8D44\u6E90\u6587\u4EF6`);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
521
783
|
// src/core/build.ts
|
|
522
784
|
import { Effect as Effect4 } from "effect";
|
|
523
785
|
|
|
@@ -882,6 +1144,101 @@ function sitemap_xml(docArr, config) {
|
|
|
882
1144
|
}
|
|
883
1145
|
|
|
884
1146
|
// src/core/seo.ts
|
|
1147
|
+
var simplifiedStopWords = /* @__PURE__ */ new Set([
|
|
1148
|
+
"\u7684",
|
|
1149
|
+
"\u4E86",
|
|
1150
|
+
"\u5728",
|
|
1151
|
+
"\u662F",
|
|
1152
|
+
"\u6211",
|
|
1153
|
+
"\u6709",
|
|
1154
|
+
"\u548C",
|
|
1155
|
+
"\u5C31",
|
|
1156
|
+
"\u4E0D",
|
|
1157
|
+
"\u4EBA",
|
|
1158
|
+
"\u90FD",
|
|
1159
|
+
"\u4E00",
|
|
1160
|
+
"\u4E2A",
|
|
1161
|
+
"\u4E0A",
|
|
1162
|
+
"\u4E5F",
|
|
1163
|
+
"\u5F88",
|
|
1164
|
+
"\u5230",
|
|
1165
|
+
"\u8BF4",
|
|
1166
|
+
"\u8981",
|
|
1167
|
+
"\u53BB",
|
|
1168
|
+
"\u4F60",
|
|
1169
|
+
"\u4F1A",
|
|
1170
|
+
"\u7740",
|
|
1171
|
+
"\u6CA1\u6709",
|
|
1172
|
+
"\u770B",
|
|
1173
|
+
"\u597D",
|
|
1174
|
+
"\u81EA\u5DF1",
|
|
1175
|
+
"\u8FD9",
|
|
1176
|
+
"\u90A3",
|
|
1177
|
+
"\u73B0\u5728",
|
|
1178
|
+
"\u53EF\u4EE5",
|
|
1179
|
+
"\u4F46\u662F",
|
|
1180
|
+
"\u8FD8\u662F",
|
|
1181
|
+
"\u56E0\u4E3A",
|
|
1182
|
+
"\u4EC0\u4E48",
|
|
1183
|
+
"\u5982\u679C",
|
|
1184
|
+
"\u6240\u4EE5",
|
|
1185
|
+
"the",
|
|
1186
|
+
"a",
|
|
1187
|
+
"an",
|
|
1188
|
+
"and",
|
|
1189
|
+
"or",
|
|
1190
|
+
"but",
|
|
1191
|
+
"in",
|
|
1192
|
+
"on",
|
|
1193
|
+
"at",
|
|
1194
|
+
"to",
|
|
1195
|
+
"for",
|
|
1196
|
+
"of",
|
|
1197
|
+
"with",
|
|
1198
|
+
"by",
|
|
1199
|
+
"is",
|
|
1200
|
+
"are",
|
|
1201
|
+
"was",
|
|
1202
|
+
"were",
|
|
1203
|
+
"be",
|
|
1204
|
+
"been",
|
|
1205
|
+
"being",
|
|
1206
|
+
"have",
|
|
1207
|
+
"has",
|
|
1208
|
+
"had",
|
|
1209
|
+
"do",
|
|
1210
|
+
"does",
|
|
1211
|
+
"did",
|
|
1212
|
+
"will",
|
|
1213
|
+
"would",
|
|
1214
|
+
"could",
|
|
1215
|
+
"should",
|
|
1216
|
+
"may",
|
|
1217
|
+
"might",
|
|
1218
|
+
"must",
|
|
1219
|
+
"can",
|
|
1220
|
+
"this",
|
|
1221
|
+
"that",
|
|
1222
|
+
"these",
|
|
1223
|
+
"those",
|
|
1224
|
+
"i",
|
|
1225
|
+
"you",
|
|
1226
|
+
"he",
|
|
1227
|
+
"she",
|
|
1228
|
+
"it",
|
|
1229
|
+
"we",
|
|
1230
|
+
"they"
|
|
1231
|
+
]);
|
|
1232
|
+
var htmlCleanupCache = /* @__PURE__ */ new Map();
|
|
1233
|
+
var MAX_CACHE_SIZE = 500;
|
|
1234
|
+
function manageCacheSize() {
|
|
1235
|
+
if (htmlCleanupCache.size > MAX_CACHE_SIZE) {
|
|
1236
|
+
const firstKey = htmlCleanupCache.keys().next().value;
|
|
1237
|
+
if (firstKey) {
|
|
1238
|
+
htmlCleanupCache.delete(firstKey);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
885
1242
|
function extractDateFromId(id) {
|
|
886
1243
|
if (!id || id.length < 14) return (/* @__PURE__ */ new Date()).toISOString();
|
|
887
1244
|
try {
|
|
@@ -920,369 +1277,35 @@ function formatDate(dateString) {
|
|
|
920
1277
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
921
1278
|
}
|
|
922
1279
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
"\u5F88",
|
|
943
|
-
"\u5230",
|
|
944
|
-
"\u8BF4",
|
|
945
|
-
"\u8981",
|
|
946
|
-
"\u53BB",
|
|
947
|
-
"\u4F60",
|
|
948
|
-
"\u4F1A",
|
|
949
|
-
"\u7740",
|
|
950
|
-
"\u6CA1\u6709",
|
|
951
|
-
"\u770B",
|
|
952
|
-
"\u597D",
|
|
953
|
-
"\u81EA\u5DF1",
|
|
954
|
-
"\u8FD9",
|
|
955
|
-
"\u90A3",
|
|
956
|
-
"\u73B0\u5728",
|
|
957
|
-
"\u53EF\u4EE5",
|
|
958
|
-
"\u4F46\u662F",
|
|
959
|
-
"\u8FD8\u662F",
|
|
960
|
-
"\u56E0\u4E3A",
|
|
961
|
-
"\u4EC0\u4E48",
|
|
962
|
-
"\u5982\u679C",
|
|
963
|
-
"\u6240\u4EE5",
|
|
964
|
-
"\u5BF9\u4E8E",
|
|
965
|
-
"\u5173\u4E8E",
|
|
966
|
-
"\u901A\u8FC7",
|
|
967
|
-
"\u8FDB\u884C",
|
|
968
|
-
"\u57FA\u4E8E",
|
|
969
|
-
"\u4EE5\u53CA",
|
|
970
|
-
"\u6216\u8005",
|
|
971
|
-
"\u800C\u4E14",
|
|
972
|
-
"\u7136\u540E",
|
|
973
|
-
"\u53EA\u662F",
|
|
974
|
-
"\u5DF2\u7ECF",
|
|
975
|
-
"\u6B63\u5728",
|
|
976
|
-
"\u5E94\u8BE5",
|
|
977
|
-
"\u80FD\u591F",
|
|
978
|
-
"\u9700\u8981",
|
|
979
|
-
"\u53EF\u80FD",
|
|
980
|
-
"\u4E00\u5B9A",
|
|
981
|
-
"\u8FD9\u6837",
|
|
982
|
-
"\u90A3\u6837",
|
|
983
|
-
"\u600E\u4E48",
|
|
984
|
-
"\u4E3A\u4EC0\u4E48",
|
|
985
|
-
"\u54EA\u91CC",
|
|
986
|
-
"\u54EA\u4E2A",
|
|
987
|
-
"\u591A\u5C11",
|
|
988
|
-
"\u51E0\u4E2A",
|
|
989
|
-
"\u4EC0\u4E48",
|
|
990
|
-
"\u600E\u4E48",
|
|
991
|
-
"\u5982\u4F55",
|
|
992
|
-
"\u4E3A\u4EC0\u4E48",
|
|
993
|
-
// 英文停用词
|
|
994
|
-
"the",
|
|
995
|
-
"a",
|
|
996
|
-
"an",
|
|
997
|
-
"and",
|
|
998
|
-
"or",
|
|
999
|
-
"but",
|
|
1000
|
-
"in",
|
|
1001
|
-
"on",
|
|
1002
|
-
"at",
|
|
1003
|
-
"to",
|
|
1004
|
-
"for",
|
|
1005
|
-
"of",
|
|
1006
|
-
"with",
|
|
1007
|
-
"by",
|
|
1008
|
-
"is",
|
|
1009
|
-
"are",
|
|
1010
|
-
"was",
|
|
1011
|
-
"were",
|
|
1012
|
-
"be",
|
|
1013
|
-
"been",
|
|
1014
|
-
"being",
|
|
1015
|
-
"have",
|
|
1016
|
-
"has",
|
|
1017
|
-
"had",
|
|
1018
|
-
"do",
|
|
1019
|
-
"does",
|
|
1020
|
-
"did",
|
|
1021
|
-
"will",
|
|
1022
|
-
"would",
|
|
1023
|
-
"could",
|
|
1024
|
-
"should",
|
|
1025
|
-
"may",
|
|
1026
|
-
"might",
|
|
1027
|
-
"must",
|
|
1028
|
-
"can",
|
|
1029
|
-
"this",
|
|
1030
|
-
"that",
|
|
1031
|
-
"these",
|
|
1032
|
-
"those",
|
|
1033
|
-
"i",
|
|
1034
|
-
"you",
|
|
1035
|
-
"he",
|
|
1036
|
-
"she",
|
|
1037
|
-
"it",
|
|
1038
|
-
"we",
|
|
1039
|
-
"they",
|
|
1040
|
-
"me",
|
|
1041
|
-
"him",
|
|
1042
|
-
"her",
|
|
1043
|
-
"us",
|
|
1044
|
-
"them",
|
|
1045
|
-
"my",
|
|
1046
|
-
"your",
|
|
1047
|
-
"his",
|
|
1048
|
-
"its",
|
|
1049
|
-
"our",
|
|
1050
|
-
"their",
|
|
1051
|
-
"not",
|
|
1052
|
-
"no",
|
|
1053
|
-
"yes",
|
|
1054
|
-
"so",
|
|
1055
|
-
"if",
|
|
1056
|
-
"when",
|
|
1057
|
-
"where",
|
|
1058
|
-
"how",
|
|
1059
|
-
"why",
|
|
1060
|
-
"what",
|
|
1061
|
-
"which",
|
|
1062
|
-
"who",
|
|
1063
|
-
"whom",
|
|
1064
|
-
"there",
|
|
1065
|
-
"here"
|
|
1066
|
-
]));
|
|
1067
|
-
}
|
|
1068
|
-
/**
|
|
1069
|
-
* 计算词频 (TF)
|
|
1070
|
-
*/
|
|
1071
|
-
calculateTermFrequency(text) {
|
|
1072
|
-
const words = this.tokenize(text);
|
|
1073
|
-
const tf = /* @__PURE__ */ new Map();
|
|
1074
|
-
const totalWords = words.length;
|
|
1075
|
-
for (const word of words) {
|
|
1076
|
-
tf.set(word, (tf.get(word) || 0) + 1);
|
|
1077
|
-
}
|
|
1078
|
-
for (const [word, count] of tf) {
|
|
1079
|
-
tf.set(word, count / totalWords);
|
|
1080
|
-
}
|
|
1081
|
-
return tf;
|
|
1082
|
-
}
|
|
1083
|
-
/**
|
|
1084
|
-
* 分词处理(针对中文和英文优化)
|
|
1085
|
-
*/
|
|
1086
|
-
tokenize(text) {
|
|
1087
|
-
const tokens = [];
|
|
1088
|
-
const plainText = text.replace(/<[^>]*>/g, " ").replace(/&[^;]+;/g, " ").replace(/\s+/g, " ").trim();
|
|
1089
|
-
const englishWords = plainText.match(/[a-zA-Z]{3,}/g) || [];
|
|
1090
|
-
tokens.push(...englishWords.map((word) => word.toLowerCase()).filter((word) => !this.stopWords.has(word)));
|
|
1091
|
-
const camelCaseWords = plainText.match(/[a-z]+[A-Z][a-zA-Z0-9]*|[A-Z][a-z0-9]+[A-Z][a-zA-Z0-9]*/g) || [];
|
|
1092
|
-
tokens.push(...camelCaseWords.map((word) => word.toLowerCase()));
|
|
1093
|
-
const chineseTokens = this.extractChineseTokens(plainText);
|
|
1094
|
-
tokens.push(...chineseTokens);
|
|
1095
|
-
const filteredTokens = tokens.filter((token) => {
|
|
1096
|
-
if (token.length < 2) return false;
|
|
1097
|
-
const htmlRelatedWords = ["div", "span", "class", "id", "type", "data", "href", "src", "alt", "title", "style", "width", "height", "rootid", "endid", "nodedocument", "ezcqbj", "cktc", "quot"];
|
|
1098
|
-
if (htmlRelatedWords.includes(token.toLowerCase())) {
|
|
1099
|
-
return false;
|
|
1100
|
-
}
|
|
1101
|
-
if (token.match(/^[a-f0-9]{6,}$/)) return false;
|
|
1102
|
-
if (/[\u4e00-\u9fa5]/.test(token) && token.length < 2) {
|
|
1103
|
-
return false;
|
|
1104
|
-
}
|
|
1105
|
-
return true;
|
|
1106
|
-
});
|
|
1107
|
-
return filteredTokens;
|
|
1108
|
-
}
|
|
1109
|
-
/**
|
|
1110
|
-
* 智能中文分词 - 基于文本特征分析
|
|
1111
|
-
*/
|
|
1112
|
-
extractChineseTokens(text) {
|
|
1113
|
-
const tokens = [];
|
|
1114
|
-
const chineseText = text.replace(/[^\u4e00-\u9fa5\s]/g, " ");
|
|
1115
|
-
const wordPatterns = [
|
|
1116
|
-
/[\u4e00-\u9fa5]{4}/g,
|
|
1117
|
-
// 4字词汇
|
|
1118
|
-
/[\u4e00-\u9fa5]{3}/g,
|
|
1119
|
-
// 3字词汇
|
|
1120
|
-
/[\u4e00-\u9fa5]{2}/g
|
|
1121
|
-
// 2字词汇
|
|
1122
|
-
];
|
|
1123
|
-
for (const pattern of wordPatterns) {
|
|
1124
|
-
const matches = chineseText.match(pattern) || [];
|
|
1125
|
-
for (const word of matches) {
|
|
1126
|
-
if (!this.stopWords.has(word) && this.isMeaningfulChineseWord(word)) {
|
|
1127
|
-
tokens.push(word.toLowerCase());
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1280
|
+
function cleanHtmlContent(content) {
|
|
1281
|
+
const cacheKey = content;
|
|
1282
|
+
if (htmlCleanupCache.has(cacheKey)) {
|
|
1283
|
+
return htmlCleanupCache.get(cacheKey);
|
|
1284
|
+
}
|
|
1285
|
+
const cleaned = content.replace(/<[^>]*>/g, " ").replace(/&[^;]+;/g, " ").replace(/\s+/g, " ").trim();
|
|
1286
|
+
htmlCleanupCache.set(cacheKey, cleaned);
|
|
1287
|
+
manageCacheSize();
|
|
1288
|
+
return cleaned;
|
|
1289
|
+
}
|
|
1290
|
+
function simplifiedKeywordExtraction(text, maxKeywords = 8) {
|
|
1291
|
+
const words = [];
|
|
1292
|
+
const wordCount = /* @__PURE__ */ new Map();
|
|
1293
|
+
const englishWords = text.match(/[a-zA-Z]{3,}/g) || [];
|
|
1294
|
+
for (const word of englishWords) {
|
|
1295
|
+
const lowerWord = word.toLowerCase();
|
|
1296
|
+
if (!simplifiedStopWords.has(lowerWord) && lowerWord.length >= 3) {
|
|
1297
|
+
words.push(lowerWord);
|
|
1298
|
+
wordCount.set(lowerWord, (wordCount.get(lowerWord) || 0) + 1);
|
|
1130
1299
|
}
|
|
1131
|
-
return tokens;
|
|
1132
1300
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
/^的.*$/,
|
|
1139
|
-
/^.*的$/,
|
|
1140
|
-
/^了.*$/,
|
|
1141
|
-
/^.*了$/,
|
|
1142
|
-
/^在.*$/,
|
|
1143
|
-
/^.*在$/,
|
|
1144
|
-
/^是.*$/,
|
|
1145
|
-
/^.*是$/,
|
|
1146
|
-
/^我.*$/,
|
|
1147
|
-
/^.*我$/,
|
|
1148
|
-
/^有.*$/,
|
|
1149
|
-
/^.*有$/,
|
|
1150
|
-
/^和.*$/,
|
|
1151
|
-
/^.*和$/,
|
|
1152
|
-
/^就.*$/,
|
|
1153
|
-
/^.*就$/,
|
|
1154
|
-
/^不.*$/,
|
|
1155
|
-
/^.*不$/,
|
|
1156
|
-
/^人.*$/,
|
|
1157
|
-
/^.*人$/
|
|
1158
|
-
];
|
|
1159
|
-
for (const pattern of meaninglessPatterns) {
|
|
1160
|
-
if (pattern.test(word)) {
|
|
1161
|
-
return false;
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
if (/(.)\1{2,}/.test(word)) {
|
|
1165
|
-
return false;
|
|
1301
|
+
const chineseWords = text.match(/[\u4e00-\u9fa5]{2,4}/g) || [];
|
|
1302
|
+
for (const word of chineseWords) {
|
|
1303
|
+
if (!simplifiedStopWords.has(word)) {
|
|
1304
|
+
words.push(word);
|
|
1305
|
+
wordCount.set(word, (wordCount.get(word) || 0) + 1);
|
|
1166
1306
|
}
|
|
1167
|
-
return true;
|
|
1168
|
-
}
|
|
1169
|
-
/**
|
|
1170
|
-
* 计算逆文档频率 (IDF) - 简化版本
|
|
1171
|
-
*/
|
|
1172
|
-
calculateInverseDocumentFrequency(term) {
|
|
1173
|
-
const commonTerms = /* @__PURE__ */ new Set([
|
|
1174
|
-
"\u6280\u672F",
|
|
1175
|
-
"\u5F00\u53D1",
|
|
1176
|
-
"\u4EE3\u7801",
|
|
1177
|
-
"\u7CFB\u7EDF",
|
|
1178
|
-
"\u6570\u636E",
|
|
1179
|
-
"\u529F\u80FD",
|
|
1180
|
-
"\u5E94\u7528",
|
|
1181
|
-
"\u5B9E\u73B0",
|
|
1182
|
-
"\u65B9\u6CD5",
|
|
1183
|
-
"\u95EE\u9898",
|
|
1184
|
-
"time",
|
|
1185
|
-
"data",
|
|
1186
|
-
"system",
|
|
1187
|
-
"code",
|
|
1188
|
-
"development",
|
|
1189
|
-
"application",
|
|
1190
|
-
"function",
|
|
1191
|
-
"method",
|
|
1192
|
-
"problem",
|
|
1193
|
-
"solution"
|
|
1194
|
-
]);
|
|
1195
|
-
if (commonTerms.has(term.toLowerCase())) {
|
|
1196
|
-
return Math.log(1e3 / 500);
|
|
1197
|
-
}
|
|
1198
|
-
return Math.log(1e3 / 10);
|
|
1199
1307
|
}
|
|
1200
|
-
|
|
1201
|
-
* 提取关键词
|
|
1202
|
-
*/
|
|
1203
|
-
extractKeywords(content, maxKeywords = 10) {
|
|
1204
|
-
const tf = this.calculateTermFrequency(content);
|
|
1205
|
-
const keywordScores = /* @__PURE__ */ new Map();
|
|
1206
|
-
for (const [term, frequency] of tf) {
|
|
1207
|
-
const idf = this.calculateInverseDocumentFrequency(term);
|
|
1208
|
-
const tfidf = frequency * idf;
|
|
1209
|
-
let bonus = 1;
|
|
1210
|
-
if (content.toLowerCase().includes(term.toLowerCase()) && (content.match(new RegExp(`^${term}`, "mi")) || content.match(new RegExp(`${term}$`, "mi")))) {
|
|
1211
|
-
bonus *= 1.5;
|
|
1212
|
-
}
|
|
1213
|
-
if (term.length >= 2 && term.length <= 6) {
|
|
1214
|
-
bonus *= 1.2;
|
|
1215
|
-
}
|
|
1216
|
-
keywordScores.set(term, tfidf * bonus);
|
|
1217
|
-
}
|
|
1218
|
-
return Array.from(keywordScores.entries()).sort((a, b) => b[1] - a[1]).slice(0, maxKeywords).map(([term]) => term);
|
|
1219
|
-
}
|
|
1220
|
-
};
|
|
1221
|
-
function extractDescription(content, maxLength = 160) {
|
|
1222
|
-
const plainText = content.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
1223
|
-
if (plainText.length <= maxLength) return plainText;
|
|
1224
|
-
const truncated = plainText.substring(0, maxLength);
|
|
1225
|
-
const lastSentenceEnd = Math.max(
|
|
1226
|
-
truncated.lastIndexOf("\u3002"),
|
|
1227
|
-
truncated.lastIndexOf("\uFF01"),
|
|
1228
|
-
truncated.lastIndexOf("\uFF1F"),
|
|
1229
|
-
truncated.lastIndexOf("."),
|
|
1230
|
-
truncated.lastIndexOf("!"),
|
|
1231
|
-
truncated.lastIndexOf("?")
|
|
1232
|
-
);
|
|
1233
|
-
if (lastSentenceEnd > maxLength * 0.7) {
|
|
1234
|
-
return truncated.substring(0, lastSentenceEnd + 1);
|
|
1235
|
-
}
|
|
1236
|
-
return truncated + "...";
|
|
1237
|
-
}
|
|
1238
|
-
function extractKeywords(content) {
|
|
1239
|
-
const tfidfExtractor = new TFIDFKeywordExtractor();
|
|
1240
|
-
return tfidfExtractor.extractKeywords(content, 8);
|
|
1241
|
-
}
|
|
1242
|
-
function generateArticleJsonLd(doc, config, pageUrl, content) {
|
|
1243
|
-
const title = doc.Properties?.title || "\u672A\u547D\u540D\u6587\u6863";
|
|
1244
|
-
const description = extractDescription(content);
|
|
1245
|
-
const keywords = extractKeywords(content);
|
|
1246
|
-
const datePublished = extractDateFromId(doc.ID);
|
|
1247
|
-
const dateModified = formatDate(doc.Properties?.updated);
|
|
1248
|
-
const jsonLd = {
|
|
1249
|
-
"@context": "https://schema.org",
|
|
1250
|
-
"@type": "Article",
|
|
1251
|
-
headline: title,
|
|
1252
|
-
description,
|
|
1253
|
-
keywords: keywords.join(", "),
|
|
1254
|
-
datePublished,
|
|
1255
|
-
dateModified,
|
|
1256
|
-
author: {
|
|
1257
|
-
"@type": "Person",
|
|
1258
|
-
name: config.sitemap?.title || "\u5D2E\u751F",
|
|
1259
|
-
url: config.sitemap?.siteLink || ""
|
|
1260
|
-
},
|
|
1261
|
-
publisher: {
|
|
1262
|
-
"@type": "Organization",
|
|
1263
|
-
name: config.sitemap?.title || "OceanPress",
|
|
1264
|
-
logo: {
|
|
1265
|
-
"@type": "ImageObject",
|
|
1266
|
-
url: `${config.sitemap?.siteLink || ""}/assets/logo.png`
|
|
1267
|
-
}
|
|
1268
|
-
},
|
|
1269
|
-
mainEntityOfPage: {
|
|
1270
|
-
"@type": "WebPage",
|
|
1271
|
-
"@id": pageUrl
|
|
1272
|
-
},
|
|
1273
|
-
image: doc.Properties?.["title-img"] ? {
|
|
1274
|
-
"@type": "ImageObject",
|
|
1275
|
-
url: `${config.sitemap?.siteLink || ""}${doc.Properties["title-img"].replace("assets", "/assets")}`,
|
|
1276
|
-
width: 1200,
|
|
1277
|
-
height: 630
|
|
1278
|
-
} : void 0,
|
|
1279
|
-
wordCount: content.replace(/<[^>]*>/g, "").length,
|
|
1280
|
-
articleSection: "\u6280\u672F\u6587\u6863",
|
|
1281
|
-
inLanguage: "zh-CN"
|
|
1282
|
-
};
|
|
1283
|
-
return `<script type="application/ld+json">
|
|
1284
|
-
${JSON.stringify(jsonLd, null, 2)}
|
|
1285
|
-
</script>`;
|
|
1308
|
+
return Array.from(wordCount.entries()).sort((a, b) => b[1] - a[1]).slice(0, maxKeywords).map(([word]) => word);
|
|
1286
1309
|
}
|
|
1287
1310
|
function generateBreadcrumbJsonLd(breadcrumbs) {
|
|
1288
1311
|
const itemListElement = breadcrumbs.map((crumb, index) => ({
|
|
@@ -1300,10 +1323,20 @@ function generateBreadcrumbJsonLd(breadcrumbs) {
|
|
|
1300
1323
|
${JSON.stringify(jsonLd, null, 2)}
|
|
1301
1324
|
</script>`;
|
|
1302
1325
|
}
|
|
1303
|
-
function
|
|
1326
|
+
function generateSeoContent(data) {
|
|
1327
|
+
const plainText = cleanHtmlContent(data.content);
|
|
1328
|
+
const description = extractDescriptionFromPlainText(plainText);
|
|
1329
|
+
const keywords = simplifiedKeywordExtraction(plainText, 8);
|
|
1330
|
+
const metaTags = generateSeoMetaTagsFromCache(data.doc, data.config, data.pageUrl, { plainText, description, keywords });
|
|
1331
|
+
let jsonLd = generateArticleJsonLdFromCache(data.doc, data.config, data.pageUrl, { plainText, description, keywords });
|
|
1332
|
+
if (data.breadcrumbs && data.breadcrumbs.length > 0) {
|
|
1333
|
+
jsonLd += "\n" + generateBreadcrumbJsonLd(data.breadcrumbs);
|
|
1334
|
+
}
|
|
1335
|
+
return { metaTags, jsonLd };
|
|
1336
|
+
}
|
|
1337
|
+
function generateSeoMetaTagsFromCache(doc, config, pageUrl, cache2) {
|
|
1304
1338
|
const title = doc.Properties?.title || "\u672A\u547D\u540D\u6587\u6863";
|
|
1305
|
-
const description =
|
|
1306
|
-
const keywords = extractKeywords(content);
|
|
1339
|
+
const { description, keywords } = cache2;
|
|
1307
1340
|
let metaTags = "";
|
|
1308
1341
|
metaTags += ` <meta name="description" content="${description.replace(/"/g, """)}" />
|
|
1309
1342
|
`;
|
|
@@ -1353,16 +1386,62 @@ function generateSeoMetaTags(doc, config, pageUrl, content) {
|
|
|
1353
1386
|
`;
|
|
1354
1387
|
return metaTags;
|
|
1355
1388
|
}
|
|
1356
|
-
function
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1389
|
+
function generateArticleJsonLdFromCache(doc, config, pageUrl, cache2) {
|
|
1390
|
+
const title = doc.Properties?.title || "\u672A\u547D\u540D\u6587\u6863";
|
|
1391
|
+
const { description, keywords } = cache2;
|
|
1392
|
+
const datePublished = extractDateFromId(doc.ID);
|
|
1393
|
+
const dateModified = formatDate(doc.Properties?.updated);
|
|
1394
|
+
const jsonLd = {
|
|
1395
|
+
"@context": "https://schema.org",
|
|
1396
|
+
"@type": "Article",
|
|
1397
|
+
headline: title,
|
|
1398
|
+
description,
|
|
1399
|
+
keywords: keywords.join(", "),
|
|
1400
|
+
datePublished,
|
|
1401
|
+
dateModified,
|
|
1402
|
+
author: {
|
|
1403
|
+
"@type": "Person",
|
|
1404
|
+
name: config.sitemap?.title || "\u5D2E\u751F",
|
|
1405
|
+
url: config.sitemap?.siteLink || ""
|
|
1406
|
+
},
|
|
1407
|
+
publisher: {
|
|
1408
|
+
"@type": "Organization",
|
|
1409
|
+
name: config.sitemap?.title || "OceanPress",
|
|
1410
|
+
logo: {
|
|
1411
|
+
"@type": "ImageObject",
|
|
1412
|
+
url: `${config.sitemap?.siteLink || ""}/assets/logo.png`
|
|
1413
|
+
}
|
|
1414
|
+
},
|
|
1415
|
+
mainEntityOfPage: {
|
|
1416
|
+
"@type": "WebPage",
|
|
1417
|
+
"@id": pageUrl
|
|
1418
|
+
},
|
|
1419
|
+
image: doc.Properties?.["title-img"] ? {
|
|
1420
|
+
"@type": "ImageObject",
|
|
1421
|
+
url: `${config.sitemap?.siteLink || ""}${doc.Properties["title-img"].replace("assets", "/assets")}`,
|
|
1422
|
+
width: 1200,
|
|
1423
|
+
height: 630
|
|
1424
|
+
} : void 0,
|
|
1425
|
+
wordCount: cache2.plainText.length,
|
|
1426
|
+
articleSection: "\u6280\u672F\u6587\u6863",
|
|
1427
|
+
inLanguage: "zh-CN"
|
|
1365
1428
|
};
|
|
1429
|
+
return `<script type="application/ld+json">
|
|
1430
|
+
${JSON.stringify(jsonLd, null, 2)}
|
|
1431
|
+
</script>`;
|
|
1432
|
+
}
|
|
1433
|
+
function extractDescriptionFromPlainText(plainText, maxLength = 160) {
|
|
1434
|
+
if (plainText.length <= maxLength) return plainText;
|
|
1435
|
+
const truncated = plainText.substring(0, maxLength);
|
|
1436
|
+
const lastSentenceEnd = Math.max(
|
|
1437
|
+
truncated.lastIndexOf("\u3002"),
|
|
1438
|
+
truncated.lastIndexOf("\uFF01"),
|
|
1439
|
+
truncated.lastIndexOf("\uFF1F"),
|
|
1440
|
+
truncated.lastIndexOf("."),
|
|
1441
|
+
truncated.lastIndexOf("!"),
|
|
1442
|
+
truncated.lastIndexOf("?")
|
|
1443
|
+
);
|
|
1444
|
+
return lastSentenceEnd > maxLength * 0.7 ? truncated.substring(0, lastSentenceEnd + 1) : truncated + "...";
|
|
1366
1445
|
}
|
|
1367
1446
|
|
|
1368
1447
|
// src/core/htmlTemplate.ts
|
|
@@ -2529,7 +2608,8 @@ function build(config, otherConfig) {
|
|
|
2529
2608
|
return config.enableIncrementalCompilation_doc;
|
|
2530
2609
|
})();
|
|
2531
2610
|
effectLog.log(`=== \u5F00\u59CB\u6E32\u67D3\u6587\u6863 ===`);
|
|
2532
|
-
|
|
2611
|
+
const renderStartTime = Date.now();
|
|
2612
|
+
yield* Effect4.all(Object.entries(docTree).map(([path, { sy, docBlock }]) => {
|
|
2533
2613
|
return Effect4.gen(function* () {
|
|
2534
2614
|
if (config.enableIncrementalCompilation && enableIncrementalCompilation_doc && /** 文档本身没有发生变化 */
|
|
2535
2615
|
config.__skipBuilds__[docBlock.id]?.hash === docBlock.hash && /** docBlock所引用的文档也没有更新 */
|
|
@@ -2574,16 +2654,19 @@ function build(config, otherConfig) {
|
|
|
2574
2654
|
[...renderInstance.refs.values()]
|
|
2575
2655
|
)
|
|
2576
2656
|
});
|
|
2577
|
-
effectLog.log(`\u6E32\u67D3\u5B8C\u6BD5:${path}`);
|
|
2578
2657
|
} catch (error) {
|
|
2579
2658
|
effectLog.log(`${path} \u6E32\u67D3\u5931\u8D25:${error}`);
|
|
2580
2659
|
console.log(error);
|
|
2581
2660
|
}
|
|
2582
|
-
|
|
2583
|
-
return `\u6E32\u67D3\u5B8C\u6BD5:${path}`;
|
|
2661
|
+
return;
|
|
2584
2662
|
});
|
|
2663
|
+
}), {
|
|
2664
|
+
/** 这里设置为 6 会比 'unbounded' 更快,猜测是并发太高会导致栈切换不过来. 在我的电脑上测试 6 是一个最佳数值 */
|
|
2665
|
+
concurrency: 6
|
|
2585
2666
|
});
|
|
2586
|
-
|
|
2667
|
+
const renderEndTime = Date.now();
|
|
2668
|
+
const renderDuration = ((renderEndTime - renderStartTime) / 1e3).toFixed(2);
|
|
2669
|
+
effectLog.log(`=== \u6587\u6863\u6E32\u67D3\u5B8C\u6BD5\uFF0C\u5171 ${Object.keys(docTree).length + 1}\u7BC7\uFF0C\u8017\u65F6: ${renderDuration}\u79D2 ===`);
|
|
2587
2670
|
effectLog.log(`=== \u5F00\u59CB\u751F\u6210 sitemap.xml ===`);
|
|
2588
2671
|
if (config.sitemap.enable) {
|
|
2589
2672
|
fileTree["sitemap.xml"] = sitemap_xml(Doc_blocks, config.sitemap);
|
|
@@ -2646,8 +2729,11 @@ function build(config, otherConfig) {
|
|
|
2646
2729
|
fileTree[renderDocTreeJsPath] = yield* renderDocTree();
|
|
2647
2730
|
effectLog.log(`=== \u6587\u6863\u6811\u751F\u6210\u5B8C\u6210 ===`);
|
|
2648
2731
|
}
|
|
2732
|
+
if (otherConfig?.beforeFileTree) {
|
|
2733
|
+
yield* Effect4.tryPromise(() => Promise.resolve(otherConfig.beforeFileTree(fileTree, effectLog)));
|
|
2734
|
+
}
|
|
2649
2735
|
if (otherConfig?.onFileTree) {
|
|
2650
|
-
otherConfig.onFileTree(fileTree, effectLog);
|
|
2736
|
+
yield* Effect4.tryPromise(() => Promise.resolve(otherConfig.onFileTree(fileTree, effectLog)));
|
|
2651
2737
|
}
|
|
2652
2738
|
if (config.compressedZip) {
|
|
2653
2739
|
effectLog.log(`=== \u5F00\u59CB\u751F\u6210\u538B\u7F29\u5305 ===`);
|
|
@@ -2779,6 +2865,11 @@ var OceanPress = class {
|
|
|
2779
2865
|
deployOceanPressServer_plugin(this.config)
|
|
2780
2866
|
);
|
|
2781
2867
|
}
|
|
2868
|
+
if (config.markdownMirror?.enable) {
|
|
2869
|
+
this.pluginCenter.registerPlugin(
|
|
2870
|
+
new MarkdownMirrorPlugin(config.markdownMirror)
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2782
2873
|
}
|
|
2783
2874
|
build() {
|
|
2784
2875
|
const build_res = this.pluginCenter.fun.build(this.config, {
|
|
@@ -2859,15 +2950,22 @@ var nodeApiDep = {
|
|
|
2859
2950
|
// src/cli/common.ts
|
|
2860
2951
|
import { Command } from "commander";
|
|
2861
2952
|
var program = new Command();
|
|
2862
|
-
program.name("OceanPress").description("\u8FD9\u662F\u4E00\u6B3E\u4ECE\u601D\u6E90\u7B14\u8BB0\u672C\u751F\u6210\u4E00\u4E2A\u9759\u6001\u7AD9\u70B9\u7684\u5DE5\u5177");
|
|
2953
|
+
program.name("OceanPress").description("\u8FD9\u662F\u4E00\u6B3E\u4ECE\u601D\u6E90\u7B14\u8BB0\u672C\u751F\u6210\u4E00\u4E2A\u9759\u6001\u7AD9\u70B9\u7684\u5DE5\u5177").version(package_default.version);
|
|
2863
2954
|
|
|
2864
2955
|
// src/cli/deploy.ts
|
|
2865
2956
|
import { Context as Context3, Effect as Effect5 } from "effect";
|
|
2866
2957
|
program.command("deploy").description("\u90E8\u7F72\u7AD9\u70B9").option("-c, --config <string>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u7684\u4F4D\u7F6E").option("-h, --apiBase <string>", "OceanPress server \u5730\u5740").option("-k, --apiKey <string>", "OceanPress server Api \u5BC6\u94A5").action(async (opt) => {
|
|
2867
2958
|
if (!opt.apiBase || !opt.apiKey) {
|
|
2868
|
-
|
|
2959
|
+
console.error(`\u8BF7\u914D\u7F6E apiBase \u548C apiKey`);
|
|
2960
|
+
throw new Error("\u8BF7\u914D\u7F6E apiBase \u548C apiKey");
|
|
2961
|
+
}
|
|
2962
|
+
let config;
|
|
2963
|
+
try {
|
|
2964
|
+
config = await readFile(opt.config, "utf-8");
|
|
2965
|
+
} catch (error) {
|
|
2966
|
+
console.error("\u914D\u7F6E\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25:", error);
|
|
2967
|
+
throw new Error("\u914D\u7F6E\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25");
|
|
2869
2968
|
}
|
|
2870
|
-
const config = await readFile(opt.config, "utf-8");
|
|
2871
2969
|
const client = await createRPC("apiConsumer", {
|
|
2872
2970
|
remoteCall(method, data) {
|
|
2873
2971
|
let body;
|
|
@@ -2877,7 +2975,9 @@ program.command("deploy").description("\u90E8\u7F72\u7AD9\u70B9").option("-c, --
|
|
|
2877
2975
|
content_type = "application/octet-stream";
|
|
2878
2976
|
} else {
|
|
2879
2977
|
body = stringify2(data);
|
|
2880
|
-
|
|
2978
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2979
|
+
console.log("[body]", body);
|
|
2980
|
+
}
|
|
2881
2981
|
content_type = "application/json";
|
|
2882
2982
|
}
|
|
2883
2983
|
return fetch(`${opt.apiBase}/api/${method}`, {
|
|
@@ -2893,7 +2993,7 @@ program.command("deploy").description("\u90E8\u7F72\u7AD9\u70B9").option("-c, --
|
|
|
2893
2993
|
}).then((res) => res.json()).then((r) => {
|
|
2894
2994
|
if (r.error) {
|
|
2895
2995
|
console.log("[r]", r);
|
|
2896
|
-
throw new Error();
|
|
2996
|
+
throw new Error(r.error.message || "API\u8BF7\u6C42\u5931\u8D25");
|
|
2897
2997
|
}
|
|
2898
2998
|
return r.result;
|
|
2899
2999
|
});
|
|
@@ -2919,7 +3019,14 @@ ${msg}`);
|
|
|
2919
3019
|
);
|
|
2920
3020
|
const p = Effect5.provide(
|
|
2921
3021
|
Effect5.gen(function* () {
|
|
2922
|
-
|
|
3022
|
+
let parsedConfig;
|
|
3023
|
+
try {
|
|
3024
|
+
parsedConfig = JSON.parse(config);
|
|
3025
|
+
} catch (error) {
|
|
3026
|
+
console.error("\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25:", error);
|
|
3027
|
+
throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF");
|
|
3028
|
+
}
|
|
3029
|
+
yield* loadConfigFile(parsedConfig);
|
|
2923
3030
|
const ocean_press = new OceanPress(currentConfig.value);
|
|
2924
3031
|
ocean_press.pluginCenter.registerPlugin({
|
|
2925
3032
|
async build_onFileTree([tree], next) {
|
|
@@ -2947,16 +3054,36 @@ import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
|
2947
3054
|
import { resolve } from "path";
|
|
2948
3055
|
import { join } from "path/posix";
|
|
2949
3056
|
import { Context as Context4, Effect as Effect6 } from "effect";
|
|
2950
|
-
program.command("build").description("\u8F93\u51FA\u9759\u6001\u7AD9\u70B9\u6E90\u7801").option("-c, --config <string>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u7684\u4F4D\u7F6E").option("-o, --output <string>", "\u6307\u5B9A\u8F93\u51FA\u76EE\u5F55\u4F4D\u7F6E").action(async (opt) => {
|
|
3057
|
+
program.command("build").description("\u8F93\u51FA\u9759\u6001\u7AD9\u70B9\u6E90\u7801").option("-c, --config <string>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u7684\u4F4D\u7F6E").option("-o, --output <string>", "\u6307\u5B9A\u8F93\u51FA\u76EE\u5F55\u4F4D\u7F6E").option("--md", "\u542F\u7528 Markdown \u955C\u50CF\u5BFC\u51FA\u6A21\u5F0F").option("--watch", "\u542F\u7528\u76D1\u542C\u6A21\u5F0F\uFF0C\u5B9A\u65F6\u81EA\u52A8\u91CD\u65B0\u6784\u5EFA").option("--watch-interval <number>", "\u76D1\u542C\u6A21\u5F0F\u4E0B\u7684\u540C\u6B65\u95F4\u9694\uFF08\u79D2\uFF09", "60").action(async (opt) => {
|
|
2951
3058
|
if (!opt.config || !opt.output) {
|
|
2952
3059
|
console.log(`\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E\u548C\u8F93\u51FA\u76EE\u5F55\u4F4D\u7F6E`);
|
|
2953
3060
|
throw new Error("\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E\u548C\u8F93\u51FA\u76EE\u5F55\u4F4D\u7F6E");
|
|
2954
3061
|
}
|
|
2955
3062
|
const config = await readFile2(opt.config, "utf-8");
|
|
2956
3063
|
const filePath = resolve(opt.output);
|
|
3064
|
+
let parsedConfig;
|
|
3065
|
+
try {
|
|
3066
|
+
parsedConfig = JSON.parse(config);
|
|
3067
|
+
} catch (error) {
|
|
3068
|
+
console.error("\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25:", error);
|
|
3069
|
+
throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF");
|
|
3070
|
+
}
|
|
3071
|
+
if (opt.md) {
|
|
3072
|
+
const currentProfileName = parsedConfig.__current__;
|
|
3073
|
+
const currentProfile = parsedConfig[currentProfileName];
|
|
3074
|
+
if (currentProfile) {
|
|
3075
|
+
currentProfile.markdownMirror = {
|
|
3076
|
+
enable: true,
|
|
3077
|
+
// --md 参数强制启用
|
|
3078
|
+
includeAssets: currentProfile.markdownMirror?.includeAssets ?? false
|
|
3079
|
+
// 默认不同步资源
|
|
3080
|
+
};
|
|
3081
|
+
console.log("\u2705 \u5DF2\u542F\u7528 Markdown \u955C\u50CF\u5BFC\u51FA\u6A21\u5F0F\n");
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
2957
3084
|
await Effect6.runPromise(
|
|
2958
3085
|
Effect6.provideService(
|
|
2959
|
-
loadConfigFile(
|
|
3086
|
+
loadConfigFile(parsedConfig),
|
|
2960
3087
|
EffectLocalStorageDep,
|
|
2961
3088
|
nodeApiDep
|
|
2962
3089
|
)
|
|
@@ -2979,35 +3106,62 @@ ${msg}`);
|
|
|
2979
3106
|
}
|
|
2980
3107
|
})
|
|
2981
3108
|
);
|
|
2982
|
-
const
|
|
2983
|
-
Effect6.
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
const
|
|
2989
|
-
const
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
await
|
|
2995
|
-
}
|
|
2996
|
-
|
|
3109
|
+
const runBuild = async () => {
|
|
3110
|
+
const p = Effect6.provide(
|
|
3111
|
+
Effect6.gen(function* () {
|
|
3112
|
+
const ocean_press = new OceanPress(currentConfig.value);
|
|
3113
|
+
ocean_press.pluginCenter.registerPlugin({
|
|
3114
|
+
async build_onFileTree([tree]) {
|
|
3115
|
+
const dirPromises = /* @__PURE__ */ new Set();
|
|
3116
|
+
for (const [path, data] of Object.entries(tree)) {
|
|
3117
|
+
const fullPath = join(filePath, path);
|
|
3118
|
+
const dirPath = resolve(fullPath, "..");
|
|
3119
|
+
if (!dirPromises.has(dirPath)) {
|
|
3120
|
+
dirPromises.add(dirPath);
|
|
3121
|
+
await mkdir(dirPath, { recursive: true });
|
|
3122
|
+
}
|
|
3123
|
+
try {
|
|
3124
|
+
if (typeof data === "string") {
|
|
3125
|
+
await writeFile(fullPath, data, "utf-8");
|
|
3126
|
+
} else {
|
|
3127
|
+
await writeFile(fullPath, new DataView(data));
|
|
3128
|
+
}
|
|
3129
|
+
} catch (error) {
|
|
3130
|
+
console.error(`${fullPath} \u65E0\u6CD5\u5199\u5165:`, error);
|
|
2997
3131
|
}
|
|
2998
|
-
} catch (error) {
|
|
2999
|
-
console.log(`${fullPath} \u65E0\u6CD5\u5199\u5165`);
|
|
3000
3132
|
}
|
|
3001
3133
|
}
|
|
3002
|
-
}
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3134
|
+
});
|
|
3135
|
+
console.log("[config.__current__]", parsedConfig.__current__);
|
|
3136
|
+
console.log("[currentConfig.value.name]", currentConfig.value.name);
|
|
3137
|
+
return yield* ocean_press.build();
|
|
3138
|
+
}),
|
|
3139
|
+
context
|
|
3140
|
+
);
|
|
3141
|
+
await Effect6.runPromise(p);
|
|
3142
|
+
};
|
|
3143
|
+
if (opt.watch) {
|
|
3144
|
+
const intervalSeconds = parseInt(opt.watchInterval || "60", 10);
|
|
3145
|
+
const intervalMs = intervalSeconds * 1e3;
|
|
3146
|
+
console.log(`
|
|
3147
|
+
\u{1F504} \u76D1\u542C\u6A21\u5F0F\u5DF2\u542F\u52A8\uFF0C\u6BCF ${intervalSeconds} \u79D2\u81EA\u52A8\u6784\u5EFA\u4E00\u6B21
|
|
3148
|
+
`);
|
|
3149
|
+
await runBuild();
|
|
3150
|
+
setInterval(async () => {
|
|
3151
|
+
console.log("\n\u23F0 \u5B9A\u65F6\u4EFB\u52A1\u89E6\u53D1\uFF0C\u5F00\u59CB\u6784\u5EFA...");
|
|
3152
|
+
try {
|
|
3153
|
+
await runBuild();
|
|
3154
|
+
const nextTime = new Date(Date.now() + intervalMs).toLocaleTimeString();
|
|
3155
|
+
console.log(`
|
|
3156
|
+
\u23F0 \u4E0B\u6B21\u6784\u5EFA: ${nextTime}
|
|
3157
|
+
`);
|
|
3158
|
+
} catch (error) {
|
|
3159
|
+
console.error("\u274C \u6784\u5EFA\u5931\u8D25:", error);
|
|
3160
|
+
}
|
|
3161
|
+
}, intervalMs);
|
|
3162
|
+
} else {
|
|
3163
|
+
await runBuild();
|
|
3164
|
+
}
|
|
3011
3165
|
});
|
|
3012
3166
|
|
|
3013
3167
|
// src/cli/server.ts
|
|
@@ -3135,6 +3289,13 @@ function server(config = { port: 80, hostname: "0.0.0.0" }) {
|
|
|
3135
3289
|
|
|
3136
3290
|
// src/cli/server.ts
|
|
3137
3291
|
import { Context as Context6, Effect as Effect9 } from "effect";
|
|
3292
|
+
function validatePort(port) {
|
|
3293
|
+
const portNum = Number(port);
|
|
3294
|
+
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
3295
|
+
throw new Error(`\u7AEF\u53E3\u53F7\u5FC5\u987B\u5728 1-65535 \u4E4B\u95F4\uFF0C\u5F53\u524D\u503C: ${port}`);
|
|
3296
|
+
}
|
|
3297
|
+
return portNum;
|
|
3298
|
+
}
|
|
3138
3299
|
program.command("server").description("\u542F\u52A8\u52A8\u6001\u4EE3\u7406").option("-c, --config <string>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u7684\u4F4D\u7F6E").option("-h, --host <string>", "web\u670D\u52A1\u7ED1\u5B9A\u5230\u7684\u5730\u5740", "127.0.0.1").option("-p, --port <number>", "web\u670D\u52A1\u7ED1\u5B9A\u5230\u7684\u7AEF\u53E3", "80").option(
|
|
3139
3300
|
"--cache <boolean>",
|
|
3140
3301
|
"\u914D\u7F6E\u4E3A true \u65F6\u5F00\u542F\u7F13\u5B58,\u9ED8\u8BA4\u4E3A false \u4E0D\u5F00\u542F\u7F13\u5B58",
|
|
@@ -3143,8 +3304,15 @@ program.command("server").description("\u542F\u52A8\u52A8\u6001\u4EE3\u7406").op
|
|
|
3143
3304
|
async (opt) => {
|
|
3144
3305
|
if (!opt.config) {
|
|
3145
3306
|
console.log(`\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E`);
|
|
3307
|
+
throw new Error("\u8BF7\u8BBE\u7F6E\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E");
|
|
3308
|
+
}
|
|
3309
|
+
let config;
|
|
3310
|
+
try {
|
|
3311
|
+
config = await readFile3(opt.config, "utf-8");
|
|
3312
|
+
} catch (error) {
|
|
3313
|
+
console.error("\u914D\u7F6E\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25:", error);
|
|
3314
|
+
throw new Error("\u914D\u7F6E\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25");
|
|
3146
3315
|
}
|
|
3147
|
-
const config = await readFile3(opt.config, "utf-8");
|
|
3148
3316
|
setCache(opt.cache !== "false");
|
|
3149
3317
|
const context = Context6.empty().pipe(
|
|
3150
3318
|
Context6.add(EffectRender, renderApiDep),
|
|
@@ -3165,11 +3333,18 @@ ${msg}`);
|
|
|
3165
3333
|
);
|
|
3166
3334
|
const p = Effect9.provide(
|
|
3167
3335
|
Effect9.gen(function* () {
|
|
3168
|
-
|
|
3336
|
+
let parsedConfig;
|
|
3337
|
+
try {
|
|
3338
|
+
parsedConfig = JSON.parse(config);
|
|
3339
|
+
} catch (error) {
|
|
3340
|
+
console.error("\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25:", error);
|
|
3341
|
+
throw new Error("\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF");
|
|
3342
|
+
}
|
|
3343
|
+
yield* loadConfigFile(parsedConfig);
|
|
3169
3344
|
tempConfig.cdn.siyuanPrefix = "/notebook/";
|
|
3170
3345
|
return yield* server({
|
|
3171
3346
|
hostname: opt.host,
|
|
3172
|
-
port:
|
|
3347
|
+
port: validatePort(opt.port)
|
|
3173
3348
|
});
|
|
3174
3349
|
}),
|
|
3175
3350
|
context
|
|
@@ -3179,5 +3354,13 @@ ${msg}`);
|
|
|
3179
3354
|
);
|
|
3180
3355
|
|
|
3181
3356
|
// src/cli.ts
|
|
3357
|
+
process.on("uncaughtException", (error) => {
|
|
3358
|
+
console.error("\u672A\u6355\u83B7\u7684\u5F02\u5E38:", error);
|
|
3359
|
+
process.exit(1);
|
|
3360
|
+
});
|
|
3361
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
3362
|
+
console.error("\u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:", reason);
|
|
3363
|
+
process.exit(1);
|
|
3364
|
+
});
|
|
3182
3365
|
program.parse(process.argv);
|
|
3183
3366
|
//# sourceMappingURL=cli.js.map
|