@pruddiman/mdmirror 0.1.0
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 +1 -0
- package/dist/build/builder.d.ts +41 -0
- package/dist/build/builder.js +108 -0
- package/dist/build/builder.js.map +1 -0
- package/dist/cli/build.d.ts +14 -0
- package/dist/cli/build.js +116 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +104 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/serve.d.ts +15 -0
- package/dist/cli/serve.js +233 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/core/discovery.d.ts +12 -0
- package/dist/core/discovery.js +91 -0
- package/dist/core/discovery.js.map +1 -0
- package/dist/core/navigation.d.ts +12 -0
- package/dist/core/navigation.js +155 -0
- package/dist/core/navigation.js.map +1 -0
- package/dist/core/slug.d.ts +47 -0
- package/dist/core/slug.js +132 -0
- package/dist/core/slug.js.map +1 -0
- package/dist/core/title.d.ts +22 -0
- package/dist/core/title.js +43 -0
- package/dist/core/title.js.map +1 -0
- package/dist/render/highlight.d.ts +23 -0
- package/dist/render/highlight.js +36 -0
- package/dist/render/highlight.js.map +1 -0
- package/dist/render/mermaid.d.ts +13 -0
- package/dist/render/mermaid.js +66 -0
- package/dist/render/mermaid.js.map +1 -0
- package/dist/render/pipeline.d.ts +32 -0
- package/dist/render/pipeline.js +141 -0
- package/dist/render/pipeline.js.map +1 -0
- package/dist/search/index.d.ts +19 -0
- package/dist/search/index.js +43 -0
- package/dist/search/index.js.map +1 -0
- package/dist/server/reload.d.ts +23 -0
- package/dist/server/reload.js +80 -0
- package/dist/server/reload.js.map +1 -0
- package/dist/server/server.d.ts +22 -0
- package/dist/server/server.js +137 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/watcher.d.ts +24 -0
- package/dist/server/watcher.js +62 -0
- package/dist/server/watcher.js.map +1 -0
- package/dist/theme/index.d.ts +8 -0
- package/dist/theme/index.js +19 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/layout.d.ts +37 -0
- package/dist/theme/layout.js +141 -0
- package/dist/theme/layout.js.map +1 -0
- package/dist/theme/scripts.d.ts +29 -0
- package/dist/theme/scripts.js +159 -0
- package/dist/theme/scripts.js.map +1 -0
- package/dist/theme/styles.css +462 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/cli/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursive file discovery module.
|
|
3
|
+
* Accepts a folder path, recursively finds all .md and .txt files,
|
|
4
|
+
* and returns partially-populated Document objects.
|
|
5
|
+
*/
|
|
6
|
+
import type { Document } from "../types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Discover all supported files in a directory recursively.
|
|
9
|
+
* Returns Document objects with sourcePath, absolutePath, fileType, and isIndex populated.
|
|
10
|
+
* Other fields (slug, title, content, renderedHtml, sortKey) are left as empty strings.
|
|
11
|
+
*/
|
|
12
|
+
export declare function discoverFiles(rootPath: string): Promise<Document[]>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursive file discovery module.
|
|
3
|
+
* Accepts a folder path, recursively finds all .md and .txt files,
|
|
4
|
+
* and returns partially-populated Document objects.
|
|
5
|
+
*/
|
|
6
|
+
import { readdir, stat } from "node:fs/promises";
|
|
7
|
+
import { resolve, relative, extname, basename } from "node:path";
|
|
8
|
+
const SUPPORTED_EXTENSIONS = new Set([".md", ".txt"]);
|
|
9
|
+
const INDEX_FILENAMES = new Set(["index.md", "readme.md", "overview.md"]);
|
|
10
|
+
/**
|
|
11
|
+
* Discover all supported files in a directory recursively.
|
|
12
|
+
* Returns Document objects with sourcePath, absolutePath, fileType, and isIndex populated.
|
|
13
|
+
* Other fields (slug, title, content, renderedHtml, sortKey) are left as empty strings.
|
|
14
|
+
*/
|
|
15
|
+
export async function discoverFiles(rootPath) {
|
|
16
|
+
const absoluteRoot = resolve(rootPath);
|
|
17
|
+
// Validate path exists
|
|
18
|
+
let rootStat;
|
|
19
|
+
try {
|
|
20
|
+
rootStat = await stat(absoluteRoot);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
throw new Error(`Path does not exist: ${absoluteRoot}`);
|
|
24
|
+
}
|
|
25
|
+
// Handle single file input
|
|
26
|
+
if (rootStat.isFile()) {
|
|
27
|
+
const ext = extname(absoluteRoot).toLowerCase();
|
|
28
|
+
if (!SUPPORTED_EXTENSIONS.has(ext)) {
|
|
29
|
+
throw new Error(`File is not a supported type (.md, .txt): ${absoluteRoot}`);
|
|
30
|
+
}
|
|
31
|
+
const fileName = basename(absoluteRoot);
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
sourcePath: fileName,
|
|
35
|
+
absolutePath: absoluteRoot,
|
|
36
|
+
slug: "",
|
|
37
|
+
title: "",
|
|
38
|
+
description: "",
|
|
39
|
+
content: "",
|
|
40
|
+
renderedHtml: "",
|
|
41
|
+
fileType: ext.slice(1),
|
|
42
|
+
isIndex: INDEX_FILENAMES.has(fileName.toLowerCase()),
|
|
43
|
+
sortKey: "",
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
if (!rootStat.isDirectory()) {
|
|
48
|
+
throw new Error(`Path is not a file or directory: ${absoluteRoot}`);
|
|
49
|
+
}
|
|
50
|
+
const documents = [];
|
|
51
|
+
await walkDirectory(absoluteRoot, absoluteRoot, documents);
|
|
52
|
+
if (documents.length === 0) {
|
|
53
|
+
throw new Error(`No .md or .txt files found in ${absoluteRoot}`);
|
|
54
|
+
}
|
|
55
|
+
return documents;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Recursively walk a directory and collect Document objects.
|
|
59
|
+
*/
|
|
60
|
+
async function walkDirectory(currentPath, rootPath, documents) {
|
|
61
|
+
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
const fullPath = resolve(currentPath, entry.name);
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
// Skip hidden directories
|
|
66
|
+
if (entry.name.startsWith("."))
|
|
67
|
+
continue;
|
|
68
|
+
await walkDirectory(fullPath, rootPath, documents);
|
|
69
|
+
}
|
|
70
|
+
else if (entry.isFile()) {
|
|
71
|
+
const ext = extname(entry.name).toLowerCase();
|
|
72
|
+
if (!SUPPORTED_EXTENSIONS.has(ext))
|
|
73
|
+
continue;
|
|
74
|
+
const sourcePath = relative(rootPath, fullPath);
|
|
75
|
+
const fileName = basename(entry.name).toLowerCase();
|
|
76
|
+
documents.push({
|
|
77
|
+
sourcePath,
|
|
78
|
+
absolutePath: fullPath,
|
|
79
|
+
slug: "",
|
|
80
|
+
title: "",
|
|
81
|
+
description: "",
|
|
82
|
+
content: "",
|
|
83
|
+
renderedHtml: "",
|
|
84
|
+
fileType: ext.slice(1),
|
|
85
|
+
isIndex: INDEX_FILENAMES.has(fileName),
|
|
86
|
+
sortKey: "",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/core/discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation tree builder.
|
|
3
|
+
* Takes Document[] and builds a NavigationTree with NavigationNode hierarchy
|
|
4
|
+
* mirroring folder structure.
|
|
5
|
+
*/
|
|
6
|
+
import type { Document, NavigationTree } from "../types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Build a NavigationTree from a list of documents.
|
|
9
|
+
* Creates a hierarchy of sections (folders) and pages (files),
|
|
10
|
+
* sorted by numeric prefix then alphabetically.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildNavigationTree(documents: Document[]): NavigationTree;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation tree builder.
|
|
3
|
+
* Takes Document[] and builds a NavigationTree with NavigationNode hierarchy
|
|
4
|
+
* mirroring folder structure.
|
|
5
|
+
*/
|
|
6
|
+
import { dirname, basename } from "node:path";
|
|
7
|
+
import { generateSortKey } from "./slug.js";
|
|
8
|
+
import { humanizeFilename } from "./title.js";
|
|
9
|
+
/**
|
|
10
|
+
* Build a NavigationTree from a list of documents.
|
|
11
|
+
* Creates a hierarchy of sections (folders) and pages (files),
|
|
12
|
+
* sorted by numeric prefix then alphabetically.
|
|
13
|
+
*/
|
|
14
|
+
export function buildNavigationTree(documents) {
|
|
15
|
+
// Create root node
|
|
16
|
+
const root = {
|
|
17
|
+
label: "",
|
|
18
|
+
slug: "",
|
|
19
|
+
sortKey: "",
|
|
20
|
+
children: new Map(),
|
|
21
|
+
pages: [],
|
|
22
|
+
indexDocument: null,
|
|
23
|
+
};
|
|
24
|
+
// Place each document into the tree
|
|
25
|
+
for (const doc of documents) {
|
|
26
|
+
const dir = dirname(doc.sourcePath);
|
|
27
|
+
const segments = dir === "." ? [] : dir.split(/[/\\]/);
|
|
28
|
+
// Navigate to the correct folder node, creating intermediate nodes as needed
|
|
29
|
+
let current = root;
|
|
30
|
+
let currentSlug = "";
|
|
31
|
+
for (const segment of segments) {
|
|
32
|
+
currentSlug = currentSlug ? `${currentSlug}/${segment}` : segment;
|
|
33
|
+
if (!current.children.has(segment)) {
|
|
34
|
+
current.children.set(segment, {
|
|
35
|
+
label: humanizeFilename(segment + ".md"), // reuse humanize logic
|
|
36
|
+
slug: currentSlug,
|
|
37
|
+
sortKey: generateSortKey(segment),
|
|
38
|
+
children: new Map(),
|
|
39
|
+
pages: [],
|
|
40
|
+
indexDocument: null,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
current = current.children.get(segment);
|
|
44
|
+
}
|
|
45
|
+
// Place the document
|
|
46
|
+
if (doc.isIndex) {
|
|
47
|
+
current.indexDocument = doc;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
current.pages.push(doc);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Resolve index documents using priority chain:
|
|
54
|
+
// index.md > readme.md > overview.md > first file alphabetically
|
|
55
|
+
const INDEX_PRIORITY = ["index.md", "readme.md", "overview.md"];
|
|
56
|
+
function resolveIndexDocuments(node) {
|
|
57
|
+
// Recurse into children first
|
|
58
|
+
for (const child of node.children.values()) {
|
|
59
|
+
resolveIndexDocuments(child);
|
|
60
|
+
}
|
|
61
|
+
// Collect all candidate pages (existing index doc + regular pages)
|
|
62
|
+
const allPages = [...node.pages];
|
|
63
|
+
if (node.indexDocument) {
|
|
64
|
+
allPages.unshift(node.indexDocument);
|
|
65
|
+
node.indexDocument = null;
|
|
66
|
+
}
|
|
67
|
+
if (allPages.length === 0)
|
|
68
|
+
return;
|
|
69
|
+
// Try priority filenames first
|
|
70
|
+
let chosen = null;
|
|
71
|
+
for (const target of INDEX_PRIORITY) {
|
|
72
|
+
chosen =
|
|
73
|
+
allPages.find((d) => basename(d.sourcePath).toLowerCase() === target) ?? null;
|
|
74
|
+
if (chosen)
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
// Fallback: first page alphabetically by filename
|
|
78
|
+
if (!chosen) {
|
|
79
|
+
allPages.sort((a, b) => basename(a.sourcePath)
|
|
80
|
+
.toLowerCase()
|
|
81
|
+
.localeCompare(basename(b.sourcePath).toLowerCase()));
|
|
82
|
+
chosen = allPages[0];
|
|
83
|
+
}
|
|
84
|
+
// Set as index, remove from pages list
|
|
85
|
+
node.indexDocument = chosen;
|
|
86
|
+
node.pages = allPages.filter((d) => d !== chosen);
|
|
87
|
+
}
|
|
88
|
+
resolveIndexDocuments(root);
|
|
89
|
+
// Convert tree to NavigationNode[]
|
|
90
|
+
const flatPages = [];
|
|
91
|
+
function convertNode(node, depth) {
|
|
92
|
+
const result = [];
|
|
93
|
+
// Add section nodes for child folders
|
|
94
|
+
for (const [_name, child] of node.children) {
|
|
95
|
+
const sectionSlug = child.indexDocument ? child.indexDocument.slug : child.slug;
|
|
96
|
+
const childNavNodes = convertNode(child, depth + 1);
|
|
97
|
+
const section = {
|
|
98
|
+
label: child.label,
|
|
99
|
+
slug: sectionSlug,
|
|
100
|
+
type: "section",
|
|
101
|
+
children: childNavNodes,
|
|
102
|
+
sortKey: child.sortKey,
|
|
103
|
+
isActive: false,
|
|
104
|
+
indexDocument: child.indexDocument,
|
|
105
|
+
depth,
|
|
106
|
+
};
|
|
107
|
+
if (child.indexDocument) {
|
|
108
|
+
flatPages.push(child.indexDocument);
|
|
109
|
+
}
|
|
110
|
+
result.push(section);
|
|
111
|
+
}
|
|
112
|
+
// Add page nodes for files in this folder
|
|
113
|
+
for (const doc of node.pages) {
|
|
114
|
+
flatPages.push(doc);
|
|
115
|
+
result.push({
|
|
116
|
+
label: doc.title,
|
|
117
|
+
slug: doc.slug,
|
|
118
|
+
type: "page",
|
|
119
|
+
children: [],
|
|
120
|
+
sortKey: doc.sortKey,
|
|
121
|
+
isActive: false,
|
|
122
|
+
indexDocument: null,
|
|
123
|
+
depth,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// Sort: numeric prefixes first (numerically), then alphabetical
|
|
127
|
+
result.sort((a, b) => {
|
|
128
|
+
const aIsNumeric = a.sortKey.match(/^\d+$/);
|
|
129
|
+
const bIsNumeric = b.sortKey.match(/^\d+$/);
|
|
130
|
+
// Numeric-prefixed items come before non-prefixed
|
|
131
|
+
if (aIsNumeric && !bIsNumeric)
|
|
132
|
+
return -1;
|
|
133
|
+
if (!aIsNumeric && bIsNumeric)
|
|
134
|
+
return 1;
|
|
135
|
+
// Both numeric: compare numerically
|
|
136
|
+
if (aIsNumeric && bIsNumeric) {
|
|
137
|
+
return a.sortKey.localeCompare(b.sortKey);
|
|
138
|
+
}
|
|
139
|
+
// Both non-numeric: compare alphabetically
|
|
140
|
+
return a.sortKey.localeCompare(b.sortKey);
|
|
141
|
+
});
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
// Handle root-level index document
|
|
145
|
+
if (root.indexDocument) {
|
|
146
|
+
flatPages.unshift(root.indexDocument);
|
|
147
|
+
}
|
|
148
|
+
const rootNodes = convertNode(root, 0);
|
|
149
|
+
return {
|
|
150
|
+
root: rootNodes,
|
|
151
|
+
flatPages,
|
|
152
|
+
totalPages: flatPages.length,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL slug generation module.
|
|
3
|
+
* Converts file/folder paths to clean URL slugs.
|
|
4
|
+
* Handles spaces, special characters, numeric prefixes, and slug collisions.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Strip the numeric prefix from a filename/folder name.
|
|
8
|
+
* "01-intro" -> "intro", "setup" -> "setup"
|
|
9
|
+
*/
|
|
10
|
+
export declare function stripNumericPrefix(name: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Extract the numeric prefix value for sorting.
|
|
13
|
+
* "01-intro" -> 1, "setup" -> Infinity (sorts after numeric-prefixed items)
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractNumericPrefix(name: string): number;
|
|
16
|
+
/**
|
|
17
|
+
* Generate a sort key from a filename.
|
|
18
|
+
* Returns the numeric prefix (zero-padded) or the name for alphabetical sorting.
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateSortKey(name: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Generate a clean URL slug from a document's source path.
|
|
23
|
+
*
|
|
24
|
+
* Rules:
|
|
25
|
+
* - Strip file extension
|
|
26
|
+
* - Strip numeric prefixes from each path segment
|
|
27
|
+
* - index.md / README.md map to the folder slug (e.g., guides/index.md -> guides)
|
|
28
|
+
* - Root index.md maps to empty string (root page)
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateSlug(sourcePath: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Detect slug collisions in a set of documents and warn about them.
|
|
33
|
+
* Returns a map of slug -> source paths for any collisions.
|
|
34
|
+
*/
|
|
35
|
+
export declare function detectSlugCollisions(slugMap: Map<string, string[]>): Map<string, string[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Disambiguate slug collisions by appending numeric suffixes.
|
|
38
|
+
* When two or more documents produce the same slug, all but the first
|
|
39
|
+
* get a "-2", "-3", etc. suffix appended.
|
|
40
|
+
*
|
|
41
|
+
* Takes an array of objects with { slug, sourcePath } and mutates
|
|
42
|
+
* the slug property in-place.
|
|
43
|
+
*/
|
|
44
|
+
export declare function disambiguateSlugs(items: Array<{
|
|
45
|
+
slug: string;
|
|
46
|
+
sourcePath: string;
|
|
47
|
+
}>): void;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL slug generation module.
|
|
3
|
+
* Converts file/folder paths to clean URL slugs.
|
|
4
|
+
* Handles spaces, special characters, numeric prefixes, and slug collisions.
|
|
5
|
+
*/
|
|
6
|
+
import { dirname, basename, extname } from "node:path";
|
|
7
|
+
/** Regex to detect numeric prefixes like "01-", "02-", "10-" */
|
|
8
|
+
const NUMERIC_PREFIX_RE = /^(\d+)-(.+)$/;
|
|
9
|
+
/**
|
|
10
|
+
* Strip the numeric prefix from a filename/folder name.
|
|
11
|
+
* "01-intro" -> "intro", "setup" -> "setup"
|
|
12
|
+
*/
|
|
13
|
+
export function stripNumericPrefix(name) {
|
|
14
|
+
const match = name.match(NUMERIC_PREFIX_RE);
|
|
15
|
+
return match ? match[2] : name;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract the numeric prefix value for sorting.
|
|
19
|
+
* "01-intro" -> 1, "setup" -> Infinity (sorts after numeric-prefixed items)
|
|
20
|
+
*/
|
|
21
|
+
export function extractNumericPrefix(name) {
|
|
22
|
+
const match = name.match(NUMERIC_PREFIX_RE);
|
|
23
|
+
return match ? parseInt(match[1], 10) : Infinity;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Generate a sort key from a filename.
|
|
27
|
+
* Returns the numeric prefix (zero-padded) or the name for alphabetical sorting.
|
|
28
|
+
*/
|
|
29
|
+
export function generateSortKey(name) {
|
|
30
|
+
const match = name.match(NUMERIC_PREFIX_RE);
|
|
31
|
+
if (match) {
|
|
32
|
+
// Zero-pad to 10 digits for consistent numeric sorting
|
|
33
|
+
return match[1].padStart(10, "0");
|
|
34
|
+
}
|
|
35
|
+
// For non-numeric names, use the lowercase name for alphabetical sorting
|
|
36
|
+
return name.toLowerCase();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convert a name (filename or folder) into a URL-safe slug segment.
|
|
40
|
+
* Handles Unicode by normalizing to NFKD and stripping combining diacritics,
|
|
41
|
+
* then falls back to percent-encoding for characters that survive that step.
|
|
42
|
+
*/
|
|
43
|
+
function slugifySegment(name) {
|
|
44
|
+
// Strip numeric prefix
|
|
45
|
+
const stripped = stripNumericPrefix(name);
|
|
46
|
+
const transliterated = stripped
|
|
47
|
+
// NFKD decomposes accented chars into base letter + combining mark
|
|
48
|
+
.normalize("NFKD")
|
|
49
|
+
// Remove combining diacritical marks (accents, tildes, etc.)
|
|
50
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
51
|
+
.toLowerCase()
|
|
52
|
+
// Replace spaces and underscores with hyphens
|
|
53
|
+
.replace(/[\s_]+/g, "-")
|
|
54
|
+
// Remove any remaining non-ASCII characters (CJK, emoji, etc.)
|
|
55
|
+
// by replacing them with a hyphen so they don't silently vanish
|
|
56
|
+
// eslint-disable-next-line no-control-regex
|
|
57
|
+
.replace(/[^\x00-\x7f]/g, "-")
|
|
58
|
+
// Remove any remaining non-URL-safe ASCII characters (keep letters, numbers, hyphens)
|
|
59
|
+
.replace(/[^a-z0-9-]/g, "")
|
|
60
|
+
// Collapse multiple hyphens
|
|
61
|
+
.replace(/-+/g, "-")
|
|
62
|
+
// Trim leading/trailing hyphens
|
|
63
|
+
.replace(/^-|-$/g, "");
|
|
64
|
+
return transliterated;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generate a clean URL slug from a document's source path.
|
|
68
|
+
*
|
|
69
|
+
* Rules:
|
|
70
|
+
* - Strip file extension
|
|
71
|
+
* - Strip numeric prefixes from each path segment
|
|
72
|
+
* - index.md / README.md map to the folder slug (e.g., guides/index.md -> guides)
|
|
73
|
+
* - Root index.md maps to empty string (root page)
|
|
74
|
+
*/
|
|
75
|
+
export function generateSlug(sourcePath) {
|
|
76
|
+
const ext = extname(sourcePath);
|
|
77
|
+
const withoutExt = sourcePath.slice(0, -ext.length);
|
|
78
|
+
// Split into path segments
|
|
79
|
+
const dir = dirname(withoutExt);
|
|
80
|
+
const file = basename(withoutExt);
|
|
81
|
+
// Check if this is an index/readme file
|
|
82
|
+
const isIndex = ["index", "readme"].includes(file.toLowerCase());
|
|
83
|
+
// Process directory segments
|
|
84
|
+
const dirSegments = dir === "." ? [] : dir.split(/[/\\]/).map(slugifySegment).filter(Boolean);
|
|
85
|
+
if (isIndex) {
|
|
86
|
+
// index.md / README.md -> folder slug or root
|
|
87
|
+
return dirSegments.join("/");
|
|
88
|
+
}
|
|
89
|
+
// Regular file -> add slugified filename
|
|
90
|
+
const fileSlug = slugifySegment(file);
|
|
91
|
+
return [...dirSegments, fileSlug].filter(Boolean).join("/");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Detect slug collisions in a set of documents and warn about them.
|
|
95
|
+
* Returns a map of slug -> source paths for any collisions.
|
|
96
|
+
*/
|
|
97
|
+
export function detectSlugCollisions(slugMap) {
|
|
98
|
+
const collisions = new Map();
|
|
99
|
+
for (const [slug, paths] of slugMap) {
|
|
100
|
+
if (paths.length > 1) {
|
|
101
|
+
collisions.set(slug, paths);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return collisions;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Disambiguate slug collisions by appending numeric suffixes.
|
|
108
|
+
* When two or more documents produce the same slug, all but the first
|
|
109
|
+
* get a "-2", "-3", etc. suffix appended.
|
|
110
|
+
*
|
|
111
|
+
* Takes an array of objects with { slug, sourcePath } and mutates
|
|
112
|
+
* the slug property in-place.
|
|
113
|
+
*/
|
|
114
|
+
export function disambiguateSlugs(items) {
|
|
115
|
+
const slugMap = new Map();
|
|
116
|
+
for (const item of items) {
|
|
117
|
+
const existing = slugMap.get(item.slug) || [];
|
|
118
|
+
existing.push(item);
|
|
119
|
+
slugMap.set(item.slug, existing);
|
|
120
|
+
}
|
|
121
|
+
for (const [slug, owners] of slugMap) {
|
|
122
|
+
if (owners.length <= 1)
|
|
123
|
+
continue;
|
|
124
|
+
// Sort by sourcePath for deterministic disambiguation
|
|
125
|
+
owners.sort((a, b) => a.sourcePath.localeCompare(b.sourcePath));
|
|
126
|
+
// First one keeps the original slug, subsequent ones get suffixes
|
|
127
|
+
for (let i = 1; i < owners.length; i++) {
|
|
128
|
+
owners[i].slug = `${slug}-${i + 1}`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=slug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.js","sourceRoot":"","sources":["../../src/core/slug.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Title extraction module.
|
|
3
|
+
* Parses markdown content to extract the first heading as the page title.
|
|
4
|
+
* Falls back to a humanized filename if no heading is found.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Extract the title from markdown content.
|
|
8
|
+
* Returns the text of the first heading found, or null if none exists.
|
|
9
|
+
*/
|
|
10
|
+
export declare function extractTitleFromContent(content: string): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Convert a filename into a human-readable title.
|
|
13
|
+
* "getting-started.md" -> "Getting Started"
|
|
14
|
+
* "01-intro.md" -> "Intro"
|
|
15
|
+
* "my_cool_doc.txt" -> "My Cool Doc"
|
|
16
|
+
*/
|
|
17
|
+
export declare function humanizeFilename(filename: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Get the title for a document.
|
|
20
|
+
* Tries to extract from content first, falls back to humanized filename.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getTitle(content: string, sourcePath: string): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Title extraction module.
|
|
3
|
+
* Parses markdown content to extract the first heading as the page title.
|
|
4
|
+
* Falls back to a humanized filename if no heading is found.
|
|
5
|
+
*/
|
|
6
|
+
import { basename, extname } from "node:path";
|
|
7
|
+
import { stripNumericPrefix } from "./slug.js";
|
|
8
|
+
/** Regex to match a markdown heading (# Title) */
|
|
9
|
+
const HEADING_RE = /^#{1,6}\s+(.+)$/m;
|
|
10
|
+
/**
|
|
11
|
+
* Extract the title from markdown content.
|
|
12
|
+
* Returns the text of the first heading found, or null if none exists.
|
|
13
|
+
*/
|
|
14
|
+
export function extractTitleFromContent(content) {
|
|
15
|
+
const match = content.match(HEADING_RE);
|
|
16
|
+
return match ? match[1].trim() : null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Convert a filename into a human-readable title.
|
|
20
|
+
* "getting-started.md" -> "Getting Started"
|
|
21
|
+
* "01-intro.md" -> "Intro"
|
|
22
|
+
* "my_cool_doc.txt" -> "My Cool Doc"
|
|
23
|
+
*/
|
|
24
|
+
export function humanizeFilename(filename) {
|
|
25
|
+
// Remove extension
|
|
26
|
+
const withoutExt = basename(filename, extname(filename));
|
|
27
|
+
// Strip numeric prefix
|
|
28
|
+
const stripped = stripNumericPrefix(withoutExt);
|
|
29
|
+
return (stripped
|
|
30
|
+
// Replace hyphens and underscores with spaces
|
|
31
|
+
.replace(/[-_]+/g, " ")
|
|
32
|
+
// Capitalize first letter of each word
|
|
33
|
+
.replace(/\b\w/g, (c) => c.toUpperCase())
|
|
34
|
+
.trim());
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the title for a document.
|
|
38
|
+
* Tries to extract from content first, falls back to humanized filename.
|
|
39
|
+
*/
|
|
40
|
+
export function getTitle(content, sourcePath) {
|
|
41
|
+
return extractTitleFromContent(content) || humanizeFilename(sourcePath);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=title.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"title.js","sourceRoot":"","sources":["../../src/core/title.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,kDAAkD;AAClD,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAEtC;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACxC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACxC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,mBAAmB;IACnB,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEzD,uBAAuB;IACvB,MAAM,QAAQ,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAEhD,OAAO,CACL,QAAQ;QACN,8CAA8C;SAC7C,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;QACvB,uCAAuC;SACtC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SACxC,IAAI,EAAE,CACV,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,UAAkB;IAC1D,OAAO,uBAAuB,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,UAAU,CAAC,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shiki syntax highlighting configuration.
|
|
3
|
+
* Exports the @shikijs/rehype plugin configuration for the rendering pipeline.
|
|
4
|
+
* Ensures mermaid blocks are excluded from syntax highlighting.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Get the configured Shiki rehype plugin.
|
|
8
|
+
* Uses github-light theme with lazy language loading.
|
|
9
|
+
* Mermaid blocks are excluded since they're handled by the mermaid plugin.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getShikiPlugin(): readonly [import("unified").Plugin<[import("@shikijs/rehype").RehypeShikiOptions], import("hast").Root>, {
|
|
12
|
+
readonly themes: {
|
|
13
|
+
readonly light: "github-light";
|
|
14
|
+
readonly dark: "github-dark";
|
|
15
|
+
};
|
|
16
|
+
readonly defaultColor: false;
|
|
17
|
+
readonly transformers: readonly [{
|
|
18
|
+
readonly name: "mdmirror:skip-mermaid";
|
|
19
|
+
readonly preprocess: (code: string, options: {
|
|
20
|
+
lang?: string;
|
|
21
|
+
}) => string;
|
|
22
|
+
}];
|
|
23
|
+
}];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shiki syntax highlighting configuration.
|
|
3
|
+
* Exports the @shikijs/rehype plugin configuration for the rendering pipeline.
|
|
4
|
+
* Ensures mermaid blocks are excluded from syntax highlighting.
|
|
5
|
+
*/
|
|
6
|
+
import rehypeShiki from "@shikijs/rehype";
|
|
7
|
+
/**
|
|
8
|
+
* Get the configured Shiki rehype plugin.
|
|
9
|
+
* Uses github-light theme with lazy language loading.
|
|
10
|
+
* Mermaid blocks are excluded since they're handled by the mermaid plugin.
|
|
11
|
+
*/
|
|
12
|
+
export function getShikiPlugin() {
|
|
13
|
+
return [
|
|
14
|
+
rehypeShiki,
|
|
15
|
+
{
|
|
16
|
+
themes: {
|
|
17
|
+
light: "github-light",
|
|
18
|
+
dark: "github-dark",
|
|
19
|
+
},
|
|
20
|
+
defaultColor: false,
|
|
21
|
+
// Don't highlight mermaid blocks — they're handled separately
|
|
22
|
+
transformers: [
|
|
23
|
+
{
|
|
24
|
+
name: "mdmirror:skip-mermaid",
|
|
25
|
+
preprocess(code, options) {
|
|
26
|
+
if (options.lang === "mermaid") {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return code;
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=highlight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"highlight.js","sourceRoot":"","sources":["../../src/render/highlight.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO;QACL,WAAW;QACX;YACE,MAAM,EAAE;gBACN,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,aAAa;aACpB;YACD,YAAY,EAAE,KAAK;YACnB,8DAA8D;YAC9D,YAAY,EAAE;gBACZ;oBACE,IAAI,EAAE,uBAAuB;oBAC7B,UAAU,CAAC,IAAY,EAAE,OAA0B;wBACjD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;4BAC/B,OAAO,KAA0B,CAAC;wBACpC,CAAC;wBACD,OAAO,IAAI,CAAC;oBACd,CAAC;iBACF;aACF;SACF;KACO,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mermaid diagram rehype plugin.
|
|
3
|
+
* Detects fenced code blocks with lang="mermaid" and replaces them
|
|
4
|
+
* with <pre class="mermaid"> elements for client-side rendering.
|
|
5
|
+
*/
|
|
6
|
+
import type { Root } from "hast";
|
|
7
|
+
import type { Plugin } from "unified";
|
|
8
|
+
/**
|
|
9
|
+
* Rehype plugin that transforms mermaid code blocks into renderable elements.
|
|
10
|
+
* Converts: <pre><code class="language-mermaid">...</code></pre>
|
|
11
|
+
* Into: <pre class="mermaid">...</pre>
|
|
12
|
+
*/
|
|
13
|
+
export declare const rehypeMermaid: Plugin<[], Root>;
|