@tnotesjs/core 0.1.0 → 0.1.1
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.
Potentially problematic release.
This version of @tnotesjs/core might be problematic. Click here for more details.
- package/dist/chunk-NYLJBCWL.js +207 -0
- package/dist/cli/index.js +1635 -347
- package/dist/index.js +1 -1
- package/package.json +74 -74
- package/vitepress/assets/icons/icon__mindmap.svg +1 -1
- package/vitepress/assets/icons/icon__search.svg +1 -1
- package/vitepress/components/SidebarCard/MindMapView.vue +483 -483
- package/vitepress/components/SidebarCard/NotesTrendChart.vue +108 -108
- package/dist/chunk-K3X5OP3N.js +0 -1532
- package/dist/cli/index.d.ts +0 -2
- package/dist/index.d.ts +0 -138
package/dist/cli/index.js
CHANGED
|
@@ -1,44 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
CONSTANTS,
|
|
4
3
|
ConfigManager,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Logger,
|
|
8
|
-
NOTES_DIR_PATH,
|
|
9
|
-
NOTES_PATH,
|
|
10
|
-
NoteManager,
|
|
11
|
-
REPO_NOTES_URL,
|
|
12
|
-
ROOT_CONFIG_PATH,
|
|
13
|
-
ROOT_DIR_PATH,
|
|
14
|
-
ROOT_README_PATH,
|
|
15
|
-
TNOTES_BASE_DIR,
|
|
16
|
-
TNOTES_CORE_DIR,
|
|
17
|
-
VP_SIDEBAR_PATH,
|
|
18
|
-
buildNoteLineMarkdown,
|
|
19
|
-
createAddNumberToTitle,
|
|
20
|
-
createError,
|
|
21
|
-
createLogger,
|
|
22
|
-
ensureDirectory,
|
|
23
|
-
genHierarchicalSidebar,
|
|
24
|
-
generateToc,
|
|
25
|
-
getChangedIds,
|
|
26
|
-
getTargetDirs,
|
|
27
|
-
handleError,
|
|
28
|
-
isPortInUse,
|
|
29
|
-
killPortProcess,
|
|
30
|
-
logger,
|
|
31
|
-
parseArgs,
|
|
32
|
-
parseNoteLine,
|
|
33
|
-
parseReadmeCompletedNotes,
|
|
34
|
-
processEmptyLines,
|
|
35
|
-
pullAllRepos,
|
|
36
|
-
pushAllRepos,
|
|
37
|
-
runCommand,
|
|
38
|
-
syncAllRepos,
|
|
39
|
-
validateNoteTitle,
|
|
40
|
-
waitForPort
|
|
41
|
-
} from "../chunk-K3X5OP3N.js";
|
|
4
|
+
getConfigManager
|
|
5
|
+
} from "../chunk-NYLJBCWL.js";
|
|
42
6
|
|
|
43
7
|
// commands/models.ts
|
|
44
8
|
var COMMAND_NAMES = {
|
|
@@ -67,7 +31,7 @@ var COMMAND_DESCRIPTIONS = {
|
|
|
67
31
|
[COMMAND_NAMES.PUSH]: "\u5C06\u77E5\u8BC6\u5E93\u63A8\u9001\u5230 GitHub (\u4F7F\u7528 --all \u63A8\u9001\u6240\u6709\u77E5\u8BC6\u5E93)",
|
|
68
32
|
[COMMAND_NAMES.PULL]: "\u5C06 GitHub \u7684\u77E5\u8BC6\u5E93\u62C9\u4E0B\u6765 (\u4F7F\u7528 --all \u62C9\u53D6\u6240\u6709\u77E5\u8BC6\u5E93)",
|
|
69
33
|
[COMMAND_NAMES.SYNC]: "\u540C\u6B65\u672C\u5730\u548C\u8FDC\u7A0B\u7684\u77E5\u8BC6\u5E93\u72B6\u6001 (\u4F7F\u7528 --all \u540C\u6B65\u6240\u6709\u77E5\u8BC6\u5E93)",
|
|
70
|
-
[COMMAND_NAMES.SYNC_CORE]: "\u540C\u6B65\u6240\u6709\u5144\u5F1F\u77E5\u8BC6\u5E93\u7684
|
|
34
|
+
[COMMAND_NAMES.SYNC_CORE]: "\u540C\u6B65\u6240\u6709\u5144\u5F1F\u77E5\u8BC6\u5E93\u7684 tnotesjs/core \u5230\u6700\u65B0\u7248\u672C",
|
|
71
35
|
[COMMAND_NAMES.FIX_TIMESTAMPS]: "\u4FEE\u590D\u6240\u6709\u7B14\u8BB0\u7684\u65F6\u95F4\u6233\uFF08\u57FA\u4E8E git \u5386\u53F2\uFF09",
|
|
72
36
|
[COMMAND_NAMES.UPDATE_NOTE_CONFIG]: "\u66F4\u65B0\u7B14\u8BB0\u914D\u7F6E\u6587\u4EF6",
|
|
73
37
|
[COMMAND_NAMES.RENAME_NOTE]: "\u91CD\u547D\u540D\u7B14\u8BB0",
|
|
@@ -79,13 +43,1302 @@ var COMMAND_OPTIONS = {
|
|
|
79
43
|
FORCE: "force"
|
|
80
44
|
};
|
|
81
45
|
|
|
46
|
+
// utils/errorHandler.ts
|
|
47
|
+
var TNotesError = class _TNotesError extends Error {
|
|
48
|
+
constructor(message, code = "UNKNOWN" /* UNKNOWN */, context) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.code = code;
|
|
51
|
+
this.context = context;
|
|
52
|
+
this.name = "TNotesError";
|
|
53
|
+
if (Error.captureStackTrace) {
|
|
54
|
+
Error.captureStackTrace(this, _TNotesError);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
function handleError(error, exitOnError = false) {
|
|
59
|
+
if (error instanceof TNotesError) {
|
|
60
|
+
console.error(`\u274C TNotesError`);
|
|
61
|
+
console.error(`\u9519\u8BEF\u7801\uFF1A${error.code}`);
|
|
62
|
+
console.error(`\u9519\u8BEF\u4FE1\u606F\uFF1A${error.message}`);
|
|
63
|
+
if (error.context && Object.keys(error.context).length > 0) {
|
|
64
|
+
console.error("\u9519\u8BEF\u4E0A\u4E0B\u6587\u4FE1\u606F\uFF1A", error.context);
|
|
65
|
+
}
|
|
66
|
+
if (error.stack && process.env.DEBUG) {
|
|
67
|
+
console.error("\u9519\u8BEF\u5806\u6808\u4FE1\u606F\uFF1A", error.stack);
|
|
68
|
+
}
|
|
69
|
+
} else if (error instanceof Error) {
|
|
70
|
+
console.error(`\u274C Error`);
|
|
71
|
+
console.error(`\u9519\u8BEF\u4FE1\u606F\uFF1A${error.message}`);
|
|
72
|
+
if (error.stack && process.env.DEBUG) {
|
|
73
|
+
console.error("\u9519\u8BEF\u5806\u6808\u4FE1\u606F\uFF1A", error.stack);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
console.error("\u274C \u672A\u77E5\u9519\u8BEF\uFF1A", error);
|
|
77
|
+
}
|
|
78
|
+
if (exitOnError) {
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
var createError = {
|
|
83
|
+
fileNotFound: (path2) => new TNotesError(`\u6587\u4EF6\u672A\u627E\u5230\uFF1A${path2}`, "FILE_NOT_FOUND" /* FILE_NOT_FOUND */, {
|
|
84
|
+
path: path2
|
|
85
|
+
}),
|
|
86
|
+
fileReadError: (path2, originalError) => new TNotesError(`\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25\uFF1A${path2}`, "FILE_READ_ERROR" /* FILE_READ_ERROR */, {
|
|
87
|
+
path: path2,
|
|
88
|
+
originalError: originalError?.message
|
|
89
|
+
}),
|
|
90
|
+
fileWriteError: (path2, originalError) => new TNotesError(`\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A${path2}`, "FILE_WRITE_ERROR" /* FILE_WRITE_ERROR */, {
|
|
91
|
+
path: path2,
|
|
92
|
+
originalError: originalError?.message
|
|
93
|
+
}),
|
|
94
|
+
gitNotRepo: (dir) => new TNotesError(`\u4E0D\u662F\u4E00\u4E2A Git \u4ED3\u5E93\uFF1A${dir}`, "GIT_NOT_REPO" /* GIT_NOT_REPO */, {
|
|
95
|
+
dir
|
|
96
|
+
}),
|
|
97
|
+
gitCommandFailed: (command, dir, originalError) => new TNotesError(`Git \u547D\u4EE4\u5931\u8D25\uFF1A${command}`, "GIT_COMMAND_FAILED" /* GIT_COMMAND_FAILED */, {
|
|
98
|
+
command,
|
|
99
|
+
dir,
|
|
100
|
+
originalError: originalError?.message
|
|
101
|
+
}),
|
|
102
|
+
noteIndexInvalid: (noteIndex) => new TNotesError(
|
|
103
|
+
`\u65E0\u6548\u7684\u7B14\u8BB0\u7D22\u5F15\uFF1A${noteIndex}`,
|
|
104
|
+
"NOTE_INDEX_INVALID" /* NOTE_INDEX_INVALID */,
|
|
105
|
+
{
|
|
106
|
+
noteIndex
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
noteConfigInvalid: (notePath, reason) => new TNotesError(
|
|
110
|
+
`\u65E0\u6548\u7684\u7B14\u8BB0\u914D\u7F6E\uFF1A${notePath}`,
|
|
111
|
+
"NOTE_CONFIG_INVALID" /* NOTE_CONFIG_INVALID */,
|
|
112
|
+
{ notePath, reason }
|
|
113
|
+
),
|
|
114
|
+
configInvalid: (field, reason) => new TNotesError(`\u65E0\u6548\u7684\u914D\u7F6E\u5B57\u6BB5\uFF1A${field}`, "CONFIG_INVALID" /* CONFIG_INVALID */, {
|
|
115
|
+
field,
|
|
116
|
+
reason
|
|
117
|
+
}),
|
|
118
|
+
commandNotFound: (commandName) => new TNotesError(`\u672A\u627E\u5230\u547D\u4EE4\uFF1A${commandName}`, "COMMAND_NOT_FOUND" /* COMMAND_NOT_FOUND */, {
|
|
119
|
+
commandName
|
|
120
|
+
}),
|
|
121
|
+
commandFailed: (commandName, exitCode, originalError) => new TNotesError(`\u547D\u4EE4\u6267\u884C\u5931\u8D25\uFF1A${commandName}`, "COMMAND_FAILED" /* COMMAND_FAILED */, {
|
|
122
|
+
commandName,
|
|
123
|
+
exitCode,
|
|
124
|
+
originalError: originalError?.message
|
|
125
|
+
}),
|
|
126
|
+
serverStartFailed: (port2, originalError) => new TNotesError(
|
|
127
|
+
`\u542F\u52A8\u670D\u52A1\u5668\u5931\u8D25\uFF1A\u7AEF\u53E3 ${port2}`,
|
|
128
|
+
"SERVER_START_FAILED" /* SERVER_START_FAILED */,
|
|
129
|
+
{ port: port2, originalError: originalError?.message }
|
|
130
|
+
),
|
|
131
|
+
portInUse: (port2) => new TNotesError(`\u7AEF\u53E3 ${port2} \u5DF2\u88AB\u5360\u7528`, "PORT_IN_USE" /* PORT_IN_USE */, {
|
|
132
|
+
port: port2
|
|
133
|
+
})
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// utils/file.ts
|
|
137
|
+
import fs from "fs";
|
|
138
|
+
async function ensureDirectory(dir) {
|
|
139
|
+
if (!fs.existsSync(dir)) {
|
|
140
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// utils/generateAnchor.ts
|
|
145
|
+
import GithubSlugger from "github-slugger";
|
|
146
|
+
var slugger = new GithubSlugger();
|
|
147
|
+
var generateAnchor = (label) => {
|
|
148
|
+
slugger.reset();
|
|
149
|
+
return slugger.slug(label);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// utils/genHierarchicalSidebar.ts
|
|
153
|
+
var genHierarchicalSidebar = (itemList, titles, titlesNotesCount, sidebarIsCollapsed) => {
|
|
154
|
+
const stack = [];
|
|
155
|
+
const root = [];
|
|
156
|
+
titles.forEach((title, i) => {
|
|
157
|
+
const match = title.match(/^#+/);
|
|
158
|
+
if (!match) return;
|
|
159
|
+
const level = match[0].length;
|
|
160
|
+
const text = title.replace(/^#+\s*/, "");
|
|
161
|
+
const noteItems = itemList.splice(0, titlesNotesCount[i]);
|
|
162
|
+
const node = {
|
|
163
|
+
text,
|
|
164
|
+
collapsed: sidebarIsCollapsed,
|
|
165
|
+
items: noteItems.length > 0 ? noteItems : []
|
|
166
|
+
};
|
|
167
|
+
if (i === 0 && level === 1) return;
|
|
168
|
+
while (stack.length > 0 && stack[stack.length - 1].level >= level) {
|
|
169
|
+
stack.pop();
|
|
170
|
+
}
|
|
171
|
+
if (stack.length === 0) {
|
|
172
|
+
root.push(node);
|
|
173
|
+
} else {
|
|
174
|
+
const parent = stack[stack.length - 1].node;
|
|
175
|
+
if (!parent.items) parent.items = [];
|
|
176
|
+
parent.items.push(node);
|
|
177
|
+
}
|
|
178
|
+
stack.push({ level, node });
|
|
179
|
+
});
|
|
180
|
+
return root;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// utils/getChangedIds.ts
|
|
184
|
+
import path from "path";
|
|
185
|
+
import { execSync } from "child_process";
|
|
186
|
+
function getChangedIds() {
|
|
187
|
+
const changedFiles = execSync(
|
|
188
|
+
`git diff --name-only HEAD -- "notes/[0-9][0-9][0-9][0-9]*/README.md"`
|
|
189
|
+
// 根据当前仓库状态和最近一次提交之间的比较
|
|
190
|
+
).toString().split(/\r?\n/).filter(Boolean).map((fp) => fp.replace(/^"|"$/g, "")).map((fp) => fp.split("/").join(path.sep));
|
|
191
|
+
const changedIds = changedFiles.map((fp) => {
|
|
192
|
+
const parts = fp.split(path.sep);
|
|
193
|
+
const dirName = parts.find((p, i) => parts[i - 1] === "notes");
|
|
194
|
+
return dirName?.slice(0, 4);
|
|
195
|
+
}).filter((id) => Boolean(id));
|
|
196
|
+
return new Set(changedIds);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// utils/getTargetDirs.ts
|
|
200
|
+
import { readdirSync } from "fs";
|
|
201
|
+
import { join } from "path";
|
|
202
|
+
var getTargetDirs = (baseDir, prefix, excludeDirs = []) => {
|
|
203
|
+
try {
|
|
204
|
+
const entries = readdirSync(baseDir, { withFileTypes: true });
|
|
205
|
+
const targetDirs = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(prefix)).map((entry) => join(baseDir, entry.name)).filter((dir) => !excludeDirs.includes(dir));
|
|
206
|
+
return targetDirs;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
209
|
+
console.error(`\u8BFB\u53D6\u76EE\u5F55 ${baseDir} \u65F6\u51FA\u9519\uFF1A${errorMessage}`);
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// utils/logger.ts
|
|
215
|
+
var Logger = class _Logger {
|
|
216
|
+
level;
|
|
217
|
+
prefix;
|
|
218
|
+
timestamp;
|
|
219
|
+
colors;
|
|
220
|
+
constructor(config2 = {}) {
|
|
221
|
+
this.level = config2.level ?? 1 /* INFO */;
|
|
222
|
+
this.prefix = config2.prefix ?? "";
|
|
223
|
+
this.timestamp = config2.timestamp ?? false;
|
|
224
|
+
this.colors = config2.colors ?? true;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* 设置日志级别
|
|
228
|
+
*/
|
|
229
|
+
setLevel(level) {
|
|
230
|
+
this.level = level;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* 获取当前时间戳(精确到毫秒)
|
|
234
|
+
*/
|
|
235
|
+
getTimestamp() {
|
|
236
|
+
if (!this.timestamp) return "";
|
|
237
|
+
const now = /* @__PURE__ */ new Date();
|
|
238
|
+
const hours = String(now.getHours()).padStart(2, "0");
|
|
239
|
+
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
240
|
+
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
241
|
+
const milliseconds = String(now.getMilliseconds()).padStart(3, "0");
|
|
242
|
+
return `[${hours}:${minutes}:${seconds}.${milliseconds}] `;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* 格式化日志消息
|
|
246
|
+
*/
|
|
247
|
+
formatMessage(message) {
|
|
248
|
+
const timestamp = this.getTimestamp();
|
|
249
|
+
const prefix = this.prefix ? `[${this.prefix}] ` : "";
|
|
250
|
+
return `${timestamp}${prefix}${message}`;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* DEBUG 级别日志
|
|
254
|
+
*/
|
|
255
|
+
debug(message, ...args) {
|
|
256
|
+
if (this.level <= 0 /* DEBUG */) {
|
|
257
|
+
console.log(`\u{1F41B} ${this.formatMessage(message)}`, ...args);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* INFO 级别日志
|
|
262
|
+
*/
|
|
263
|
+
info(message, ...args) {
|
|
264
|
+
if (this.level <= 1 /* INFO */) {
|
|
265
|
+
console.log(`\u2139\uFE0F ${this.formatMessage(message)}`, ...args);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* 成功日志(特殊的 INFO 级别)
|
|
270
|
+
*/
|
|
271
|
+
success(message, ...args) {
|
|
272
|
+
if (this.level <= 1 /* INFO */) {
|
|
273
|
+
console.log(`\u2705 ${this.formatMessage(message)}`, ...args);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* 警告日志
|
|
278
|
+
*/
|
|
279
|
+
warn(message, ...args) {
|
|
280
|
+
if (this.level <= 2 /* WARN */) {
|
|
281
|
+
console.warn(`\u26A0\uFE0F ${this.formatMessage(message)}`, ...args);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* 错误日志
|
|
286
|
+
*/
|
|
287
|
+
error(message, ...args) {
|
|
288
|
+
if (this.level <= 3 /* ERROR */) {
|
|
289
|
+
console.error(`\u274C ${this.formatMessage(message)}`, ...args);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* 进度日志
|
|
294
|
+
*/
|
|
295
|
+
progress(message, ...args) {
|
|
296
|
+
if (this.level <= 1 /* INFO */) {
|
|
297
|
+
console.log(`\u23F3 ${this.formatMessage(message)}`, ...args);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* 启动日志
|
|
302
|
+
*/
|
|
303
|
+
start(message, ...args) {
|
|
304
|
+
if (this.level <= 1 /* INFO */) {
|
|
305
|
+
console.log(`\u{1F680} ${this.formatMessage(message)}`, ...args);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* 停止日志
|
|
310
|
+
*/
|
|
311
|
+
stop(message, ...args) {
|
|
312
|
+
if (this.level <= 1 /* INFO */) {
|
|
313
|
+
console.log(`\u{1F6D1} ${this.formatMessage(message)}`, ...args);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* 完成日志
|
|
318
|
+
*/
|
|
319
|
+
done(message, duration, ...args) {
|
|
320
|
+
if (this.level <= 1 /* INFO */) {
|
|
321
|
+
const durationStr = duration ? ` (${duration}ms)` : "";
|
|
322
|
+
console.log(`\u2728 ${this.formatMessage(message)}${durationStr}`, ...args);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* 链接日志
|
|
327
|
+
*/
|
|
328
|
+
link(message, url, ...args) {
|
|
329
|
+
if (this.level <= 1 /* INFO */) {
|
|
330
|
+
console.log(`\u{1F517} ${this.formatMessage(message)} ${url}`, ...args);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* 文件操作日志
|
|
335
|
+
*/
|
|
336
|
+
file(action, path2, ...args) {
|
|
337
|
+
if (this.level <= 1 /* INFO */) {
|
|
338
|
+
console.log(`\u{1F4C4} ${this.formatMessage(`${action}: ${path2}`)}`, ...args);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Git 操作日志
|
|
343
|
+
*/
|
|
344
|
+
git(message, ...args) {
|
|
345
|
+
if (this.level <= 1 /* INFO */) {
|
|
346
|
+
console.log(`\u{1F4E6} ${this.formatMessage(message)}`, ...args);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* 创建子 Logger
|
|
351
|
+
*/
|
|
352
|
+
child(prefix) {
|
|
353
|
+
return new _Logger({
|
|
354
|
+
level: this.level,
|
|
355
|
+
prefix: this.prefix ? `${this.prefix}:${prefix}` : prefix,
|
|
356
|
+
timestamp: this.timestamp,
|
|
357
|
+
colors: this.colors
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* 执行函数并记录耗时
|
|
362
|
+
*/
|
|
363
|
+
async time(label, fn) {
|
|
364
|
+
this.progress(`${label}...`);
|
|
365
|
+
const startTime = Date.now();
|
|
366
|
+
try {
|
|
367
|
+
const result = await fn();
|
|
368
|
+
const duration = Date.now() - startTime;
|
|
369
|
+
this.done(label, duration);
|
|
370
|
+
return result;
|
|
371
|
+
} catch (error) {
|
|
372
|
+
const duration = Date.now() - startTime;
|
|
373
|
+
this.error(`${label} failed after ${duration}ms`);
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
var logger = new Logger({
|
|
379
|
+
level: process.env.DEBUG ? 0 /* DEBUG */ : 1 /* INFO */,
|
|
380
|
+
timestamp: true,
|
|
381
|
+
// 启用时间戳(精确到毫秒)
|
|
382
|
+
colors: true
|
|
383
|
+
});
|
|
384
|
+
function createLogger(prefix, config2) {
|
|
385
|
+
return new Logger({
|
|
386
|
+
...config2,
|
|
387
|
+
prefix
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// utils/markdown.ts
|
|
392
|
+
function createAddNumberToTitle() {
|
|
393
|
+
const titleNumbers = Array(7).fill(0);
|
|
394
|
+
return function addNumberToTitle(title) {
|
|
395
|
+
const match = title.match(
|
|
396
|
+
/^(\#+)\s*((\d+(\.\d*)?(\.\d*)?(\.\d*)?(\.\d*)?(\.\d*)?)\.\s*)?(.*)/
|
|
397
|
+
);
|
|
398
|
+
const plainTitle = match ? match[9].trim() : title.trim();
|
|
399
|
+
const level = title.indexOf(" ");
|
|
400
|
+
const baseLevel = 2;
|
|
401
|
+
if (level === 1) return [title, plainTitle];
|
|
402
|
+
for (let i = level + 1; i < titleNumbers.length; i++) titleNumbers[i] = 0;
|
|
403
|
+
titleNumbers[level] += 1;
|
|
404
|
+
const newNumber = titleNumbers.slice(baseLevel, level + 1).join(".");
|
|
405
|
+
const headerSymbol = title.slice(0, level).trim();
|
|
406
|
+
const newTitle = `${headerSymbol} ${newNumber}. ${plainTitle}`;
|
|
407
|
+
return [newTitle, plainTitle];
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function generateToc(titles, baseLevel = 2, eol = "\n") {
|
|
411
|
+
const toc = titles.map((title) => {
|
|
412
|
+
const level = title.indexOf(" ");
|
|
413
|
+
const text = title.slice(level).trim();
|
|
414
|
+
const anchor = generateAnchor(text);
|
|
415
|
+
const indent = Math.max(0, (level - baseLevel) * 2);
|
|
416
|
+
return " ".repeat(indent) + `- [${text}](#${anchor})`;
|
|
417
|
+
}).join(eol);
|
|
418
|
+
return `${eol}${toc}${eol}`;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// utils/parseArgs.ts
|
|
422
|
+
function parseArgs(args) {
|
|
423
|
+
const result = { _: [] };
|
|
424
|
+
for (let i = 0; i < args.length; i++) {
|
|
425
|
+
const arg = args[i];
|
|
426
|
+
if (arg.startsWith("--") && arg.includes("=")) {
|
|
427
|
+
const eqIndex = arg.indexOf("=");
|
|
428
|
+
const key = arg.slice(2, eqIndex);
|
|
429
|
+
const value = arg.slice(eqIndex + 1);
|
|
430
|
+
result[key] = value;
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (arg.startsWith("--no-")) {
|
|
434
|
+
const key = arg.slice(5);
|
|
435
|
+
result[key] = false;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (arg.startsWith("--")) {
|
|
439
|
+
const key = arg.slice(2);
|
|
440
|
+
const next = args[i + 1];
|
|
441
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
442
|
+
if (next === "true") {
|
|
443
|
+
result[key] = true;
|
|
444
|
+
i++;
|
|
445
|
+
} else if (next === "false") {
|
|
446
|
+
result[key] = false;
|
|
447
|
+
i++;
|
|
448
|
+
} else {
|
|
449
|
+
result[key] = true;
|
|
450
|
+
}
|
|
451
|
+
} else {
|
|
452
|
+
result[key] = true;
|
|
453
|
+
}
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (arg.startsWith("-") && arg.length > 1 && !arg.startsWith("--")) {
|
|
457
|
+
const flags = arg.slice(1);
|
|
458
|
+
for (const flag of flags) {
|
|
459
|
+
result[flag] = true;
|
|
460
|
+
}
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
result._.push(arg);
|
|
464
|
+
}
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// utils/parseReadmeCompletedNotes.ts
|
|
469
|
+
function parseReadmeCompletedNotes(content) {
|
|
470
|
+
const lines = content.split("\n");
|
|
471
|
+
const noteMap = /* @__PURE__ */ new Map();
|
|
472
|
+
const noteIndexRegex = /\[(\d{4})\./;
|
|
473
|
+
for (const line of lines) {
|
|
474
|
+
const match = line.match(noteIndexRegex);
|
|
475
|
+
if (!match) continue;
|
|
476
|
+
const noteIndex = match[1];
|
|
477
|
+
let completed;
|
|
478
|
+
if (line.includes("\u274C")) {
|
|
479
|
+
completed = false;
|
|
480
|
+
} else if (line.includes("\u23F0")) {
|
|
481
|
+
completed = false;
|
|
482
|
+
} else if (line.includes("\u2705")) {
|
|
483
|
+
completed = true;
|
|
484
|
+
} else if (line.trim().startsWith("- [ ]")) {
|
|
485
|
+
completed = false;
|
|
486
|
+
} else if (line.trim().startsWith("- [x]")) {
|
|
487
|
+
completed = true;
|
|
488
|
+
} else {
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (noteMap.has(noteIndex)) {
|
|
492
|
+
const existing = noteMap.get(noteIndex);
|
|
493
|
+
if (existing.completed !== completed) {
|
|
494
|
+
throw new Error(
|
|
495
|
+
`\u53D1\u73B0\u76F8\u540C\u7F16\u53F7 ${noteIndex} \u7684\u7B14\u8BB0\u6709\u4E0D\u540C\u7684\u5B8C\u6210\u72B6\u6001:
|
|
496
|
+
\u7B2C\u4E00\u6B21\u51FA\u73B0: ${existing.line}
|
|
497
|
+
\u7B2C\u4E8C\u6B21\u51FA\u73B0: ${line}`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
noteMap.set(noteIndex, {
|
|
503
|
+
noteIndex,
|
|
504
|
+
completed,
|
|
505
|
+
line: line.trim()
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
const notes = Array.from(noteMap.values());
|
|
509
|
+
const completedCount = notes.filter((note) => note.completed).length;
|
|
510
|
+
const totalCount = notes.length;
|
|
511
|
+
return {
|
|
512
|
+
completedCount,
|
|
513
|
+
totalCount,
|
|
514
|
+
notes
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// utils/portUtils.ts
|
|
519
|
+
import { execSync as execSync2 } from "child_process";
|
|
520
|
+
function isPortInUse(port2) {
|
|
521
|
+
try {
|
|
522
|
+
if (process.platform === "win32") {
|
|
523
|
+
const output = execSync2(
|
|
524
|
+
`netstat -ano | findstr :${port2} | findstr LISTENING`,
|
|
525
|
+
{ encoding: "utf-8", stdio: "pipe" }
|
|
526
|
+
);
|
|
527
|
+
return output.trim().length > 0;
|
|
528
|
+
} else {
|
|
529
|
+
const output = execSync2(`lsof -i :${port2}`, {
|
|
530
|
+
encoding: "utf-8",
|
|
531
|
+
stdio: "pipe"
|
|
532
|
+
});
|
|
533
|
+
return output.trim().length > 0;
|
|
534
|
+
}
|
|
535
|
+
} catch (error) {
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function getPortPid(port2) {
|
|
540
|
+
try {
|
|
541
|
+
if (process.platform === "win32") {
|
|
542
|
+
const output = execSync2(`netstat -ano | findstr :${port2}`, {
|
|
543
|
+
encoding: "utf-8",
|
|
544
|
+
stdio: "pipe"
|
|
545
|
+
});
|
|
546
|
+
const lines = output.trim().split("\n");
|
|
547
|
+
if (lines.length > 0) {
|
|
548
|
+
const match = lines[0].match(/\s+(\d+)\s*$/);
|
|
549
|
+
if (match) {
|
|
550
|
+
return parseInt(match[1]);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
const output = execSync2(`lsof -t -i :${port2}`, {
|
|
555
|
+
encoding: "utf-8",
|
|
556
|
+
stdio: "pipe"
|
|
557
|
+
});
|
|
558
|
+
const pid = parseInt(output.trim());
|
|
559
|
+
if (!isNaN(pid)) {
|
|
560
|
+
return pid;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
} catch (error) {
|
|
564
|
+
}
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
function killPortProcess(port2) {
|
|
568
|
+
const pid = getPortPid(port2);
|
|
569
|
+
if (!pid) {
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
if (process.platform === "win32") {
|
|
574
|
+
execSync2(`taskkill /F /PID ${pid}`, { stdio: "pipe" });
|
|
575
|
+
} else {
|
|
576
|
+
execSync2(`kill -9 ${pid}`, { stdio: "pipe" });
|
|
577
|
+
}
|
|
578
|
+
logger.info(`\u5DF2\u7EC8\u6B62\u5360\u7528\u7AEF\u53E3 ${port2} \u7684\u8FDB\u7A0B (PID: ${pid})`);
|
|
579
|
+
return true;
|
|
580
|
+
} catch (error) {
|
|
581
|
+
logger.error(
|
|
582
|
+
`\u7EC8\u6B62\u8FDB\u7A0B\u5931\u8D25 (PID: ${pid}): ${error instanceof Error ? error.message : String(error)}`
|
|
583
|
+
);
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
async function waitForPort(port2, timeout = 5e3) {
|
|
588
|
+
const startTime = Date.now();
|
|
589
|
+
while (Date.now() - startTime < timeout) {
|
|
590
|
+
if (!isPortInUse(port2)) {
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
await new Promise((resolve4) => setTimeout(resolve4, 100));
|
|
594
|
+
}
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// core/NoteManager.ts
|
|
599
|
+
import { existsSync, readFileSync, readdirSync as readdirSync2, writeFileSync } from "fs";
|
|
600
|
+
import { join as join2 } from "path";
|
|
601
|
+
|
|
602
|
+
// config/constants.ts
|
|
603
|
+
import { resolve } from "path";
|
|
604
|
+
var configManager = getConfigManager();
|
|
605
|
+
var config = configManager.getAll();
|
|
606
|
+
var {
|
|
607
|
+
author,
|
|
608
|
+
ignore_dirs,
|
|
609
|
+
menuItems,
|
|
610
|
+
port,
|
|
611
|
+
repoName,
|
|
612
|
+
sidebarShowNoteId,
|
|
613
|
+
socialLinks,
|
|
614
|
+
root_item
|
|
615
|
+
} = config;
|
|
616
|
+
var rootPath = configManager.getRootPath();
|
|
617
|
+
var TNOTES_BASE_DIR = resolve(rootPath, "..");
|
|
618
|
+
var TNOTES_CORE_DIR = resolve(TNOTES_BASE_DIR, "TNotes.core");
|
|
619
|
+
var EN_WORDS_DIR = resolve(TNOTES_BASE_DIR, "TNotes.en-words");
|
|
620
|
+
var ROOT_DIR_PATH = rootPath;
|
|
621
|
+
var ROOT_README_PATH = resolve(ROOT_DIR_PATH, "README.md");
|
|
622
|
+
var ROOT_CONFIG_PATH = resolve(ROOT_DIR_PATH, ".tnotes.json");
|
|
623
|
+
var NOTES_DIR_PATH = resolve(ROOT_DIR_PATH, "notes");
|
|
624
|
+
var VP_DIR_PATH = resolve(ROOT_DIR_PATH, ".vitepress");
|
|
625
|
+
var PUBLIC_PATH = resolve(ROOT_DIR_PATH, "public");
|
|
626
|
+
var GITHUB_DIR_PATH = resolve(ROOT_DIR_PATH, ".github");
|
|
627
|
+
var GITHUB_DEPLOY_YML_PATH = resolve(
|
|
628
|
+
GITHUB_DIR_PATH,
|
|
629
|
+
"workflows",
|
|
630
|
+
"deploy.yml"
|
|
631
|
+
);
|
|
632
|
+
var VP_SIDEBAR_PATH = resolve(ROOT_DIR_PATH, "sidebar.json");
|
|
633
|
+
var ROOT_PKG_PATH = resolve(ROOT_DIR_PATH, "package.json");
|
|
634
|
+
var VSCODE_SETTINGS_PATH = resolve(
|
|
635
|
+
ROOT_DIR_PATH,
|
|
636
|
+
".vscode",
|
|
637
|
+
"settings.json"
|
|
638
|
+
);
|
|
639
|
+
var VSCODE_TASKS_PATH = resolve(ROOT_DIR_PATH, ".vscode", "tasks.json");
|
|
640
|
+
var EOL = "\n";
|
|
641
|
+
var CONSTANTS = {
|
|
642
|
+
// 端口配置
|
|
643
|
+
DEFAULT_PORT: 5173,
|
|
644
|
+
// 笔记索引配置(文件夹前缀的 4 位数字)
|
|
645
|
+
NOTE_INDEX_LENGTH: 4,
|
|
646
|
+
NOTE_INDEX_PATTERN: /^\d{4}\./,
|
|
647
|
+
NOTE_INDEX_PREFIX_PATTERN: /^\d{4}/,
|
|
648
|
+
// Git 配置
|
|
649
|
+
DEFAULT_BRANCH: "main",
|
|
650
|
+
// 缓存配置
|
|
651
|
+
CACHE_TTL: 5e3,
|
|
652
|
+
// 终端输出颜色
|
|
653
|
+
COLORS: {
|
|
654
|
+
RESET: "\x1B[0m",
|
|
655
|
+
BRIGHT: "\x1B[1m",
|
|
656
|
+
DIM: "\x1B[2m",
|
|
657
|
+
RED: "\x1B[31m",
|
|
658
|
+
GREEN: "\x1B[32m",
|
|
659
|
+
YELLOW: "\x1B[33m",
|
|
660
|
+
BLUE: "\x1B[34m",
|
|
661
|
+
MAGENTA: "\x1B[35m",
|
|
662
|
+
CYAN: "\x1B[36m"
|
|
663
|
+
},
|
|
664
|
+
// Emoji
|
|
665
|
+
EMOJI: {
|
|
666
|
+
SUCCESS: "\u2705",
|
|
667
|
+
ERROR: "\u274C",
|
|
668
|
+
WARNING: "\u26A0\uFE0F",
|
|
669
|
+
INFO: "\u2139\uFE0F",
|
|
670
|
+
PROGRESS: "\u23F3",
|
|
671
|
+
ROCKET: "\u{1F680}",
|
|
672
|
+
STOP: "\u{1F6D1}",
|
|
673
|
+
SPARKLES: "\u2728",
|
|
674
|
+
LINK: "\u{1F517}",
|
|
675
|
+
FILE: "\u{1F4C4}",
|
|
676
|
+
GIT: "\u{1F4E6}",
|
|
677
|
+
DEBUG: "\u{1F41B}"
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
var NOTES_PATH = NOTES_DIR_PATH;
|
|
681
|
+
var REPO_NOTES_URL = `https://github.com/${author}/${repoName}/tree/main/notes`;
|
|
682
|
+
|
|
683
|
+
// core/NoteManager.ts
|
|
684
|
+
var NoteManager = class _NoteManager {
|
|
685
|
+
static instance;
|
|
686
|
+
/** 笔记索引正则:4 位数字开头,后接小数点 */
|
|
687
|
+
static NOTE_INDEX_REGEX = /^(\d{4})\./;
|
|
688
|
+
constructor() {
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* 从文件夹名称或文本中提取笔记索引
|
|
692
|
+
*
|
|
693
|
+
* @param text - 要解析的文本(通常是文件夹名称)
|
|
694
|
+
* @returns 笔记索引(4 位数字字符串)或 null
|
|
695
|
+
*
|
|
696
|
+
* @example
|
|
697
|
+
* NoteManager.extractNoteIndex('0001. TNotes 简介') // '0001'
|
|
698
|
+
* NoteManager.extractNoteIndex('invalid-folder') // null
|
|
699
|
+
*/
|
|
700
|
+
static extractNoteIndex(text) {
|
|
701
|
+
const match = text.match(_NoteManager.NOTE_INDEX_REGEX);
|
|
702
|
+
return match ? match[1] : null;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* 输出无效笔记名称的警告日志
|
|
706
|
+
*
|
|
707
|
+
* @param name - 无效的笔记名称
|
|
708
|
+
*/
|
|
709
|
+
static warnInvalidNoteIndex(name) {
|
|
710
|
+
logger.warn(`\u65E0\u6548\u7684\u7B14\u8BB0\u540D: ${name}`);
|
|
711
|
+
logger.warn("\u7B14\u8BB0\u540D\u5FC5\u987B\u4EE5 4 \u4E2A\u6570\u5B57\u5F00\u5934");
|
|
712
|
+
logger.warn("\u8303\u56F4\uFF1A0001-9999");
|
|
713
|
+
}
|
|
714
|
+
static getInstance() {
|
|
715
|
+
if (!_NoteManager.instance) {
|
|
716
|
+
_NoteManager.instance = new _NoteManager();
|
|
717
|
+
}
|
|
718
|
+
return _NoteManager.instance;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* 获取 notes 目录下所有合法的笔记目录名(已排序)
|
|
722
|
+
* 合法条件:是目录、不以 . 开头、以 4 位数字 + . 开头
|
|
723
|
+
*/
|
|
724
|
+
getNoteDirs() {
|
|
725
|
+
if (!existsSync(NOTES_PATH)) return [];
|
|
726
|
+
return readdirSync2(NOTES_PATH, { withFileTypes: true }).filter(
|
|
727
|
+
(entry) => entry.isDirectory() && !entry.name.startsWith(".") && _NoteManager.NOTE_INDEX_REGEX.test(entry.name)
|
|
728
|
+
).map((entry) => entry.name).sort();
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* 根据目录名构建单条 NoteInfo
|
|
732
|
+
* @returns NoteInfo 或 undefined(README 不存在时)
|
|
733
|
+
*/
|
|
734
|
+
buildNoteInfo(dirName) {
|
|
735
|
+
const notePath = join2(NOTES_PATH, dirName);
|
|
736
|
+
const readmePath = join2(notePath, "README.md");
|
|
737
|
+
const configPath = join2(notePath, ".tnotes.json");
|
|
738
|
+
if (!existsSync(readmePath)) {
|
|
739
|
+
logger.warn(`README not found in note: ${dirName}`);
|
|
740
|
+
return void 0;
|
|
741
|
+
}
|
|
742
|
+
let config2;
|
|
743
|
+
if (existsSync(configPath)) {
|
|
744
|
+
config2 = this.validateAndFixConfig(configPath) || void 0;
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
index: _NoteManager.extractNoteIndex(dirName),
|
|
748
|
+
path: notePath,
|
|
749
|
+
dirName,
|
|
750
|
+
readmePath,
|
|
751
|
+
configPath,
|
|
752
|
+
config: config2
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* 扫描所有笔记并校验数据完整性
|
|
757
|
+
*
|
|
758
|
+
* @returns 笔记信息数组
|
|
759
|
+
*/
|
|
760
|
+
scanNotes() {
|
|
761
|
+
const noteDirs = this.getNoteDirs();
|
|
762
|
+
if (noteDirs.length === 0) {
|
|
763
|
+
logger.warn(`${NOTES_PATH} \u672A\u68C0\u6D4B\u5230\u7B14\u8BB0\u76EE\u5F55`);
|
|
764
|
+
return [];
|
|
765
|
+
}
|
|
766
|
+
const notes = [];
|
|
767
|
+
for (const dirName of noteDirs) {
|
|
768
|
+
const note = this.buildNoteInfo(dirName);
|
|
769
|
+
if (note) notes.push(note);
|
|
770
|
+
}
|
|
771
|
+
this.validateNotes(notes);
|
|
772
|
+
return notes;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* 校验笔记数据完整性
|
|
776
|
+
*
|
|
777
|
+
* - 检查 noteIndex 冲突 + config id 缺失/重复
|
|
778
|
+
* - 任一检查失败则终止进程
|
|
779
|
+
*/
|
|
780
|
+
validateNotes(notes) {
|
|
781
|
+
const errors = [];
|
|
782
|
+
const L1 = " ".repeat(3);
|
|
783
|
+
const L2 = " ".repeat(6);
|
|
784
|
+
const indexMap = this.buildNoteIndexMap(notes.map((n) => n.dirName));
|
|
785
|
+
for (const [index, dirNames] of indexMap.entries()) {
|
|
786
|
+
if (dirNames.length > 1) {
|
|
787
|
+
errors.push(`\u26A0\uFE0F \u68C0\u6D4B\u5230\u91CD\u590D\u7684\u7B14\u8BB0\u7F16\u53F7\uFF1A`);
|
|
788
|
+
errors.push(`${L1}\u7D22\u5F15 ${index} \u88AB\u4EE5\u4E0B\u7B14\u8BB0\u91CD\u590D\u4F7F\u7528\uFF1A`);
|
|
789
|
+
dirNames.forEach((dirName) => errors.push(`${L2}- ${dirName}`));
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
const missingConfigId = [];
|
|
793
|
+
for (const note of notes) {
|
|
794
|
+
if (!note.config || !note.config.id) {
|
|
795
|
+
missingConfigId.push(note.dirName);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (missingConfigId.length > 0) {
|
|
799
|
+
errors.push(`\u26A0\uFE0F \u68C0\u6D4B\u5230\u7B14\u8BB0\u914D\u7F6E ID \u7F3A\u5931\uFF1A`);
|
|
800
|
+
missingConfigId.forEach((dirName) => errors.push(`${L2}- ${dirName}`));
|
|
801
|
+
}
|
|
802
|
+
const configIdMap = /* @__PURE__ */ new Map();
|
|
803
|
+
for (const note of notes) {
|
|
804
|
+
if (note.config?.id) {
|
|
805
|
+
if (!configIdMap.has(note.config.id))
|
|
806
|
+
configIdMap.set(note.config.id, []);
|
|
807
|
+
configIdMap.get(note.config.id).push(note.dirName);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
for (const [configId, dirNames] of configIdMap.entries()) {
|
|
811
|
+
if (dirNames.length > 1) {
|
|
812
|
+
errors.push(`\u26A0\uFE0F \u68C0\u6D4B\u5230\u91CD\u590D\u7684\u7B14\u8BB0\u914D\u7F6E ID\uFF1A`);
|
|
813
|
+
errors.push(`${L1}\u914D\u7F6E ID ${configId} \u88AB\u4EE5\u4E0B\u7B14\u8BB0\u91CD\u590D\u4F7F\u7528\uFF1A`);
|
|
814
|
+
dirNames.forEach((dirName) => errors.push(`${L2}- ${dirName}`));
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (errors.length > 0) {
|
|
818
|
+
for (const line of errors) {
|
|
819
|
+
logger.error(line);
|
|
820
|
+
}
|
|
821
|
+
logger.error("\n\n\u8BF7\u4FEE\u590D\u4E0A\u8FF0\u95EE\u9898\u540E\u91CD\u65B0\u542F\u52A8\u670D\u52A1\u3002\n\n");
|
|
822
|
+
process.exit(1);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* 按 4 位数字编号对目录名分组
|
|
827
|
+
* @param dirNames - 目录名数组
|
|
828
|
+
* @returns 编号 -> 目录名数组 的映射
|
|
829
|
+
*/
|
|
830
|
+
buildNoteIndexMap(dirNames) {
|
|
831
|
+
const indexMap = /* @__PURE__ */ new Map();
|
|
832
|
+
for (const name of dirNames) {
|
|
833
|
+
const index = _NoteManager.extractNoteIndex(name);
|
|
834
|
+
if (!indexMap.has(index)) indexMap.set(index, []);
|
|
835
|
+
indexMap.get(index).push(name);
|
|
836
|
+
}
|
|
837
|
+
return indexMap;
|
|
838
|
+
}
|
|
839
|
+
/** 配置字段顺序 */
|
|
840
|
+
static FIELD_ORDER = [
|
|
841
|
+
"bilibili",
|
|
842
|
+
"tnotes",
|
|
843
|
+
"yuque",
|
|
844
|
+
"done",
|
|
845
|
+
"category",
|
|
846
|
+
"enableDiscussions",
|
|
847
|
+
"description",
|
|
848
|
+
"id",
|
|
849
|
+
"created_at",
|
|
850
|
+
"updated_at"
|
|
851
|
+
];
|
|
852
|
+
/** 默认配置字段 */
|
|
853
|
+
static DEFAULT_CONFIG_FIELDS = {
|
|
854
|
+
bilibili: [],
|
|
855
|
+
tnotes: [],
|
|
856
|
+
yuque: [],
|
|
857
|
+
done: false,
|
|
858
|
+
enableDiscussions: false,
|
|
859
|
+
description: ""
|
|
860
|
+
};
|
|
861
|
+
/** 必需字段(不能缺失) */
|
|
862
|
+
static REQUIRED_FIELDS = ["id"];
|
|
863
|
+
/**
|
|
864
|
+
* 验证并修复配置文件
|
|
865
|
+
* @param configPath - 配置文件路径
|
|
866
|
+
* @returns 修复后的配置对象,失败时返回 null
|
|
867
|
+
*/
|
|
868
|
+
validateAndFixConfig(configPath) {
|
|
869
|
+
const configContent = readFileSync(configPath, "utf-8");
|
|
870
|
+
let config2;
|
|
871
|
+
try {
|
|
872
|
+
config2 = JSON.parse(configContent);
|
|
873
|
+
} catch (error) {
|
|
874
|
+
logger.error(`\u914D\u7F6E\u6587\u4EF6 JSON \u89E3\u6790\u5931\u8D25: ${configPath}`, error);
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
let needsUpdate = false;
|
|
878
|
+
for (const field of _NoteManager.REQUIRED_FIELDS) {
|
|
879
|
+
if (!config2[field]) {
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
for (const [key, defaultValue] of Object.entries(
|
|
884
|
+
_NoteManager.DEFAULT_CONFIG_FIELDS
|
|
885
|
+
)) {
|
|
886
|
+
if (!(key in config2)) {
|
|
887
|
+
;
|
|
888
|
+
config2[key] = defaultValue;
|
|
889
|
+
needsUpdate = true;
|
|
890
|
+
logger.info(`\u8865\u5145\u7F3A\u5931\u5B57\u6BB5 "${key}": ${configPath}`);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
const now = Date.now();
|
|
894
|
+
if (!config2.created_at) {
|
|
895
|
+
config2.created_at = now;
|
|
896
|
+
needsUpdate = true;
|
|
897
|
+
logger.info(
|
|
898
|
+
`\u68C0\u6D4B\u5230 ${configPath} \u7F3A\u5931 created_at \u5B57\u6BB5\uFF0C\u8BF7\u6267\u884C tn:fix-timestamps \u6821\u51C6\u4E3A\u7B14\u8BB0\u9996\u6B21 git commit \u7684\u65F6\u95F4\uFF09`
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
if (!config2.updated_at) {
|
|
902
|
+
config2.updated_at = now;
|
|
903
|
+
needsUpdate = true;
|
|
904
|
+
logger.info(
|
|
905
|
+
`\u68C0\u6D4B\u5230 ${configPath} \u7F3A\u5931 updated_at \u5B57\u6BB5\uFF0C\u8BF7\u6267\u884C tn:fix-timestamps \u6821\u51C6\u4E3A\u7B14\u8BB0\u6700\u540E\u4E00\u6B21 git commit \u7684\u65F6\u95F4\uFF09`
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
const sortedConfig = this.sortConfigKeys(config2);
|
|
909
|
+
if (needsUpdate) {
|
|
910
|
+
this.writeNoteConfig(configPath, sortedConfig);
|
|
911
|
+
logger.info(`\u914D\u7F6E\u6587\u4EF6\u5DF2\u4FEE\u590D: ${configPath}`);
|
|
912
|
+
}
|
|
913
|
+
return sortedConfig;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* 按指定顺序排序配置对象的键
|
|
917
|
+
*/
|
|
918
|
+
sortConfigKeys(config2) {
|
|
919
|
+
const configRecord = config2;
|
|
920
|
+
const sorted = {};
|
|
921
|
+
for (const key of _NoteManager.FIELD_ORDER) {
|
|
922
|
+
if (key in config2) {
|
|
923
|
+
sorted[key] = configRecord[key];
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
for (const key of Object.keys(config2)) {
|
|
927
|
+
if (!(key in sorted)) {
|
|
928
|
+
sorted[key] = configRecord[key];
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
return sorted;
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* 序列化 NoteConfig 为格式化的 JSON 字符串
|
|
935
|
+
* 保持字段顺序,使用 2 空格缩进,末尾含换行符
|
|
936
|
+
*/
|
|
937
|
+
serializeNoteConfig(config2) {
|
|
938
|
+
const sorted = this.sortConfigKeys(config2);
|
|
939
|
+
return JSON.stringify(sorted, null, 2) + "\n";
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* 统一写入笔记配置文件
|
|
943
|
+
* @param configPath - 配置文件路径
|
|
944
|
+
* @param config - 笔记配置
|
|
945
|
+
*/
|
|
946
|
+
writeNoteConfig(configPath, config2) {
|
|
947
|
+
writeFileSync(configPath, this.serializeNoteConfig(config2), "utf-8");
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* 验证笔记配置对象的结构合法性
|
|
951
|
+
* @param config - 笔记配置
|
|
952
|
+
* @returns 是否有效
|
|
953
|
+
*/
|
|
954
|
+
validateConfig(config2) {
|
|
955
|
+
if (!config2.id) {
|
|
956
|
+
logger.error("Note config missing id");
|
|
957
|
+
return false;
|
|
958
|
+
}
|
|
959
|
+
if (!Array.isArray(config2.bilibili)) {
|
|
960
|
+
logger.error(`Invalid bilibili config in note: ${config2.id}`);
|
|
961
|
+
return false;
|
|
962
|
+
}
|
|
963
|
+
if (!Array.isArray(config2.tnotes)) {
|
|
964
|
+
logger.error(`Invalid tnotes config in note: ${config2.id}`);
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
if (!Array.isArray(config2.yuque)) {
|
|
968
|
+
logger.error(`Invalid yuque config in note: ${config2.id}`);
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
if (typeof config2.done !== "boolean") {
|
|
972
|
+
logger.error(`Invalid done status in note: ${config2.id}`);
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
if (typeof config2.enableDiscussions !== "boolean") {
|
|
976
|
+
logger.error(`Invalid enableDiscussions status in note: ${config2.id}`);
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
return true;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* 更新笔记配置
|
|
983
|
+
* @param noteInfo - 笔记信息
|
|
984
|
+
* @param config - 新的配置
|
|
985
|
+
*/
|
|
986
|
+
updateNoteConfig(noteInfo, config2) {
|
|
987
|
+
if (!this.validateConfig(config2)) {
|
|
988
|
+
throw new Error(`Invalid config for note: ${noteInfo.dirName}`);
|
|
989
|
+
}
|
|
990
|
+
config2.updated_at = Date.now();
|
|
991
|
+
this.writeNoteConfig(noteInfo.configPath, config2);
|
|
992
|
+
logger.info(`Updated config for note: ${noteInfo.dirName}`);
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* 获取笔记信息(通过索引)- 直接查找不扫描所有笔记
|
|
996
|
+
* @param noteIndex - 笔记索引
|
|
997
|
+
* @returns 笔记信息,未找到时返回 undefined
|
|
998
|
+
*/
|
|
999
|
+
getNoteByIndex(noteIndex) {
|
|
1000
|
+
const noteDirs = this.getNoteDirs();
|
|
1001
|
+
for (const dirName of noteDirs) {
|
|
1002
|
+
if (_NoteManager.extractNoteIndex(dirName) === noteIndex) {
|
|
1003
|
+
return this.buildNoteInfo(dirName);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return void 0;
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
// utils/readmeHelpers.ts
|
|
1011
|
+
var NOTE_LINE_REGEX = /^( *)- \[.\] \[(\d{4}\. .+?)\]/;
|
|
1012
|
+
function parseNoteLine(line) {
|
|
1013
|
+
const noteMatch = line.match(NOTE_LINE_REGEX);
|
|
1014
|
+
if (!noteMatch) {
|
|
1015
|
+
return {
|
|
1016
|
+
isMatch: false,
|
|
1017
|
+
noteIndex: null
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
const [, , text] = noteMatch;
|
|
1021
|
+
const noteIndex = NoteManager.extractNoteIndex(text);
|
|
1022
|
+
return {
|
|
1023
|
+
isMatch: true,
|
|
1024
|
+
noteIndex
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function buildNoteLink(note, repoOwner, repoName2) {
|
|
1028
|
+
const encodedDirName = encodeURIComponent(note.dirName);
|
|
1029
|
+
return `https://github.com/${repoOwner}/${repoName2}/tree/main/notes/${encodedDirName}/README.md`;
|
|
1030
|
+
}
|
|
1031
|
+
function updateNoteStatus(note) {
|
|
1032
|
+
let status = " ";
|
|
1033
|
+
let deprecatedMark = "";
|
|
1034
|
+
if (note.config) {
|
|
1035
|
+
if (note.config.done) {
|
|
1036
|
+
status = "x";
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return { status, deprecatedMark };
|
|
1040
|
+
}
|
|
1041
|
+
function buildNoteLineMarkdown(note, repoOwner, repoName2) {
|
|
1042
|
+
const url = buildNoteLink(note, repoOwner, repoName2);
|
|
1043
|
+
const { status, deprecatedMark } = updateNoteStatus(note);
|
|
1044
|
+
return `- [${status}] [${note.dirName}](${url})${deprecatedMark}`;
|
|
1045
|
+
}
|
|
1046
|
+
function isNoteLine(line) {
|
|
1047
|
+
return NOTE_LINE_REGEX.test(line);
|
|
1048
|
+
}
|
|
1049
|
+
function mergeConsecutiveEmptyLines(lines) {
|
|
1050
|
+
const result = [];
|
|
1051
|
+
let previousLineIsEmpty = false;
|
|
1052
|
+
for (const line of lines) {
|
|
1053
|
+
const isCurrentLineEmpty = line === "";
|
|
1054
|
+
if (isCurrentLineEmpty) {
|
|
1055
|
+
if (!previousLineIsEmpty) {
|
|
1056
|
+
result.push(line);
|
|
1057
|
+
previousLineIsEmpty = true;
|
|
1058
|
+
}
|
|
1059
|
+
} else {
|
|
1060
|
+
result.push(line);
|
|
1061
|
+
previousLineIsEmpty = false;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return result;
|
|
1065
|
+
}
|
|
1066
|
+
function removeEmptyLinesBetweenNotes(lines) {
|
|
1067
|
+
const result = [];
|
|
1068
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1069
|
+
const currentLine = lines[i];
|
|
1070
|
+
const prevLine = i > 0 ? lines[i - 1] : null;
|
|
1071
|
+
const nextLine = i < lines.length - 1 ? lines[i + 1] : null;
|
|
1072
|
+
if (currentLine === "" && prevLine && nextLine) {
|
|
1073
|
+
const isPrevLineNote = isNoteLine(prevLine);
|
|
1074
|
+
const isNextLineNote = isNoteLine(nextLine);
|
|
1075
|
+
if (isPrevLineNote && isNextLineNote) {
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
result.push(currentLine);
|
|
1080
|
+
}
|
|
1081
|
+
return result;
|
|
1082
|
+
}
|
|
1083
|
+
function processEmptyLines(lines) {
|
|
1084
|
+
const stepOne = mergeConsecutiveEmptyLines(lines);
|
|
1085
|
+
const stepTwo = removeEmptyLinesBetweenNotes(stepOne);
|
|
1086
|
+
return stepTwo;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// utils/runCommand.ts
|
|
1090
|
+
import { exec } from "child_process";
|
|
1091
|
+
async function runCommand(command, dir) {
|
|
1092
|
+
return new Promise((resolve4, reject) => {
|
|
1093
|
+
exec(command, { cwd: dir }, (error, stdout, stderr) => {
|
|
1094
|
+
if (error) {
|
|
1095
|
+
console.error(`\u5904\u7406 ${dir} \u65F6\u51FA\u9519\uFF1A${stderr}`);
|
|
1096
|
+
reject(error);
|
|
1097
|
+
} else {
|
|
1098
|
+
resolve4(stdout.trim());
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// utils/syncRepo.ts
|
|
1105
|
+
async function pushAllRepos(options) {
|
|
1106
|
+
const {
|
|
1107
|
+
parallel = true,
|
|
1108
|
+
continueOnError = true,
|
|
1109
|
+
force = false
|
|
1110
|
+
} = options || {};
|
|
1111
|
+
const targetDirs = getTargetDirs(TNOTES_BASE_DIR, "TNotes.", [EN_WORDS_DIR, TNOTES_CORE_DIR]);
|
|
1112
|
+
logger.info(`\u6B63\u5728\u63A8\u9001 ${targetDirs.length} \u4E2A\u4ED3\u5E93...`);
|
|
1113
|
+
if (force) {
|
|
1114
|
+
logger.warn("\u4F7F\u7528\u5F3A\u5236\u63A8\u9001\u6A21\u5F0F");
|
|
1115
|
+
}
|
|
1116
|
+
const results = [];
|
|
1117
|
+
const pushCmd = force ? "pnpm tn:push --force" : "pnpm tn:push";
|
|
1118
|
+
if (parallel) {
|
|
1119
|
+
const promises = targetDirs.map(async (dir, index) => {
|
|
1120
|
+
try {
|
|
1121
|
+
await runCommand(pushCmd, dir);
|
|
1122
|
+
process.stdout.write(`\r \u8FDB\u5EA6: ~${index + 1}/${targetDirs.length}`);
|
|
1123
|
+
return { dir, success: true };
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1126
|
+
return { dir, success: false, error: errorMessage };
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
results.push(...await Promise.all(promises));
|
|
1130
|
+
console.log();
|
|
1131
|
+
} else {
|
|
1132
|
+
for (let i = 0; i < targetDirs.length; i++) {
|
|
1133
|
+
const dir = targetDirs[i];
|
|
1134
|
+
try {
|
|
1135
|
+
await runCommand(pushCmd, dir);
|
|
1136
|
+
process.stdout.write(`\r \u8FDB\u5EA6: ${i + 1}/${targetDirs.length}`);
|
|
1137
|
+
results.push({ dir, success: true });
|
|
1138
|
+
} catch (error) {
|
|
1139
|
+
process.stdout.write(`\r \u8FDB\u5EA6: ${i + 1}/${targetDirs.length}`);
|
|
1140
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1141
|
+
results.push({ dir, success: false, error: errorMessage });
|
|
1142
|
+
if (!continueOnError) {
|
|
1143
|
+
console.log();
|
|
1144
|
+
throw error;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
console.log();
|
|
1149
|
+
}
|
|
1150
|
+
const successCount = results.filter((r) => r.success).length;
|
|
1151
|
+
const failCount = results.length - successCount;
|
|
1152
|
+
if (failCount === 0) {
|
|
1153
|
+
logger.success(`\u63A8\u9001\u5B8C\u6210: ${successCount}/${results.length} \u4E2A\u4ED3\u5E93\u6210\u529F`);
|
|
1154
|
+
} else {
|
|
1155
|
+
logger.warn(
|
|
1156
|
+
`\u63A8\u9001\u5B8C\u6210: ${successCount} \u6210\u529F, ${failCount} \u5931\u8D25 (\u5171 ${results.length} \u4E2A)`
|
|
1157
|
+
);
|
|
1158
|
+
console.log("\n\u5931\u8D25\u7684\u4ED3\u5E93:");
|
|
1159
|
+
results.filter((r) => !r.success).forEach((r, index) => {
|
|
1160
|
+
const repoName2 = r.dir.split("\\").pop() || r.dir;
|
|
1161
|
+
console.log(` ${index + 1}. ${repoName2}`);
|
|
1162
|
+
console.log(` \u9519\u8BEF: ${r.error}`);
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
async function pullAllRepos(options) {
|
|
1167
|
+
const { parallel = true, continueOnError = true } = options || {};
|
|
1168
|
+
const targetDirs = getTargetDirs(TNOTES_BASE_DIR, "TNotes.", [EN_WORDS_DIR, TNOTES_CORE_DIR]);
|
|
1169
|
+
logger.info(`\u6B63\u5728\u62C9\u53D6 ${targetDirs.length} \u4E2A\u4ED3\u5E93...`);
|
|
1170
|
+
const results = [];
|
|
1171
|
+
if (parallel) {
|
|
1172
|
+
const promises = targetDirs.map(async (dir, index) => {
|
|
1173
|
+
try {
|
|
1174
|
+
await runCommand("pnpm tn:pull", dir);
|
|
1175
|
+
process.stdout.write(`\r \u8FDB\u5EA6: ~${index + 1}/${targetDirs.length}`);
|
|
1176
|
+
return { dir, success: true };
|
|
1177
|
+
} catch (error) {
|
|
1178
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1179
|
+
return { dir, success: false, error: errorMessage };
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
results.push(...await Promise.all(promises));
|
|
1183
|
+
console.log();
|
|
1184
|
+
} else {
|
|
1185
|
+
for (let i = 0; i < targetDirs.length; i++) {
|
|
1186
|
+
const dir = targetDirs[i];
|
|
1187
|
+
try {
|
|
1188
|
+
await runCommand("pnpm tn:pull", dir);
|
|
1189
|
+
process.stdout.write(`\r \u8FDB\u5EA6: ${i + 1}/${targetDirs.length}`);
|
|
1190
|
+
results.push({ dir, success: true });
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
process.stdout.write(`\r \u8FDB\u5EA6: ${i + 1}/${targetDirs.length}`);
|
|
1193
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1194
|
+
results.push({ dir, success: false, error: errorMessage });
|
|
1195
|
+
if (!continueOnError) {
|
|
1196
|
+
console.log();
|
|
1197
|
+
throw error;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
console.log();
|
|
1202
|
+
}
|
|
1203
|
+
const successCount = results.filter((r) => r.success).length;
|
|
1204
|
+
const failCount = results.length - successCount;
|
|
1205
|
+
if (failCount === 0) {
|
|
1206
|
+
logger.success(`\u62C9\u53D6\u5B8C\u6210: ${successCount}/${results.length} \u4E2A\u4ED3\u5E93\u6210\u529F`);
|
|
1207
|
+
} else {
|
|
1208
|
+
logger.warn(
|
|
1209
|
+
`\u62C9\u53D6\u5B8C\u6210: ${successCount} \u6210\u529F, ${failCount} \u5931\u8D25 (\u5171 ${results.length} \u4E2A)`
|
|
1210
|
+
);
|
|
1211
|
+
console.log("\n\u5931\u8D25\u7684\u4ED3\u5E93:");
|
|
1212
|
+
results.filter((r) => !r.success).forEach((r, index) => {
|
|
1213
|
+
const repoName2 = r.dir.split("\\").pop() || r.dir;
|
|
1214
|
+
console.log(` ${index + 1}. ${repoName2}`);
|
|
1215
|
+
console.log(` \u9519\u8BEF: ${r.error}`);
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
async function syncAllRepos(options) {
|
|
1220
|
+
const { parallel = true, continueOnError = true } = options || {};
|
|
1221
|
+
const targetDirs = getTargetDirs(TNOTES_BASE_DIR, "TNotes.", [EN_WORDS_DIR, TNOTES_CORE_DIR]);
|
|
1222
|
+
logger.info(`\u6B63\u5728\u540C\u6B65 ${targetDirs.length} \u4E2A\u4ED3\u5E93...`);
|
|
1223
|
+
const results = [];
|
|
1224
|
+
if (parallel) {
|
|
1225
|
+
const promises = targetDirs.map(async (dir, index) => {
|
|
1226
|
+
try {
|
|
1227
|
+
await runCommand("pnpm tn:sync", dir);
|
|
1228
|
+
process.stdout.write(`\r \u8FDB\u5EA6: ~${index + 1}/${targetDirs.length}`);
|
|
1229
|
+
return { dir, success: true };
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1232
|
+
return { dir, success: false, error: errorMessage };
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
results.push(...await Promise.all(promises));
|
|
1236
|
+
} else {
|
|
1237
|
+
for (let i = 0; i < targetDirs.length; i++) {
|
|
1238
|
+
const dir = targetDirs[i];
|
|
1239
|
+
try {
|
|
1240
|
+
await runCommand("pnpm tn:sync", dir);
|
|
1241
|
+
process.stdout.write(`\r \u8FDB\u5EA6: ${i + 1}/${targetDirs.length}`);
|
|
1242
|
+
results.push({ dir, success: true });
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
process.stdout.write(`\r \u8FDB\u5EA6: ${i + 1}/${targetDirs.length}`);
|
|
1245
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1246
|
+
results.push({ dir, success: false, error: errorMessage });
|
|
1247
|
+
if (!continueOnError) {
|
|
1248
|
+
throw error;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
console.log();
|
|
1254
|
+
const successCount = results.filter((r) => r.success).length;
|
|
1255
|
+
const failCount = results.length - successCount;
|
|
1256
|
+
if (failCount === 0) {
|
|
1257
|
+
logger.success(`\u540C\u6B65\u5B8C\u6210: ${successCount}/${results.length} \u4E2A\u4ED3\u5E93\u6210\u529F`);
|
|
1258
|
+
} else {
|
|
1259
|
+
logger.warn(
|
|
1260
|
+
`\u540C\u6B65\u5B8C\u6210: ${successCount} \u6210\u529F, ${failCount} \u5931\u8D25 (\u5171 ${results.length} \u4E2A)`
|
|
1261
|
+
);
|
|
1262
|
+
console.log("\n\u5931\u8D25\u7684\u4ED3\u5E93:");
|
|
1263
|
+
results.filter((r) => !r.success).forEach((r, index) => {
|
|
1264
|
+
const repoName2 = r.dir.split("\\").pop() || r.dir;
|
|
1265
|
+
console.log(` ${index + 1}. ${repoName2}`);
|
|
1266
|
+
console.log(` \u9519\u8BEF: ${r.error}`);
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// utils/validators.ts
|
|
1272
|
+
var INVALID_FILENAME_CHARS = /[<>:"/\\|?*\x00-\x1F]/;
|
|
1273
|
+
var WINDOWS_RESERVED_NAMES = /* @__PURE__ */ new Set([
|
|
1274
|
+
"CON",
|
|
1275
|
+
"PRN",
|
|
1276
|
+
"AUX",
|
|
1277
|
+
"NUL",
|
|
1278
|
+
"COM1",
|
|
1279
|
+
"COM2",
|
|
1280
|
+
"COM3",
|
|
1281
|
+
"COM4",
|
|
1282
|
+
"COM5",
|
|
1283
|
+
"COM6",
|
|
1284
|
+
"COM7",
|
|
1285
|
+
"COM8",
|
|
1286
|
+
"COM9",
|
|
1287
|
+
"LPT1",
|
|
1288
|
+
"LPT2",
|
|
1289
|
+
"LPT3",
|
|
1290
|
+
"LPT4",
|
|
1291
|
+
"LPT5",
|
|
1292
|
+
"LPT6",
|
|
1293
|
+
"LPT7",
|
|
1294
|
+
"LPT8",
|
|
1295
|
+
"LPT9"
|
|
1296
|
+
]);
|
|
1297
|
+
function validateNoteTitle(title) {
|
|
1298
|
+
if (!title || title.trim().length === 0) {
|
|
1299
|
+
return { valid: false, error: "\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
1300
|
+
}
|
|
1301
|
+
const trimmedTitle = title.trim();
|
|
1302
|
+
if (trimmedTitle.length > 200) {
|
|
1303
|
+
return { valid: false, error: "\u6807\u9898\u8FC7\u957F(\u6700\u591A200\u4E2A\u5B57\u7B26)" };
|
|
1304
|
+
}
|
|
1305
|
+
if (INVALID_FILENAME_CHARS.test(trimmedTitle)) {
|
|
1306
|
+
return {
|
|
1307
|
+
valid: false,
|
|
1308
|
+
error: '\u6807\u9898\u5305\u542B\u975E\u6CD5\u5B57\u7B26(\u4E0D\u5141\u8BB8: < > : " / \\ | ? *)'
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
if (/^[.\s]|[.\s]$/.test(trimmedTitle)) {
|
|
1312
|
+
return {
|
|
1313
|
+
valid: false,
|
|
1314
|
+
error: "\u6807\u9898\u4E0D\u80FD\u4EE5\u70B9\u6216\u7A7A\u683C\u5F00\u5934/\u7ED3\u5C3E"
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
const upperTitle = trimmedTitle.toUpperCase();
|
|
1318
|
+
if (WINDOWS_RESERVED_NAMES.has(upperTitle)) {
|
|
1319
|
+
return {
|
|
1320
|
+
valid: false,
|
|
1321
|
+
error: `"${trimmedTitle}" \u662F Windows \u7CFB\u7EDF\u4FDD\u7559\u540D\u79F0`
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
const baseName = trimmedTitle.split(".")[0].toUpperCase();
|
|
1325
|
+
if (WINDOWS_RESERVED_NAMES.has(baseName)) {
|
|
1326
|
+
return {
|
|
1327
|
+
valid: false,
|
|
1328
|
+
error: `"${trimmedTitle}" \u5305\u542B Windows \u7CFB\u7EDF\u4FDD\u7559\u540D\u79F0`
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
return { valid: true };
|
|
1332
|
+
}
|
|
1333
|
+
|
|
82
1334
|
// commands/BaseCommand.ts
|
|
83
1335
|
var BaseCommand = class {
|
|
84
1336
|
constructor(name) {
|
|
85
1337
|
this.name = name;
|
|
86
|
-
this.options = {};
|
|
87
1338
|
this.logger = logger.child(name);
|
|
88
1339
|
}
|
|
1340
|
+
logger;
|
|
1341
|
+
options = {};
|
|
89
1342
|
/** 命令描述(从静态配置读取) */
|
|
90
1343
|
get description() {
|
|
91
1344
|
return COMMAND_DESCRIPTIONS[this.name];
|
|
@@ -129,19 +1382,19 @@ async function safeExecute(label, fn, logger2) {
|
|
|
129
1382
|
}
|
|
130
1383
|
|
|
131
1384
|
// services/file-watcher/watchState.ts
|
|
132
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
1385
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync as readdirSync3, statSync } from "fs";
|
|
133
1386
|
import { createHash } from "crypto";
|
|
134
|
-
import { join } from "path";
|
|
1387
|
+
import { join as join3 } from "path";
|
|
135
1388
|
var WatchState = class {
|
|
136
|
-
constructor(
|
|
137
|
-
this.config =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
1389
|
+
constructor(config2) {
|
|
1390
|
+
this.config = config2;
|
|
1391
|
+
}
|
|
1392
|
+
/** 文件哈希缓存 */
|
|
1393
|
+
fileHashes = /* @__PURE__ */ new Map();
|
|
1394
|
+
/** 笔记目录缓存 */
|
|
1395
|
+
noteDirCache = /* @__PURE__ */ new Set();
|
|
1396
|
+
/** 笔记配置缓存 */
|
|
1397
|
+
configCache = /* @__PURE__ */ new Map();
|
|
145
1398
|
/**
|
|
146
1399
|
* 获取指定文件的 MD5 哈希值,若文件不存在或读取失败返回 null
|
|
147
1400
|
*
|
|
@@ -150,8 +1403,8 @@ var WatchState = class {
|
|
|
150
1403
|
*/
|
|
151
1404
|
getFileHash(filePath) {
|
|
152
1405
|
try {
|
|
153
|
-
if (!
|
|
154
|
-
const content =
|
|
1406
|
+
if (!existsSync2(filePath)) return null;
|
|
1407
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
155
1408
|
if (content.length === 0) return null;
|
|
156
1409
|
return createHash("md5").update(content).digest("hex");
|
|
157
1410
|
} catch {
|
|
@@ -211,8 +1464,8 @@ var WatchState = class {
|
|
|
211
1464
|
* @param noteDirName 笔记目录名称
|
|
212
1465
|
*/
|
|
213
1466
|
clearNoteCaches(noteDirName) {
|
|
214
|
-
const readmePath =
|
|
215
|
-
const configPath =
|
|
1467
|
+
const readmePath = join3(this.config.notesDir, noteDirName, "README.md");
|
|
1468
|
+
const configPath = join3(this.config.notesDir, noteDirName, ".tnotes.json");
|
|
216
1469
|
this.fileHashes.delete(readmePath);
|
|
217
1470
|
this.fileHashes.delete(configPath);
|
|
218
1471
|
this.configCache.delete(configPath);
|
|
@@ -245,13 +1498,13 @@ var WatchState = class {
|
|
|
245
1498
|
*/
|
|
246
1499
|
readConfigSnapshot(configPath) {
|
|
247
1500
|
try {
|
|
248
|
-
if (!
|
|
249
|
-
const content =
|
|
250
|
-
const
|
|
1501
|
+
if (!existsSync2(configPath)) return null;
|
|
1502
|
+
const content = readFileSync2(configPath, "utf-8");
|
|
1503
|
+
const config2 = JSON.parse(content);
|
|
251
1504
|
return {
|
|
252
|
-
done: Boolean(
|
|
253
|
-
enableDiscussions: Boolean(
|
|
254
|
-
description:
|
|
1505
|
+
done: Boolean(config2.done),
|
|
1506
|
+
enableDiscussions: Boolean(config2.enableDiscussions),
|
|
1507
|
+
description: config2.description || ""
|
|
255
1508
|
};
|
|
256
1509
|
} catch (error) {
|
|
257
1510
|
this.config.logger.error(`[\u8BFB\u53D6\u914D\u7F6E\u5FEB\u7167] ${error}`);
|
|
@@ -265,16 +1518,16 @@ var WatchState = class {
|
|
|
265
1518
|
*/
|
|
266
1519
|
initializeFromDisk() {
|
|
267
1520
|
try {
|
|
268
|
-
const noteDirs =
|
|
1521
|
+
const noteDirs = readdirSync3(this.config.notesDir);
|
|
269
1522
|
this.clearAll();
|
|
270
1523
|
for (const noteDir of noteDirs) {
|
|
271
|
-
const noteDirPath =
|
|
1524
|
+
const noteDirPath = join3(this.config.notesDir, noteDir);
|
|
272
1525
|
if (!statSync(noteDirPath).isDirectory()) continue;
|
|
273
1526
|
this.noteDirCache.add(noteDir);
|
|
274
|
-
const readmePath =
|
|
1527
|
+
const readmePath = join3(noteDirPath, "README.md");
|
|
275
1528
|
const readmeHash = this.getFileHash(readmePath);
|
|
276
1529
|
if (readmeHash) this.fileHashes.set(readmePath, readmeHash);
|
|
277
|
-
const configPath =
|
|
1530
|
+
const configPath = join3(noteDirPath, ".tnotes.json");
|
|
278
1531
|
const configHash = this.getFileHash(configPath);
|
|
279
1532
|
if (configHash) {
|
|
280
1533
|
this.fileHashes.set(configPath, configHash);
|
|
@@ -296,23 +1549,27 @@ var DEFAULT_BATCH_WINDOW_MS = 1e3;
|
|
|
296
1549
|
var DEFAULT_BATCH_THRESHOLD = 3;
|
|
297
1550
|
var DEFAULT_BATCH_BUFFER_MS = 2e3;
|
|
298
1551
|
var EventScheduler = class {
|
|
299
|
-
constructor(
|
|
300
|
-
this.config =
|
|
301
|
-
|
|
302
|
-
this.
|
|
303
|
-
|
|
304
|
-
this.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
1552
|
+
constructor(config2) {
|
|
1553
|
+
this.config = config2;
|
|
1554
|
+
this.debounceMs = config2.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
1555
|
+
this.batchWindowMs = config2.batchWindowMs ?? DEFAULT_BATCH_WINDOW_MS;
|
|
1556
|
+
this.batchThreshold = config2.batchThreshold ?? DEFAULT_BATCH_THRESHOLD;
|
|
1557
|
+
this.batchBufferMs = config2.batchBufferMs ?? DEFAULT_BATCH_BUFFER_MS;
|
|
1558
|
+
}
|
|
1559
|
+
/** 待处理的文件变更事件队列 */
|
|
1560
|
+
pendingEvents = /* @__PURE__ */ new Map();
|
|
1561
|
+
/** 防抖定时器 */
|
|
1562
|
+
updateTimer = null;
|
|
1563
|
+
/** 批量更新恢复定时器 */
|
|
1564
|
+
batchResumeTimer = null;
|
|
1565
|
+
/** 记录最近的变更时间戳 */
|
|
1566
|
+
recentChanges = [];
|
|
1567
|
+
/** 标记是否正在更新,避免循环触发 - 类似一把更新行为锁 */
|
|
1568
|
+
isUpdating = false;
|
|
1569
|
+
debounceMs;
|
|
1570
|
+
batchWindowMs;
|
|
1571
|
+
batchThreshold;
|
|
1572
|
+
batchBufferMs;
|
|
316
1573
|
/**
|
|
317
1574
|
* 设置更新状态锁,用于防止在执行耗时更新操作时被新的文件变更事件打断
|
|
318
1575
|
*
|
|
@@ -409,21 +1666,21 @@ var EventScheduler = class {
|
|
|
409
1666
|
};
|
|
410
1667
|
|
|
411
1668
|
// services/file-watcher/renameDetector.ts
|
|
412
|
-
import { existsSync as
|
|
413
|
-
import { join as
|
|
1669
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1670
|
+
import { join as join4 } from "path";
|
|
414
1671
|
var FOLDER_RENAME_DETECT_WINDOW_MS = 500;
|
|
415
1672
|
var RenameDetector = class {
|
|
416
|
-
constructor(
|
|
417
|
-
this.config =
|
|
418
|
-
/** 待处理的文件夹重命名 */
|
|
419
|
-
this.pendingFolderRename = null;
|
|
420
|
-
/** 文件夹重命名检测定时器 */
|
|
421
|
-
this.folderRenameTimer = null;
|
|
1673
|
+
constructor(config2) {
|
|
1674
|
+
this.config = config2;
|
|
422
1675
|
}
|
|
1676
|
+
/** 待处理的文件夹重命名 */
|
|
1677
|
+
pendingFolderRename = null;
|
|
1678
|
+
/** 文件夹重命名检测定时器 */
|
|
1679
|
+
folderRenameTimer = null;
|
|
423
1680
|
handleFsRename(folderName) {
|
|
424
1681
|
const { notesDir, dirCache, logger: logger2, onDelete, onRename } = this.config;
|
|
425
|
-
const folderPath =
|
|
426
|
-
const folderExists =
|
|
1682
|
+
const folderPath = join4(notesDir, folderName);
|
|
1683
|
+
const folderExists = existsSync3(folderPath);
|
|
427
1684
|
const noteIndex = NoteManager.extractNoteIndex(folderName);
|
|
428
1685
|
if (!noteIndex) {
|
|
429
1686
|
logger2.warn(`\u65E0\u6CD5\u4ECE\u6587\u4EF6\u5939\u540D\u79F0\u63D0\u53D6\u7B14\u8BB0\u7D22\u5F15: ${folderName}`);
|
|
@@ -479,8 +1736,8 @@ var RenameDetector = class {
|
|
|
479
1736
|
|
|
480
1737
|
// services/file-watcher/configChangeHandler.ts
|
|
481
1738
|
var ConfigChangeHandler = class {
|
|
482
|
-
constructor(
|
|
483
|
-
this.config =
|
|
1739
|
+
constructor(config2) {
|
|
1740
|
+
this.config = config2;
|
|
484
1741
|
}
|
|
485
1742
|
async handle(events) {
|
|
486
1743
|
if (events.length === 0) return [];
|
|
@@ -512,8 +1769,8 @@ var ConfigChangeHandler = class {
|
|
|
512
1769
|
|
|
513
1770
|
// services/file-watcher/readmeChangeHandler.ts
|
|
514
1771
|
var ReadmeChangeHandler = class {
|
|
515
|
-
constructor(
|
|
516
|
-
this.config =
|
|
1772
|
+
constructor(config2) {
|
|
1773
|
+
this.config = config2;
|
|
517
1774
|
}
|
|
518
1775
|
async handle(events) {
|
|
519
1776
|
if (events.length === 0) return;
|
|
@@ -529,8 +1786,8 @@ var ReadmeChangeHandler = class {
|
|
|
529
1786
|
|
|
530
1787
|
// services/file-watcher/globalUpdateCoordinator.ts
|
|
531
1788
|
var GlobalUpdateCoordinator = class {
|
|
532
|
-
constructor(
|
|
533
|
-
this.config =
|
|
1789
|
+
constructor(config2) {
|
|
1790
|
+
this.config = config2;
|
|
534
1791
|
}
|
|
535
1792
|
async applyConfigUpdates(changedNoteIndexes) {
|
|
536
1793
|
if (changedNoteIndexes.length === 0) return;
|
|
@@ -560,17 +1817,17 @@ var GlobalUpdateCoordinator = class {
|
|
|
560
1817
|
};
|
|
561
1818
|
|
|
562
1819
|
// services/file-watcher/folderChangeHandler.ts
|
|
563
|
-
import { existsSync as
|
|
564
|
-
import { join as
|
|
1820
|
+
import { existsSync as existsSync4, promises as fsPromises } from "fs";
|
|
1821
|
+
import { join as join5 } from "path";
|
|
565
1822
|
var RENAME_REVERT_DELAY_MS = 2e3;
|
|
566
1823
|
var DELETE_REINIT_DELAY_MS = 1e3;
|
|
567
1824
|
var UPDATE_UNLOCK_DELAY_MS = 500;
|
|
568
1825
|
var FolderChangeHandler = class {
|
|
569
|
-
constructor(
|
|
570
|
-
this.config =
|
|
571
|
-
/** 活跃的定时器 ID 集合,用于统一清理 */
|
|
572
|
-
this.activeTimers = /* @__PURE__ */ new Set();
|
|
1826
|
+
constructor(config2) {
|
|
1827
|
+
this.config = config2;
|
|
573
1828
|
}
|
|
1829
|
+
/** 活跃的定时器 ID 集合,用于统一清理 */
|
|
1830
|
+
activeTimers = /* @__PURE__ */ new Set();
|
|
574
1831
|
/**
|
|
575
1832
|
* 清理所有活跃定时器,释放资源
|
|
576
1833
|
*
|
|
@@ -695,9 +1952,9 @@ var FolderChangeHandler = class {
|
|
|
695
1952
|
async revertFolderRename(oldName, newName) {
|
|
696
1953
|
const { notesDir, scheduler, watchState, logger: logger2 } = this.config;
|
|
697
1954
|
try {
|
|
698
|
-
const oldPath =
|
|
699
|
-
const newPath =
|
|
700
|
-
if (
|
|
1955
|
+
const oldPath = join5(notesDir, oldName);
|
|
1956
|
+
const newPath = join5(notesDir, newName);
|
|
1957
|
+
if (existsSync4(newPath)) {
|
|
701
1958
|
scheduler.setUpdating(true);
|
|
702
1959
|
await fsPromises.rename(newPath, oldPath);
|
|
703
1960
|
logger2.warn(`\u6587\u4EF6\u5939\u5DF2\u56DE\u9000: ${newName} \u2192 ${oldName}`);
|
|
@@ -722,13 +1979,13 @@ var FolderChangeHandler = class {
|
|
|
722
1979
|
|
|
723
1980
|
// services/file-watcher/fsWatcherAdapter.ts
|
|
724
1981
|
import { watch } from "fs";
|
|
725
|
-
import { basename, dirname, join as
|
|
1982
|
+
import { basename, dirname, join as join6, sep } from "path";
|
|
726
1983
|
var FsWatcherAdapter = class {
|
|
727
|
-
constructor(
|
|
728
|
-
this.config =
|
|
729
|
-
/** 文件系统监听器实例 */
|
|
730
|
-
this.watcher = null;
|
|
1984
|
+
constructor(config2) {
|
|
1985
|
+
this.config = config2;
|
|
731
1986
|
}
|
|
1987
|
+
/** 文件系统监听器实例 */
|
|
1988
|
+
watcher = null;
|
|
732
1989
|
start() {
|
|
733
1990
|
const { logger: logger2 } = this.config;
|
|
734
1991
|
if (this.watcher) {
|
|
@@ -765,7 +2022,7 @@ var FsWatcherAdapter = class {
|
|
|
765
2022
|
if (baseFilename !== "README.md" && baseFilename !== ".tnotes.json") {
|
|
766
2023
|
return;
|
|
767
2024
|
}
|
|
768
|
-
const fullPath =
|
|
2025
|
+
const fullPath = join6(this.config.notesDir, filename);
|
|
769
2026
|
const event = this.buildWatchEvent(fullPath, filename);
|
|
770
2027
|
if (!event) {
|
|
771
2028
|
return;
|
|
@@ -791,7 +2048,7 @@ var FsWatcherAdapter = class {
|
|
|
791
2048
|
};
|
|
792
2049
|
|
|
793
2050
|
// core/ReadmeGenerator.ts
|
|
794
|
-
import { readFileSync as
|
|
2051
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync5 } from "fs";
|
|
795
2052
|
|
|
796
2053
|
// core/TocGenerator.ts
|
|
797
2054
|
var BILIBILI_VIDEO_BASE_URL = "https://www.bilibili.com/video/";
|
|
@@ -806,7 +2063,7 @@ var TocGenerator = class {
|
|
|
806
2063
|
* @param noteConfig - 笔记配置
|
|
807
2064
|
* @param repoName - 仓库名称
|
|
808
2065
|
*/
|
|
809
|
-
updateNoteToc(noteIndex, lines, noteConfig,
|
|
2066
|
+
updateNoteToc(noteIndex, lines, noteConfig, repoName2) {
|
|
810
2067
|
let startLineIdx = -1, endLineIdx = -1;
|
|
811
2068
|
lines.forEach((line, idx) => {
|
|
812
2069
|
if (line.startsWith(NOTES_TOC_START_TAG)) startLineIdx = idx;
|
|
@@ -866,7 +2123,7 @@ var TocGenerator = class {
|
|
|
866
2123
|
if (noteConfig.bilibili.length > 0) {
|
|
867
2124
|
noteConfig.bilibili.forEach((bvid, i) => {
|
|
868
2125
|
bilibiliTOCItems.push(
|
|
869
|
-
` - [bilibili.${
|
|
2126
|
+
` - [bilibili.${repoName2}.${noteIndex}.${i + 1}](${BILIBILI_VIDEO_BASE_URL + bvid})`
|
|
870
2127
|
);
|
|
871
2128
|
});
|
|
872
2129
|
}
|
|
@@ -874,16 +2131,16 @@ var TocGenerator = class {
|
|
|
874
2131
|
tnotesTOCItems.push(
|
|
875
2132
|
`- [\u{1F4D2} TNotes\uFF08\u76F8\u5173\u77E5\u8BC6\u5E93\uFF09](https://tnotesjs.github.io/TNotes/)`
|
|
876
2133
|
);
|
|
877
|
-
noteConfig.tnotes.forEach((
|
|
2134
|
+
noteConfig.tnotes.forEach((repoName3) => {
|
|
878
2135
|
tnotesTOCItems.push(
|
|
879
|
-
` - [TNotes.${
|
|
2136
|
+
` - [TNotes.${repoName3}](https://tnotesjs.github.io/TNotes.${repoName3}/)`
|
|
880
2137
|
);
|
|
881
2138
|
});
|
|
882
2139
|
}
|
|
883
2140
|
if (noteConfig.yuque.length > 0) {
|
|
884
2141
|
noteConfig.yuque.forEach((slug, i) => {
|
|
885
2142
|
yuqueTOCItems.push(
|
|
886
|
-
` - [TNotes.yuque.${
|
|
2143
|
+
` - [TNotes.yuque.${repoName2.replace(
|
|
887
2144
|
"TNotes.",
|
|
888
2145
|
""
|
|
889
2146
|
)}.${noteIndex}](${TNOTES_YUQUE_BASE_URL + slug})`
|
|
@@ -944,6 +2201,8 @@ var TocGenerator = class {
|
|
|
944
2201
|
|
|
945
2202
|
// core/ReadmeGenerator.ts
|
|
946
2203
|
var ReadmeGenerator = class {
|
|
2204
|
+
tocGenerator;
|
|
2205
|
+
configManager;
|
|
947
2206
|
constructor() {
|
|
948
2207
|
this.tocGenerator = new TocGenerator();
|
|
949
2208
|
this.configManager = ConfigManager.getInstance();
|
|
@@ -957,18 +2216,18 @@ var ReadmeGenerator = class {
|
|
|
957
2216
|
logger.warn(`\u7B14\u8BB0 ${noteInfo.dirName} \u7F3A\u5C11\u914D\u7F6E\u6587\u4EF6`);
|
|
958
2217
|
return;
|
|
959
2218
|
}
|
|
960
|
-
const content =
|
|
2219
|
+
const content = readFileSync3(noteInfo.readmePath, "utf-8");
|
|
961
2220
|
if (content.length === 0) return;
|
|
962
2221
|
const lines = content.split(EOL);
|
|
963
|
-
const
|
|
2222
|
+
const repoName2 = this.configManager.get("repoName");
|
|
964
2223
|
this.tocGenerator.updateNoteToc(
|
|
965
2224
|
noteInfo.index,
|
|
966
2225
|
lines,
|
|
967
2226
|
noteInfo.config,
|
|
968
|
-
|
|
2227
|
+
repoName2
|
|
969
2228
|
);
|
|
970
2229
|
const updatedContent = lines.join(EOL);
|
|
971
|
-
|
|
2230
|
+
writeFileSync2(noteInfo.readmePath, updatedContent, "utf-8");
|
|
972
2231
|
}
|
|
973
2232
|
/**
|
|
974
2233
|
* 更新首页 README
|
|
@@ -977,18 +2236,18 @@ var ReadmeGenerator = class {
|
|
|
977
2236
|
* @param homeReadmePath - 首页 README 路径
|
|
978
2237
|
*/
|
|
979
2238
|
updateHomeReadme(notes, homeReadmePath) {
|
|
980
|
-
if (!
|
|
2239
|
+
if (!existsSync5(homeReadmePath)) {
|
|
981
2240
|
logger.error(`\u6839\u76EE\u5F55\u4E0B\u7684 README.md \u6587\u4EF6\u672A\u627E\u5230\uFF1A${homeReadmePath}`);
|
|
982
2241
|
return;
|
|
983
2242
|
}
|
|
984
|
-
const content =
|
|
2243
|
+
const content = readFileSync3(homeReadmePath, "utf-8");
|
|
985
2244
|
const lines = content.split(EOL);
|
|
986
2245
|
const noteByIndexMap = /* @__PURE__ */ new Map();
|
|
987
2246
|
for (const note of notes) {
|
|
988
2247
|
noteByIndexMap.set(note.index, note);
|
|
989
2248
|
}
|
|
990
2249
|
const repoOwner = this.configManager.get("author");
|
|
991
|
-
const
|
|
2250
|
+
const repoName2 = this.configManager.get("repoName");
|
|
992
2251
|
const existingNoteIndexes = /* @__PURE__ */ new Set();
|
|
993
2252
|
const linesToRemove = /* @__PURE__ */ new Set();
|
|
994
2253
|
const titles = [];
|
|
@@ -1019,7 +2278,7 @@ var ReadmeGenerator = class {
|
|
|
1019
2278
|
continue;
|
|
1020
2279
|
}
|
|
1021
2280
|
existingNoteIndexes.add(parsed.noteIndex);
|
|
1022
|
-
lines[i] = buildNoteLineMarkdown(note, repoOwner,
|
|
2281
|
+
lines[i] = buildNoteLineMarkdown(note, repoOwner, repoName2);
|
|
1023
2282
|
currentNoteCount++;
|
|
1024
2283
|
continue;
|
|
1025
2284
|
}
|
|
@@ -1062,7 +2321,7 @@ var ReadmeGenerator = class {
|
|
|
1062
2321
|
logger.info(`\u6DFB\u52A0 ${missingNotes.length} \u7BC7\u7F3A\u5931\u7684\u7B14\u8BB0\u5230 README`);
|
|
1063
2322
|
missingNotes.sort((a, b) => a.index.localeCompare(b.index));
|
|
1064
2323
|
for (const note of missingNotes) {
|
|
1065
|
-
const noteLine = buildNoteLineMarkdown(note, repoOwner,
|
|
2324
|
+
const noteLine = buildNoteLineMarkdown(note, repoOwner, repoName2);
|
|
1066
2325
|
lines.push(noteLine);
|
|
1067
2326
|
currentNoteCount++;
|
|
1068
2327
|
}
|
|
@@ -1073,24 +2332,22 @@ var ReadmeGenerator = class {
|
|
|
1073
2332
|
this.tocGenerator.updateHomeToc(lines, titles, titlesNotesCount);
|
|
1074
2333
|
const processedLines = processEmptyLines(lines);
|
|
1075
2334
|
const updatedContent = processedLines.join(EOL);
|
|
1076
|
-
|
|
2335
|
+
writeFileSync2(homeReadmePath, updatedContent, "utf-8");
|
|
1077
2336
|
logger.info("\u5DF2\u66F4\u65B0\u9996\u9875 README");
|
|
1078
2337
|
}
|
|
1079
2338
|
};
|
|
1080
2339
|
|
|
1081
2340
|
// core/NoteIndexCache.ts
|
|
1082
|
-
import { join as
|
|
2341
|
+
import { join as join7 } from "path";
|
|
1083
2342
|
var NoteIndexCache = class _NoteIndexCache {
|
|
2343
|
+
static instance = null;
|
|
2344
|
+
/** noteIndex -> NoteIndexItem 的映射 */
|
|
2345
|
+
byNoteIndex = /* @__PURE__ */ new Map();
|
|
2346
|
+
/** configId (UUID) -> noteIndex 的映射,用于快速反向查询 */
|
|
2347
|
+
byConfigId = /* @__PURE__ */ new Map();
|
|
2348
|
+
/** 是否已完成初始化 */
|
|
2349
|
+
_initialized = false;
|
|
1084
2350
|
constructor() {
|
|
1085
|
-
/** noteIndex -> NoteIndexItem 的映射 */
|
|
1086
|
-
this.byNoteIndex = /* @__PURE__ */ new Map();
|
|
1087
|
-
/** configId (UUID) -> noteIndex 的映射,用于快速反向查询 */
|
|
1088
|
-
this.byConfigId = /* @__PURE__ */ new Map();
|
|
1089
|
-
/** 是否已完成初始化 */
|
|
1090
|
-
this._initialized = false;
|
|
1091
|
-
}
|
|
1092
|
-
static {
|
|
1093
|
-
this.instance = null;
|
|
1094
2351
|
}
|
|
1095
2352
|
/**
|
|
1096
2353
|
* 获取单例实例
|
|
@@ -1132,13 +2389,13 @@ var NoteIndexCache = class _NoteIndexCache {
|
|
|
1132
2389
|
toNoteInfoList() {
|
|
1133
2390
|
const result = [];
|
|
1134
2391
|
for (const item of this.byNoteIndex.values()) {
|
|
1135
|
-
const notePath =
|
|
2392
|
+
const notePath = join7(NOTES_PATH, item.folderName);
|
|
1136
2393
|
result.push({
|
|
1137
2394
|
index: item.noteIndex,
|
|
1138
2395
|
path: notePath,
|
|
1139
2396
|
dirName: item.folderName,
|
|
1140
|
-
readmePath:
|
|
1141
|
-
configPath:
|
|
2397
|
+
readmePath: join7(notePath, "README.md"),
|
|
2398
|
+
configPath: join7(notePath, ".tnotes.json"),
|
|
1142
2399
|
config: item.noteConfig
|
|
1143
2400
|
});
|
|
1144
2401
|
}
|
|
@@ -1224,12 +2481,17 @@ var NoteIndexCache = class _NoteIndexCache {
|
|
|
1224
2481
|
|
|
1225
2482
|
// services/readme/service.ts
|
|
1226
2483
|
import {
|
|
1227
|
-
existsSync as
|
|
1228
|
-
readFileSync as
|
|
1229
|
-
writeFileSync as
|
|
2484
|
+
existsSync as existsSync6,
|
|
2485
|
+
readFileSync as readFileSync4,
|
|
2486
|
+
writeFileSync as writeFileSync3,
|
|
1230
2487
|
promises as fsPromises2
|
|
1231
2488
|
} from "fs";
|
|
1232
2489
|
var ReadmeService = class _ReadmeService {
|
|
2490
|
+
static instance;
|
|
2491
|
+
noteManager;
|
|
2492
|
+
readmeGenerator;
|
|
2493
|
+
configManager;
|
|
2494
|
+
noteIndexCache;
|
|
1233
2495
|
constructor() {
|
|
1234
2496
|
this.noteManager = NoteManager.getInstance();
|
|
1235
2497
|
this.readmeGenerator = new ReadmeGenerator();
|
|
@@ -1354,11 +2616,11 @@ var ReadmeService = class _ReadmeService {
|
|
|
1354
2616
|
* @param notes - 笔记信息数组
|
|
1355
2617
|
*/
|
|
1356
2618
|
async updateSidebar(notes) {
|
|
1357
|
-
if (!
|
|
2619
|
+
if (!existsSync6(ROOT_README_PATH)) {
|
|
1358
2620
|
logger.error("\u672A\u627E\u5230\u9996\u9875 README\uFF0C\u65E0\u6CD5\u751F\u6210\u4FA7\u8FB9\u680F");
|
|
1359
2621
|
return;
|
|
1360
2622
|
}
|
|
1361
|
-
const content =
|
|
2623
|
+
const content = readFileSync4(ROOT_README_PATH, "utf-8");
|
|
1362
2624
|
const lines = content.split("\n");
|
|
1363
2625
|
const itemList = [];
|
|
1364
2626
|
const titles = [];
|
|
@@ -1390,9 +2652,9 @@ var ReadmeService = class _ReadmeService {
|
|
|
1390
2652
|
statusEmoji = "\u2705 ";
|
|
1391
2653
|
}
|
|
1392
2654
|
}
|
|
1393
|
-
const
|
|
2655
|
+
const sidebarShowNoteId2 = this.configManager.get("sidebarShowNoteId");
|
|
1394
2656
|
let displayText = note.dirName;
|
|
1395
|
-
if (!
|
|
2657
|
+
if (!sidebarShowNoteId2) {
|
|
1396
2658
|
displayText = note.dirName.replace(/^\d{4}\.\s/, "");
|
|
1397
2659
|
}
|
|
1398
2660
|
itemList.push({
|
|
@@ -1421,7 +2683,7 @@ var ReadmeService = class _ReadmeService {
|
|
|
1421
2683
|
titlesNotesCount,
|
|
1422
2684
|
sidebarIsCollapsed
|
|
1423
2685
|
);
|
|
1424
|
-
|
|
2686
|
+
writeFileSync3(
|
|
1425
2687
|
VP_SIDEBAR_PATH,
|
|
1426
2688
|
JSON.stringify(hierarchicalSidebar, null, 2),
|
|
1427
2689
|
"utf-8"
|
|
@@ -1449,7 +2711,7 @@ var ReadmeService = class _ReadmeService {
|
|
|
1449
2711
|
const content = await fsPromises2.readFile(ROOT_README_PATH, "utf-8");
|
|
1450
2712
|
const lines = content.split("\n");
|
|
1451
2713
|
const repoOwner = this.configManager.get("author");
|
|
1452
|
-
const
|
|
2714
|
+
const repoName2 = this.configManager.get("repoName");
|
|
1453
2715
|
const mergedConfig = { ...item.noteConfig, ...updates };
|
|
1454
2716
|
const tempNoteInfo = {
|
|
1455
2717
|
index: noteIndex,
|
|
@@ -1463,7 +2725,7 @@ var ReadmeService = class _ReadmeService {
|
|
|
1463
2725
|
for (let i = 0; i < lines.length; i++) {
|
|
1464
2726
|
const parsed = parseNoteLine(lines[i]);
|
|
1465
2727
|
if (parsed.noteIndex === noteIndex) {
|
|
1466
|
-
lines[i] = buildNoteLineMarkdown(tempNoteInfo, repoOwner,
|
|
2728
|
+
lines[i] = buildNoteLineMarkdown(tempNoteInfo, repoOwner, repoName2);
|
|
1467
2729
|
updated = true;
|
|
1468
2730
|
}
|
|
1469
2731
|
}
|
|
@@ -1513,7 +2775,7 @@ var ReadmeService = class _ReadmeService {
|
|
|
1513
2775
|
const content = await fsPromises2.readFile(ROOT_README_PATH, "utf-8");
|
|
1514
2776
|
const lines = content.split("\n");
|
|
1515
2777
|
const repoOwner = this.configManager.get("author");
|
|
1516
|
-
const
|
|
2778
|
+
const repoName2 = this.configManager.get("repoName");
|
|
1517
2779
|
const tempNoteInfo = {
|
|
1518
2780
|
index: noteIndex,
|
|
1519
2781
|
dirName: item.folderName,
|
|
@@ -1522,7 +2784,7 @@ var ReadmeService = class _ReadmeService {
|
|
|
1522
2784
|
configPath: "",
|
|
1523
2785
|
config: item.noteConfig
|
|
1524
2786
|
};
|
|
1525
|
-
const noteLine = buildNoteLineMarkdown(tempNoteInfo, repoOwner,
|
|
2787
|
+
const noteLine = buildNoteLineMarkdown(tempNoteInfo, repoOwner, repoName2);
|
|
1526
2788
|
lines.push(noteLine);
|
|
1527
2789
|
await fsPromises2.writeFile(ROOT_README_PATH, lines.join("\n"), "utf-8");
|
|
1528
2790
|
logger.info(`\u5728 README.md \u672B\u5C3E\u6DFB\u52A0\u7B14\u8BB0: ${noteIndex}`);
|
|
@@ -1539,8 +2801,8 @@ var ReadmeService = class _ReadmeService {
|
|
|
1539
2801
|
};
|
|
1540
2802
|
|
|
1541
2803
|
// services/note/service.ts
|
|
1542
|
-
import { writeFileSync as
|
|
1543
|
-
import { join as
|
|
2804
|
+
import { writeFileSync as writeFileSync4, readFileSync as readFileSync5 } from "fs";
|
|
2805
|
+
import { join as join8 } from "path";
|
|
1544
2806
|
import { v4 as uuidv4 } from "uuid";
|
|
1545
2807
|
|
|
1546
2808
|
// config/templates.ts
|
|
@@ -1568,8 +2830,11 @@ var NEW_NOTES_README_MD_TEMPLATE = `
|
|
|
1568
2830
|
- todo
|
|
1569
2831
|
`;
|
|
1570
2832
|
var NoteService = class _NoteService {
|
|
2833
|
+
static instance;
|
|
2834
|
+
noteManager;
|
|
2835
|
+
noteIndexCache;
|
|
2836
|
+
ignoredConfigPaths = /* @__PURE__ */ new Set();
|
|
1571
2837
|
constructor() {
|
|
1572
|
-
this.ignoredConfigPaths = /* @__PURE__ */ new Set();
|
|
1573
2838
|
this.noteManager = NoteManager.getInstance();
|
|
1574
2839
|
this.noteIndexCache = NoteIndexCache.getInstance();
|
|
1575
2840
|
}
|
|
@@ -1632,15 +2897,15 @@ var NoteService = class _NoteService {
|
|
|
1632
2897
|
} = options;
|
|
1633
2898
|
const noteIndex = this.generateNextNoteIndex(usedIndexes);
|
|
1634
2899
|
const dirName = `${noteIndex}. ${title}`;
|
|
1635
|
-
const notePath =
|
|
2900
|
+
const notePath = join8(NOTES_PATH, dirName);
|
|
1636
2901
|
await ensureDirectory(notePath);
|
|
1637
|
-
const readmePath =
|
|
2902
|
+
const readmePath = join8(notePath, "README.md");
|
|
1638
2903
|
const noteTitle = generateNoteTitle(noteIndex, title, REPO_NOTES_URL);
|
|
1639
2904
|
const readmeContent = noteTitle + "\n" + NEW_NOTES_README_MD_TEMPLATE;
|
|
1640
|
-
|
|
1641
|
-
const configPath =
|
|
2905
|
+
writeFileSync4(readmePath, readmeContent, "utf-8");
|
|
2906
|
+
const configPath = join8(notePath, ".tnotes.json");
|
|
1642
2907
|
const now = Date.now();
|
|
1643
|
-
const
|
|
2908
|
+
const config2 = {
|
|
1644
2909
|
id: configId || uuidv4(),
|
|
1645
2910
|
// 配置 ID 使用 UUID(跨知识库唯一)
|
|
1646
2911
|
bilibili: [],
|
|
@@ -1652,7 +2917,7 @@ var NoteService = class _NoteService {
|
|
|
1652
2917
|
created_at: now,
|
|
1653
2918
|
updated_at: now
|
|
1654
2919
|
};
|
|
1655
|
-
this.noteManager.writeNoteConfig(configPath,
|
|
2920
|
+
this.noteManager.writeNoteConfig(configPath, config2);
|
|
1656
2921
|
logger.info(`Created new note: ${dirName}`);
|
|
1657
2922
|
return {
|
|
1658
2923
|
index: noteIndex,
|
|
@@ -1661,7 +2926,7 @@ var NoteService = class _NoteService {
|
|
|
1661
2926
|
dirName,
|
|
1662
2927
|
readmePath,
|
|
1663
2928
|
configPath,
|
|
1664
|
-
config
|
|
2929
|
+
config: config2
|
|
1665
2930
|
};
|
|
1666
2931
|
}
|
|
1667
2932
|
/**
|
|
@@ -1745,7 +3010,7 @@ var NoteService = class _NoteService {
|
|
|
1745
3010
|
*/
|
|
1746
3011
|
async fixNoteTitle(noteInfo) {
|
|
1747
3012
|
try {
|
|
1748
|
-
const readmeContent =
|
|
3013
|
+
const readmeContent = readFileSync5(noteInfo.readmePath, "utf-8");
|
|
1749
3014
|
if (readmeContent.length === 0) return false;
|
|
1750
3015
|
const lines = readmeContent.split("\n");
|
|
1751
3016
|
const match = noteInfo.dirName.match(/^\d{4}\.\s+(.+)$/);
|
|
@@ -1762,13 +3027,13 @@ var NoteService = class _NoteService {
|
|
|
1762
3027
|
const firstLine = lines[0].trim();
|
|
1763
3028
|
if (!firstLine.startsWith("# ")) {
|
|
1764
3029
|
lines.unshift(expectedH1);
|
|
1765
|
-
|
|
3030
|
+
writeFileSync4(noteInfo.readmePath, lines.join("\n"), "utf-8");
|
|
1766
3031
|
logger.info(`Added title to: ${noteInfo.dirName}`);
|
|
1767
3032
|
return true;
|
|
1768
3033
|
}
|
|
1769
3034
|
if (firstLine !== expectedH1) {
|
|
1770
3035
|
lines[0] = expectedH1;
|
|
1771
|
-
|
|
3036
|
+
writeFileSync4(noteInfo.readmePath, lines.join("\n"), "utf-8");
|
|
1772
3037
|
logger.info(`Fixed title for: ${noteInfo.dirName}`);
|
|
1773
3038
|
return true;
|
|
1774
3039
|
}
|
|
@@ -1805,12 +3070,23 @@ var UPDATE_UNLOCK_DELAY_MS2 = 500;
|
|
|
1805
3070
|
var FileWatcherService = class {
|
|
1806
3071
|
constructor(notesDir = NOTES_DIR_PATH) {
|
|
1807
3072
|
this.notesDir = notesDir;
|
|
1808
|
-
this.unlockTimer = null;
|
|
1809
3073
|
if (!this.notesDir) {
|
|
1810
3074
|
throw new Error(NOTES_DIR_NOT_SET_ERROR);
|
|
1811
3075
|
}
|
|
1812
3076
|
this.init();
|
|
1813
3077
|
}
|
|
3078
|
+
watchState;
|
|
3079
|
+
scheduler;
|
|
3080
|
+
renameDetector;
|
|
3081
|
+
configHandler;
|
|
3082
|
+
readmeHandler;
|
|
3083
|
+
coordinator;
|
|
3084
|
+
folderHandler;
|
|
3085
|
+
adapter;
|
|
3086
|
+
noteService;
|
|
3087
|
+
readmeService;
|
|
3088
|
+
noteIndexCache;
|
|
3089
|
+
unlockTimer = null;
|
|
1814
3090
|
init() {
|
|
1815
3091
|
this.noteService = NoteService.getInstance();
|
|
1816
3092
|
this.readmeService = ReadmeService.getInstance();
|
|
@@ -1962,9 +3238,11 @@ var FileWatcherService = class {
|
|
|
1962
3238
|
};
|
|
1963
3239
|
|
|
1964
3240
|
// core/GitManager.ts
|
|
1965
|
-
import { resolve } from "path";
|
|
1966
|
-
import { existsSync as
|
|
3241
|
+
import { resolve as resolve2 } from "path";
|
|
3242
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1967
3243
|
var GitManager = class {
|
|
3244
|
+
logger;
|
|
3245
|
+
dir;
|
|
1968
3246
|
constructor(dir, logger2) {
|
|
1969
3247
|
this.dir = dir;
|
|
1970
3248
|
this.logger = logger2?.child("git") || new Logger({ prefix: "git" });
|
|
@@ -2003,8 +3281,8 @@ var GitManager = class {
|
|
|
2003
3281
|
const lines = statusOutput.trim().split("\n").filter((line) => line);
|
|
2004
3282
|
const files = lines.map((line) => {
|
|
2005
3283
|
const statusCode = line.substring(0, 2);
|
|
2006
|
-
let
|
|
2007
|
-
|
|
3284
|
+
let path2 = line.substring(3);
|
|
3285
|
+
path2 = path2.replace(/^"(.*)"$/, "$1");
|
|
2008
3286
|
let status = "modified";
|
|
2009
3287
|
if (line.startsWith("??")) {
|
|
2010
3288
|
status = "untracked";
|
|
@@ -2013,7 +3291,7 @@ var GitManager = class {
|
|
|
2013
3291
|
} else if (/^.[MD]/.test(statusCode)) {
|
|
2014
3292
|
status = "unstaged";
|
|
2015
3293
|
}
|
|
2016
|
-
return { path, status, statusCode };
|
|
3294
|
+
return { path: path2, status, statusCode };
|
|
2017
3295
|
});
|
|
2018
3296
|
const staged = files.filter((f) => f.status === "staged").length;
|
|
2019
3297
|
const unstaged = files.filter((f) => f.status === "unstaged").length;
|
|
@@ -2278,7 +3556,7 @@ var GitManager = class {
|
|
|
2278
3556
|
* 检查仓库是否包含 submodule
|
|
2279
3557
|
*/
|
|
2280
3558
|
hasSubmodules() {
|
|
2281
|
-
return
|
|
3559
|
+
return existsSync7(resolve2(this.dir, ".gitmodules"));
|
|
2282
3560
|
}
|
|
2283
3561
|
/**
|
|
2284
3562
|
* 获取所有 submodule 的路径
|
|
@@ -2302,7 +3580,7 @@ var GitManager = class {
|
|
|
2302
3580
|
const paths = await this.getSubmodulePaths();
|
|
2303
3581
|
if (paths.length === 0) return;
|
|
2304
3582
|
for (const subPath of paths) {
|
|
2305
|
-
const absPath =
|
|
3583
|
+
const absPath = resolve2(this.dir, subPath);
|
|
2306
3584
|
let hasChanges = false;
|
|
2307
3585
|
try {
|
|
2308
3586
|
const status = await runCommand("git status --porcelain", absPath);
|
|
@@ -2406,8 +3684,9 @@ var GitManager = class {
|
|
|
2406
3684
|
// core/ProcessManager.ts
|
|
2407
3685
|
import { spawn } from "child_process";
|
|
2408
3686
|
var ProcessManager = class {
|
|
3687
|
+
processes = /* @__PURE__ */ new Map();
|
|
3688
|
+
logger;
|
|
2409
3689
|
constructor() {
|
|
2410
|
-
this.processes = /* @__PURE__ */ new Map();
|
|
2411
3690
|
this.logger = new Logger({ prefix: "process" });
|
|
2412
3691
|
process.on("exit", () => {
|
|
2413
3692
|
this.killAll();
|
|
@@ -2528,6 +3807,7 @@ var ProcessManager = class {
|
|
|
2528
3807
|
|
|
2529
3808
|
// services/git/service.ts
|
|
2530
3809
|
var GitService = class {
|
|
3810
|
+
gitManager;
|
|
2531
3811
|
constructor() {
|
|
2532
3812
|
this.gitManager = new GitManager(ROOT_DIR_PATH);
|
|
2533
3813
|
}
|
|
@@ -2603,26 +3883,26 @@ var GitService = class {
|
|
|
2603
3883
|
};
|
|
2604
3884
|
|
|
2605
3885
|
// services/sync-core/service.ts
|
|
2606
|
-
import { existsSync as
|
|
2607
|
-
import { join as
|
|
3886
|
+
import { existsSync as existsSync8 } from "fs";
|
|
3887
|
+
import { join as join9, basename as basename2 } from "path";
|
|
2608
3888
|
var SyncCoreService = class {
|
|
2609
3889
|
/**
|
|
2610
3890
|
* 同步单个仓库的 submodule 到最新版本
|
|
2611
3891
|
*/
|
|
2612
3892
|
async syncSingleRepo(targetDir) {
|
|
2613
|
-
const
|
|
2614
|
-
const submodulePath =
|
|
3893
|
+
const repoName2 = basename2(targetDir);
|
|
3894
|
+
const submodulePath = join9(targetDir, ".vitepress", "tnotes");
|
|
2615
3895
|
try {
|
|
2616
|
-
if (!
|
|
3896
|
+
if (!existsSync8(join9(targetDir, ".gitmodules"))) {
|
|
2617
3897
|
return {
|
|
2618
3898
|
dir: targetDir,
|
|
2619
|
-
repoName,
|
|
3899
|
+
repoName: repoName2,
|
|
2620
3900
|
success: false,
|
|
2621
3901
|
updated: false,
|
|
2622
3902
|
error: "\u672A\u627E\u5230 .gitmodules\uFF0C\u8BE5\u4ED3\u5E93\u672A\u914D\u7F6E submodule"
|
|
2623
3903
|
};
|
|
2624
3904
|
}
|
|
2625
|
-
if (!
|
|
3905
|
+
if (!existsSync8(submodulePath)) {
|
|
2626
3906
|
await runCommand("git submodule update --init", targetDir);
|
|
2627
3907
|
}
|
|
2628
3908
|
const beforeHash = (await runCommand("git rev-parse HEAD", submodulePath)).trim();
|
|
@@ -2634,11 +3914,14 @@ var SyncCoreService = class {
|
|
|
2634
3914
|
const updated = beforeHash !== afterHash;
|
|
2635
3915
|
if (updated) {
|
|
2636
3916
|
await runCommand("git add .vitepress/tnotes", targetDir);
|
|
2637
|
-
await runCommand(
|
|
3917
|
+
await runCommand(
|
|
3918
|
+
'git commit -m "chore: update tnotesjs/core"',
|
|
3919
|
+
targetDir
|
|
3920
|
+
);
|
|
2638
3921
|
}
|
|
2639
3922
|
return {
|
|
2640
3923
|
dir: targetDir,
|
|
2641
|
-
repoName,
|
|
3924
|
+
repoName: repoName2,
|
|
2642
3925
|
success: true,
|
|
2643
3926
|
updated,
|
|
2644
3927
|
beforeHash: beforeHash.substring(0, 7),
|
|
@@ -2650,7 +3933,7 @@ var SyncCoreService = class {
|
|
|
2650
3933
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2651
3934
|
return {
|
|
2652
3935
|
dir: targetDir,
|
|
2653
|
-
repoName,
|
|
3936
|
+
repoName: repoName2,
|
|
2654
3937
|
success: false,
|
|
2655
3938
|
updated: false,
|
|
2656
3939
|
error: errorMessage
|
|
@@ -2658,7 +3941,7 @@ var SyncCoreService = class {
|
|
|
2658
3941
|
}
|
|
2659
3942
|
}
|
|
2660
3943
|
/**
|
|
2661
|
-
* 同步所有兄弟仓库的
|
|
3944
|
+
* 同步所有兄弟仓库的 tnotesjs/core 到最新版本
|
|
2662
3945
|
*/
|
|
2663
3946
|
async syncToAllRepos() {
|
|
2664
3947
|
try {
|
|
@@ -2671,13 +3954,13 @@ var SyncCoreService = class {
|
|
|
2671
3954
|
logger.warn("\u672A\u627E\u5230\u7B26\u5408\u6761\u4EF6\u7684\u76EE\u6807\u76EE\u5F55");
|
|
2672
3955
|
return;
|
|
2673
3956
|
}
|
|
2674
|
-
logger.info(`\u6B63\u5728\u540C\u6B65 ${targetDirs.length} \u4E2A\u4ED3\u5E93\u7684
|
|
3957
|
+
logger.info(`\u6B63\u5728\u540C\u6B65 ${targetDirs.length} \u4E2A\u4ED3\u5E93\u7684 tnotesjs/core...`);
|
|
2675
3958
|
console.log();
|
|
2676
3959
|
const results = [];
|
|
2677
3960
|
for (let i = 0; i < targetDirs.length; i++) {
|
|
2678
3961
|
const dir = targetDirs[i];
|
|
2679
|
-
const
|
|
2680
|
-
logger.info(`[${i + 1}/${targetDirs.length}] ${
|
|
3962
|
+
const repoName2 = basename2(dir);
|
|
3963
|
+
logger.info(`[${i + 1}/${targetDirs.length}] ${repoName2}`);
|
|
2681
3964
|
const result = await this.syncSingleRepo(dir);
|
|
2682
3965
|
results.push(result);
|
|
2683
3966
|
if (result.success) {
|
|
@@ -2717,7 +4000,7 @@ var SyncCoreService = class {
|
|
|
2717
4000
|
}
|
|
2718
4001
|
} catch (error) {
|
|
2719
4002
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2720
|
-
logger.error(`
|
|
4003
|
+
logger.error(`tnotesjs/core \u540C\u6B65\u5931\u8D25: ${errorMessage}`);
|
|
2721
4004
|
throw error;
|
|
2722
4005
|
}
|
|
2723
4006
|
}
|
|
@@ -2725,16 +4008,17 @@ var SyncCoreService = class {
|
|
|
2725
4008
|
|
|
2726
4009
|
// services/timestamp/service.ts
|
|
2727
4010
|
import {
|
|
2728
|
-
existsSync as
|
|
2729
|
-
readFileSync as
|
|
2730
|
-
writeFileSync as
|
|
2731
|
-
readdirSync as
|
|
4011
|
+
existsSync as existsSync9,
|
|
4012
|
+
readFileSync as readFileSync6,
|
|
4013
|
+
writeFileSync as writeFileSync5,
|
|
4014
|
+
readdirSync as readdirSync4,
|
|
2732
4015
|
statSync as statSync2
|
|
2733
4016
|
} from "fs";
|
|
2734
|
-
import { join as
|
|
2735
|
-
import { execSync } from "child_process";
|
|
4017
|
+
import { join as join10 } from "path";
|
|
4018
|
+
import { execSync as execSync3 } from "child_process";
|
|
2736
4019
|
var BIRTH_DATE = (/* @__PURE__ */ new Date("1999-06-29T00:00:00+08:00")).getTime();
|
|
2737
4020
|
var TimestampService = class {
|
|
4021
|
+
noteManager;
|
|
2738
4022
|
constructor() {
|
|
2739
4023
|
this.noteManager = NoteManager.getInstance();
|
|
2740
4024
|
}
|
|
@@ -2745,8 +4029,8 @@ var TimestampService = class {
|
|
|
2745
4029
|
*/
|
|
2746
4030
|
getGitTimestamps(noteDirPath) {
|
|
2747
4031
|
try {
|
|
2748
|
-
const readmePath =
|
|
2749
|
-
const createdAtOutput =
|
|
4032
|
+
const readmePath = join10(noteDirPath, "README.md");
|
|
4033
|
+
const createdAtOutput = execSync3(
|
|
2750
4034
|
`git log --diff-filter=A --follow --format=%ct -- "${readmePath}"`,
|
|
2751
4035
|
{
|
|
2752
4036
|
cwd: ROOT_DIR_PATH,
|
|
@@ -2754,7 +4038,7 @@ var TimestampService = class {
|
|
|
2754
4038
|
stdio: ["pipe", "pipe", "ignore"]
|
|
2755
4039
|
}
|
|
2756
4040
|
).split(/\r?\n/).filter(Boolean).pop();
|
|
2757
|
-
const updatedAtOutput =
|
|
4041
|
+
const updatedAtOutput = execSync3(
|
|
2758
4042
|
`git log -1 --format=%ct -- "${readmePath}"`,
|
|
2759
4043
|
{
|
|
2760
4044
|
cwd: ROOT_DIR_PATH,
|
|
@@ -2782,39 +4066,39 @@ var TimestampService = class {
|
|
|
2782
4066
|
* @returns 是否进行了修复
|
|
2783
4067
|
*/
|
|
2784
4068
|
fixNoteTimestamps(noteDir, forceUpdate = false) {
|
|
2785
|
-
const configPath =
|
|
2786
|
-
if (!
|
|
4069
|
+
const configPath = join10(NOTES_DIR_PATH, noteDir, ".tnotes.json");
|
|
4070
|
+
if (!existsSync9(configPath)) {
|
|
2787
4071
|
return false;
|
|
2788
4072
|
}
|
|
2789
4073
|
try {
|
|
2790
|
-
const configContent =
|
|
2791
|
-
const
|
|
2792
|
-
const noteDirPath =
|
|
4074
|
+
const configContent = readFileSync6(configPath, "utf-8");
|
|
4075
|
+
const config2 = JSON.parse(configContent);
|
|
4076
|
+
const noteDirPath = join10(NOTES_DIR_PATH, noteDir);
|
|
2793
4077
|
const timestamps = this.getGitTimestamps(noteDirPath);
|
|
2794
4078
|
if (!timestamps) {
|
|
2795
4079
|
return false;
|
|
2796
4080
|
}
|
|
2797
4081
|
let modified = false;
|
|
2798
|
-
if (forceUpdate || !
|
|
2799
|
-
|
|
4082
|
+
if (forceUpdate || !config2.created_at || config2.created_at !== timestamps.created_at) {
|
|
4083
|
+
config2.created_at = timestamps.created_at;
|
|
2800
4084
|
modified = true;
|
|
2801
4085
|
}
|
|
2802
4086
|
if (forceUpdate) {
|
|
2803
|
-
if (
|
|
2804
|
-
|
|
4087
|
+
if (config2.updated_at !== timestamps.updated_at) {
|
|
4088
|
+
config2.updated_at = timestamps.updated_at;
|
|
2805
4089
|
modified = true;
|
|
2806
4090
|
}
|
|
2807
4091
|
} else {
|
|
2808
|
-
if (!
|
|
2809
|
-
|
|
4092
|
+
if (!config2.updated_at) {
|
|
4093
|
+
config2.updated_at = timestamps.updated_at;
|
|
2810
4094
|
modified = true;
|
|
2811
|
-
} else if (timestamps.updated_at >
|
|
2812
|
-
|
|
4095
|
+
} else if (timestamps.updated_at > config2.updated_at) {
|
|
4096
|
+
config2.updated_at = timestamps.updated_at;
|
|
2813
4097
|
modified = true;
|
|
2814
4098
|
}
|
|
2815
4099
|
}
|
|
2816
4100
|
if (modified) {
|
|
2817
|
-
this.noteManager.writeNoteConfig(configPath,
|
|
4101
|
+
this.noteManager.writeNoteConfig(configPath, config2);
|
|
2818
4102
|
return true;
|
|
2819
4103
|
}
|
|
2820
4104
|
return false;
|
|
@@ -2830,14 +4114,14 @@ var TimestampService = class {
|
|
|
2830
4114
|
*/
|
|
2831
4115
|
fixRootConfigTimestamps(forceUpdate = false) {
|
|
2832
4116
|
try {
|
|
2833
|
-
const configContent =
|
|
2834
|
-
const
|
|
2835
|
-
const createdAtOutput =
|
|
4117
|
+
const configContent = readFileSync6(ROOT_CONFIG_PATH, "utf-8");
|
|
4118
|
+
const config2 = JSON.parse(configContent);
|
|
4119
|
+
const createdAtOutput = execSync3("git log --reverse --format=%ct", {
|
|
2836
4120
|
cwd: ROOT_DIR_PATH,
|
|
2837
4121
|
encoding: "utf-8",
|
|
2838
4122
|
stdio: ["pipe", "pipe", "ignore"]
|
|
2839
4123
|
}).trim();
|
|
2840
|
-
const updatedAtOutput =
|
|
4124
|
+
const updatedAtOutput = execSync3("git log -1 --format=%ct", {
|
|
2841
4125
|
cwd: ROOT_DIR_PATH,
|
|
2842
4126
|
encoding: "utf-8",
|
|
2843
4127
|
stdio: ["pipe", "pipe", "ignore"]
|
|
@@ -2849,20 +4133,20 @@ var TimestampService = class {
|
|
|
2849
4133
|
const createdAt = parseInt(firstTimestamp) * 1e3;
|
|
2850
4134
|
const updatedAt = parseInt(updatedAtOutput) * 1e3;
|
|
2851
4135
|
let modified = false;
|
|
2852
|
-
if (forceUpdate || !
|
|
2853
|
-
|
|
4136
|
+
if (forceUpdate || !config2.root_item.created_at || config2.root_item.created_at !== createdAt) {
|
|
4137
|
+
config2.root_item.created_at = createdAt;
|
|
2854
4138
|
modified = true;
|
|
2855
4139
|
}
|
|
2856
|
-
if (forceUpdate || !
|
|
2857
|
-
|
|
4140
|
+
if (forceUpdate || !config2.root_item.updated_at || config2.root_item.updated_at !== updatedAt) {
|
|
4141
|
+
config2.root_item.updated_at = updatedAt;
|
|
2858
4142
|
modified = true;
|
|
2859
4143
|
}
|
|
2860
4144
|
if (modified) {
|
|
2861
4145
|
const daysSinceBirth = Math.floor((updatedAt - BIRTH_DATE) / (1e3 * 60 * 60 * 24)) + 1;
|
|
2862
|
-
|
|
2863
|
-
|
|
4146
|
+
config2.root_item.days_since_birth = daysSinceBirth;
|
|
4147
|
+
writeFileSync5(
|
|
2864
4148
|
ROOT_CONFIG_PATH,
|
|
2865
|
-
JSON.stringify(
|
|
4149
|
+
JSON.stringify(config2, null, 2) + "\n",
|
|
2866
4150
|
"utf-8"
|
|
2867
4151
|
);
|
|
2868
4152
|
return true;
|
|
@@ -2888,12 +4172,12 @@ var TimestampService = class {
|
|
|
2888
4172
|
if (rootConfigFixed) {
|
|
2889
4173
|
logger.success("\u2705 \u6839\u914D\u7F6E\u6587\u4EF6\u65F6\u95F4\u6233\u5DF2\u4FEE\u590D");
|
|
2890
4174
|
}
|
|
2891
|
-
if (!
|
|
4175
|
+
if (!existsSync9(NOTES_DIR_PATH)) {
|
|
2892
4176
|
logger.error("notes \u76EE\u5F55\u4E0D\u5B58\u5728");
|
|
2893
4177
|
return { fixed: 0, skipped: 0, total: 0, rootConfigFixed };
|
|
2894
4178
|
}
|
|
2895
|
-
const noteDirs =
|
|
2896
|
-
const fullPath =
|
|
4179
|
+
const noteDirs = readdirSync4(NOTES_DIR_PATH).filter((name) => {
|
|
4180
|
+
const fullPath = join10(NOTES_DIR_PATH, name);
|
|
2897
4181
|
return statSync2(fullPath).isDirectory() && /^\d{4}\./.test(name);
|
|
2898
4182
|
}).sort();
|
|
2899
4183
|
let fixedCount = 0;
|
|
@@ -2930,15 +4214,15 @@ var TimestampService = class {
|
|
|
2930
4214
|
const now = Date.now();
|
|
2931
4215
|
let updatedCount = 0;
|
|
2932
4216
|
for (const noteDir of noteDirNames) {
|
|
2933
|
-
const configPath =
|
|
2934
|
-
if (!
|
|
4217
|
+
const configPath = join10(NOTES_DIR_PATH, noteDir, ".tnotes.json");
|
|
4218
|
+
if (!existsSync9(configPath)) {
|
|
2935
4219
|
continue;
|
|
2936
4220
|
}
|
|
2937
4221
|
try {
|
|
2938
|
-
const configContent =
|
|
2939
|
-
const
|
|
2940
|
-
|
|
2941
|
-
this.noteManager.writeNoteConfig(configPath,
|
|
4222
|
+
const configContent = readFileSync6(configPath, "utf-8");
|
|
4223
|
+
const config2 = JSON.parse(configContent);
|
|
4224
|
+
config2.updated_at = now;
|
|
4225
|
+
this.noteManager.writeNoteConfig(configPath, config2);
|
|
2942
4226
|
updatedCount++;
|
|
2943
4227
|
} catch (error) {
|
|
2944
4228
|
logger.error(`\u66F4\u65B0\u65F6\u95F4\u6233\u5931\u8D25: ${noteDir}`, error);
|
|
@@ -2967,42 +4251,26 @@ var TimestampService = class {
|
|
|
2967
4251
|
// services/vitepress/service.ts
|
|
2968
4252
|
import { spawn as spawn2 } from "child_process";
|
|
2969
4253
|
var VitepressService = class _VitepressService {
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
static {
|
|
2991
|
-
/** 端口释放等待超时时间(毫秒) */
|
|
2992
|
-
this.PORT_RELEASE_TIMEOUT = 3e3;
|
|
2993
|
-
}
|
|
2994
|
-
static {
|
|
2995
|
-
/** 进程清理等待时间(毫秒) */
|
|
2996
|
-
this.PROCESS_CLEANUP_DELAY = 3e3;
|
|
2997
|
-
}
|
|
2998
|
-
static {
|
|
2999
|
-
/** 显示启动服务状态行间隔(毫秒) */
|
|
3000
|
-
this.SERVER_STATUS_LINE_INTERVAL = 1e3;
|
|
3001
|
-
}
|
|
3002
|
-
static {
|
|
3003
|
-
/** 默认包管理器 */
|
|
3004
|
-
this.DEFAULT_PACKAGE_MANAGER = "pnpm";
|
|
3005
|
-
}
|
|
4254
|
+
/** VitePress 开发服务器默认端口 */
|
|
4255
|
+
static DEFAULT_DEV_PORT = 5173;
|
|
4256
|
+
/** VitePress 预览服务器默认端口 */
|
|
4257
|
+
static DEFAULT_PREVIEW_PORT = 4173;
|
|
4258
|
+
/** 开发服务器进程 ID 后缀 */
|
|
4259
|
+
static PROCESS_ID_DEV_SUFFIX = "vitepress-dev";
|
|
4260
|
+
/** 预览服务器进程 ID 后缀 */
|
|
4261
|
+
static PROCESS_ID_PREVIEW_SUFFIX = "vitepress-preview";
|
|
4262
|
+
/** 服务启动超时时间(毫秒) */
|
|
4263
|
+
static SERVER_STARTUP_TIMEOUT = 6e4;
|
|
4264
|
+
/** 端口释放等待超时时间(毫秒) */
|
|
4265
|
+
static PORT_RELEASE_TIMEOUT = 3e3;
|
|
4266
|
+
/** 进程清理等待时间(毫秒) */
|
|
4267
|
+
static PROCESS_CLEANUP_DELAY = 3e3;
|
|
4268
|
+
/** 显示启动服务状态行间隔(毫秒) */
|
|
4269
|
+
static SERVER_STATUS_LINE_INTERVAL = 1e3;
|
|
4270
|
+
/** 默认包管理器 */
|
|
4271
|
+
static DEFAULT_PACKAGE_MANAGER = "pnpm";
|
|
4272
|
+
processManager;
|
|
4273
|
+
configManager;
|
|
3006
4274
|
constructor() {
|
|
3007
4275
|
this.processManager = new ProcessManager();
|
|
3008
4276
|
this.configManager = ConfigManager.getInstance();
|
|
@@ -3012,32 +4280,32 @@ var VitepressService = class _VitepressService {
|
|
|
3012
4280
|
* @returns 启动结果(服务就绪后返回),失败时返回 undefined
|
|
3013
4281
|
*/
|
|
3014
4282
|
async startServer() {
|
|
3015
|
-
const
|
|
3016
|
-
const
|
|
3017
|
-
const processId = `${
|
|
4283
|
+
const port2 = this.configManager.get("port") || _VitepressService.DEFAULT_DEV_PORT;
|
|
4284
|
+
const repoName2 = this.configManager.get("repoName");
|
|
4285
|
+
const processId = `${repoName2}-${_VitepressService.PROCESS_ID_DEV_SUFFIX}`;
|
|
3018
4286
|
if (this.processManager.has(processId) && this.processManager.isRunning(processId)) {
|
|
3019
4287
|
this.processManager.kill(processId);
|
|
3020
4288
|
await new Promise(
|
|
3021
|
-
(
|
|
4289
|
+
(resolve4) => setTimeout(resolve4, _VitepressService.PROCESS_CLEANUP_DELAY)
|
|
3022
4290
|
);
|
|
3023
4291
|
}
|
|
3024
|
-
if (isPortInUse(
|
|
3025
|
-
logger.warn(`\u7AEF\u53E3 ${
|
|
3026
|
-
killPortProcess(
|
|
4292
|
+
if (isPortInUse(port2)) {
|
|
4293
|
+
logger.warn(`\u7AEF\u53E3 ${port2} \u88AB\u5360\u7528\uFF0C\u6B63\u5728\u6E05\u7406...`);
|
|
4294
|
+
killPortProcess(port2);
|
|
3027
4295
|
const available = await waitForPort(
|
|
3028
|
-
|
|
4296
|
+
port2,
|
|
3029
4297
|
_VitepressService.PORT_RELEASE_TIMEOUT
|
|
3030
4298
|
);
|
|
3031
4299
|
if (available) {
|
|
3032
|
-
logger.info(`\u7AEF\u53E3 ${
|
|
4300
|
+
logger.info(`\u7AEF\u53E3 ${port2} \u5DF2\u91CA\u653E\uFF0C\u7EE7\u7EED\u542F\u52A8\u670D\u52A1`);
|
|
3033
4301
|
} else {
|
|
3034
4302
|
logger.warn(
|
|
3035
|
-
`\u7AEF\u53E3 ${
|
|
4303
|
+
`\u7AEF\u53E3 ${port2} \u672A\u786E\u8BA4\u91CA\u653E\uFF0C\u4ECD\u5C06\u5C1D\u8BD5\u542F\u52A8\uFF1B\u5982\u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6E05\u7406\u8BE5\u7AEF\u53E3`
|
|
3036
4304
|
);
|
|
3037
4305
|
}
|
|
3038
4306
|
}
|
|
3039
4307
|
const pm = this.configManager.get("packageManager") || _VitepressService.DEFAULT_PACKAGE_MANAGER;
|
|
3040
|
-
const args = ["vitepress", "dev", "--port",
|
|
4308
|
+
const args = ["vitepress", "dev", "--port", port2.toString()];
|
|
3041
4309
|
const processInfo = this.processManager.spawn(processId, pm, args, {
|
|
3042
4310
|
cwd: ROOT_DIR_PATH,
|
|
3043
4311
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -3056,7 +4324,7 @@ var VitepressService = class _VitepressService {
|
|
|
3056
4324
|
* @param childProcess - 子进程
|
|
3057
4325
|
*/
|
|
3058
4326
|
waitForServerReady(childProcess) {
|
|
3059
|
-
return new Promise((
|
|
4327
|
+
return new Promise((resolve4) => {
|
|
3060
4328
|
const startTime = Date.now();
|
|
3061
4329
|
let serverReady = false;
|
|
3062
4330
|
let version = "";
|
|
@@ -3083,7 +4351,7 @@ var VitepressService = class _VitepressService {
|
|
|
3083
4351
|
process.stderr.clearLine?.(0);
|
|
3084
4352
|
process.stderr.cursorTo?.(0);
|
|
3085
4353
|
const elapsed = Date.now() - startTime;
|
|
3086
|
-
setTimeout(() =>
|
|
4354
|
+
setTimeout(() => resolve4({ version, elapsed }), 200);
|
|
3087
4355
|
return;
|
|
3088
4356
|
}
|
|
3089
4357
|
if (!serverReady) {
|
|
@@ -3109,7 +4377,7 @@ var VitepressService = class _VitepressService {
|
|
|
3109
4377
|
process.stderr.clearLine?.(0);
|
|
3110
4378
|
process.stderr.cursorTo?.(0);
|
|
3111
4379
|
logger.warn("\u542F\u52A8\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5 VitePress \u8F93\u51FA");
|
|
3112
|
-
|
|
4380
|
+
resolve4({ version, elapsed: _VitepressService.SERVER_STARTUP_TIMEOUT });
|
|
3113
4381
|
}
|
|
3114
4382
|
}, _VitepressService.SERVER_STARTUP_TIMEOUT);
|
|
3115
4383
|
});
|
|
@@ -3118,7 +4386,7 @@ var VitepressService = class _VitepressService {
|
|
|
3118
4386
|
* 构建生产版本
|
|
3119
4387
|
*/
|
|
3120
4388
|
build() {
|
|
3121
|
-
return new Promise((
|
|
4389
|
+
return new Promise((resolve4, reject) => {
|
|
3122
4390
|
const pm = this.configManager.get("packageManager") || _VitepressService.DEFAULT_PACKAGE_MANAGER;
|
|
3123
4391
|
const child = spawn2(pm, ["vitepress", "build"], {
|
|
3124
4392
|
cwd: ROOT_DIR_PATH,
|
|
@@ -3142,7 +4410,7 @@ var VitepressService = class _VitepressService {
|
|
|
3142
4410
|
});
|
|
3143
4411
|
child.on("close", (code) => {
|
|
3144
4412
|
if (code === 0) {
|
|
3145
|
-
|
|
4413
|
+
resolve4();
|
|
3146
4414
|
} else {
|
|
3147
4415
|
reject(new Error(`Command failed with code ${code}`));
|
|
3148
4416
|
}
|
|
@@ -3153,8 +4421,8 @@ var VitepressService = class _VitepressService {
|
|
|
3153
4421
|
* 预览构建后的站点
|
|
3154
4422
|
*/
|
|
3155
4423
|
async preview() {
|
|
3156
|
-
const
|
|
3157
|
-
const processId = `${
|
|
4424
|
+
const repoName2 = this.configManager.get("repoName");
|
|
4425
|
+
const processId = `${repoName2}-${_VitepressService.PROCESS_ID_PREVIEW_SUFFIX}`;
|
|
3158
4426
|
const pm = this.configManager.get("packageManager") || _VitepressService.DEFAULT_PACKAGE_MANAGER;
|
|
3159
4427
|
const args = ["vitepress", "preview"];
|
|
3160
4428
|
const previewPort = _VitepressService.DEFAULT_PREVIEW_PORT;
|
|
@@ -3187,13 +4455,15 @@ var VitepressService = class _VitepressService {
|
|
|
3187
4455
|
};
|
|
3188
4456
|
|
|
3189
4457
|
// commands/update/UpdateCommand.ts
|
|
3190
|
-
import { existsSync as
|
|
3191
|
-
import { resolve as
|
|
4458
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
4459
|
+
import { resolve as resolve3 } from "path";
|
|
3192
4460
|
var UpdateCommand = class extends BaseCommand {
|
|
4461
|
+
readmeService;
|
|
4462
|
+
noteService;
|
|
4463
|
+
quiet = false;
|
|
4464
|
+
updateAll = false;
|
|
3193
4465
|
constructor() {
|
|
3194
4466
|
super("update");
|
|
3195
|
-
this.quiet = false;
|
|
3196
|
-
this.updateAll = false;
|
|
3197
4467
|
this.readmeService = ReadmeService.getInstance();
|
|
3198
4468
|
this.noteService = NoteService.getInstance();
|
|
3199
4469
|
}
|
|
@@ -3263,10 +4533,10 @@ var UpdateCommand = class extends BaseCommand {
|
|
|
3263
4533
|
let failCount = 0;
|
|
3264
4534
|
for (let i = 0; i < targetDirs.length; i++) {
|
|
3265
4535
|
const dir = targetDirs[i];
|
|
3266
|
-
const
|
|
4536
|
+
const repoName2 = dir.split("/").pop() || dir;
|
|
3267
4537
|
try {
|
|
3268
4538
|
process.stdout.write(
|
|
3269
|
-
`\r [${i + 1}/${targetDirs.length}] \u6B63\u5728\u66F4\u65B0: ${
|
|
4539
|
+
`\r [${i + 1}/${targetDirs.length}] \u6B63\u5728\u66F4\u65B0: ${repoName2}...`
|
|
3270
4540
|
);
|
|
3271
4541
|
await runCommand("pnpm tn:update --quiet", dir);
|
|
3272
4542
|
successCount++;
|
|
@@ -3274,7 +4544,7 @@ var UpdateCommand = class extends BaseCommand {
|
|
|
3274
4544
|
failCount++;
|
|
3275
4545
|
console.log();
|
|
3276
4546
|
this.logger.error(
|
|
3277
|
-
`\u66F4\u65B0\u5931\u8D25: ${
|
|
4547
|
+
`\u66F4\u65B0\u5931\u8D25: ${repoName2} - ${error instanceof Error ? error.message : String(error)}`
|
|
3278
4548
|
);
|
|
3279
4549
|
}
|
|
3280
4550
|
}
|
|
@@ -3301,30 +4571,30 @@ var UpdateCommand = class extends BaseCommand {
|
|
|
3301
4571
|
*/
|
|
3302
4572
|
async updateRootItem() {
|
|
3303
4573
|
try {
|
|
3304
|
-
const configContent =
|
|
3305
|
-
const
|
|
3306
|
-
const readmePath =
|
|
3307
|
-
if (!
|
|
4574
|
+
const configContent = readFileSync7(ROOT_CONFIG_PATH, "utf-8");
|
|
4575
|
+
const config2 = JSON.parse(configContent);
|
|
4576
|
+
const readmePath = resolve3(ROOT_DIR_PATH, "README.md");
|
|
4577
|
+
if (!existsSync10(readmePath)) {
|
|
3308
4578
|
throw new Error("\u6839\u76EE\u5F55 README.md \u4E0D\u5B58\u5728");
|
|
3309
4579
|
}
|
|
3310
|
-
const readmeContent =
|
|
4580
|
+
const readmeContent = readFileSync7(readmePath, "utf-8");
|
|
3311
4581
|
const { completedCount } = parseReadmeCompletedNotes(readmeContent);
|
|
3312
4582
|
const now = /* @__PURE__ */ new Date();
|
|
3313
4583
|
const yearShort = String(now.getFullYear()).slice(-2);
|
|
3314
4584
|
const monthStr = String(now.getMonth() + 1).padStart(2, "0");
|
|
3315
4585
|
const currentKey = `${yearShort}.${monthStr}`;
|
|
3316
4586
|
const completedNotesCount = {
|
|
3317
|
-
...
|
|
4587
|
+
...config2.root_item.completed_notes_count || {},
|
|
3318
4588
|
[currentKey]: completedCount
|
|
3319
4589
|
};
|
|
3320
4590
|
const updatedAt = Date.now();
|
|
3321
|
-
|
|
3322
|
-
...
|
|
4591
|
+
config2.root_item = {
|
|
4592
|
+
...config2.root_item,
|
|
3323
4593
|
completed_notes_count: completedNotesCount,
|
|
3324
4594
|
updated_at: updatedAt
|
|
3325
4595
|
};
|
|
3326
|
-
delete
|
|
3327
|
-
|
|
4596
|
+
delete config2.root_item.completed_notes_count_last_month;
|
|
4597
|
+
writeFileSync6(ROOT_CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
|
|
3328
4598
|
if (!this.quiet) {
|
|
3329
4599
|
this.logger.success(
|
|
3330
4600
|
`root_item \u914D\u7F6E\u5DF2\u66F4\u65B0: ${currentKey} \u6708\u5B8C\u6210 ${completedCount} \u7BC7\u7B14\u8BB0`
|
|
@@ -3342,12 +4612,12 @@ var UpdateCommand = class extends BaseCommand {
|
|
|
3342
4612
|
};
|
|
3343
4613
|
|
|
3344
4614
|
// commands/update-completed-count/UpdateCompletedCountCommand.ts
|
|
3345
|
-
import { readFileSync as
|
|
3346
|
-
import { execSync as
|
|
4615
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
4616
|
+
import { execSync as execSync4 } from "child_process";
|
|
3347
4617
|
var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
4618
|
+
updateAll = false;
|
|
3348
4619
|
constructor() {
|
|
3349
4620
|
super("update-completed-count");
|
|
3350
|
-
this.updateAll = false;
|
|
3351
4621
|
}
|
|
3352
4622
|
/**
|
|
3353
4623
|
* 设置是否更新所有知识库
|
|
@@ -3368,16 +4638,16 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
3368
4638
|
async updateCurrentRepo() {
|
|
3369
4639
|
const startTime = Date.now();
|
|
3370
4640
|
try {
|
|
3371
|
-
const configContent =
|
|
3372
|
-
const
|
|
4641
|
+
const configContent = readFileSync8(ROOT_CONFIG_PATH, "utf-8");
|
|
4642
|
+
const config2 = JSON.parse(configContent);
|
|
3373
4643
|
this.logger.info("\u5F00\u59CB\u66F4\u65B0\u5B8C\u6210\u7B14\u8BB0\u6570\u91CF\u5386\u53F2\u8BB0\u5F55...");
|
|
3374
|
-
const completedNotesCountHistory = await this.getCompletedNotesCountHistory(
|
|
3375
|
-
|
|
3376
|
-
...
|
|
4644
|
+
const completedNotesCountHistory = await this.getCompletedNotesCountHistory(config2.root_item.created_at);
|
|
4645
|
+
config2.root_item = {
|
|
4646
|
+
...config2.root_item,
|
|
3377
4647
|
completed_notes_count: completedNotesCountHistory,
|
|
3378
4648
|
updated_at: Date.now()
|
|
3379
4649
|
};
|
|
3380
|
-
|
|
4650
|
+
writeFileSync7(ROOT_CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
|
|
3381
4651
|
const duration = Date.now() - startTime;
|
|
3382
4652
|
const monthKeys = Object.keys(completedNotesCountHistory);
|
|
3383
4653
|
const currentKey = monthKeys[monthKeys.length - 1];
|
|
@@ -3412,10 +4682,10 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
3412
4682
|
let failCount = 0;
|
|
3413
4683
|
for (let i = 0; i < targetDirs.length; i++) {
|
|
3414
4684
|
const dir = targetDirs[i];
|
|
3415
|
-
const
|
|
4685
|
+
const repoName2 = dir.split("/").pop() || dir;
|
|
3416
4686
|
try {
|
|
3417
4687
|
process.stdout.write(
|
|
3418
|
-
`\r [${i + 1}/${targetDirs.length}] \u6B63\u5728\u66F4\u65B0: ${
|
|
4688
|
+
`\r [${i + 1}/${targetDirs.length}] \u6B63\u5728\u66F4\u65B0: ${repoName2}...`
|
|
3419
4689
|
);
|
|
3420
4690
|
await runCommand("pnpm tn:update-completed-count", dir);
|
|
3421
4691
|
successCount++;
|
|
@@ -3423,7 +4693,7 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
3423
4693
|
failCount++;
|
|
3424
4694
|
console.log();
|
|
3425
4695
|
this.logger.error(
|
|
3426
|
-
`\u66F4\u65B0\u5931\u8D25: ${
|
|
4696
|
+
`\u66F4\u65B0\u5931\u8D25: ${repoName2} - ${error instanceof Error ? error.message : String(error)}`
|
|
3427
4697
|
);
|
|
3428
4698
|
}
|
|
3429
4699
|
}
|
|
@@ -3518,7 +4788,7 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
3518
4788
|
const monthStr = String(lastDayOfMonth.getMonth() + 1).padStart(2, "0");
|
|
3519
4789
|
const dayStr = String(lastDayOfMonth.getDate()).padStart(2, "0");
|
|
3520
4790
|
const untilDate = `${yearStr}-${monthStr}-${dayStr} 23:59:59 +0800`;
|
|
3521
|
-
const commitHash =
|
|
4791
|
+
const commitHash = execSync4(
|
|
3522
4792
|
`git log --until="${untilDate}" --format=%H -1 -- README.md`,
|
|
3523
4793
|
{
|
|
3524
4794
|
cwd: ROOT_DIR_PATH,
|
|
@@ -3530,7 +4800,7 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
3530
4800
|
}
|
|
3531
4801
|
let readmeContent;
|
|
3532
4802
|
try {
|
|
3533
|
-
readmeContent =
|
|
4803
|
+
readmeContent = execSync4(`git show ${commitHash}:README.md`, {
|
|
3534
4804
|
cwd: ROOT_DIR_PATH,
|
|
3535
4805
|
encoding: "utf-8"
|
|
3536
4806
|
});
|
|
@@ -3544,9 +4814,11 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
3544
4814
|
|
|
3545
4815
|
// commands/git/PushCommand.ts
|
|
3546
4816
|
var PushCommand = class extends BaseCommand {
|
|
4817
|
+
gitService;
|
|
4818
|
+
timestampService;
|
|
4819
|
+
pushAll = false;
|
|
3547
4820
|
constructor() {
|
|
3548
4821
|
super("push");
|
|
3549
|
-
this.pushAll = false;
|
|
3550
4822
|
this.gitService = new GitService();
|
|
3551
4823
|
this.timestampService = new TimestampService();
|
|
3552
4824
|
}
|
|
@@ -3610,9 +4882,10 @@ var PushCommand = class extends BaseCommand {
|
|
|
3610
4882
|
|
|
3611
4883
|
// commands/git/PullCommand.ts
|
|
3612
4884
|
var PullCommand = class extends BaseCommand {
|
|
4885
|
+
gitService;
|
|
4886
|
+
pullAll = false;
|
|
3613
4887
|
constructor() {
|
|
3614
4888
|
super("pull");
|
|
3615
|
-
this.pullAll = false;
|
|
3616
4889
|
this.gitService = new GitService();
|
|
3617
4890
|
}
|
|
3618
4891
|
/**
|
|
@@ -3638,9 +4911,10 @@ var PullCommand = class extends BaseCommand {
|
|
|
3638
4911
|
|
|
3639
4912
|
// commands/git/SyncCommand.ts
|
|
3640
4913
|
var SyncCommand = class extends BaseCommand {
|
|
4914
|
+
gitService;
|
|
4915
|
+
syncAll = false;
|
|
3641
4916
|
constructor() {
|
|
3642
4917
|
super("sync");
|
|
3643
|
-
this.syncAll = false;
|
|
3644
4918
|
this.gitService = new GitService();
|
|
3645
4919
|
}
|
|
3646
4920
|
/**
|
|
@@ -3668,6 +4942,11 @@ var SyncCommand = class extends BaseCommand {
|
|
|
3668
4942
|
|
|
3669
4943
|
// commands/dev/DevCommand.ts
|
|
3670
4944
|
var DevCommand = class extends BaseCommand {
|
|
4945
|
+
fileWatcherService;
|
|
4946
|
+
vitepressService;
|
|
4947
|
+
noteManager;
|
|
4948
|
+
noteIndexCache;
|
|
4949
|
+
configManager;
|
|
3671
4950
|
constructor() {
|
|
3672
4951
|
super("dev");
|
|
3673
4952
|
this.fileWatcherService = new FileWatcherService();
|
|
@@ -3690,10 +4969,10 @@ var DevCommand = class extends BaseCommand {
|
|
|
3690
4969
|
this.fileWatcherService.start();
|
|
3691
4970
|
const watcherElapsed = Date.now() - watcherStart;
|
|
3692
4971
|
this.logger.success(`\u6587\u4EF6\u76D1\u542C\u670D\u52A1\u5DF2\u5C31\u7EEA\uFF0C\u8017\u65F6\uFF1A${watcherElapsed} ms`);
|
|
3693
|
-
const
|
|
3694
|
-
const
|
|
4972
|
+
const port2 = this.configManager.get("port") || VitepressService.DEFAULT_DEV_PORT;
|
|
4973
|
+
const repoName2 = this.configManager.get("repoName");
|
|
3695
4974
|
this.logger.info(
|
|
3696
|
-
`\u672C\u5730\u5F00\u53D1\u670D\u52A1\u5730\u5740\uFF1Ahttp://localhost:${
|
|
4975
|
+
`\u672C\u5730\u5F00\u53D1\u670D\u52A1\u5730\u5740\uFF1Ahttp://localhost:${port2}/${repoName2}/`
|
|
3697
4976
|
);
|
|
3698
4977
|
} else {
|
|
3699
4978
|
this.logger.error("\u542F\u52A8\u670D\u52A1\u5668\u5931\u8D25");
|
|
@@ -3703,6 +4982,7 @@ var DevCommand = class extends BaseCommand {
|
|
|
3703
4982
|
|
|
3704
4983
|
// commands/build/BuildCommand.ts
|
|
3705
4984
|
var BuildCommand = class extends BaseCommand {
|
|
4985
|
+
vitepressService;
|
|
3706
4986
|
constructor() {
|
|
3707
4987
|
super("build");
|
|
3708
4988
|
this.vitepressService = new VitepressService();
|
|
@@ -3716,6 +4996,7 @@ var BuildCommand = class extends BaseCommand {
|
|
|
3716
4996
|
|
|
3717
4997
|
// commands/build/PreviewCommand.ts
|
|
3718
4998
|
var PreviewCommand = class extends BaseCommand {
|
|
4999
|
+
vitepressService;
|
|
3719
5000
|
constructor() {
|
|
3720
5001
|
super("preview");
|
|
3721
5002
|
this.vitepressService = new VitepressService();
|
|
@@ -3735,6 +5016,8 @@ var PreviewCommand = class extends BaseCommand {
|
|
|
3735
5016
|
import { createInterface } from "readline";
|
|
3736
5017
|
import { v4 as uuidv42 } from "uuid";
|
|
3737
5018
|
var CreateNoteCommand = class extends BaseCommand {
|
|
5019
|
+
noteService;
|
|
5020
|
+
readmeService;
|
|
3738
5021
|
constructor() {
|
|
3739
5022
|
super("create-notes");
|
|
3740
5023
|
this.noteService = NoteService.getInstance();
|
|
@@ -3811,23 +5094,23 @@ var CreateNoteCommand = class extends BaseCommand {
|
|
|
3811
5094
|
input: process.stdin,
|
|
3812
5095
|
output: process.stdout
|
|
3813
5096
|
});
|
|
3814
|
-
return new Promise((
|
|
5097
|
+
return new Promise((resolve4) => {
|
|
3815
5098
|
rl.question("\n\u{1F4DD} \u8BF7\u8F93\u5165\u8981\u521B\u5EFA\u7684\u7B14\u8BB0\u6570\u91CF\uFF08\u9ED8\u8BA4\u4E3A 1\uFF09: ", (answer) => {
|
|
3816
5099
|
rl.close();
|
|
3817
5100
|
const trimmed = answer.trim();
|
|
3818
5101
|
if (!trimmed) {
|
|
3819
5102
|
this.logger.info("\u4F7F\u7528\u9ED8\u8BA4\u6570\u91CF: 1");
|
|
3820
|
-
|
|
5103
|
+
resolve4(1);
|
|
3821
5104
|
return;
|
|
3822
5105
|
}
|
|
3823
5106
|
const num = parseInt(trimmed, 10);
|
|
3824
5107
|
if (isNaN(num) || num < 1 || !Number.isInteger(num)) {
|
|
3825
5108
|
this.logger.warn(`\u8F93\u5165 "${trimmed}" \u4E0D\u662F\u6709\u6548\u7684\u6B63\u6574\u6570\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C: 1`);
|
|
3826
|
-
|
|
5109
|
+
resolve4(1);
|
|
3827
5110
|
return;
|
|
3828
5111
|
}
|
|
3829
5112
|
this.logger.info(`\u5C06\u521B\u5EFA ${num} \u7BC7\u7B14\u8BB0`);
|
|
3830
|
-
|
|
5113
|
+
resolve4(num);
|
|
3831
5114
|
});
|
|
3832
5115
|
});
|
|
3833
5116
|
}
|
|
@@ -3839,10 +5122,10 @@ var CreateNoteCommand = class extends BaseCommand {
|
|
|
3839
5122
|
input: process.stdin,
|
|
3840
5123
|
output: process.stdout
|
|
3841
5124
|
});
|
|
3842
|
-
return new Promise((
|
|
5125
|
+
return new Promise((resolve4) => {
|
|
3843
5126
|
rl.question("\u8BF7\u8F93\u5165\u7B14\u8BB0\u6807\u9898: ", (answer) => {
|
|
3844
5127
|
rl.close();
|
|
3845
|
-
|
|
5128
|
+
resolve4(answer.trim());
|
|
3846
5129
|
});
|
|
3847
5130
|
});
|
|
3848
5131
|
}
|
|
@@ -3850,6 +5133,7 @@ var CreateNoteCommand = class extends BaseCommand {
|
|
|
3850
5133
|
|
|
3851
5134
|
// commands/note/UpdateNoteConfigCommand.ts
|
|
3852
5135
|
var UpdateNoteConfigCommand = class extends BaseCommand {
|
|
5136
|
+
noteService;
|
|
3853
5137
|
constructor() {
|
|
3854
5138
|
super("update-note-config");
|
|
3855
5139
|
this.noteService = NoteService.getInstance();
|
|
@@ -3881,26 +5165,28 @@ var UpdateNoteConfigCommand = class extends BaseCommand {
|
|
|
3881
5165
|
* 更新笔记配置(可被外部调用)
|
|
3882
5166
|
*/
|
|
3883
5167
|
async updateConfig(params) {
|
|
3884
|
-
const { noteIndex, config } = params;
|
|
5168
|
+
const { noteIndex, config: config2 } = params;
|
|
3885
5169
|
const note = this.noteService.getNoteByIndex(noteIndex);
|
|
3886
5170
|
if (!note) {
|
|
3887
5171
|
throw new Error(`\u7B14\u8BB0\u672A\u627E\u5230: ${noteIndex}`);
|
|
3888
5172
|
}
|
|
3889
|
-
await this.noteService.updateNoteConfig(noteIndex,
|
|
5173
|
+
await this.noteService.updateNoteConfig(noteIndex, config2);
|
|
3890
5174
|
this.logger.info(`\u2705 \u7B14\u8BB0 ${noteIndex} \u914D\u7F6E\u5DF2\u66F4\u65B0:`);
|
|
3891
|
-
if (
|
|
3892
|
-
this.logger.info(` - \u5B8C\u6210\u72B6\u6001: ${
|
|
3893
|
-
if (
|
|
3894
|
-
this.logger.info(` - \u8BC4\u8BBA\u72B6\u6001: ${
|
|
3895
|
-
if (
|
|
3896
|
-
this.logger.info(` - \u7B14\u8BB0\u7B80\u4ECB: ${
|
|
5175
|
+
if (config2.done !== void 0)
|
|
5176
|
+
this.logger.info(` - \u5B8C\u6210\u72B6\u6001: ${config2.done}`);
|
|
5177
|
+
if (config2.enableDiscussions !== void 0)
|
|
5178
|
+
this.logger.info(` - \u8BC4\u8BBA\u72B6\u6001: ${config2.enableDiscussions}`);
|
|
5179
|
+
if (config2.description !== void 0)
|
|
5180
|
+
this.logger.info(` - \u7B14\u8BB0\u7B80\u4ECB: ${config2.description || "(\u7A7A)"}`);
|
|
3897
5181
|
}
|
|
3898
5182
|
};
|
|
3899
5183
|
|
|
3900
5184
|
// commands/note/RenameNoteCommand.ts
|
|
3901
|
-
import { existsSync as
|
|
3902
|
-
import { join as
|
|
5185
|
+
import { existsSync as existsSync11, renameSync, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
5186
|
+
import { join as join11 } from "path";
|
|
3903
5187
|
var RenameNoteCommand = class extends BaseCommand {
|
|
5188
|
+
noteService;
|
|
5189
|
+
readmeService;
|
|
3904
5190
|
constructor() {
|
|
3905
5191
|
super("rename-note");
|
|
3906
5192
|
this.noteService = NoteService.getInstance();
|
|
@@ -3934,8 +5220,8 @@ var RenameNoteCommand = class extends BaseCommand {
|
|
|
3934
5220
|
throw new Error(validation.error || "\u6807\u9898\u683C\u5F0F\u65E0\u6548");
|
|
3935
5221
|
}
|
|
3936
5222
|
const newDirName = `${noteIndex}. ${newTitle.trim()}`;
|
|
3937
|
-
const newPath =
|
|
3938
|
-
if (
|
|
5223
|
+
const newPath = join11(NOTES_PATH, newDirName);
|
|
5224
|
+
if (existsSync11(newPath)) {
|
|
3939
5225
|
throw new Error(`\u76EE\u6807\u6587\u4EF6\u5939\u5DF2\u5B58\u5728: ${newDirName}`);
|
|
3940
5226
|
}
|
|
3941
5227
|
try {
|
|
@@ -3950,9 +5236,9 @@ var RenameNoteCommand = class extends BaseCommand {
|
|
|
3950
5236
|
}
|
|
3951
5237
|
try {
|
|
3952
5238
|
this.logger.info("\u6B63\u5728\u66F4\u65B0\u7B14\u8BB0\u5185\u90E8\u6807\u9898...");
|
|
3953
|
-
const readmePath =
|
|
3954
|
-
if (
|
|
3955
|
-
const content =
|
|
5239
|
+
const readmePath = join11(newPath, "README.md");
|
|
5240
|
+
if (existsSync11(readmePath)) {
|
|
5241
|
+
const content = readFileSync9(readmePath, "utf-8");
|
|
3956
5242
|
const lines = content.split("\n");
|
|
3957
5243
|
let h1Index = -1;
|
|
3958
5244
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -3968,7 +5254,7 @@ var RenameNoteCommand = class extends BaseCommand {
|
|
|
3968
5254
|
REPO_NOTES_URL
|
|
3969
5255
|
);
|
|
3970
5256
|
lines[h1Index] = newH1;
|
|
3971
|
-
|
|
5257
|
+
writeFileSync8(readmePath, lines.join("\n"), "utf-8");
|
|
3972
5258
|
this.logger.success("\u2705 \u7B14\u8BB0\u6807\u9898\u5DF2\u66F4\u65B0");
|
|
3973
5259
|
} else {
|
|
3974
5260
|
this.logger.warn(
|
|
@@ -3991,6 +5277,7 @@ var RenameNoteCommand = class extends BaseCommand {
|
|
|
3991
5277
|
|
|
3992
5278
|
// commands/maintenance/SyncCoreCommand.ts
|
|
3993
5279
|
var SyncCoreCommand = class extends BaseCommand {
|
|
5280
|
+
syncCoreService;
|
|
3994
5281
|
constructor() {
|
|
3995
5282
|
super("sync-core");
|
|
3996
5283
|
this.syncCoreService = new SyncCoreService();
|
|
@@ -4002,6 +5289,7 @@ var SyncCoreCommand = class extends BaseCommand {
|
|
|
4002
5289
|
|
|
4003
5290
|
// commands/maintenance/FixTimestampsCommand.ts
|
|
4004
5291
|
var FixTimestampsCommand = class extends BaseCommand {
|
|
5292
|
+
timestampService;
|
|
4005
5293
|
constructor() {
|
|
4006
5294
|
super("fix-timestamps");
|
|
4007
5295
|
this.timestampService = new TimestampService();
|