folderblog 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -48
- package/dist/chunk-24MKFHML.cjs +143 -0
- package/dist/chunk-3RG5ZIWI.js +8 -0
- package/dist/chunk-4ZJGUMHS.cjs +78 -0
- package/dist/chunk-HMQIQUPB.cjs +387 -0
- package/dist/chunk-IXP35S24.js +1715 -0
- package/dist/chunk-OBGZSXTJ.cjs +10 -0
- package/dist/chunk-PARGDJNY.js +76 -0
- package/dist/chunk-QA4KPPTA.cjs +1787 -0
- package/dist/chunk-XP5J4LFJ.js +127 -0
- package/dist/chunk-ZRUBI3GH.js +370 -0
- package/dist/cli/bin.cjs +25 -0
- package/dist/cli/bin.d.cts +1 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +23 -0
- package/dist/cli/index.cjs +22 -0
- package/dist/cli/index.d.cts +39 -0
- package/dist/cli/index.d.ts +39 -0
- package/dist/cli/index.js +15 -0
- package/dist/config-DFr-htlO.d.cts +887 -0
- package/dist/config-DFr-htlO.d.ts +887 -0
- package/dist/index.cjs +488 -1
- package/dist/index.d.cts +76 -8
- package/dist/index.d.ts +76 -8
- package/dist/index.js +153 -1
- package/dist/processor/index.cjs +337 -0
- package/dist/processor/index.d.cts +491 -0
- package/dist/processor/index.d.ts +491 -0
- package/dist/processor/index.js +4 -0
- package/dist/processor/plugins.cjs +51 -0
- package/dist/processor/plugins.d.cts +174 -0
- package/dist/processor/plugins.d.ts +174 -0
- package/dist/processor/plugins.js +2 -0
- package/dist/processor/types.cjs +67 -0
- package/dist/processor/types.d.cts +47 -0
- package/dist/processor/types.d.ts +47 -0
- package/dist/processor/types.js +2 -0
- package/dist/server/index.cjs +36 -0
- package/dist/server/index.d.cts +56 -0
- package/dist/server/index.d.ts +56 -0
- package/dist/server/index.js +34 -0
- package/package.json +63 -11
|
@@ -0,0 +1,1715 @@
|
|
|
1
|
+
import { PluginManager } from './chunk-ZRUBI3GH.js';
|
|
2
|
+
import { __require } from './chunk-3RG5ZIWI.js';
|
|
3
|
+
import { unified } from 'unified';
|
|
4
|
+
import remarkParse from 'remark-parse';
|
|
5
|
+
import remarkGfm from 'remark-gfm';
|
|
6
|
+
import remarkRehype from 'remark-rehype';
|
|
7
|
+
import rehypeStringify from 'rehype-stringify';
|
|
8
|
+
import rehypeRaw from 'rehype-raw';
|
|
9
|
+
import rehypeSlug from 'rehype-slug';
|
|
10
|
+
import GithubSlugger from 'github-slugger';
|
|
11
|
+
import matter from 'gray-matter';
|
|
12
|
+
import crypto from 'crypto';
|
|
13
|
+
import slugify from '@sindresorhus/slugify';
|
|
14
|
+
import fs from 'fs/promises';
|
|
15
|
+
import path5 from 'path';
|
|
16
|
+
import { visit } from 'unist-util-visit';
|
|
17
|
+
|
|
18
|
+
// ../processor/src/services/issueCollector.ts
|
|
19
|
+
var IssueCollector = class {
|
|
20
|
+
issues = [];
|
|
21
|
+
startTime;
|
|
22
|
+
constructor() {
|
|
23
|
+
this.startTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
24
|
+
}
|
|
25
|
+
// --------------------------------------------------------------------------
|
|
26
|
+
// Core Methods
|
|
27
|
+
// --------------------------------------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Add a generic issue
|
|
30
|
+
*/
|
|
31
|
+
addIssue(issue) {
|
|
32
|
+
this.issues.push({
|
|
33
|
+
...issue,
|
|
34
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Add a broken link issue
|
|
39
|
+
*/
|
|
40
|
+
addBrokenLink(params) {
|
|
41
|
+
this.addIssue({
|
|
42
|
+
severity: "warning",
|
|
43
|
+
category: "broken-link",
|
|
44
|
+
module: "link-resolver",
|
|
45
|
+
message: `Broken ${params.linkType} link: [[${params.linkTarget}]] - target not found`,
|
|
46
|
+
filePath: params.filePath,
|
|
47
|
+
lineNumber: params.lineNumber,
|
|
48
|
+
context: {
|
|
49
|
+
linkText: params.linkText,
|
|
50
|
+
linkTarget: params.linkTarget,
|
|
51
|
+
linkType: params.linkType,
|
|
52
|
+
suggestions: params.suggestions
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Add a missing media issue
|
|
58
|
+
*/
|
|
59
|
+
addMissingMedia(params) {
|
|
60
|
+
this.addIssue({
|
|
61
|
+
severity: "warning",
|
|
62
|
+
category: "missing-media",
|
|
63
|
+
module: params.module ?? "embed-media",
|
|
64
|
+
message: `Missing media file: ${params.mediaPath}`,
|
|
65
|
+
filePath: params.filePath,
|
|
66
|
+
lineNumber: params.lineNumber,
|
|
67
|
+
context: {
|
|
68
|
+
mediaPath: params.mediaPath,
|
|
69
|
+
referencedFrom: params.referencedFrom,
|
|
70
|
+
originalReference: params.originalReference
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Add a media processing error
|
|
76
|
+
*/
|
|
77
|
+
addMediaProcessingError(params) {
|
|
78
|
+
this.addIssue({
|
|
79
|
+
severity: "error",
|
|
80
|
+
category: "media-processing",
|
|
81
|
+
module: "image-processor",
|
|
82
|
+
message: `Failed to ${params.operation} media: ${params.mediaPath} - ${params.errorMessage}`,
|
|
83
|
+
filePath: params.filePath,
|
|
84
|
+
context: {
|
|
85
|
+
mediaPath: params.mediaPath,
|
|
86
|
+
operation: params.operation,
|
|
87
|
+
errorMessage: params.errorMessage,
|
|
88
|
+
errorCode: params.errorCode
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Add a slug conflict warning
|
|
94
|
+
*/
|
|
95
|
+
addSlugConflict(params) {
|
|
96
|
+
this.addIssue({
|
|
97
|
+
severity: "info",
|
|
98
|
+
category: "slug-conflict",
|
|
99
|
+
module: "slug-generator",
|
|
100
|
+
message: `Slug conflict resolved: "${params.originalSlug}" \u2192 "${params.finalSlug}"`,
|
|
101
|
+
filePath: params.filePath,
|
|
102
|
+
context: {
|
|
103
|
+
originalSlug: params.originalSlug,
|
|
104
|
+
finalSlug: params.finalSlug,
|
|
105
|
+
conflictingFiles: params.conflictingFiles
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Add a mermaid error
|
|
111
|
+
*/
|
|
112
|
+
addMermaidError(params) {
|
|
113
|
+
this.addIssue({
|
|
114
|
+
severity: params.errorType === "missing-deps" ? "info" : "warning",
|
|
115
|
+
category: "mermaid-error",
|
|
116
|
+
module: "embed-mermaid",
|
|
117
|
+
message: params.message,
|
|
118
|
+
filePath: params.filePath,
|
|
119
|
+
lineNumber: params.lineNumber,
|
|
120
|
+
context: {
|
|
121
|
+
errorType: params.errorType,
|
|
122
|
+
diagramContent: params.diagramContent,
|
|
123
|
+
fallback: params.fallback
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Add an embedding error
|
|
129
|
+
*/
|
|
130
|
+
addEmbeddingError(params) {
|
|
131
|
+
this.addIssue({
|
|
132
|
+
severity: "error",
|
|
133
|
+
category: "embedding-error",
|
|
134
|
+
module: params.embeddingType === "text" ? "text-embeddings" : "image-embeddings",
|
|
135
|
+
message: `${params.embeddingType} embedding ${params.operation} failed: ${params.errorMessage}`,
|
|
136
|
+
filePath: params.filePath,
|
|
137
|
+
context: {
|
|
138
|
+
embeddingType: params.embeddingType,
|
|
139
|
+
operation: params.operation,
|
|
140
|
+
errorMessage: params.errorMessage
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Add a plugin error
|
|
146
|
+
*/
|
|
147
|
+
addPluginError(params) {
|
|
148
|
+
this.addIssue({
|
|
149
|
+
severity: "error",
|
|
150
|
+
category: "plugin-error",
|
|
151
|
+
module: "plugin-manager",
|
|
152
|
+
message: `Plugin "${params.pluginName}" ${params.operation} failed: ${params.errorMessage}`,
|
|
153
|
+
context: {
|
|
154
|
+
pluginName: params.pluginName,
|
|
155
|
+
operation: params.operation,
|
|
156
|
+
errorMessage: params.errorMessage
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// --------------------------------------------------------------------------
|
|
161
|
+
// Query Methods
|
|
162
|
+
// --------------------------------------------------------------------------
|
|
163
|
+
/**
|
|
164
|
+
* Get all issues
|
|
165
|
+
*/
|
|
166
|
+
getIssues() {
|
|
167
|
+
return [...this.issues];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Filter issues
|
|
171
|
+
*/
|
|
172
|
+
filterIssues(options) {
|
|
173
|
+
return filterIssues(this.issues, options);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get issue count by severity
|
|
177
|
+
*/
|
|
178
|
+
getCountBySeverity(severity) {
|
|
179
|
+
return this.issues.filter((i) => i.severity === severity).length;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Check if there are any errors
|
|
183
|
+
*/
|
|
184
|
+
hasErrors() {
|
|
185
|
+
return this.getCountBySeverity("error") > 0;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Get summary string for console output
|
|
189
|
+
*/
|
|
190
|
+
getSummaryString() {
|
|
191
|
+
const errors = this.getCountBySeverity("error");
|
|
192
|
+
const warnings = this.getCountBySeverity("warning");
|
|
193
|
+
const info = this.getCountBySeverity("info");
|
|
194
|
+
const parts = [];
|
|
195
|
+
if (errors > 0) parts.push(`${errors} error${errors > 1 ? "s" : ""}`);
|
|
196
|
+
if (warnings > 0) parts.push(`${warnings} warning${warnings > 1 ? "s" : ""}`);
|
|
197
|
+
if (info > 0) parts.push(`${info} info`);
|
|
198
|
+
return parts.length > 0 ? `Processing completed with ${parts.join(", ")}` : "Processing completed successfully";
|
|
199
|
+
}
|
|
200
|
+
// --------------------------------------------------------------------------
|
|
201
|
+
// Report Generation
|
|
202
|
+
// --------------------------------------------------------------------------
|
|
203
|
+
/**
|
|
204
|
+
* Generate the final report
|
|
205
|
+
*/
|
|
206
|
+
generateReport() {
|
|
207
|
+
const endTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
208
|
+
return generateIssueReport(this.issues, this.startTime, endTime);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Clear all issues (for testing)
|
|
212
|
+
*/
|
|
213
|
+
clear() {
|
|
214
|
+
this.issues = [];
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
var filterIssues = (issues, options) => {
|
|
218
|
+
let filtered = [...issues];
|
|
219
|
+
if (options.severity) {
|
|
220
|
+
const severities = Array.isArray(options.severity) ? options.severity : [options.severity];
|
|
221
|
+
filtered = filtered.filter((i) => severities.includes(i.severity));
|
|
222
|
+
}
|
|
223
|
+
if (options.category) {
|
|
224
|
+
const categories = Array.isArray(options.category) ? options.category : [options.category];
|
|
225
|
+
filtered = filtered.filter((i) => categories.includes(i.category));
|
|
226
|
+
}
|
|
227
|
+
if (options.module) {
|
|
228
|
+
const modules = Array.isArray(options.module) ? options.module : [options.module];
|
|
229
|
+
filtered = filtered.filter((i) => modules.includes(i.module));
|
|
230
|
+
}
|
|
231
|
+
if (options.filePath) {
|
|
232
|
+
const paths = Array.isArray(options.filePath) ? options.filePath : [options.filePath];
|
|
233
|
+
filtered = filtered.filter((i) => i.filePath && paths.includes(i.filePath));
|
|
234
|
+
}
|
|
235
|
+
return filtered;
|
|
236
|
+
};
|
|
237
|
+
var calculateSummary = (issues) => {
|
|
238
|
+
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
239
|
+
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
240
|
+
const infoCount = issues.filter((i) => i.severity === "info").length;
|
|
241
|
+
const uniqueFiles = new Set(issues.map((i) => i.filePath).filter(Boolean));
|
|
242
|
+
const categoryCounts = {};
|
|
243
|
+
const moduleCounts = {};
|
|
244
|
+
for (const issue of issues) {
|
|
245
|
+
categoryCounts[issue.category] = (categoryCounts[issue.category] ?? 0) + 1;
|
|
246
|
+
moduleCounts[issue.module] = (moduleCounts[issue.module] ?? 0) + 1;
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
totalIssues: issues.length,
|
|
250
|
+
errorCount,
|
|
251
|
+
warningCount,
|
|
252
|
+
infoCount,
|
|
253
|
+
filesAffected: uniqueFiles.size,
|
|
254
|
+
categoryCounts,
|
|
255
|
+
moduleCounts
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
var generateIssueReport = (issues, startTime, endTime) => ({
|
|
259
|
+
issues,
|
|
260
|
+
summary: calculateSummary(issues),
|
|
261
|
+
metadata: {
|
|
262
|
+
processStartTime: startTime,
|
|
263
|
+
processEndTime: endTime
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
var parseFrontmatter = (content) => {
|
|
267
|
+
const { content: markdown, data } = matter(content);
|
|
268
|
+
return {
|
|
269
|
+
content: markdown,
|
|
270
|
+
data
|
|
271
|
+
};
|
|
272
|
+
};
|
|
273
|
+
var DEFAULT_PIPELINE_OPTIONS = {
|
|
274
|
+
gfm: true,
|
|
275
|
+
allowRawHtml: true,
|
|
276
|
+
remarkPlugins: [],
|
|
277
|
+
rehypePlugins: []
|
|
278
|
+
};
|
|
279
|
+
var createBasePipeline = (options = {}) => {
|
|
280
|
+
const opts = { ...DEFAULT_PIPELINE_OPTIONS, ...options };
|
|
281
|
+
let processor = unified().use(remarkParse);
|
|
282
|
+
if (opts.gfm) {
|
|
283
|
+
processor = processor.use(remarkGfm);
|
|
284
|
+
}
|
|
285
|
+
for (const { plugin, options: pluginOpts } of opts.remarkPlugins ?? []) {
|
|
286
|
+
processor = processor.use(plugin, pluginOpts);
|
|
287
|
+
}
|
|
288
|
+
processor = processor.use(remarkRehype, {
|
|
289
|
+
allowDangerousHtml: opts.allowRawHtml
|
|
290
|
+
});
|
|
291
|
+
if (opts.allowRawHtml) {
|
|
292
|
+
processor = processor.use(rehypeRaw);
|
|
293
|
+
}
|
|
294
|
+
processor = processor.use(rehypeSlug);
|
|
295
|
+
for (const { plugin, options: pluginOpts } of opts.rehypePlugins ?? []) {
|
|
296
|
+
processor = processor.use(plugin, pluginOpts);
|
|
297
|
+
}
|
|
298
|
+
processor = processor.use(rehypeStringify);
|
|
299
|
+
return processor;
|
|
300
|
+
};
|
|
301
|
+
var parseToMdast = (markdown) => {
|
|
302
|
+
const processor = unified().use(remarkParse).use(remarkGfm);
|
|
303
|
+
return processor.parse(markdown);
|
|
304
|
+
};
|
|
305
|
+
var mdastToHast = async (mdast, options = {}) => {
|
|
306
|
+
const processor = unified().use(remarkRehype, { allowDangerousHtml: options.allowDangerousHtml ?? true }).use(rehypeRaw).use(rehypeSlug);
|
|
307
|
+
const result = await processor.run(mdast);
|
|
308
|
+
return result;
|
|
309
|
+
};
|
|
310
|
+
var hastToHtml = (hast) => {
|
|
311
|
+
const processor = unified().use(rehypeStringify);
|
|
312
|
+
return processor.stringify(hast);
|
|
313
|
+
};
|
|
314
|
+
var processMarkdown = async (content, options = {}) => {
|
|
315
|
+
const { content: markdown, data: frontmatter } = parseFrontmatter(content);
|
|
316
|
+
const mdast = parseToMdast(markdown);
|
|
317
|
+
const pipeline = createBasePipeline(options);
|
|
318
|
+
const file = await pipeline.process(markdown);
|
|
319
|
+
const hast = await mdastToHast(mdast, {
|
|
320
|
+
allowDangerousHtml: options.allowRawHtml ?? true
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
html: String(file),
|
|
324
|
+
markdown,
|
|
325
|
+
frontmatter,
|
|
326
|
+
mdast,
|
|
327
|
+
hast
|
|
328
|
+
};
|
|
329
|
+
};
|
|
330
|
+
var mdastToText = (node) => {
|
|
331
|
+
const parts = [];
|
|
332
|
+
const visit2 = (n) => {
|
|
333
|
+
if (n.type === "text" || n.type === "inlineCode") {
|
|
334
|
+
parts.push(n.value);
|
|
335
|
+
}
|
|
336
|
+
if (n.children) {
|
|
337
|
+
for (const child of n.children) {
|
|
338
|
+
visit2(child);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
visit2(node);
|
|
343
|
+
return parts.join(" ");
|
|
344
|
+
};
|
|
345
|
+
var extractFirstParagraph = (mdast) => {
|
|
346
|
+
for (const node of mdast.children) {
|
|
347
|
+
if (node.type === "paragraph") {
|
|
348
|
+
return mdastToText({ type: "root", children: [node] });
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return "";
|
|
352
|
+
};
|
|
353
|
+
var extractHeadings = (mdast) => {
|
|
354
|
+
const headings = [];
|
|
355
|
+
const visit2 = (node) => {
|
|
356
|
+
if (node.type === "heading") {
|
|
357
|
+
headings.push({
|
|
358
|
+
depth: node.depth,
|
|
359
|
+
text: mdastToText({ type: "root", children: node.children })
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
if (node.children) {
|
|
363
|
+
for (const child of node.children) {
|
|
364
|
+
visit2(child);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
visit2(mdast);
|
|
369
|
+
return headings;
|
|
370
|
+
};
|
|
371
|
+
var buildToc = (headings) => {
|
|
372
|
+
const slugger = new GithubSlugger();
|
|
373
|
+
return headings.map((h) => ({
|
|
374
|
+
...h,
|
|
375
|
+
slug: slugger.slug(h.text)
|
|
376
|
+
}));
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// ../processor/src/markdown/wordCount.ts
|
|
380
|
+
var countWords = (text) => {
|
|
381
|
+
if (!text || typeof text !== "string") {
|
|
382
|
+
return 0;
|
|
383
|
+
}
|
|
384
|
+
const normalized = text.trim().replace(/\s+/g, " ");
|
|
385
|
+
if (normalized.length === 0) {
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
const words = normalized.split(" ").filter((word) => word.length > 0);
|
|
389
|
+
return words.length;
|
|
390
|
+
};
|
|
391
|
+
var estimateReadingTime = (wordCount, wordsPerMinute = 200) => {
|
|
392
|
+
return Math.max(1, Math.ceil(wordCount / wordsPerMinute));
|
|
393
|
+
};
|
|
394
|
+
var getContentStats = (text) => {
|
|
395
|
+
const wordCount = countWords(text);
|
|
396
|
+
return {
|
|
397
|
+
wordCount,
|
|
398
|
+
readingTimeMinutes: estimateReadingTime(wordCount)
|
|
399
|
+
};
|
|
400
|
+
};
|
|
401
|
+
var hashContent = (content) => {
|
|
402
|
+
const hashSum = crypto.createHash("sha256");
|
|
403
|
+
hashSum.update(content);
|
|
404
|
+
return hashSum.digest("hex");
|
|
405
|
+
};
|
|
406
|
+
var hashBuffer = (buffer) => {
|
|
407
|
+
const hashSum = crypto.createHash("sha256");
|
|
408
|
+
hashSum.update(new Uint8Array(buffer));
|
|
409
|
+
return hashSum.digest("hex");
|
|
410
|
+
};
|
|
411
|
+
var shortHash = (content, length = 8) => hashContent(content).substring(0, length);
|
|
412
|
+
var combineHashes = (...values) => hashContent(values.join(":"));
|
|
413
|
+
var toSlug = (s) => slugify(s, { decamelize: false });
|
|
414
|
+
var generateBaseSlug = (options) => {
|
|
415
|
+
const { fileName, parentFolder, siblingCount, frontmatterSlug } = options;
|
|
416
|
+
if (frontmatterSlug) {
|
|
417
|
+
return {
|
|
418
|
+
slug: frontmatterSlug,
|
|
419
|
+
source: "frontmatter"
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
if (fileName === "index" && parentFolder && (siblingCount === void 0 || siblingCount === 1)) {
|
|
423
|
+
return {
|
|
424
|
+
slug: toSlug(parentFolder),
|
|
425
|
+
source: "folder"
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
slug: toSlug(fileName),
|
|
430
|
+
source: "filename"
|
|
431
|
+
};
|
|
432
|
+
};
|
|
433
|
+
var resolveSlugConflict = (baseSlug, usedSlugs, strategy = "number") => {
|
|
434
|
+
if (!usedSlugs.has(baseSlug)) {
|
|
435
|
+
return baseSlug;
|
|
436
|
+
}
|
|
437
|
+
if (strategy === "number") {
|
|
438
|
+
let counter = 2;
|
|
439
|
+
let newSlug = `${baseSlug}${counter}`;
|
|
440
|
+
while (usedSlugs.has(newSlug)) {
|
|
441
|
+
counter++;
|
|
442
|
+
newSlug = `${baseSlug}${counter}`;
|
|
443
|
+
}
|
|
444
|
+
return newSlug;
|
|
445
|
+
}
|
|
446
|
+
const hash = __require("crypto").createHash("sha256").update(baseSlug + Date.now().toString()).digest("hex").substring(0, 6);
|
|
447
|
+
return `${baseSlug}-${hash}`;
|
|
448
|
+
};
|
|
449
|
+
var SlugManager = class {
|
|
450
|
+
usedSlugs = /* @__PURE__ */ new Map();
|
|
451
|
+
scopedSlugs = /* @__PURE__ */ new Map();
|
|
452
|
+
fileSlugs = /* @__PURE__ */ new Map();
|
|
453
|
+
strategy;
|
|
454
|
+
constructor(strategy = "number") {
|
|
455
|
+
this.strategy = strategy;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Check if a slug is already used
|
|
459
|
+
*/
|
|
460
|
+
isUsed(slug) {
|
|
461
|
+
return this.usedSlugs.has(slug);
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Get slug info for a file path
|
|
465
|
+
*/
|
|
466
|
+
getSlugInfo(filePath) {
|
|
467
|
+
return this.fileSlugs.get(filePath);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Get the slug map for a given scope, creating it if needed
|
|
471
|
+
*/
|
|
472
|
+
getScopeMap(scope) {
|
|
473
|
+
let map = this.scopedSlugs.get(scope);
|
|
474
|
+
if (!map) {
|
|
475
|
+
map = /* @__PURE__ */ new Map();
|
|
476
|
+
this.scopedSlugs.set(scope, map);
|
|
477
|
+
}
|
|
478
|
+
return map;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Reserve a slug for a file
|
|
482
|
+
* @param scope - Optional scope for slug deduplication (e.g. top-level folder)
|
|
483
|
+
*/
|
|
484
|
+
reserve(filePath, options, scope) {
|
|
485
|
+
const { slug: baseSlug, source } = generateBaseSlug(options);
|
|
486
|
+
const slugMap = scope !== void 0 ? this.getScopeMap(scope) : this.usedSlugs;
|
|
487
|
+
const existingFile = slugMap.get(baseSlug);
|
|
488
|
+
const wasModified = existingFile !== void 0 && existingFile !== filePath;
|
|
489
|
+
const finalSlug = wasModified ? resolveSlugConflict(baseSlug, new Set(slugMap.keys()), this.strategy) : baseSlug;
|
|
490
|
+
const info = {
|
|
491
|
+
slug: finalSlug,
|
|
492
|
+
originalSlug: baseSlug,
|
|
493
|
+
source,
|
|
494
|
+
wasModified
|
|
495
|
+
};
|
|
496
|
+
slugMap.set(finalSlug, filePath);
|
|
497
|
+
this.fileSlugs.set(filePath, info);
|
|
498
|
+
return info;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Get all assigned slugs
|
|
502
|
+
*/
|
|
503
|
+
getAllSlugs() {
|
|
504
|
+
return this.fileSlugs;
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
var getFileName = (filePath) => {
|
|
508
|
+
const { name } = path5.parse(filePath);
|
|
509
|
+
return name;
|
|
510
|
+
};
|
|
511
|
+
var getExtension = (filePath) => {
|
|
512
|
+
const { ext } = path5.parse(filePath);
|
|
513
|
+
return ext.slice(1).toLowerCase();
|
|
514
|
+
};
|
|
515
|
+
var fileExists = async (filePath) => {
|
|
516
|
+
try {
|
|
517
|
+
await fs.access(filePath);
|
|
518
|
+
return true;
|
|
519
|
+
} catch {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
var ensureDir = async (dirPath) => {
|
|
524
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
525
|
+
};
|
|
526
|
+
var writeJson = async (filePath, data) => {
|
|
527
|
+
await ensureDir(path5.dirname(filePath));
|
|
528
|
+
await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf8");
|
|
529
|
+
};
|
|
530
|
+
var readJson = async (filePath) => {
|
|
531
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
532
|
+
return JSON.parse(content);
|
|
533
|
+
};
|
|
534
|
+
var writeText = async (filePath, content) => {
|
|
535
|
+
await ensureDir(path5.dirname(filePath));
|
|
536
|
+
await fs.writeFile(filePath, content, "utf8");
|
|
537
|
+
};
|
|
538
|
+
var readText = async (filePath) => {
|
|
539
|
+
return fs.readFile(filePath, "utf8");
|
|
540
|
+
};
|
|
541
|
+
var copyFile = async (src, dest) => {
|
|
542
|
+
await ensureDir(path5.dirname(dest));
|
|
543
|
+
await fs.copyFile(src, dest);
|
|
544
|
+
};
|
|
545
|
+
var getStats = async (filePath) => {
|
|
546
|
+
const stats = await fs.stat(filePath);
|
|
547
|
+
return {
|
|
548
|
+
size: stats.size,
|
|
549
|
+
created: stats.birthtime,
|
|
550
|
+
modified: stats.mtime
|
|
551
|
+
};
|
|
552
|
+
};
|
|
553
|
+
var findFiles = async (dir, predicate, recursive = true) => {
|
|
554
|
+
const results = [];
|
|
555
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
556
|
+
for (const entry of entries) {
|
|
557
|
+
const fullPath = path5.join(dir, entry.name);
|
|
558
|
+
if (entry.isDirectory() && recursive) {
|
|
559
|
+
const nested = await findFiles(fullPath, predicate, recursive);
|
|
560
|
+
results.push(...nested);
|
|
561
|
+
} else if (entry.isFile() && predicate(entry.name)) {
|
|
562
|
+
results.push(fullPath);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return results;
|
|
566
|
+
};
|
|
567
|
+
var findMarkdownFiles = (dir, recursive = true) => findFiles(dir, (name) => name.endsWith(".md"), recursive);
|
|
568
|
+
var relativePath = (from, to) => path5.relative(from, to);
|
|
569
|
+
var normalizePath = (p) => p.split(path5.sep).join("/");
|
|
570
|
+
function findByFileNameCaseInsensitive(fileName, fileMap) {
|
|
571
|
+
const exact = fileMap.get(fileName);
|
|
572
|
+
if (exact) return exact;
|
|
573
|
+
const lowerFileName = fileName.toLowerCase();
|
|
574
|
+
for (const [key, value] of fileMap.entries()) {
|
|
575
|
+
if (key.toLowerCase() === lowerFileName) {
|
|
576
|
+
return value;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return [];
|
|
580
|
+
}
|
|
581
|
+
function resolveFromCandidates(candidates, currentFilePath) {
|
|
582
|
+
if (candidates.length === 1) {
|
|
583
|
+
return candidates[0];
|
|
584
|
+
}
|
|
585
|
+
if (!candidates.length) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
if (currentFilePath) {
|
|
589
|
+
const currentDir = path5.dirname(currentFilePath);
|
|
590
|
+
const sameDir = candidates.find(
|
|
591
|
+
(f) => path5.dirname(f.originalPath) === currentDir
|
|
592
|
+
);
|
|
593
|
+
if (sameDir) return sameDir;
|
|
594
|
+
let parentDir = currentDir;
|
|
595
|
+
while (parentDir && parentDir !== "." && parentDir !== "/") {
|
|
596
|
+
parentDir = path5.dirname(parentDir);
|
|
597
|
+
const parentMatch = candidates.find(
|
|
598
|
+
(f) => path5.dirname(f.originalPath) === parentDir
|
|
599
|
+
);
|
|
600
|
+
if (parentMatch) return parentMatch;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return candidates[0];
|
|
604
|
+
}
|
|
605
|
+
function createPathVariations(targetPath, currentFilePath) {
|
|
606
|
+
const normalizedPath = targetPath.replace(/\\/g, "/");
|
|
607
|
+
const withLeadingSlash = normalizedPath.startsWith("/") ? normalizedPath : `/${normalizedPath}`;
|
|
608
|
+
const withoutLeadingSlash = normalizedPath.startsWith("/") ? normalizedPath.substring(1) : normalizedPath;
|
|
609
|
+
const variations = [
|
|
610
|
+
normalizedPath,
|
|
611
|
+
normalizedPath.toLowerCase(),
|
|
612
|
+
withLeadingSlash,
|
|
613
|
+
withLeadingSlash.toLowerCase(),
|
|
614
|
+
withoutLeadingSlash,
|
|
615
|
+
withoutLeadingSlash.toLowerCase()
|
|
616
|
+
];
|
|
617
|
+
if (currentFilePath && (normalizedPath.includes("../") || normalizedPath.includes("./"))) {
|
|
618
|
+
const currentDir = path5.dirname(currentFilePath);
|
|
619
|
+
const resolvedPath = path5.join(currentDir, normalizedPath).replace(/\\/g, "/");
|
|
620
|
+
const normalizedResolved = path5.normalize(resolvedPath).replace(/\\/g, "/");
|
|
621
|
+
variations.unshift(normalizedResolved, normalizedResolved.toLowerCase());
|
|
622
|
+
}
|
|
623
|
+
if (currentFilePath && !normalizedPath.includes("/")) {
|
|
624
|
+
const currentDir = path5.dirname(currentFilePath);
|
|
625
|
+
if (currentDir && currentDir !== ".") {
|
|
626
|
+
const inCurrentDir = path5.join(currentDir, normalizedPath).replace(/\\/g, "/");
|
|
627
|
+
variations.unshift(inCurrentDir, inCurrentDir.toLowerCase());
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
const baseName = path5.basename(normalizedPath);
|
|
631
|
+
if (baseName !== normalizedPath) {
|
|
632
|
+
variations.push(baseName, baseName.toLowerCase());
|
|
633
|
+
}
|
|
634
|
+
return [...new Set(variations)];
|
|
635
|
+
}
|
|
636
|
+
function resolveFile(targetPath, options) {
|
|
637
|
+
const { byPath, byName, currentFilePath, extensions = [] } = options;
|
|
638
|
+
const baseVariations = createPathVariations(targetPath, currentFilePath);
|
|
639
|
+
const allVariations = [];
|
|
640
|
+
for (const variation of baseVariations) {
|
|
641
|
+
allVariations.push(variation);
|
|
642
|
+
const hasExtension = extensions.some((ext) => variation.endsWith(ext));
|
|
643
|
+
if (!hasExtension) {
|
|
644
|
+
for (const ext of extensions) {
|
|
645
|
+
allVariations.push(variation + ext);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
for (const variation of allVariations) {
|
|
650
|
+
const exactMatch = byPath.get(variation);
|
|
651
|
+
if (exactMatch) return exactMatch;
|
|
652
|
+
}
|
|
653
|
+
if (byName) {
|
|
654
|
+
const fileName = path5.basename(targetPath);
|
|
655
|
+
const candidates = findByFileNameCaseInsensitive(fileName, byName);
|
|
656
|
+
if (candidates.length > 0) {
|
|
657
|
+
return resolveFromCandidates(candidates, currentFilePath);
|
|
658
|
+
}
|
|
659
|
+
for (const ext of extensions) {
|
|
660
|
+
if (fileName.endsWith(ext)) {
|
|
661
|
+
const nameWithoutExt = fileName.slice(0, -ext.length);
|
|
662
|
+
const candidatesNoExt = findByFileNameCaseInsensitive(
|
|
663
|
+
nameWithoutExt,
|
|
664
|
+
byName
|
|
665
|
+
);
|
|
666
|
+
if (candidatesNoExt.length > 0) {
|
|
667
|
+
return resolveFromCandidates(candidatesNoExt, currentFilePath);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
function buildFileMaps(posts) {
|
|
675
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
676
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
677
|
+
const byFileName = /* @__PURE__ */ new Map();
|
|
678
|
+
const byAlias = /* @__PURE__ */ new Map();
|
|
679
|
+
for (const post of posts) {
|
|
680
|
+
byPath.set(post.originalPath, post);
|
|
681
|
+
bySlug.set(post.slug, post);
|
|
682
|
+
const fileName = path5.basename(post.originalPath, ".md");
|
|
683
|
+
if (!byFileName.has(fileName)) {
|
|
684
|
+
byFileName.set(fileName, []);
|
|
685
|
+
}
|
|
686
|
+
byFileName.get(fileName).push(post);
|
|
687
|
+
const aliases = post.frontmatter?.aliases;
|
|
688
|
+
if (Array.isArray(aliases)) {
|
|
689
|
+
for (const alias of aliases) {
|
|
690
|
+
if (typeof alias === "string" && alias.trim()) {
|
|
691
|
+
const normalizedAlias = alias.trim().toLowerCase();
|
|
692
|
+
if (!byAlias.has(normalizedAlias)) {
|
|
693
|
+
byAlias.set(normalizedAlias, []);
|
|
694
|
+
}
|
|
695
|
+
byAlias.get(normalizedAlias).push(post);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return { byPath, bySlug, byFileName, byAlias };
|
|
701
|
+
}
|
|
702
|
+
function findBySlugCaseInsensitive(slug, bySlug) {
|
|
703
|
+
const exact = bySlug.get(slug);
|
|
704
|
+
if (exact) return exact;
|
|
705
|
+
const lowerSlug = slug.toLowerCase();
|
|
706
|
+
for (const [key, value] of bySlug.entries()) {
|
|
707
|
+
if (key.toLowerCase() === lowerSlug) {
|
|
708
|
+
return value;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
const slugifiedSearch = toSlug(slug);
|
|
712
|
+
const exactSlugified = bySlug.get(slugifiedSearch);
|
|
713
|
+
if (exactSlugified) return exactSlugified;
|
|
714
|
+
const lowerSlugified = slugifiedSearch.toLowerCase();
|
|
715
|
+
for (const [key, value] of bySlug.entries()) {
|
|
716
|
+
if (key.toLowerCase() === lowerSlugified) {
|
|
717
|
+
return value;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
function findByFileNameCI(fileName, byFileName) {
|
|
723
|
+
const results = findByFileNameCaseInsensitive(fileName, byFileName);
|
|
724
|
+
if (results.length > 0) return results;
|
|
725
|
+
const slugifiedSearch = toSlug(fileName).replace(/-/g, " ");
|
|
726
|
+
const slugResults = findByFileNameCaseInsensitive(slugifiedSearch, byFileName);
|
|
727
|
+
if (slugResults.length > 0) return slugResults;
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
730
|
+
function resolveWikilink(wikilink, fileMap, currentFilePath) {
|
|
731
|
+
const cleanLink = wikilink.split("#")[0].split("#^")[0];
|
|
732
|
+
const bySlug = findBySlugCaseInsensitive(cleanLink, fileMap.bySlug);
|
|
733
|
+
if (bySlug) return bySlug;
|
|
734
|
+
const byAlias = fileMap.byAlias.get(cleanLink.toLowerCase());
|
|
735
|
+
if (byAlias && byAlias.length > 0) {
|
|
736
|
+
return resolveFromCandidates(byAlias, currentFilePath);
|
|
737
|
+
}
|
|
738
|
+
if (cleanLink.includes("/")) {
|
|
739
|
+
const possiblePaths = [
|
|
740
|
+
cleanLink + ".md",
|
|
741
|
+
cleanLink,
|
|
742
|
+
path5.join(path5.dirname(currentFilePath), cleanLink + ".md"),
|
|
743
|
+
path5.join(path5.dirname(currentFilePath), cleanLink)
|
|
744
|
+
];
|
|
745
|
+
for (const possiblePath of possiblePaths) {
|
|
746
|
+
const normalized = path5.normalize(possiblePath);
|
|
747
|
+
const file = fileMap.byPath.get(normalized);
|
|
748
|
+
if (file) return file;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
const candidates = findByFileNameCI(cleanLink, fileMap.byFileName);
|
|
752
|
+
if (!candidates || candidates.length === 0) {
|
|
753
|
+
const withoutExt = cleanLink.replace(/\.md$/, "");
|
|
754
|
+
const candidatesWithoutExt = findByFileNameCI(
|
|
755
|
+
withoutExt,
|
|
756
|
+
fileMap.byFileName
|
|
757
|
+
);
|
|
758
|
+
if (!candidatesWithoutExt || candidatesWithoutExt.length === 0) {
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
return resolveFromCandidates(candidatesWithoutExt, currentFilePath);
|
|
762
|
+
}
|
|
763
|
+
return resolveFromCandidates(candidates, currentFilePath);
|
|
764
|
+
}
|
|
765
|
+
function slugifyAnchor(text) {
|
|
766
|
+
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
|
|
767
|
+
}
|
|
768
|
+
function wikilinkToMarkdownLink(wikilink, alias, targetFile, options) {
|
|
769
|
+
let anchor = "";
|
|
770
|
+
if (wikilink.includes("#^")) ; else if (wikilink.includes("#")) {
|
|
771
|
+
const parts = wikilink.split("#");
|
|
772
|
+
anchor = "#" + slugifyAnchor(parts[1]);
|
|
773
|
+
}
|
|
774
|
+
const url = `${options.urlPrefix}/${targetFile.slug}${anchor}`;
|
|
775
|
+
const text = alias || targetFile.title || targetFile.fileName;
|
|
776
|
+
return { url, text };
|
|
777
|
+
}
|
|
778
|
+
function resolveMarkdownLink(href, fileMap, currentFilePath, options) {
|
|
779
|
+
if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("#") || href.startsWith(options.urlPrefix)) {
|
|
780
|
+
return href;
|
|
781
|
+
}
|
|
782
|
+
let cleanHref = href;
|
|
783
|
+
if (cleanHref.startsWith("/content/")) {
|
|
784
|
+
cleanHref = cleanHref.substring("/content/".length);
|
|
785
|
+
}
|
|
786
|
+
let anchor = "";
|
|
787
|
+
const anchorIndex = cleanHref.indexOf("#");
|
|
788
|
+
if (anchorIndex !== -1) {
|
|
789
|
+
anchor = cleanHref.substring(anchorIndex);
|
|
790
|
+
cleanHref = cleanHref.substring(0, anchorIndex);
|
|
791
|
+
}
|
|
792
|
+
const bySlug = findBySlugCaseInsensitive(cleanHref, fileMap.bySlug);
|
|
793
|
+
if (bySlug) {
|
|
794
|
+
return `${options.urlPrefix}/${bySlug.slug}${anchor}`;
|
|
795
|
+
}
|
|
796
|
+
const possiblePaths = [
|
|
797
|
+
cleanHref + ".md",
|
|
798
|
+
cleanHref,
|
|
799
|
+
path5.join(path5.dirname(currentFilePath), cleanHref + ".md"),
|
|
800
|
+
path5.join(path5.dirname(currentFilePath), cleanHref)
|
|
801
|
+
];
|
|
802
|
+
for (const possiblePath of possiblePaths) {
|
|
803
|
+
const normalized = path5.normalize(possiblePath);
|
|
804
|
+
const file = fileMap.byPath.get(normalized);
|
|
805
|
+
if (file) {
|
|
806
|
+
return `${options.urlPrefix}/${file.slug}${anchor}`;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return `${options.urlPrefix}/${cleanHref}${anchor}`;
|
|
810
|
+
}
|
|
811
|
+
var Regex = {
|
|
812
|
+
HeaderOnly: /^#[^\^]+/,
|
|
813
|
+
BlockOnly: /^#\^.+/,
|
|
814
|
+
PageAndHeader: /.+#[^\^]+/,
|
|
815
|
+
PageAndBlock: /.+#\^.+/
|
|
816
|
+
};
|
|
817
|
+
function wikiToObsidian(wikiLink) {
|
|
818
|
+
const { value, alias } = wikiLink;
|
|
819
|
+
switch (true) {
|
|
820
|
+
case Regex.BlockOnly.test(value): {
|
|
821
|
+
const blockOnly = { type: "block", block: value.slice(2) };
|
|
822
|
+
if (alias) blockOnly.alias = alias;
|
|
823
|
+
return blockOnly;
|
|
824
|
+
}
|
|
825
|
+
case Regex.HeaderOnly.test(value): {
|
|
826
|
+
const headerOnly = { type: "header", header: value.slice(1) };
|
|
827
|
+
if (alias) headerOnly.alias = alias;
|
|
828
|
+
return headerOnly;
|
|
829
|
+
}
|
|
830
|
+
case Regex.PageAndBlock.test(value): {
|
|
831
|
+
const [page, block] = value.split("#^");
|
|
832
|
+
const pageAndBlock = { type: "page-block", page, block };
|
|
833
|
+
if (alias) pageAndBlock.alias = alias;
|
|
834
|
+
return pageAndBlock;
|
|
835
|
+
}
|
|
836
|
+
case Regex.PageAndHeader.test(value): {
|
|
837
|
+
const [page, header] = value.split("#");
|
|
838
|
+
const pageAndHeader = { type: "page-header", page, header };
|
|
839
|
+
if (alias) pageAndHeader.alias = alias;
|
|
840
|
+
return pageAndHeader;
|
|
841
|
+
}
|
|
842
|
+
default: {
|
|
843
|
+
const page = { type: "page", page: value };
|
|
844
|
+
if (alias) page.alias = alias;
|
|
845
|
+
return page;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
function slugifyAnchor2(text) {
|
|
850
|
+
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
|
|
851
|
+
}
|
|
852
|
+
function createCustomToLink(options) {
|
|
853
|
+
const {
|
|
854
|
+
filesBySlug,
|
|
855
|
+
filesByName,
|
|
856
|
+
filesByAlias,
|
|
857
|
+
urlPrefix,
|
|
858
|
+
currentFile,
|
|
859
|
+
issueCollector
|
|
860
|
+
} = options;
|
|
861
|
+
return (wikiLink) => {
|
|
862
|
+
const obsidianLink = wikiToObsidian(wikiLink);
|
|
863
|
+
switch (obsidianLink.type) {
|
|
864
|
+
case "page":
|
|
865
|
+
case "page-header":
|
|
866
|
+
case "page-block": {
|
|
867
|
+
let targetFile = filesBySlug.get(obsidianLink.page);
|
|
868
|
+
if (!targetFile) {
|
|
869
|
+
const aliasCandidates = filesByAlias.get(
|
|
870
|
+
obsidianLink.page.toLowerCase()
|
|
871
|
+
);
|
|
872
|
+
if (aliasCandidates && aliasCandidates.length > 0) {
|
|
873
|
+
if (aliasCandidates.length === 1) {
|
|
874
|
+
targetFile = aliasCandidates[0];
|
|
875
|
+
} else if (currentFile) {
|
|
876
|
+
const currentDir = path5.dirname(currentFile.originalPath);
|
|
877
|
+
const sameDir = aliasCandidates.find(
|
|
878
|
+
(f) => path5.dirname(f.originalPath) === currentDir
|
|
879
|
+
);
|
|
880
|
+
targetFile = sameDir || aliasCandidates[0];
|
|
881
|
+
} else {
|
|
882
|
+
targetFile = aliasCandidates[0];
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (!targetFile) {
|
|
887
|
+
targetFile = filesByName.get(obsidianLink.page);
|
|
888
|
+
}
|
|
889
|
+
if (!targetFile) {
|
|
890
|
+
if (issueCollector && currentFile) {
|
|
891
|
+
issueCollector.addBrokenLink({
|
|
892
|
+
filePath: currentFile.originalPath,
|
|
893
|
+
linkText: obsidianLink.alias || obsidianLink.page,
|
|
894
|
+
linkTarget: obsidianLink.page,
|
|
895
|
+
linkType: "wiki"
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
return {
|
|
899
|
+
value: obsidianLink.alias || obsidianLink.page,
|
|
900
|
+
uri: `#broken-link:${obsidianLink.page}`
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
let uri = `${urlPrefix}/${targetFile.slug}`;
|
|
904
|
+
if (obsidianLink.type === "page-header") {
|
|
905
|
+
uri += `#${slugifyAnchor2(obsidianLink.header)}`;
|
|
906
|
+
}
|
|
907
|
+
return {
|
|
908
|
+
value: obsidianLink.alias || obsidianLink.page,
|
|
909
|
+
uri
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
case "header":
|
|
913
|
+
return {
|
|
914
|
+
value: obsidianLink.alias || `#${obsidianLink.header}`,
|
|
915
|
+
uri: `#${slugifyAnchor2(obsidianLink.header)}`
|
|
916
|
+
};
|
|
917
|
+
case "block":
|
|
918
|
+
return {
|
|
919
|
+
value: obsidianLink.alias || `#^${obsidianLink.block}`,
|
|
920
|
+
uri: ""
|
|
921
|
+
};
|
|
922
|
+
default: {
|
|
923
|
+
return { value: String(obsidianLink), uri: "" };
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
function resolveMarkdownLinkPath(linkPath, currentFilePath, filesByPath) {
|
|
929
|
+
if (linkPath.startsWith("http://") || linkPath.startsWith("https://") || linkPath.startsWith("mailto:") || linkPath.startsWith("#")) {
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
const cleanPath = linkPath.split("#")[0];
|
|
933
|
+
const currentDir = path5.dirname(currentFilePath);
|
|
934
|
+
const resolvedPath = path5.normalize(path5.join(currentDir, cleanPath));
|
|
935
|
+
const candidates = [resolvedPath, resolvedPath.replace(/\.md$/, "") + ".md"];
|
|
936
|
+
for (const candidate of candidates) {
|
|
937
|
+
const file = filesByPath.get(candidate);
|
|
938
|
+
if (file) return file;
|
|
939
|
+
}
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
var remarkMarkdownLinkResolver = (options) => {
|
|
943
|
+
const { filesByPath, urlPrefix, currentFilePath } = options;
|
|
944
|
+
return (tree) => {
|
|
945
|
+
visit(tree, "link", (node) => {
|
|
946
|
+
if (node.url && !node.url.startsWith(urlPrefix) && !node.url.startsWith("http")) {
|
|
947
|
+
const targetFile = resolveMarkdownLinkPath(
|
|
948
|
+
node.url,
|
|
949
|
+
currentFilePath,
|
|
950
|
+
filesByPath
|
|
951
|
+
);
|
|
952
|
+
if (targetFile) {
|
|
953
|
+
const anchorIndex = node.url.indexOf("#");
|
|
954
|
+
const anchor = anchorIndex !== -1 ? node.url.substring(anchorIndex) : "";
|
|
955
|
+
node.url = `${urlPrefix}/${targetFile.slug}${anchor}`;
|
|
956
|
+
} else {
|
|
957
|
+
const filename = node.url.replace(/\.md$/, "");
|
|
958
|
+
for (const [, file] of filesByPath.entries()) {
|
|
959
|
+
if (file.fileName === filename) {
|
|
960
|
+
const anchorIndex = node.url.indexOf("#");
|
|
961
|
+
const anchor = anchorIndex !== -1 ? node.url.substring(anchorIndex) : "";
|
|
962
|
+
node.url = `${urlPrefix}/${file.slug}${anchor}`;
|
|
963
|
+
break;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
};
|
|
970
|
+
};
|
|
971
|
+
var remarkObsidianLink;
|
|
972
|
+
var rehypeAutolinkHeadings;
|
|
973
|
+
var rehypeExternalLinks;
|
|
974
|
+
var createDefaultLogger = (debugLevel) => {
|
|
975
|
+
return (message, level = "info") => {
|
|
976
|
+
const levelNum = level === "debug" ? 3 : level === "info" ? 2 : level === "warn" ? 1 : 0;
|
|
977
|
+
if (levelNum <= debugLevel) {
|
|
978
|
+
const prefix = "[processor-core]";
|
|
979
|
+
switch (level) {
|
|
980
|
+
case "error":
|
|
981
|
+
console.error(`${prefix} ERROR: ${message}`);
|
|
982
|
+
break;
|
|
983
|
+
case "warn":
|
|
984
|
+
console.warn(`${prefix} WARN: ${message}`);
|
|
985
|
+
break;
|
|
986
|
+
case "debug":
|
|
987
|
+
console.log(`${prefix} DEBUG: ${message}`);
|
|
988
|
+
break;
|
|
989
|
+
default:
|
|
990
|
+
console.log(`${prefix} ${message}`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
};
|
|
995
|
+
var Processor = class {
|
|
996
|
+
config;
|
|
997
|
+
pluginManager;
|
|
998
|
+
issues;
|
|
999
|
+
log;
|
|
1000
|
+
initialized = false;
|
|
1001
|
+
constructor(options) {
|
|
1002
|
+
this.config = options.config;
|
|
1003
|
+
this.issues = new IssueCollector();
|
|
1004
|
+
this.log = options.log ?? createDefaultLogger(this.config.debug?.level ?? 1);
|
|
1005
|
+
this.pluginManager = new PluginManager({
|
|
1006
|
+
config: this.config,
|
|
1007
|
+
outputDir: this.resolveOutputDir(),
|
|
1008
|
+
issues: this.issues,
|
|
1009
|
+
log: this.log
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
// --------------------------------------------------------------------------
|
|
1013
|
+
// Initialization
|
|
1014
|
+
// --------------------------------------------------------------------------
|
|
1015
|
+
/**
|
|
1016
|
+
* Initialize the processor and plugins
|
|
1017
|
+
*/
|
|
1018
|
+
async initialize() {
|
|
1019
|
+
if (this.initialized) {
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
this.log("Initializing processor...", "info");
|
|
1023
|
+
await this.pluginManager.initialize();
|
|
1024
|
+
this.initialized = true;
|
|
1025
|
+
this.log("Processor initialized", "info");
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Dispose the processor and cleanup resources
|
|
1029
|
+
*/
|
|
1030
|
+
async dispose() {
|
|
1031
|
+
await this.pluginManager.dispose();
|
|
1032
|
+
this.initialized = false;
|
|
1033
|
+
}
|
|
1034
|
+
// --------------------------------------------------------------------------
|
|
1035
|
+
// Path Resolution
|
|
1036
|
+
// --------------------------------------------------------------------------
|
|
1037
|
+
resolveInputDir() {
|
|
1038
|
+
const base = this.config.dir.base ?? process.cwd();
|
|
1039
|
+
return path5.resolve(base, this.config.dir.input);
|
|
1040
|
+
}
|
|
1041
|
+
resolveOutputDir() {
|
|
1042
|
+
const base = this.config.dir.base ?? process.cwd();
|
|
1043
|
+
return path5.resolve(base, this.config.dir.output ?? "build");
|
|
1044
|
+
}
|
|
1045
|
+
// --------------------------------------------------------------------------
|
|
1046
|
+
// Processing
|
|
1047
|
+
// --------------------------------------------------------------------------
|
|
1048
|
+
/**
|
|
1049
|
+
* Process the folder and generate output
|
|
1050
|
+
*/
|
|
1051
|
+
async process() {
|
|
1052
|
+
if (!this.initialized) {
|
|
1053
|
+
await this.initialize();
|
|
1054
|
+
}
|
|
1055
|
+
const inputDir = this.resolveInputDir();
|
|
1056
|
+
const outputDir = this.resolveOutputDir();
|
|
1057
|
+
this.log(`Processing folder: ${inputDir}`, "info");
|
|
1058
|
+
this.log(`Output directory: ${outputDir}`, "info");
|
|
1059
|
+
await ensureDir(outputDir);
|
|
1060
|
+
const state = {
|
|
1061
|
+
inputDir,
|
|
1062
|
+
outputDir,
|
|
1063
|
+
posts: [],
|
|
1064
|
+
media: [],
|
|
1065
|
+
slugManager: new SlugManager("number"),
|
|
1066
|
+
issues: this.issues,
|
|
1067
|
+
mediaPathMap: /* @__PURE__ */ new Map(),
|
|
1068
|
+
cacheStats: {
|
|
1069
|
+
mediaCacheHits: 0,
|
|
1070
|
+
mediaCacheMisses: 0,
|
|
1071
|
+
textEmbeddingCacheHits: 0,
|
|
1072
|
+
textEmbeddingCacheMisses: 0,
|
|
1073
|
+
imageEmbeddingCacheHits: 0,
|
|
1074
|
+
imageEmbeddingCacheMisses: 0
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
if (!this.config.media?.skip) {
|
|
1078
|
+
await this.processMedia(state);
|
|
1079
|
+
} else {
|
|
1080
|
+
this.log("Skipping media processing", "info");
|
|
1081
|
+
}
|
|
1082
|
+
await this.processMarkdownFiles(state);
|
|
1083
|
+
await this.generateEmbeddings(state);
|
|
1084
|
+
await this.generateSimilarity(state);
|
|
1085
|
+
await this.buildDatabase(state);
|
|
1086
|
+
const outputFiles = await this.writeOutput(state);
|
|
1087
|
+
const issueReport = this.issues.generateReport();
|
|
1088
|
+
this.log(this.issues.getSummaryString(), "info");
|
|
1089
|
+
const hasCacheActivity = state.cacheStats.mediaCacheHits > 0 || state.cacheStats.mediaCacheMisses > 0 || state.cacheStats.textEmbeddingCacheHits > 0 || state.cacheStats.imageEmbeddingCacheHits > 0;
|
|
1090
|
+
if (hasCacheActivity) {
|
|
1091
|
+
this.log(
|
|
1092
|
+
`Cache stats: media ${state.cacheStats.mediaCacheHits}/${state.cacheStats.mediaCacheHits + state.cacheStats.mediaCacheMisses} hits, text embeddings ${state.cacheStats.textEmbeddingCacheHits}/${state.cacheStats.textEmbeddingCacheHits + state.cacheStats.textEmbeddingCacheMisses} hits, image embeddings ${state.cacheStats.imageEmbeddingCacheHits}/${state.cacheStats.imageEmbeddingCacheHits + state.cacheStats.imageEmbeddingCacheMisses} hits`,
|
|
1093
|
+
"info"
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
return {
|
|
1097
|
+
posts: state.posts,
|
|
1098
|
+
media: state.media,
|
|
1099
|
+
outputDir,
|
|
1100
|
+
outputFiles,
|
|
1101
|
+
issues: issueReport,
|
|
1102
|
+
cacheStats: this.config.cache ? state.cacheStats : void 0
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
// --------------------------------------------------------------------------
|
|
1106
|
+
// Media Processing
|
|
1107
|
+
// --------------------------------------------------------------------------
|
|
1108
|
+
async processMedia(state) {
|
|
1109
|
+
this.log("Processing media files...", "info");
|
|
1110
|
+
const imageProcessor = this.pluginManager.getPluginByKey("imageProcessor");
|
|
1111
|
+
if (!imageProcessor) {
|
|
1112
|
+
this.log("No image processor plugin, skipping media optimization", "debug");
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
const mediaExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".avif", ".svg"];
|
|
1116
|
+
const mediaFiles = [];
|
|
1117
|
+
const findMedia = async (dir) => {
|
|
1118
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
1119
|
+
for (const entry of entries) {
|
|
1120
|
+
const fullPath = path5.join(dir, entry.name);
|
|
1121
|
+
if (entry.isDirectory()) {
|
|
1122
|
+
await findMedia(fullPath);
|
|
1123
|
+
} else if (entry.isFile() && mediaExtensions.some((ext) => entry.name.toLowerCase().endsWith(ext))) {
|
|
1124
|
+
mediaFiles.push(fullPath);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
await findMedia(state.inputDir);
|
|
1129
|
+
this.log(`Found ${mediaFiles.length} media files`, "info");
|
|
1130
|
+
const mediaOutputDir = path5.join(state.outputDir, this.config.dir.mediaOutput ?? "_media");
|
|
1131
|
+
await ensureDir(mediaOutputDir);
|
|
1132
|
+
const imageSizes = this.config.media?.sizes ?? [];
|
|
1133
|
+
const imageFormat = this.config.media?.format ?? "webp";
|
|
1134
|
+
const imageQuality = this.config.media?.quality ?? 80;
|
|
1135
|
+
const useHash = this.config.media?.useHash ?? true;
|
|
1136
|
+
const useSharding = this.config.media?.useSharding ?? false;
|
|
1137
|
+
for (const mediaPath of mediaFiles) {
|
|
1138
|
+
try {
|
|
1139
|
+
const relativePath2 = normalizePath(path5.relative(state.inputDir, mediaPath));
|
|
1140
|
+
const fileName = path5.basename(mediaPath);
|
|
1141
|
+
const fileBuffer = await fs.readFile(mediaPath);
|
|
1142
|
+
const contentHash = hashBuffer(fileBuffer);
|
|
1143
|
+
const shortContentHash = contentHash.substring(0, 16);
|
|
1144
|
+
const mediaCache = this.config.cache?.media;
|
|
1145
|
+
const cachedMedia = mediaCache?.get(contentHash);
|
|
1146
|
+
if (cachedMedia) {
|
|
1147
|
+
state.cacheStats.mediaCacheHits++;
|
|
1148
|
+
this.log(`Cache hit for ${fileName} (${contentHash.substring(0, 8)}...)`, "debug");
|
|
1149
|
+
const processedMedia = {
|
|
1150
|
+
originalPath: relativePath2,
|
|
1151
|
+
outputPath: cachedMedia.outputPath,
|
|
1152
|
+
fileName,
|
|
1153
|
+
type: "image",
|
|
1154
|
+
metadata: {
|
|
1155
|
+
width: cachedMedia.width,
|
|
1156
|
+
height: cachedMedia.height,
|
|
1157
|
+
format: cachedMedia.format,
|
|
1158
|
+
size: cachedMedia.size,
|
|
1159
|
+
originalSize: cachedMedia.originalSize,
|
|
1160
|
+
hash: contentHash
|
|
1161
|
+
},
|
|
1162
|
+
sizes: cachedMedia.sizes.length > 0 ? cachedMedia.sizes.map((s) => ({
|
|
1163
|
+
suffix: s.suffix,
|
|
1164
|
+
outputPath: s.outputPath,
|
|
1165
|
+
width: s.width,
|
|
1166
|
+
height: s.height,
|
|
1167
|
+
size: s.size
|
|
1168
|
+
})) : void 0
|
|
1169
|
+
};
|
|
1170
|
+
state.media.push(processedMedia);
|
|
1171
|
+
state.mediaPathMap.set(relativePath2, processedMedia);
|
|
1172
|
+
continue;
|
|
1173
|
+
}
|
|
1174
|
+
state.cacheStats.mediaCacheMisses++;
|
|
1175
|
+
let outputFileName;
|
|
1176
|
+
let outputSubDir = mediaOutputDir;
|
|
1177
|
+
if (useHash) {
|
|
1178
|
+
outputFileName = `${shortContentHash}.${imageFormat}`;
|
|
1179
|
+
if (useSharding) {
|
|
1180
|
+
const shardDir = contentHash.substring(0, 2);
|
|
1181
|
+
outputSubDir = path5.join(mediaOutputDir, shardDir);
|
|
1182
|
+
await ensureDir(outputSubDir);
|
|
1183
|
+
}
|
|
1184
|
+
} else {
|
|
1185
|
+
const fileNameWithoutExt = path5.basename(mediaPath, path5.extname(mediaPath));
|
|
1186
|
+
outputFileName = `${fileNameWithoutExt}.${imageFormat}`;
|
|
1187
|
+
}
|
|
1188
|
+
const outputPath = path5.join(outputSubDir, outputFileName);
|
|
1189
|
+
const stats = await getStats(mediaPath);
|
|
1190
|
+
if (imageProcessor.canProcess(mediaPath)) {
|
|
1191
|
+
const metadata = await imageProcessor.getMetadata(mediaPath);
|
|
1192
|
+
const originalWidth = metadata.width;
|
|
1193
|
+
const result = await imageProcessor.process(mediaPath, outputPath, {
|
|
1194
|
+
format: imageFormat,
|
|
1195
|
+
quality: imageQuality
|
|
1196
|
+
});
|
|
1197
|
+
const sizeVariants = [];
|
|
1198
|
+
for (const sizeConfig of imageSizes) {
|
|
1199
|
+
if (sizeConfig.width === null || sizeConfig.width >= originalWidth) {
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
try {
|
|
1203
|
+
let sizeOutputFileName;
|
|
1204
|
+
if (useHash) {
|
|
1205
|
+
sizeOutputFileName = `${shortContentHash}-${sizeConfig.suffix}.${imageFormat}`;
|
|
1206
|
+
} else {
|
|
1207
|
+
const fileNameWithoutExt = path5.basename(mediaPath, path5.extname(mediaPath));
|
|
1208
|
+
sizeOutputFileName = `${fileNameWithoutExt}-${sizeConfig.suffix}.${imageFormat}`;
|
|
1209
|
+
}
|
|
1210
|
+
const sizeOutputPath = path5.join(outputSubDir, sizeOutputFileName);
|
|
1211
|
+
const sizeResult = await imageProcessor.process(mediaPath, sizeOutputPath, {
|
|
1212
|
+
width: sizeConfig.width,
|
|
1213
|
+
height: sizeConfig.height ?? void 0,
|
|
1214
|
+
format: imageFormat,
|
|
1215
|
+
quality: imageQuality
|
|
1216
|
+
});
|
|
1217
|
+
sizeVariants.push({
|
|
1218
|
+
suffix: sizeConfig.suffix,
|
|
1219
|
+
outputPath: normalizePath(path5.relative(state.outputDir, sizeResult.outputPath)),
|
|
1220
|
+
width: sizeResult.width,
|
|
1221
|
+
height: sizeResult.height,
|
|
1222
|
+
size: sizeResult.size
|
|
1223
|
+
});
|
|
1224
|
+
} catch (sizeError) {
|
|
1225
|
+
const sizeErrorMsg = sizeError instanceof Error ? sizeError.message : String(sizeError);
|
|
1226
|
+
this.log(`Failed to generate ${sizeConfig.suffix} size for ${fileName}: ${sizeErrorMsg}`, "warn");
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
const processedMedia = {
|
|
1230
|
+
originalPath: relativePath2,
|
|
1231
|
+
outputPath: normalizePath(path5.relative(state.outputDir, result.outputPath)),
|
|
1232
|
+
fileName,
|
|
1233
|
+
type: "image",
|
|
1234
|
+
metadata: {
|
|
1235
|
+
width: result.width,
|
|
1236
|
+
height: result.height,
|
|
1237
|
+
format: result.format,
|
|
1238
|
+
size: result.size,
|
|
1239
|
+
originalSize: stats.size,
|
|
1240
|
+
hash: contentHash
|
|
1241
|
+
},
|
|
1242
|
+
sizes: sizeVariants.length > 0 ? sizeVariants : void 0
|
|
1243
|
+
};
|
|
1244
|
+
state.media.push(processedMedia);
|
|
1245
|
+
state.mediaPathMap.set(relativePath2, processedMedia);
|
|
1246
|
+
} else {
|
|
1247
|
+
const ext = path5.extname(mediaPath);
|
|
1248
|
+
let copyOutputPath;
|
|
1249
|
+
if (useHash) {
|
|
1250
|
+
const copyFileName = `${shortContentHash}${ext}`;
|
|
1251
|
+
copyOutputPath = path5.join(outputSubDir, copyFileName);
|
|
1252
|
+
} else {
|
|
1253
|
+
copyOutputPath = path5.join(outputSubDir, fileName);
|
|
1254
|
+
}
|
|
1255
|
+
await imageProcessor.copy(mediaPath, copyOutputPath);
|
|
1256
|
+
const copiedMedia = {
|
|
1257
|
+
originalPath: relativePath2,
|
|
1258
|
+
outputPath: normalizePath(path5.relative(state.outputDir, copyOutputPath)),
|
|
1259
|
+
fileName,
|
|
1260
|
+
type: "media",
|
|
1261
|
+
metadata: {
|
|
1262
|
+
size: stats.size,
|
|
1263
|
+
originalSize: stats.size,
|
|
1264
|
+
hash: contentHash
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
state.media.push(copiedMedia);
|
|
1268
|
+
state.mediaPathMap.set(relativePath2, copiedMedia);
|
|
1269
|
+
}
|
|
1270
|
+
} catch (error) {
|
|
1271
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1272
|
+
this.issues.addMediaProcessingError({
|
|
1273
|
+
filePath: mediaPath,
|
|
1274
|
+
mediaPath,
|
|
1275
|
+
operation: "read",
|
|
1276
|
+
errorMessage
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
this.log(`Processed ${state.media.length} media files`, "info");
|
|
1281
|
+
}
|
|
1282
|
+
// --------------------------------------------------------------------------
|
|
1283
|
+
// Markdown Processing
|
|
1284
|
+
// --------------------------------------------------------------------------
|
|
1285
|
+
async processMarkdownFiles(state) {
|
|
1286
|
+
this.log("Processing markdown files...", "info");
|
|
1287
|
+
const mdFiles = await findMarkdownFiles(state.inputDir);
|
|
1288
|
+
this.log(`Found ${mdFiles.length} markdown files`, "info");
|
|
1289
|
+
const parsedFiles = [];
|
|
1290
|
+
for (const filePath of mdFiles) {
|
|
1291
|
+
try {
|
|
1292
|
+
const content = await readText(filePath);
|
|
1293
|
+
const stats = await getStats(filePath);
|
|
1294
|
+
parsedFiles.push({ filePath, content, stats });
|
|
1295
|
+
} catch (error) {
|
|
1296
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1297
|
+
this.log(`Failed to read ${filePath}: ${errorMessage}`, "warn");
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
for (const { filePath, content, stats } of parsedFiles) {
|
|
1301
|
+
try {
|
|
1302
|
+
const post = await this.parseMarkdownFile(state, filePath, content, stats);
|
|
1303
|
+
if (post) {
|
|
1304
|
+
state.posts.push(post);
|
|
1305
|
+
}
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1308
|
+
this.log(`Failed to process ${filePath}: ${errorMessage}`, "error");
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
const wikiLinksEnabled = this.config.pipeline?.wikiLinks !== false;
|
|
1312
|
+
if (wikiLinksEnabled && state.posts.length > 0) {
|
|
1313
|
+
await this.renderPostsWithLinks(state);
|
|
1314
|
+
}
|
|
1315
|
+
this.log(`Processed ${state.posts.length} posts`, "info");
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Phase 1: Parse a markdown file — extract all metadata but use basic HTML.
|
|
1319
|
+
* The markdown source is stored for Phase 2 re-rendering.
|
|
1320
|
+
*/
|
|
1321
|
+
async parseMarkdownFile(state, filePath, content, stats) {
|
|
1322
|
+
const relativePath2 = normalizePath(path5.relative(state.inputDir, filePath));
|
|
1323
|
+
const fileName = getFileName(filePath);
|
|
1324
|
+
const pipelineConfig = this.config.pipeline ?? {};
|
|
1325
|
+
const result = await processMarkdown(content, {
|
|
1326
|
+
gfm: pipelineConfig.gfm ?? true,
|
|
1327
|
+
allowRawHtml: pipelineConfig.allowRawHtml ?? true
|
|
1328
|
+
});
|
|
1329
|
+
const isPublished = result.frontmatter.published !== false && result.frontmatter.draft !== true;
|
|
1330
|
+
if (!isPublished && !this.config.content?.processAllFiles) {
|
|
1331
|
+
this.log(`Skipping unpublished: ${relativePath2}`, "debug");
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
const parentFolder = path5.dirname(relativePath2);
|
|
1335
|
+
const scope = this.config.content?.slugScope === "top-folder" ? relativePath2.split("/")[0] : void 0;
|
|
1336
|
+
const slugInfo = state.slugManager.reserve(filePath, {
|
|
1337
|
+
fileName,
|
|
1338
|
+
parentFolder: parentFolder !== "." ? parentFolder : void 0,
|
|
1339
|
+
frontmatterSlug: result.frontmatter.slug
|
|
1340
|
+
}, scope);
|
|
1341
|
+
if (slugInfo.wasModified) {
|
|
1342
|
+
this.issues.addSlugConflict({
|
|
1343
|
+
filePath: relativePath2,
|
|
1344
|
+
originalSlug: slugInfo.originalSlug,
|
|
1345
|
+
finalSlug: slugInfo.slug,
|
|
1346
|
+
conflictingFiles: []
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
const plainText = mdastToText(result.mdast);
|
|
1350
|
+
const headings = extractHeadings(result.mdast);
|
|
1351
|
+
const toc = buildToc(headings);
|
|
1352
|
+
const firstParagraph = extractFirstParagraph(result.mdast);
|
|
1353
|
+
const wordCount = countWords(plainText);
|
|
1354
|
+
const hash = hashContent(content);
|
|
1355
|
+
const title = result.frontmatter.title || (headings.length > 0 && headings[0].depth === 1 ? headings[0].text : fileName);
|
|
1356
|
+
const cover = this.resolveCover(
|
|
1357
|
+
result.frontmatter,
|
|
1358
|
+
relativePath2,
|
|
1359
|
+
state.mediaPathMap
|
|
1360
|
+
);
|
|
1361
|
+
const post = {
|
|
1362
|
+
hash,
|
|
1363
|
+
slug: slugInfo.slug,
|
|
1364
|
+
fileName,
|
|
1365
|
+
title,
|
|
1366
|
+
content: result.html,
|
|
1367
|
+
markdown: result.markdown,
|
|
1368
|
+
frontmatter: result.frontmatter,
|
|
1369
|
+
plainText,
|
|
1370
|
+
excerpt: firstParagraph,
|
|
1371
|
+
wordCount,
|
|
1372
|
+
toc,
|
|
1373
|
+
originalPath: relativePath2,
|
|
1374
|
+
metadata: {
|
|
1375
|
+
createdAt: stats.created.toISOString(),
|
|
1376
|
+
modifiedAt: stats.modified.toISOString(),
|
|
1377
|
+
processedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
if (cover) {
|
|
1381
|
+
post.cover = cover;
|
|
1382
|
+
}
|
|
1383
|
+
return post;
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Phase 2: Build file maps from all parsed posts, then re-render each
|
|
1387
|
+
* file's markdown through a pipeline that includes wiki-link resolution.
|
|
1388
|
+
*/
|
|
1389
|
+
async renderPostsWithLinks(state) {
|
|
1390
|
+
this.log("Phase 2: Rendering with wiki-link resolution...", "info");
|
|
1391
|
+
if (!remarkObsidianLink) {
|
|
1392
|
+
const [rolMod, rahMod, relMod] = await Promise.all([
|
|
1393
|
+
import('remark-obsidian-link'),
|
|
1394
|
+
import('rehype-autolink-headings'),
|
|
1395
|
+
import('rehype-external-links')
|
|
1396
|
+
]);
|
|
1397
|
+
remarkObsidianLink = rolMod.remarkObsidianLink;
|
|
1398
|
+
rehypeAutolinkHeadings = rahMod.default ?? rahMod;
|
|
1399
|
+
rehypeExternalLinks = relMod.default ?? relMod;
|
|
1400
|
+
}
|
|
1401
|
+
const fileMaps = buildFileMaps(state.posts);
|
|
1402
|
+
const filesByName = /* @__PURE__ */ new Map();
|
|
1403
|
+
for (const [name, posts] of fileMaps.byFileName) {
|
|
1404
|
+
if (posts.length > 0) {
|
|
1405
|
+
filesByName.set(name, posts[0]);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
const urlPrefix = this.config.content?.notePathPrefix ?? "/content";
|
|
1409
|
+
const pipelineConfig = this.config.pipeline ?? {};
|
|
1410
|
+
const { unified: unified2 } = await import('unified');
|
|
1411
|
+
const remarkParse2 = (await import('remark-parse')).default;
|
|
1412
|
+
const remarkGfm2 = (await import('remark-gfm')).default;
|
|
1413
|
+
const remarkRehype2 = (await import('remark-rehype')).default;
|
|
1414
|
+
const rehypeRaw2 = (await import('rehype-raw')).default;
|
|
1415
|
+
const rehypeSlug2 = (await import('rehype-slug')).default;
|
|
1416
|
+
const rehypeStringify2 = (await import('rehype-stringify')).default;
|
|
1417
|
+
for (let i = 0; i < state.posts.length; i++) {
|
|
1418
|
+
const post = state.posts[i];
|
|
1419
|
+
try {
|
|
1420
|
+
const toLink = createCustomToLink({
|
|
1421
|
+
filesBySlug: fileMaps.bySlug,
|
|
1422
|
+
filesByName,
|
|
1423
|
+
filesByPath: fileMaps.byPath,
|
|
1424
|
+
filesByAlias: fileMaps.byAlias,
|
|
1425
|
+
urlPrefix,
|
|
1426
|
+
currentFile: post,
|
|
1427
|
+
issueCollector: this.issues
|
|
1428
|
+
});
|
|
1429
|
+
let pipeline = unified2().use(remarkParse2);
|
|
1430
|
+
if (pipelineConfig.gfm !== false) {
|
|
1431
|
+
pipeline = pipeline.use(remarkGfm2);
|
|
1432
|
+
}
|
|
1433
|
+
pipeline = pipeline.use(remarkObsidianLink, { toLink }).use(remarkMarkdownLinkResolver, {
|
|
1434
|
+
filesByPath: fileMaps.byPath,
|
|
1435
|
+
urlPrefix,
|
|
1436
|
+
currentFilePath: post.originalPath
|
|
1437
|
+
}).use(remarkRehype2, {
|
|
1438
|
+
allowDangerousHtml: pipelineConfig.allowRawHtml !== false
|
|
1439
|
+
});
|
|
1440
|
+
if (pipelineConfig.allowRawHtml !== false) {
|
|
1441
|
+
pipeline = pipeline.use(rehypeRaw2);
|
|
1442
|
+
}
|
|
1443
|
+
pipeline = pipeline.use(rehypeSlug2).use(rehypeExternalLinks, { target: "_blank", rel: ["nofollow", "noopener", "noreferrer"] }).use(rehypeAutolinkHeadings, { behavior: "wrap" }).use(rehypeStringify2);
|
|
1444
|
+
const file = await pipeline.process(post.markdown);
|
|
1445
|
+
state.posts[i].content = String(file);
|
|
1446
|
+
} catch (error) {
|
|
1447
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1448
|
+
this.log(
|
|
1449
|
+
`Failed to render links for ${post.originalPath}: ${errorMessage}`,
|
|
1450
|
+
"warn"
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Build absolute URL from relative path using domain config
|
|
1457
|
+
* URL structure: {domain}/_shared/medias/{filename}
|
|
1458
|
+
*/
|
|
1459
|
+
buildAbsoluteUrl(relativePath2) {
|
|
1460
|
+
const domain = this.config.media?.domain;
|
|
1461
|
+
if (!domain) {
|
|
1462
|
+
return void 0;
|
|
1463
|
+
}
|
|
1464
|
+
const filename = relativePath2.split("/").pop();
|
|
1465
|
+
if (!filename) {
|
|
1466
|
+
return void 0;
|
|
1467
|
+
}
|
|
1468
|
+
const baseUrl = domain.replace(/\/$/, "");
|
|
1469
|
+
return `${baseUrl}/_shared/medias/${filename}`;
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Resolve cover image from frontmatter
|
|
1473
|
+
* Returns PostCover if found, PostCoverError if specified but not found, undefined if not specified
|
|
1474
|
+
*/
|
|
1475
|
+
resolveCover(frontmatter, postPath, mediaPathMap) {
|
|
1476
|
+
const coverFields = ["cover", "image", "thumbnail", "coverImage", "cover_image"];
|
|
1477
|
+
let coverValue;
|
|
1478
|
+
for (const field of coverFields) {
|
|
1479
|
+
const value = frontmatter[field];
|
|
1480
|
+
if (typeof value === "string" && value.length > 0) {
|
|
1481
|
+
coverValue = value;
|
|
1482
|
+
break;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
if (!coverValue) {
|
|
1486
|
+
return void 0;
|
|
1487
|
+
}
|
|
1488
|
+
const postDir = path5.dirname(postPath);
|
|
1489
|
+
const pathsToTry = [
|
|
1490
|
+
normalizePath(coverValue),
|
|
1491
|
+
// Direct path from root
|
|
1492
|
+
normalizePath(path5.join(postDir, coverValue)),
|
|
1493
|
+
// Relative to post directory
|
|
1494
|
+
normalizePath(coverValue.replace(/^\//, ""))
|
|
1495
|
+
// Remove leading slash if present
|
|
1496
|
+
];
|
|
1497
|
+
let mediaInfo;
|
|
1498
|
+
for (const tryPath of pathsToTry) {
|
|
1499
|
+
mediaInfo = mediaPathMap.get(tryPath);
|
|
1500
|
+
if (mediaInfo) {
|
|
1501
|
+
break;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
if (!mediaInfo) {
|
|
1505
|
+
this.log(`Cover image not found for "${postPath}": "${coverValue}"`, "warn");
|
|
1506
|
+
return {
|
|
1507
|
+
original: coverValue,
|
|
1508
|
+
error: `Cover image not found: "${coverValue}"`
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
const cover = {
|
|
1512
|
+
original: coverValue,
|
|
1513
|
+
path: mediaInfo.outputPath,
|
|
1514
|
+
url: this.buildAbsoluteUrl(mediaInfo.outputPath),
|
|
1515
|
+
hash: mediaInfo.metadata?.hash,
|
|
1516
|
+
width: mediaInfo.metadata?.width,
|
|
1517
|
+
height: mediaInfo.metadata?.height,
|
|
1518
|
+
sizes: mediaInfo.sizes?.map((s) => ({
|
|
1519
|
+
suffix: s.suffix,
|
|
1520
|
+
path: s.outputPath,
|
|
1521
|
+
url: this.buildAbsoluteUrl(s.outputPath),
|
|
1522
|
+
width: s.width,
|
|
1523
|
+
height: s.height
|
|
1524
|
+
}))
|
|
1525
|
+
};
|
|
1526
|
+
return cover;
|
|
1527
|
+
}
|
|
1528
|
+
// --------------------------------------------------------------------------
|
|
1529
|
+
// Embeddings
|
|
1530
|
+
// --------------------------------------------------------------------------
|
|
1531
|
+
async generateEmbeddings(state) {
|
|
1532
|
+
const textEmbedder = this.pluginManager.getPluginByKey("textEmbedder");
|
|
1533
|
+
if (!textEmbedder || textEmbedder.dimensions === 0) {
|
|
1534
|
+
this.log("No text embedder plugin, skipping embeddings", "debug");
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
this.log("Generating text embeddings...", "info");
|
|
1538
|
+
const textEmbeddingsCache = this.config.cache?.textEmbeddings;
|
|
1539
|
+
try {
|
|
1540
|
+
const uncachedPosts = [];
|
|
1541
|
+
const cachedCount = { hits: 0, misses: 0 };
|
|
1542
|
+
for (let i = 0; i < state.posts.length; i++) {
|
|
1543
|
+
const post = state.posts[i];
|
|
1544
|
+
if (!post) continue;
|
|
1545
|
+
const cachedEmbedding = textEmbeddingsCache?.get(post.hash);
|
|
1546
|
+
if (cachedEmbedding) {
|
|
1547
|
+
post.embedding = cachedEmbedding;
|
|
1548
|
+
cachedCount.hits++;
|
|
1549
|
+
state.cacheStats.textEmbeddingCacheHits++;
|
|
1550
|
+
} else {
|
|
1551
|
+
uncachedPosts.push({ index: i, text: post.plainText });
|
|
1552
|
+
cachedCount.misses++;
|
|
1553
|
+
state.cacheStats.textEmbeddingCacheMisses++;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
if (uncachedPosts.length > 0) {
|
|
1557
|
+
const texts = uncachedPosts.map((p) => p.text);
|
|
1558
|
+
const embeddings = await textEmbedder.batchEmbed(texts);
|
|
1559
|
+
for (let i = 0; i < uncachedPosts.length; i++) {
|
|
1560
|
+
const postInfo = uncachedPosts[i];
|
|
1561
|
+
if (!postInfo) continue;
|
|
1562
|
+
const post = state.posts[postInfo.index];
|
|
1563
|
+
if (post && embeddings[i]) {
|
|
1564
|
+
post.embedding = embeddings[i];
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
this.log(`Generated ${embeddings.length} text embeddings (${cachedCount.hits} from cache)`, "info");
|
|
1568
|
+
} else {
|
|
1569
|
+
this.log(`All ${cachedCount.hits} text embeddings loaded from cache`, "info");
|
|
1570
|
+
}
|
|
1571
|
+
} catch (error) {
|
|
1572
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1573
|
+
this.issues.addEmbeddingError({
|
|
1574
|
+
embeddingType: "text",
|
|
1575
|
+
operation: "batch",
|
|
1576
|
+
errorMessage
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
const imageEmbedder = this.pluginManager.getPluginByKey("imageEmbedder");
|
|
1580
|
+
if (imageEmbedder && imageEmbedder.dimensions > 0 && state.media.length > 0) {
|
|
1581
|
+
this.log("Generating image embeddings...", "info");
|
|
1582
|
+
const imageEmbeddingsCache = this.config.cache?.imageEmbeddings;
|
|
1583
|
+
let cachedCount = 0;
|
|
1584
|
+
let generatedCount = 0;
|
|
1585
|
+
for (const media of state.media) {
|
|
1586
|
+
if (media.type === "image") {
|
|
1587
|
+
const mediaHash = media.metadata?.hash;
|
|
1588
|
+
const cachedEmbedding = mediaHash ? imageEmbeddingsCache?.get(mediaHash) : void 0;
|
|
1589
|
+
if (cachedEmbedding) {
|
|
1590
|
+
media.embedding = cachedEmbedding;
|
|
1591
|
+
cachedCount++;
|
|
1592
|
+
state.cacheStats.imageEmbeddingCacheHits++;
|
|
1593
|
+
} else {
|
|
1594
|
+
state.cacheStats.imageEmbeddingCacheMisses++;
|
|
1595
|
+
try {
|
|
1596
|
+
const mediaPath = path5.join(state.outputDir, media.outputPath);
|
|
1597
|
+
const embedding = await imageEmbedder.embedFile(mediaPath);
|
|
1598
|
+
media.embedding = embedding;
|
|
1599
|
+
generatedCount++;
|
|
1600
|
+
} catch (error) {
|
|
1601
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1602
|
+
this.issues.addEmbeddingError({
|
|
1603
|
+
filePath: media.originalPath,
|
|
1604
|
+
embeddingType: "image",
|
|
1605
|
+
operation: "embed",
|
|
1606
|
+
errorMessage
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
this.log(`Image embeddings: ${generatedCount} generated, ${cachedCount} from cache`, "info");
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
// --------------------------------------------------------------------------
|
|
1616
|
+
// Similarity
|
|
1617
|
+
// --------------------------------------------------------------------------
|
|
1618
|
+
async generateSimilarity(state) {
|
|
1619
|
+
const similarity = this.pluginManager.getPluginByKey("similarity");
|
|
1620
|
+
if (!similarity) {
|
|
1621
|
+
this.log("No similarity plugin, skipping similarity computation", "debug");
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
this.log("Generating similarity data...", "info");
|
|
1625
|
+
try {
|
|
1626
|
+
const result = await similarity.generateSimilarityMap(state.posts);
|
|
1627
|
+
const similarityPath = path5.join(state.outputDir, "similarity.json");
|
|
1628
|
+
await writeJson(similarityPath, {
|
|
1629
|
+
pairwiseScores: Object.fromEntries(result.pairwiseScores),
|
|
1630
|
+
similarPosts: Object.fromEntries(result.similarPosts),
|
|
1631
|
+
metadata: result.metadata
|
|
1632
|
+
});
|
|
1633
|
+
this.log(
|
|
1634
|
+
`Generated similarity data: ${result.metadata.pairCount} pairs`,
|
|
1635
|
+
"info"
|
|
1636
|
+
);
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1639
|
+
this.log(`Failed to generate similarity: ${errorMessage}`, "error");
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
// --------------------------------------------------------------------------
|
|
1643
|
+
// Database
|
|
1644
|
+
// --------------------------------------------------------------------------
|
|
1645
|
+
async buildDatabase(state) {
|
|
1646
|
+
const database = this.pluginManager.getPluginByKey("database");
|
|
1647
|
+
if (!database) {
|
|
1648
|
+
this.log("No database plugin, skipping database generation", "debug");
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
this.log("Building database...", "info");
|
|
1652
|
+
try {
|
|
1653
|
+
const result = await database.build({
|
|
1654
|
+
posts: state.posts,
|
|
1655
|
+
media: state.media
|
|
1656
|
+
});
|
|
1657
|
+
this.log(
|
|
1658
|
+
`Database built: ${result.tables.length} tables, ${result.hasVectorSearch ? "with" : "without"} vector search`,
|
|
1659
|
+
"info"
|
|
1660
|
+
);
|
|
1661
|
+
} catch (error) {
|
|
1662
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1663
|
+
this.log(`Failed to build database: ${errorMessage}`, "error");
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
// --------------------------------------------------------------------------
|
|
1667
|
+
// Output
|
|
1668
|
+
// --------------------------------------------------------------------------
|
|
1669
|
+
async writeOutput(state) {
|
|
1670
|
+
const { outputDir, posts, media } = state;
|
|
1671
|
+
this.log("Writing output files...", "info");
|
|
1672
|
+
const postsPath = path5.join(outputDir, "posts.json");
|
|
1673
|
+
const mediaPath = path5.join(outputDir, "medias.json");
|
|
1674
|
+
const slugMapPath = path5.join(outputDir, "posts-slug-map.json");
|
|
1675
|
+
const pathMapPath = path5.join(outputDir, "posts-path-map.json");
|
|
1676
|
+
const issuesPath = path5.join(outputDir, "processor-issues.json");
|
|
1677
|
+
const slugMap = {};
|
|
1678
|
+
const pathMap = {};
|
|
1679
|
+
for (const post of posts) {
|
|
1680
|
+
slugMap[post.slug] = post.hash;
|
|
1681
|
+
pathMap[post.originalPath] = post.hash;
|
|
1682
|
+
}
|
|
1683
|
+
await Promise.all([
|
|
1684
|
+
writeJson(postsPath, posts),
|
|
1685
|
+
writeJson(mediaPath, media),
|
|
1686
|
+
writeJson(slugMapPath, slugMap),
|
|
1687
|
+
writeJson(pathMapPath, pathMap),
|
|
1688
|
+
writeJson(issuesPath, this.issues.generateReport())
|
|
1689
|
+
]);
|
|
1690
|
+
this.log(`Output written to ${outputDir}`, "info");
|
|
1691
|
+
return {
|
|
1692
|
+
posts: postsPath,
|
|
1693
|
+
media: mediaPath,
|
|
1694
|
+
slugMap: slugMapPath,
|
|
1695
|
+
pathMap: pathMapPath,
|
|
1696
|
+
issues: issuesPath
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
};
|
|
1700
|
+
var createProcessor = async (config) => {
|
|
1701
|
+
const processor = new Processor({ config });
|
|
1702
|
+
await processor.initialize();
|
|
1703
|
+
return processor;
|
|
1704
|
+
};
|
|
1705
|
+
var processFolder = async (config) => {
|
|
1706
|
+
const processor = await createProcessor(config);
|
|
1707
|
+
try {
|
|
1708
|
+
return await processor.process();
|
|
1709
|
+
} finally {
|
|
1710
|
+
await processor.dispose();
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
var processVault = processFolder;
|
|
1714
|
+
|
|
1715
|
+
export { DEFAULT_PIPELINE_OPTIONS, IssueCollector, Processor, SlugManager, buildFileMaps, buildToc, calculateSummary, combineHashes, copyFile, countWords, createBasePipeline, createCustomToLink, createPathVariations, createProcessor, ensureDir, estimateReadingTime, extractFirstParagraph, extractHeadings, fileExists, filterIssues, findByFileNameCaseInsensitive, findFiles, findMarkdownFiles, generateBaseSlug, generateIssueReport, getContentStats, getExtension, getFileName, getStats, hashBuffer, hashContent, hastToHtml, mdastToHast, mdastToText, normalizePath, parseFrontmatter, parseToMdast, processFolder, processMarkdown, processVault, readJson, readText, relativePath, remarkMarkdownLinkResolver, resolveFile, resolveFromCandidates, resolveMarkdownLink, resolveMarkdownLinkPath, resolveSlugConflict, resolveWikilink, shortHash, toSlug, wikiToObsidian, wikilinkToMarkdownLink, writeJson, writeText };
|