docmedown 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/LICENSE +21 -0
- package/README.md +277 -0
- package/bin/docmedown.js +2 -0
- package/dist/cli.js +1053 -0
- package/dist/docmedown.cjs +356 -0
- package/dist/docmedown.iife.js +356 -0
- package/dist/docmedown.mjs +33543 -0
- package/dist/types/runtime/app.d.ts +9 -0
- package/dist/types/runtime/components/Builtins.d.ts +44 -0
- package/dist/types/runtime/components/Content.d.ts +2 -0
- package/dist/types/runtime/components/DmdRegistry.d.ts +17 -0
- package/dist/types/runtime/components/Layout.d.ts +2 -0
- package/dist/types/runtime/components/Navbar.d.ts +2 -0
- package/dist/types/runtime/components/Sidebar.d.ts +2 -0
- package/dist/types/runtime/components/TableOfContents.d.ts +8 -0
- package/dist/types/runtime/config.d.ts +14 -0
- package/dist/types/runtime/index.d.ts +6 -0
- package/dist/types/runtime/loader/auto-indexer.d.ts +3 -0
- package/dist/types/runtime/loader/local-loader.d.ts +11 -0
- package/dist/types/runtime/loader/remote-github.d.ts +10 -0
- package/dist/types/runtime/loader/remote-gitlab.d.ts +9 -0
- package/dist/types/runtime/markdown/callouts.d.ts +17 -0
- package/dist/types/runtime/markdown/highlighter.d.ts +19 -0
- package/dist/types/runtime/markdown/katex.d.ts +1 -0
- package/dist/types/runtime/markdown/mermaid.d.ts +1 -0
- package/dist/types/runtime/markdown/parser.d.ts +17 -0
- package/dist/types/runtime/markdown/renderer.d.ts +7 -0
- package/dist/types/runtime/provider/DocProvider.d.ts +38 -0
- package/dist/types/runtime/provider/ThemeProvider.d.ts +18 -0
- package/dist/types/runtime/router.d.ts +23 -0
- package/dist/types/runtime/search/SearchModal.d.ts +11 -0
- package/dist/types/runtime/search/search-index.d.ts +9 -0
- package/dist/types/runtime/types.d.ts +165 -0
- package/package.json +110 -0
- package/templates/.dmd/components.js +25 -0
- package/templates/README.md +68 -0
- package/templates/docs.json +25 -0
- package/templates/getting-started.md +64 -0
- package/templates/guides/custom-components.md +78 -0
- package/templates/index.html +19 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1053 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli/index.ts
|
|
27
|
+
var import_commander = require("commander");
|
|
28
|
+
var import_fs7 = __toESM(require("fs"));
|
|
29
|
+
var import_path7 = __toESM(require("path"));
|
|
30
|
+
|
|
31
|
+
// src/cli/commands/init.ts
|
|
32
|
+
var import_fs4 = __toESM(require("fs"));
|
|
33
|
+
var import_path4 = __toESM(require("path"));
|
|
34
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
35
|
+
var import_prompts = __toESM(require("prompts"));
|
|
36
|
+
|
|
37
|
+
// src/cli/utils/server.ts
|
|
38
|
+
var import_http = __toESM(require("http"));
|
|
39
|
+
var import_fs3 = __toESM(require("fs"));
|
|
40
|
+
var import_path3 = __toESM(require("path"));
|
|
41
|
+
var import_mime_types = __toESM(require("mime-types"));
|
|
42
|
+
var import_ws = require("ws");
|
|
43
|
+
var import_chokidar2 = __toESM(require("chokidar"));
|
|
44
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
45
|
+
|
|
46
|
+
// src/cli/commands/build.ts
|
|
47
|
+
var import_fs2 = __toESM(require("fs"));
|
|
48
|
+
var import_path2 = __toESM(require("path"));
|
|
49
|
+
var import_chalk = __toESM(require("chalk"));
|
|
50
|
+
var import_chokidar = __toESM(require("chokidar"));
|
|
51
|
+
|
|
52
|
+
// src/cli/utils/scanner.ts
|
|
53
|
+
var import_fs = __toESM(require("fs"));
|
|
54
|
+
var import_path = __toESM(require("path"));
|
|
55
|
+
var import_gray_matter = __toESM(require("gray-matter"));
|
|
56
|
+
|
|
57
|
+
// src/runtime/loader/auto-indexer.ts
|
|
58
|
+
function formatTitleFromFilename(name) {
|
|
59
|
+
let clean = name.replace(/\.(md|mdx|html)$/i, "");
|
|
60
|
+
clean = clean.replace(/^\d+[-_.]/, "");
|
|
61
|
+
if (clean.toLowerCase() === "readme" || clean.toLowerCase() === "index") {
|
|
62
|
+
return "Overview";
|
|
63
|
+
}
|
|
64
|
+
return clean.split(/[-_]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
65
|
+
}
|
|
66
|
+
function buildSidebarTree(files, frontmatters = {}, manualSidebar) {
|
|
67
|
+
if (manualSidebar && manualSidebar.length > 0) {
|
|
68
|
+
return mapManualSidebar(manualSidebar);
|
|
69
|
+
}
|
|
70
|
+
const rootNodes = [];
|
|
71
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const filePath of files) {
|
|
73
|
+
const cleanPath = filePath.replace(/^\.?\//, "");
|
|
74
|
+
const parts = cleanPath.split("/");
|
|
75
|
+
const fm = frontmatters[cleanPath] || {};
|
|
76
|
+
if (fm.hidden) continue;
|
|
77
|
+
if (parts.length === 1) {
|
|
78
|
+
const filename = parts[0];
|
|
79
|
+
const slug = filename.replace(/\.(md|mdx)$/i, "");
|
|
80
|
+
const isOverview = slug.toLowerCase() === "readme" || slug.toLowerCase() === "index";
|
|
81
|
+
rootNodes.push({
|
|
82
|
+
id: slug,
|
|
83
|
+
title: fm.title || fm.sidebar_label || (isOverview ? "Overview" : formatTitleFromFilename(filename)),
|
|
84
|
+
slug: isOverview ? "README" : slug,
|
|
85
|
+
path: cleanPath,
|
|
86
|
+
icon: fm.icon,
|
|
87
|
+
badge: fm.badge,
|
|
88
|
+
badgeType: fm.badge_type,
|
|
89
|
+
order: fm.order ?? fm.sidebar_position ?? (isOverview ? -999 : 0),
|
|
90
|
+
isCategory: false
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
let currentCategory = "";
|
|
94
|
+
let parentNodeList = rootNodes;
|
|
95
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
96
|
+
const catSegment = parts[i];
|
|
97
|
+
currentCategory = currentCategory ? `${currentCategory}/${catSegment}` : catSegment;
|
|
98
|
+
let catNode = categoryMap.get(currentCategory);
|
|
99
|
+
if (!catNode) {
|
|
100
|
+
catNode = {
|
|
101
|
+
id: currentCategory,
|
|
102
|
+
title: formatTitleFromFilename(catSegment),
|
|
103
|
+
order: 10,
|
|
104
|
+
isCategory: true,
|
|
105
|
+
collapsed: false,
|
|
106
|
+
children: []
|
|
107
|
+
};
|
|
108
|
+
categoryMap.set(currentCategory, catNode);
|
|
109
|
+
parentNodeList.push(catNode);
|
|
110
|
+
}
|
|
111
|
+
parentNodeList = catNode.children;
|
|
112
|
+
}
|
|
113
|
+
const filename = parts[parts.length - 1];
|
|
114
|
+
const slug = cleanPath.replace(/\.(md|mdx)$/i, "");
|
|
115
|
+
const isOverview = filename.replace(/\.(md|mdx)$/i, "").toLowerCase() === "readme" || filename.replace(/\.(md|mdx)$/i, "").toLowerCase() === "index";
|
|
116
|
+
parentNodeList.push({
|
|
117
|
+
id: slug,
|
|
118
|
+
title: fm.title || fm.sidebar_label || (isOverview ? "Overview" : formatTitleFromFilename(filename)),
|
|
119
|
+
slug,
|
|
120
|
+
path: cleanPath,
|
|
121
|
+
icon: fm.icon,
|
|
122
|
+
badge: fm.badge,
|
|
123
|
+
badgeType: fm.badge_type,
|
|
124
|
+
order: fm.order ?? fm.sidebar_position ?? (isOverview ? -100 : 0),
|
|
125
|
+
isCategory: false
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
sortTreeNodes(rootNodes);
|
|
130
|
+
return rootNodes;
|
|
131
|
+
}
|
|
132
|
+
function sortTreeNodes(nodes) {
|
|
133
|
+
nodes.sort((a, b) => {
|
|
134
|
+
if (a.order !== b.order) {
|
|
135
|
+
return a.order - b.order;
|
|
136
|
+
}
|
|
137
|
+
if (a.slug === "README") return -1;
|
|
138
|
+
if (b.slug === "README") return 1;
|
|
139
|
+
return a.title.localeCompare(b.title);
|
|
140
|
+
});
|
|
141
|
+
for (const node of nodes) {
|
|
142
|
+
if (node.children && node.children.length > 0) {
|
|
143
|
+
sortTreeNodes(node.children);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function mapManualSidebar(items) {
|
|
148
|
+
return items.map((item, idx) => {
|
|
149
|
+
const isCategory = Boolean(item.children && item.children.length > 0);
|
|
150
|
+
const slug = item.slug || (item.path ? item.path.replace(/\.(md|mdx)$/i, "") : `item-${idx}`);
|
|
151
|
+
return {
|
|
152
|
+
id: slug,
|
|
153
|
+
title: item.title || formatTitleFromFilename(slug),
|
|
154
|
+
slug: isCategory ? void 0 : slug,
|
|
155
|
+
path: item.path,
|
|
156
|
+
icon: item.icon,
|
|
157
|
+
badge: item.badge,
|
|
158
|
+
badgeType: item.badgeType,
|
|
159
|
+
order: idx,
|
|
160
|
+
isCategory,
|
|
161
|
+
collapsed: item.collapsed ?? false,
|
|
162
|
+
children: item.children ? mapManualSidebar(item.children) : void 0
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/cli/utils/scanner.ts
|
|
168
|
+
function extractHeadings(content) {
|
|
169
|
+
const headings = [];
|
|
170
|
+
const lines = content.split("\n");
|
|
171
|
+
for (const line of lines) {
|
|
172
|
+
const match = line.match(/^(#{1,4})\s+(.+)$/);
|
|
173
|
+
if (match) {
|
|
174
|
+
const level = match[1].length;
|
|
175
|
+
const text = match[2].trim().replace(/[#*`_]/g, "");
|
|
176
|
+
const id = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/[\s_-]+/g, "-");
|
|
177
|
+
headings.push({ level, text, id });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return headings;
|
|
181
|
+
}
|
|
182
|
+
function scanDirectory(dir, baseDir = dir, excludeList = ["node_modules", ".git", ".dmd", "dist", "bin"], stopAtNestedDocsRoots = true) {
|
|
183
|
+
const results = [];
|
|
184
|
+
if (!import_fs.default.existsSync(dir)) return results;
|
|
185
|
+
const entries = import_fs.default.readdirSync(dir, { withFileTypes: true });
|
|
186
|
+
for (const entry of entries) {
|
|
187
|
+
if (excludeList.includes(entry.name) || entry.name.startsWith(".")) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const fullPath = import_path.default.join(dir, entry.name);
|
|
191
|
+
const relPath = import_path.default.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
192
|
+
if (entry.isDirectory()) {
|
|
193
|
+
const isNestedDocsRoot = stopAtNestedDocsRoots && fullPath !== baseDir && import_fs.default.existsSync(import_path.default.join(fullPath, "docs.json"));
|
|
194
|
+
if (!isNestedDocsRoot) {
|
|
195
|
+
results.push(...scanDirectory(fullPath, baseDir, excludeList, stopAtNestedDocsRoots));
|
|
196
|
+
}
|
|
197
|
+
} else if (entry.isFile() && /\.(md|mdx)$/i.test(entry.name)) {
|
|
198
|
+
results.push(relPath);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return results;
|
|
202
|
+
}
|
|
203
|
+
function generateManifest(docDir, config) {
|
|
204
|
+
const files = scanDirectory(docDir);
|
|
205
|
+
const docItems = [];
|
|
206
|
+
const frontmatters = {};
|
|
207
|
+
for (const relFile of files) {
|
|
208
|
+
const fullPath = import_path.default.join(docDir, relFile);
|
|
209
|
+
const raw = import_fs.default.readFileSync(fullPath, "utf-8");
|
|
210
|
+
const { data: fm, content } = (0, import_gray_matter.default)(raw);
|
|
211
|
+
const cleanSlug = relFile.replace(/\.(md|mdx)$/i, "");
|
|
212
|
+
const isOverview = cleanSlug.toLowerCase() === "readme" || cleanSlug.toLowerCase() === "index";
|
|
213
|
+
const slug = isOverview ? "README" : cleanSlug;
|
|
214
|
+
const title = fm.title || fm.sidebar_label || formatTitleFromFilename(import_path.default.basename(relFile));
|
|
215
|
+
frontmatters[relFile] = fm;
|
|
216
|
+
const headings = extractHeadings(content);
|
|
217
|
+
const words = content.trim().split(/\s+/).length;
|
|
218
|
+
const readingTimeMinutes = Math.max(1, Math.ceil(words / 200));
|
|
219
|
+
docItems.push({
|
|
220
|
+
slug,
|
|
221
|
+
path: relFile,
|
|
222
|
+
title,
|
|
223
|
+
category: relFile.includes("/") ? relFile.substring(0, relFile.lastIndexOf("/")) : void 0,
|
|
224
|
+
frontmatter: fm,
|
|
225
|
+
headings,
|
|
226
|
+
content,
|
|
227
|
+
readingTimeMinutes,
|
|
228
|
+
lastModified: import_fs.default.statSync(fullPath).mtime.toISOString()
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const tree = buildSidebarTree(files, frontmatters, config.sidebar);
|
|
232
|
+
return {
|
|
233
|
+
version: "1.0.0",
|
|
234
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
235
|
+
config,
|
|
236
|
+
docs: docItems,
|
|
237
|
+
tree
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/runtime/config.ts
|
|
242
|
+
var DEFAULT_CONFIG = {
|
|
243
|
+
name: "DocMeDown",
|
|
244
|
+
tagline: "The simplest Markdown documenter yet",
|
|
245
|
+
description: "Instant documentation site powered by DocMeDown",
|
|
246
|
+
version: "1.0.0",
|
|
247
|
+
rootDoc: "README.md",
|
|
248
|
+
theme: {
|
|
249
|
+
preset: "indigo",
|
|
250
|
+
defaultMode: "auto",
|
|
251
|
+
codeTheme: "github"
|
|
252
|
+
},
|
|
253
|
+
autoIndex: {
|
|
254
|
+
enabled: true,
|
|
255
|
+
sort: "natural",
|
|
256
|
+
defaultCollapsed: false,
|
|
257
|
+
exclude: ["node_modules", ".git", ".dmd", "dist", "bin", "package.json", "tsconfig.json"]
|
|
258
|
+
},
|
|
259
|
+
search: {
|
|
260
|
+
enabled: true,
|
|
261
|
+
placeholder: "Search documentation...",
|
|
262
|
+
maxResults: 10
|
|
263
|
+
},
|
|
264
|
+
footer: {
|
|
265
|
+
copyright: `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} DocMeDown. All rights reserved.`,
|
|
266
|
+
showBuiltWith: true
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// src/cli/commands/build.ts
|
|
271
|
+
var DEFAULT_OFFLINE_OUTPUT_DIRECTORY = ".dist";
|
|
272
|
+
function getBuildOutputPaths(targetDir, offlineOutDir) {
|
|
273
|
+
return {
|
|
274
|
+
onlineDir: targetDir,
|
|
275
|
+
offlineDir: offlineOutDir ? import_path2.default.resolve(process.cwd(), offlineOutDir) : import_path2.default.join(targetDir, DEFAULT_OFFLINE_OUTPUT_DIRECTORY)
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function encodeOfflinePayload(value) {
|
|
279
|
+
return Buffer.from(JSON.stringify(value), "utf-8").toString("base64");
|
|
280
|
+
}
|
|
281
|
+
function escapeInlineScriptContent(value) {
|
|
282
|
+
return value.replace(/<\/script/gi, "<\\/script");
|
|
283
|
+
}
|
|
284
|
+
function escapeHtml(value) {
|
|
285
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
286
|
+
}
|
|
287
|
+
var GENERATED_DOC_OUTPUTS = ["_manifest.json", "_docs.js", "docmedown.iife.js"];
|
|
288
|
+
function findNestedDocumentationRoots(targetDir) {
|
|
289
|
+
const roots = [];
|
|
290
|
+
function visit(directory) {
|
|
291
|
+
if (!import_fs2.default.existsSync(directory)) return;
|
|
292
|
+
for (const entry of import_fs2.default.readdirSync(directory, { withFileTypes: true })) {
|
|
293
|
+
if (!entry.isDirectory() || entry.name.startsWith(".") || ["node_modules", ".git", "dist", "bin"].includes(entry.name)) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const fullPath = import_path2.default.join(directory, entry.name);
|
|
297
|
+
if (import_fs2.default.existsSync(import_path2.default.join(fullPath, "docs.json"))) {
|
|
298
|
+
roots.push(fullPath);
|
|
299
|
+
visit(fullPath);
|
|
300
|
+
} else {
|
|
301
|
+
visit(fullPath);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
visit(targetDir);
|
|
306
|
+
return roots;
|
|
307
|
+
}
|
|
308
|
+
function shouldWatchDocumentationSource(rootDir, changedPath) {
|
|
309
|
+
const relativePath = import_path2.default.relative(rootDir, changedPath).replace(/\\/g, "/");
|
|
310
|
+
const firstSegment = relativePath.split("/")[0];
|
|
311
|
+
const isCustomComponentModule = /(^|\/)\.dmd\/(components|index)\.js$/i.test(relativePath);
|
|
312
|
+
return Boolean(relativePath) && !relativePath.startsWith("..") && (!relativePath.startsWith(".") || isCustomComponentModule) && !GENERATED_DOC_OUTPUTS.includes(relativePath) && firstSegment !== "node_modules" && firstSegment !== "dist";
|
|
313
|
+
}
|
|
314
|
+
async function buildCommand(targetDirArg = "./docs", options = {}) {
|
|
315
|
+
const targetDir = import_path2.default.resolve(process.cwd(), targetDirArg);
|
|
316
|
+
const { onlineDir, offlineDir } = getBuildOutputPaths(targetDir, options.outDir);
|
|
317
|
+
const buildOfflineBundle = options.singleFile !== false;
|
|
318
|
+
console.log(import_chalk.default.bold.magenta("\n\u26A1 DocMeDown Build\n"));
|
|
319
|
+
if (!import_fs2.default.existsSync(targetDir)) {
|
|
320
|
+
console.error(import_chalk.default.red(`\u2716 Directory not found: ${targetDir}`));
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
if (!import_fs2.default.existsSync(onlineDir)) {
|
|
324
|
+
import_fs2.default.mkdirSync(onlineDir, { recursive: true });
|
|
325
|
+
}
|
|
326
|
+
if (buildOfflineBundle && !import_fs2.default.existsSync(offlineDir)) {
|
|
327
|
+
import_fs2.default.mkdirSync(offlineDir, { recursive: true });
|
|
328
|
+
}
|
|
329
|
+
let config = DEFAULT_CONFIG;
|
|
330
|
+
const configPath = import_path2.default.join(targetDir, "docs.json");
|
|
331
|
+
if (import_fs2.default.existsSync(configPath)) {
|
|
332
|
+
try {
|
|
333
|
+
config = { ...DEFAULT_CONFIG, ...JSON.parse(import_fs2.default.readFileSync(configPath, "utf-8")) };
|
|
334
|
+
} catch (err) {
|
|
335
|
+
console.warn(import_chalk.default.yellow(`\u26A0 Could not parse docs.json, using defaults.`));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
console.log(import_chalk.default.cyan(" Indexing documentation pages..."));
|
|
339
|
+
const manifest = generateManifest(targetDir, config);
|
|
340
|
+
const manifestPath = import_path2.default.join(onlineDir, "_manifest.json");
|
|
341
|
+
import_fs2.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
342
|
+
console.log(import_chalk.default.green(` \u2714 Generated _manifest.json (${manifest.docs.length} pages indexed)`));
|
|
343
|
+
const docsMap = {};
|
|
344
|
+
const files = scanDirectory(targetDir);
|
|
345
|
+
for (const file of files) {
|
|
346
|
+
const full = import_path2.default.join(targetDir, file);
|
|
347
|
+
const raw = import_fs2.default.readFileSync(full, "utf-8");
|
|
348
|
+
const slug = file.replace(/\.(md|mdx)$/i, "");
|
|
349
|
+
const cleanSlug = slug.toLowerCase() === "readme" || slug.toLowerCase() === "index" ? "README" : slug;
|
|
350
|
+
docsMap[cleanSlug] = raw;
|
|
351
|
+
docsMap[file] = raw;
|
|
352
|
+
}
|
|
353
|
+
const componentsPath = import_path2.default.join(targetDir, ".dmd", "components.js");
|
|
354
|
+
const componentsSource = import_fs2.default.existsSync(componentsPath) ? import_fs2.default.readFileSync(componentsPath, "utf-8") : void 0;
|
|
355
|
+
const docsJsContent = `window.__DOCMEDOWN_DATA__ = ${JSON.stringify({ manifest, docs: docsMap, componentsSource }, null, 2)};
|
|
356
|
+
`;
|
|
357
|
+
const docsJsPath = import_path2.default.join(onlineDir, "_docs.js");
|
|
358
|
+
import_fs2.default.writeFileSync(docsJsPath, docsJsContent, "utf-8");
|
|
359
|
+
console.log(import_chalk.default.green(` \u2714 Generated _docs.js for offline file:/// double-click usage`));
|
|
360
|
+
if (buildOfflineBundle) {
|
|
361
|
+
console.log(import_chalk.default.cyan("\n Compiling standalone single-file offline index.html..."));
|
|
362
|
+
const candidates2 = [
|
|
363
|
+
import_path2.default.resolve(__dirname, "docmedown.iife.js"),
|
|
364
|
+
import_path2.default.resolve(__dirname, "../dist/docmedown.iife.js"),
|
|
365
|
+
import_path2.default.resolve(__dirname, "../docmedown.iife.js")
|
|
366
|
+
];
|
|
367
|
+
const distIifePath2 = candidates2.find((p) => import_fs2.default.existsSync(p));
|
|
368
|
+
let bundleJs = "";
|
|
369
|
+
if (distIifePath2) {
|
|
370
|
+
bundleJs = import_fs2.default.readFileSync(distIifePath2, "utf-8");
|
|
371
|
+
} else {
|
|
372
|
+
console.warn(import_chalk.default.yellow(" \u26A0 Local runtime bundle not found, linking CDN script."));
|
|
373
|
+
}
|
|
374
|
+
const offlinePayload = encodeOfflinePayload({ manifest, docs: docsMap, componentsSource });
|
|
375
|
+
const safeBundleJs = escapeInlineScriptContent(bundleJs);
|
|
376
|
+
const singleFileHtml = `<!DOCTYPE html>
|
|
377
|
+
<html lang="en">
|
|
378
|
+
<head>
|
|
379
|
+
<meta charset="UTF-8" />
|
|
380
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
381
|
+
<title>${escapeHtml(config.name || "Documentation")}</title>
|
|
382
|
+
</head>
|
|
383
|
+
<body>
|
|
384
|
+
<div id="dmd-app"></div>
|
|
385
|
+
<script>
|
|
386
|
+
(function () {
|
|
387
|
+
const encoded = '${offlinePayload}';
|
|
388
|
+
const bytes = Uint8Array.from(atob(encoded), (character) => character.charCodeAt(0));
|
|
389
|
+
const data = JSON.parse(new TextDecoder().decode(bytes));
|
|
390
|
+
window.__DOCMEDOWN_DATA__ = data;
|
|
391
|
+
window.__DOCMEDOWN_CONFIG__ = data.manifest.config;
|
|
392
|
+
if (data.componentsSource) {
|
|
393
|
+
const componentModule = new Blob([data.componentsSource], { type: 'text/javascript' });
|
|
394
|
+
const componentModuleUrl = URL.createObjectURL(componentModule);
|
|
395
|
+
window.__DOCMEDOWN_COMPONENTS_READY__ = import(componentModuleUrl)
|
|
396
|
+
.then((module) => module.default || module)
|
|
397
|
+
.finally(() => URL.revokeObjectURL(componentModuleUrl));
|
|
398
|
+
}
|
|
399
|
+
})();
|
|
400
|
+
</script>
|
|
401
|
+
<script>
|
|
402
|
+
${safeBundleJs}
|
|
403
|
+
</script>
|
|
404
|
+
</body>
|
|
405
|
+
</html>`;
|
|
406
|
+
const singleFilePath = import_path2.default.join(offlineDir, "index.html");
|
|
407
|
+
import_fs2.default.writeFileSync(singleFilePath, singleFileHtml, "utf-8");
|
|
408
|
+
console.log(import_chalk.default.bold.green(` \u2714 Built self-contained offline bundle: ${singleFilePath}`));
|
|
409
|
+
console.log(import_chalk.default.dim(` You can now double-click index.html to view offline without any server!
|
|
410
|
+
`));
|
|
411
|
+
}
|
|
412
|
+
const candidates = [
|
|
413
|
+
import_path2.default.resolve(__dirname, "docmedown.iife.js"),
|
|
414
|
+
import_path2.default.resolve(__dirname, "../dist/docmedown.iife.js"),
|
|
415
|
+
import_path2.default.resolve(__dirname, "../docmedown.iife.js")
|
|
416
|
+
];
|
|
417
|
+
const distIifePath = candidates.find((p) => import_fs2.default.existsSync(p));
|
|
418
|
+
const targetBundlePath = import_path2.default.join(onlineDir, "docmedown.iife.js");
|
|
419
|
+
if (distIifePath) {
|
|
420
|
+
import_fs2.default.copyFileSync(distIifePath, targetBundlePath);
|
|
421
|
+
console.log(import_chalk.default.green(` \u2714 Updated serveable runtime: ${targetBundlePath}`));
|
|
422
|
+
}
|
|
423
|
+
if (options.buildNested !== false) {
|
|
424
|
+
const nestedRoots = findNestedDocumentationRoots(targetDir);
|
|
425
|
+
for (const nestedRoot of nestedRoots) {
|
|
426
|
+
console.log(import_chalk.default.cyan(`
|
|
427
|
+
Building nested documentation site: ${import_path2.default.relative(targetDir, nestedRoot)}...`));
|
|
428
|
+
await buildCommand(nestedRoot, { buildNested: false });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
console.log(import_chalk.default.bold.green("\u2728 Build completed successfully!\n"));
|
|
432
|
+
}
|
|
433
|
+
async function watchBuildCommand(targetDirArg = "./docs", options = {}) {
|
|
434
|
+
const targetDir = import_path2.default.resolve(process.cwd(), targetDirArg);
|
|
435
|
+
await buildCommand(targetDir, { ...options, watch: false });
|
|
436
|
+
console.log(import_chalk.default.dim(` Watching source files in: ${targetDir}`));
|
|
437
|
+
console.log(import_chalk.default.dim(" Source changes rebuild serveable files and .dist/index.html. Press Ctrl+C to stop.\n"));
|
|
438
|
+
const watcher = import_chokidar.default.watch(targetDir, {
|
|
439
|
+
ignored: [
|
|
440
|
+
"**/node_modules/**",
|
|
441
|
+
"**/.git/**",
|
|
442
|
+
"**/dist/**",
|
|
443
|
+
"**/.dist/**",
|
|
444
|
+
"**/_manifest.json",
|
|
445
|
+
"**/_docs.js",
|
|
446
|
+
"**/docmedown.iife.js"
|
|
447
|
+
],
|
|
448
|
+
ignoreInitial: true
|
|
449
|
+
});
|
|
450
|
+
let rebuildTimeout = null;
|
|
451
|
+
watcher.on("all", (_event, changedPath) => {
|
|
452
|
+
if (!shouldWatchDocumentationSource(targetDir, changedPath)) return;
|
|
453
|
+
if (rebuildTimeout) clearTimeout(rebuildTimeout);
|
|
454
|
+
rebuildTimeout = setTimeout(async () => {
|
|
455
|
+
const relativePath = import_path2.default.relative(targetDir, changedPath);
|
|
456
|
+
console.log(import_chalk.default.cyan(`[DocMeDown] Changed: ${relativePath} -> Rebuilding documentation...`));
|
|
457
|
+
try {
|
|
458
|
+
await buildCommand(targetDir, { ...options, watch: false });
|
|
459
|
+
} catch (err) {
|
|
460
|
+
console.error(import_chalk.default.red(`[DocMeDown] Rebuild failed: ${err.message}`));
|
|
461
|
+
}
|
|
462
|
+
}, 100);
|
|
463
|
+
});
|
|
464
|
+
return new Promise(() => void 0);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/cli/utils/server.ts
|
|
468
|
+
function shouldRebuildDocumentation(rootDir, changedPath) {
|
|
469
|
+
return shouldWatchDocumentationSource(rootDir, changedPath);
|
|
470
|
+
}
|
|
471
|
+
function startDevServer(options) {
|
|
472
|
+
const { rootDir, host = "localhost", port: initialPort = 3e3, onSourceChange } = options;
|
|
473
|
+
return new Promise((resolve, reject) => {
|
|
474
|
+
let port = initialPort;
|
|
475
|
+
const liveReloadScript = `
|
|
476
|
+
<script>
|
|
477
|
+
(function() {
|
|
478
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
479
|
+
let ws = null;
|
|
480
|
+
function connect() {
|
|
481
|
+
ws = new WebSocket(protocol + '//' + window.location.host + '/__dmd_reload');
|
|
482
|
+
ws.onmessage = function(e) {
|
|
483
|
+
if (e.data === 'reload') {
|
|
484
|
+
console.log('[DocMeDown] File changed, reloading...');
|
|
485
|
+
window.location.reload();
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
ws.onclose = function() {
|
|
489
|
+
setTimeout(connect, 1500);
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
connect();
|
|
493
|
+
})();
|
|
494
|
+
</script>
|
|
495
|
+
`;
|
|
496
|
+
const server = import_http.default.createServer((req, res) => {
|
|
497
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
498
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
499
|
+
if (req.method === "OPTIONS") {
|
|
500
|
+
res.writeHead(204);
|
|
501
|
+
res.end();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
let reqPath = decodeURIComponent(req.url?.split("?")[0] || "/");
|
|
505
|
+
if (reqPath === "/" || reqPath === "") {
|
|
506
|
+
reqPath = "/index.html";
|
|
507
|
+
}
|
|
508
|
+
let filePath = import_path3.default.join(rootDir, reqPath);
|
|
509
|
+
if (!import_fs3.default.existsSync(filePath) || import_fs3.default.statSync(filePath).isDirectory()) {
|
|
510
|
+
const potentialIndex = import_path3.default.join(rootDir, "index.html");
|
|
511
|
+
if (import_fs3.default.existsSync(potentialIndex)) {
|
|
512
|
+
filePath = potentialIndex;
|
|
513
|
+
} else {
|
|
514
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
515
|
+
res.end(`
|
|
516
|
+
<!DOCTYPE html>
|
|
517
|
+
<html>
|
|
518
|
+
<head><title>DocMeDown</title></head>
|
|
519
|
+
<body>
|
|
520
|
+
<div id="dmd-app"></div>
|
|
521
|
+
<script src="/docmedown.iife.js"></script>
|
|
522
|
+
${liveReloadScript}
|
|
523
|
+
</body>
|
|
524
|
+
</html>
|
|
525
|
+
`);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (reqPath === "/docmedown.iife.js" && !import_fs3.default.existsSync(filePath)) {
|
|
530
|
+
const bundleCandidates = [
|
|
531
|
+
import_path3.default.resolve(__dirname, "docmedown.iife.js"),
|
|
532
|
+
import_path3.default.resolve(__dirname, "../dist/docmedown.iife.js"),
|
|
533
|
+
import_path3.default.resolve(__dirname, "../docmedown.iife.js")
|
|
534
|
+
];
|
|
535
|
+
const distFile = bundleCandidates.find((p) => import_fs3.default.existsSync(p));
|
|
536
|
+
if (distFile) {
|
|
537
|
+
filePath = distFile;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
try {
|
|
541
|
+
const stat = import_fs3.default.statSync(filePath);
|
|
542
|
+
if (stat.isDirectory()) {
|
|
543
|
+
filePath = import_path3.default.join(filePath, "index.html");
|
|
544
|
+
}
|
|
545
|
+
const mimeType = import_mime_types.default.lookup(filePath) || "application/octet-stream";
|
|
546
|
+
res.setHeader("Content-Type", mimeType);
|
|
547
|
+
if (mimeType.includes("html")) {
|
|
548
|
+
let content = import_fs3.default.readFileSync(filePath, "utf-8");
|
|
549
|
+
if (!content.includes("/__dmd_reload")) {
|
|
550
|
+
content = content.replace("</body>", `${liveReloadScript}</body>`);
|
|
551
|
+
}
|
|
552
|
+
res.writeHead(200);
|
|
553
|
+
res.end(content);
|
|
554
|
+
} else {
|
|
555
|
+
const stream = import_fs3.default.createReadStream(filePath);
|
|
556
|
+
res.writeHead(200);
|
|
557
|
+
stream.pipe(res);
|
|
558
|
+
}
|
|
559
|
+
} catch (err) {
|
|
560
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
561
|
+
res.end("404 Not Found");
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
const wss = new import_ws.WebSocketServer({ noServer: true });
|
|
565
|
+
const clients = /* @__PURE__ */ new Set();
|
|
566
|
+
server.on("upgrade", (request, socket, head) => {
|
|
567
|
+
if (request.url === "/__dmd_reload") {
|
|
568
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
569
|
+
clients.add(ws);
|
|
570
|
+
ws.on("close", () => clients.delete(ws));
|
|
571
|
+
});
|
|
572
|
+
} else {
|
|
573
|
+
socket.destroy();
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
const watcher = import_chokidar2.default.watch(rootDir, {
|
|
577
|
+
ignored: [
|
|
578
|
+
"**/node_modules/**",
|
|
579
|
+
"**/.git/**",
|
|
580
|
+
"**/dist/**",
|
|
581
|
+
"**/.dist/**",
|
|
582
|
+
"**/_manifest.json",
|
|
583
|
+
"**/_docs.js",
|
|
584
|
+
"**/docmedown.iife.js"
|
|
585
|
+
],
|
|
586
|
+
ignoreInitial: true
|
|
587
|
+
});
|
|
588
|
+
let reloadTimeout = null;
|
|
589
|
+
watcher.on("all", (event, changedPath) => {
|
|
590
|
+
if (!shouldRebuildDocumentation(rootDir, changedPath)) return;
|
|
591
|
+
if (reloadTimeout) clearTimeout(reloadTimeout);
|
|
592
|
+
reloadTimeout = setTimeout(async () => {
|
|
593
|
+
const rel = import_path3.default.relative(rootDir, changedPath);
|
|
594
|
+
try {
|
|
595
|
+
if (onSourceChange) {
|
|
596
|
+
console.log(import_chalk2.default.cyan(`[DocMeDown] Changed: ${rel} -> Rebuilding documentation...`));
|
|
597
|
+
await onSourceChange(event, changedPath);
|
|
598
|
+
} else {
|
|
599
|
+
console.log(import_chalk2.default.cyan(`[DocMeDown] Changed: ${rel} -> Reloading preview...`));
|
|
600
|
+
}
|
|
601
|
+
} catch (err) {
|
|
602
|
+
console.error(import_chalk2.default.red(`[DocMeDown] Rebuild failed: ${err.message}`));
|
|
603
|
+
}
|
|
604
|
+
clients.forEach((ws) => {
|
|
605
|
+
if (ws.readyState === import_ws.WebSocket.OPEN) {
|
|
606
|
+
ws.send("reload");
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
}, 100);
|
|
610
|
+
});
|
|
611
|
+
function tryListen() {
|
|
612
|
+
server.listen(port, host, () => {
|
|
613
|
+
resolve({
|
|
614
|
+
server,
|
|
615
|
+
port,
|
|
616
|
+
close: () => {
|
|
617
|
+
watcher.close();
|
|
618
|
+
wss.close();
|
|
619
|
+
server.close();
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
server.on("error", (err) => {
|
|
624
|
+
if (err.code === "EADDRINUSE") {
|
|
625
|
+
port++;
|
|
626
|
+
tryListen();
|
|
627
|
+
} else {
|
|
628
|
+
reject(err);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
tryListen();
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/cli/commands/init.ts
|
|
637
|
+
var import_open = __toESM(require("open"));
|
|
638
|
+
async function initCommand(targetDirArg = "./docs", options = {}) {
|
|
639
|
+
const targetDir = import_path4.default.resolve(process.cwd(), targetDirArg);
|
|
640
|
+
console.log(import_chalk3.default.bold.magenta("\n\u26A1 DocMeDown - Instant Documentation Initializer\n"));
|
|
641
|
+
if (!import_fs4.default.existsSync(targetDir)) {
|
|
642
|
+
import_fs4.default.mkdirSync(targetDir, { recursive: true });
|
|
643
|
+
}
|
|
644
|
+
const templatesDir = import_path4.default.resolve(__dirname, "../../templates");
|
|
645
|
+
const fallbackTemplatesDir = import_path4.default.resolve(__dirname, "../templates");
|
|
646
|
+
const activeTemplateDir = import_fs4.default.existsSync(templatesDir) ? templatesDir : fallbackTemplatesDir;
|
|
647
|
+
const indexPath = import_path4.default.join(targetDir, "index.html");
|
|
648
|
+
if (!import_fs4.default.existsSync(indexPath)) {
|
|
649
|
+
const templateHtml = import_fs4.default.readFileSync(import_path4.default.join(activeTemplateDir, "index.html"), "utf-8");
|
|
650
|
+
import_fs4.default.writeFileSync(indexPath, templateHtml, "utf-8");
|
|
651
|
+
console.log(import_chalk3.default.green(" \u2714 Created index.html"));
|
|
652
|
+
}
|
|
653
|
+
const configPath = import_path4.default.join(targetDir, "docs.json");
|
|
654
|
+
if (!import_fs4.default.existsSync(configPath)) {
|
|
655
|
+
const templateConfig = import_fs4.default.readFileSync(import_path4.default.join(activeTemplateDir, "docs.json"), "utf-8");
|
|
656
|
+
import_fs4.default.writeFileSync(configPath, templateConfig, "utf-8");
|
|
657
|
+
console.log(import_chalk3.default.green(" \u2714 Created docs.json"));
|
|
658
|
+
}
|
|
659
|
+
const readmePath = import_path4.default.join(targetDir, "README.md");
|
|
660
|
+
if (!import_fs4.default.existsSync(readmePath)) {
|
|
661
|
+
const templateReadme = import_fs4.default.readFileSync(import_path4.default.join(activeTemplateDir, "README.md"), "utf-8");
|
|
662
|
+
import_fs4.default.writeFileSync(readmePath, templateReadme, "utf-8");
|
|
663
|
+
console.log(import_chalk3.default.green(" \u2714 Created README.md"));
|
|
664
|
+
}
|
|
665
|
+
const gsPath = import_path4.default.join(targetDir, "getting-started.md");
|
|
666
|
+
if (!import_fs4.default.existsSync(gsPath)) {
|
|
667
|
+
const templateGs = import_fs4.default.readFileSync(import_path4.default.join(activeTemplateDir, "getting-started.md"), "utf-8");
|
|
668
|
+
import_fs4.default.writeFileSync(gsPath, templateGs, "utf-8");
|
|
669
|
+
console.log(import_chalk3.default.green(" \u2714 Created getting-started.md"));
|
|
670
|
+
}
|
|
671
|
+
const guidesDir = import_path4.default.join(targetDir, "guides");
|
|
672
|
+
if (!import_fs4.default.existsSync(guidesDir)) {
|
|
673
|
+
import_fs4.default.mkdirSync(guidesDir, { recursive: true });
|
|
674
|
+
}
|
|
675
|
+
const guidePath = import_path4.default.join(guidesDir, "custom-components.md");
|
|
676
|
+
if (!import_fs4.default.existsSync(guidePath)) {
|
|
677
|
+
const templateGuide = import_fs4.default.readFileSync(import_path4.default.join(activeTemplateDir, "guides/custom-components.md"), "utf-8");
|
|
678
|
+
import_fs4.default.writeFileSync(guidePath, templateGuide, "utf-8");
|
|
679
|
+
console.log(import_chalk3.default.green(" \u2714 Created guides/custom-components.md"));
|
|
680
|
+
}
|
|
681
|
+
const dmdDir = import_path4.default.join(targetDir, ".dmd");
|
|
682
|
+
if (!import_fs4.default.existsSync(dmdDir)) {
|
|
683
|
+
import_fs4.default.mkdirSync(dmdDir, { recursive: true });
|
|
684
|
+
}
|
|
685
|
+
const dmdComponentPath = import_path4.default.join(dmdDir, "components.js");
|
|
686
|
+
if (!import_fs4.default.existsSync(dmdComponentPath)) {
|
|
687
|
+
const templateDmd = import_fs4.default.readFileSync(import_path4.default.join(activeTemplateDir, ".dmd/components.js"), "utf-8");
|
|
688
|
+
import_fs4.default.writeFileSync(dmdComponentPath, templateDmd, "utf-8");
|
|
689
|
+
console.log(import_chalk3.default.green(" \u2714 Created .dmd/components.js"));
|
|
690
|
+
}
|
|
691
|
+
await buildCommand(targetDir);
|
|
692
|
+
console.log(import_chalk3.default.bold.green("\n\u2728 DocMeDown site successfully initialized!\n"));
|
|
693
|
+
console.log(import_chalk3.default.dim(` Location: ${targetDir}
|
|
694
|
+
`));
|
|
695
|
+
console.log(import_chalk3.default.cyan(" To preview your docs:"));
|
|
696
|
+
console.log(import_chalk3.default.white(` npx docmedown serve ${targetDirArg}
|
|
697
|
+
`));
|
|
698
|
+
console.log(import_chalk3.default.cyan(" To customize settings:"));
|
|
699
|
+
console.log(import_chalk3.default.white(` npx docmedown config ${targetDirArg}/docs.json
|
|
700
|
+
`));
|
|
701
|
+
if (options.start !== false && process.stdout.isTTY) {
|
|
702
|
+
const response = await (0, import_prompts.default)({
|
|
703
|
+
type: "confirm",
|
|
704
|
+
name: "startServer",
|
|
705
|
+
message: "Would you like to start the local preview server now?",
|
|
706
|
+
initial: true
|
|
707
|
+
});
|
|
708
|
+
if (response.startServer) {
|
|
709
|
+
console.log(import_chalk3.default.cyan("\n\u{1F680} Starting local live preview server..."));
|
|
710
|
+
const devServer = await startDevServer({ rootDir: targetDir, port: 3e3 });
|
|
711
|
+
const url = `http://localhost:${devServer.port}`;
|
|
712
|
+
console.log(import_chalk3.default.bold.green(`
|
|
713
|
+
\u2714 Documentation live at: ${import_chalk3.default.underline(url)}
|
|
714
|
+
`));
|
|
715
|
+
console.log(import_chalk3.default.dim(" Press Ctrl+C to stop the server\n"));
|
|
716
|
+
await (0, import_open.default)(url);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/cli/commands/serve.ts
|
|
722
|
+
var import_path5 = __toESM(require("path"));
|
|
723
|
+
var import_fs5 = __toESM(require("fs"));
|
|
724
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
725
|
+
var import_open2 = __toESM(require("open"));
|
|
726
|
+
async function serveCommand(targetDirArg = "./docs", options = {}) {
|
|
727
|
+
let targetDir = import_path5.default.resolve(process.cwd(), targetDirArg);
|
|
728
|
+
if (!import_fs5.default.existsSync(targetDir)) {
|
|
729
|
+
if (import_fs5.default.existsSync(import_path5.default.resolve(process.cwd(), "docs"))) {
|
|
730
|
+
targetDir = import_path5.default.resolve(process.cwd(), "docs");
|
|
731
|
+
} else {
|
|
732
|
+
targetDir = process.cwd();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
console.log(import_chalk4.default.bold.magenta("\n\u26A1 DocMeDown Dev Server\n"));
|
|
736
|
+
console.log(import_chalk4.default.dim(` Serving directory: ${targetDir}`));
|
|
737
|
+
try {
|
|
738
|
+
console.log(import_chalk4.default.dim(" Building serveable and offline documentation artifacts..."));
|
|
739
|
+
await buildCommand(targetDir);
|
|
740
|
+
const devServer = await startDevServer({
|
|
741
|
+
rootDir: targetDir,
|
|
742
|
+
host: options.host || "localhost",
|
|
743
|
+
port: options.port ? Number(options.port) : 3e3,
|
|
744
|
+
onSourceChange: () => buildCommand(targetDir)
|
|
745
|
+
});
|
|
746
|
+
const url = `http://${options.host || "localhost"}:${devServer.port}`;
|
|
747
|
+
console.log(import_chalk4.default.bold.green(`
|
|
748
|
+
\u2714 Live Documentation Server running at:`));
|
|
749
|
+
console.log(import_chalk4.default.bold.cyan(` \u279C Local: ${import_chalk4.default.underline(url)}`));
|
|
750
|
+
console.log(import_chalk4.default.dim(` \u279C Watch: Source changes rebuild preview and .dist/index.html`));
|
|
751
|
+
console.log(import_chalk4.default.dim(`
|
|
752
|
+
Press Ctrl+C to stop.
|
|
753
|
+
`));
|
|
754
|
+
if (options.open !== false) {
|
|
755
|
+
await (0, import_open2.default)(url);
|
|
756
|
+
}
|
|
757
|
+
} catch (err) {
|
|
758
|
+
console.error(import_chalk4.default.red(`
|
|
759
|
+
\u2716 Error starting server: ${err.message}
|
|
760
|
+
`));
|
|
761
|
+
process.exit(1);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/cli/commands/config-tui.ts
|
|
766
|
+
var import_fs6 = __toESM(require("fs"));
|
|
767
|
+
var import_path6 = __toESM(require("path"));
|
|
768
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
769
|
+
var import_prompts2 = __toESM(require("prompts"));
|
|
770
|
+
async function configTuiCommand(configPathArg) {
|
|
771
|
+
console.log(import_chalk5.default.bold.magenta("\n\u26A1 DocMeDown - Interactive Configuration Wizard\n"));
|
|
772
|
+
let configPath = "";
|
|
773
|
+
if (configPathArg) {
|
|
774
|
+
configPath = import_path6.default.resolve(process.cwd(), configPathArg);
|
|
775
|
+
if (import_fs6.default.existsSync(configPath) && import_fs6.default.statSync(configPath).isDirectory()) {
|
|
776
|
+
configPath = import_path6.default.join(configPath, "docs.json");
|
|
777
|
+
}
|
|
778
|
+
} else {
|
|
779
|
+
const candidates = [
|
|
780
|
+
import_path6.default.resolve(process.cwd(), "docs.json"),
|
|
781
|
+
import_path6.default.resolve(process.cwd(), "docs/docs.json"),
|
|
782
|
+
import_path6.default.resolve(process.cwd(), "dmd.json")
|
|
783
|
+
];
|
|
784
|
+
configPath = candidates.find((p) => import_fs6.default.existsSync(p)) || import_path6.default.resolve(process.cwd(), "docs/docs.json");
|
|
785
|
+
}
|
|
786
|
+
let existingConfig = DEFAULT_CONFIG;
|
|
787
|
+
if (import_fs6.default.existsSync(configPath)) {
|
|
788
|
+
try {
|
|
789
|
+
existingConfig = { ...DEFAULT_CONFIG, ...JSON.parse(import_fs6.default.readFileSync(configPath, "utf-8")) };
|
|
790
|
+
console.log(import_chalk5.default.dim(` Loaded existing configuration from: ${configPath}
|
|
791
|
+
`));
|
|
792
|
+
} catch {
|
|
793
|
+
console.warn(import_chalk5.default.yellow(` \u26A0 Error reading ${configPath}, starting fresh.
|
|
794
|
+
`));
|
|
795
|
+
}
|
|
796
|
+
} else {
|
|
797
|
+
console.log(import_chalk5.default.dim(` Creating new configuration at: ${configPath}
|
|
798
|
+
`));
|
|
799
|
+
}
|
|
800
|
+
const response = await (0, import_prompts2.default)([
|
|
801
|
+
{
|
|
802
|
+
type: "text",
|
|
803
|
+
name: "name",
|
|
804
|
+
message: "Project Name:",
|
|
805
|
+
initial: existingConfig.name || "My Project Docs"
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
type: "text",
|
|
809
|
+
name: "tagline",
|
|
810
|
+
message: "Tagline / Short description:",
|
|
811
|
+
initial: existingConfig.tagline || "The simplest Markdown documenter yet"
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
type: "select",
|
|
815
|
+
name: "preset",
|
|
816
|
+
message: "Select Theme Color Palette:",
|
|
817
|
+
choices: [
|
|
818
|
+
{ title: "Indigo (Modern & Clean)", value: "indigo" },
|
|
819
|
+
{ title: "Emerald (Fresh & Tech)", value: "emerald" },
|
|
820
|
+
{ title: "Sunset (Warm Amber)", value: "sunset" },
|
|
821
|
+
{ title: "Violet (Vibrant Purple)", value: "violet" },
|
|
822
|
+
{ title: "Rose (Bold Quartz)", value: "rose" },
|
|
823
|
+
{ title: "Slate (Minimalist Monochrome)", value: "slate" },
|
|
824
|
+
{ title: "Cyberpunk (Neon Glow)", value: "cyberpunk" }
|
|
825
|
+
],
|
|
826
|
+
initial: ["indigo", "emerald", "sunset", "violet", "rose", "slate", "cyberpunk"].indexOf(
|
|
827
|
+
existingConfig.theme?.preset || "indigo"
|
|
828
|
+
)
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
type: "select",
|
|
832
|
+
name: "defaultMode",
|
|
833
|
+
message: "Default Color Mode:",
|
|
834
|
+
choices: [
|
|
835
|
+
{ title: "Auto (Follows OS system preference)", value: "auto" },
|
|
836
|
+
{ title: "Dark (Default dark theme)", value: "dark" },
|
|
837
|
+
{ title: "Light (Default light theme)", value: "light" }
|
|
838
|
+
],
|
|
839
|
+
initial: ["auto", "dark", "light"].indexOf(existingConfig.theme?.defaultMode || "auto")
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
type: "select",
|
|
843
|
+
name: "sourceType",
|
|
844
|
+
message: "Documentation Source Mode:",
|
|
845
|
+
choices: [
|
|
846
|
+
{ title: "Local Markdown Files (hosted together in folder)", value: "local" },
|
|
847
|
+
{ title: "Remote GitHub Repository (live dynamic fetch)", value: "github" },
|
|
848
|
+
{ title: "Remote GitLab Repository (live dynamic fetch)", value: "gitlab" }
|
|
849
|
+
],
|
|
850
|
+
initial: existingConfig.source?.type ? existingConfig.source.type === "github" ? 1 : 2 : 0
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
type: (prev) => prev === "github" ? "text" : null,
|
|
854
|
+
name: "githubRepo",
|
|
855
|
+
message: "GitHub Repository (owner/repo):",
|
|
856
|
+
initial: existingConfig.source?.repo || "facebook/react"
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
type: (prev, values) => values.sourceType === "github" ? "text" : null,
|
|
860
|
+
name: "githubBranch",
|
|
861
|
+
message: "GitHub Branch:",
|
|
862
|
+
initial: existingConfig.source?.branch || "main"
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
type: (prev, values) => values.sourceType === "github" ? "text" : null,
|
|
866
|
+
name: "githubDocsDir",
|
|
867
|
+
message: "GitHub Docs Subfolder (leave empty for root):",
|
|
868
|
+
initial: existingConfig.source?.docsDir || "docs"
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
type: "text",
|
|
872
|
+
name: "githubSocial",
|
|
873
|
+
message: "GitHub Repository URL for Navbar link (optional):",
|
|
874
|
+
initial: existingConfig.socials?.find((s) => s.type === "github")?.url || ""
|
|
875
|
+
}
|
|
876
|
+
]);
|
|
877
|
+
if (!response.name) {
|
|
878
|
+
console.log(import_chalk5.default.yellow("\nConfiguration cancelled."));
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
const updatedConfig = {
|
|
882
|
+
...existingConfig,
|
|
883
|
+
name: response.name,
|
|
884
|
+
tagline: response.tagline,
|
|
885
|
+
theme: {
|
|
886
|
+
...existingConfig.theme,
|
|
887
|
+
preset: response.preset,
|
|
888
|
+
defaultMode: response.defaultMode
|
|
889
|
+
},
|
|
890
|
+
socials: response.githubSocial ? [{ type: "github", url: response.githubSocial }] : existingConfig.socials
|
|
891
|
+
};
|
|
892
|
+
if (response.sourceType === "github") {
|
|
893
|
+
updatedConfig.source = {
|
|
894
|
+
type: "github",
|
|
895
|
+
repo: response.githubRepo,
|
|
896
|
+
branch: response.githubBranch,
|
|
897
|
+
docsDir: response.githubDocsDir
|
|
898
|
+
};
|
|
899
|
+
} else if (response.sourceType === "local") {
|
|
900
|
+
delete updatedConfig.source;
|
|
901
|
+
}
|
|
902
|
+
const dir = import_path6.default.dirname(configPath);
|
|
903
|
+
if (!import_fs6.default.existsSync(dir)) {
|
|
904
|
+
import_fs6.default.mkdirSync(dir, { recursive: true });
|
|
905
|
+
}
|
|
906
|
+
import_fs6.default.writeFileSync(configPath, JSON.stringify(updatedConfig, null, 2), "utf-8");
|
|
907
|
+
console.log(import_chalk5.default.bold.green(`
|
|
908
|
+
\u2714 Configuration successfully saved to: ${configPath}
|
|
909
|
+
`));
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// package.json
|
|
913
|
+
var package_default = {
|
|
914
|
+
name: "docmedown",
|
|
915
|
+
version: "0.1.0",
|
|
916
|
+
description: "The simplest MarkDown documenter yet. CLI & Web Script React SPA documentation engine.",
|
|
917
|
+
main: "./dist/docmedown.cjs",
|
|
918
|
+
module: "./dist/docmedown.mjs",
|
|
919
|
+
types: "./dist/types/runtime/index.d.ts",
|
|
920
|
+
exports: {
|
|
921
|
+
".": {
|
|
922
|
+
types: "./dist/types/runtime/index.d.ts",
|
|
923
|
+
import: "./dist/docmedown.mjs",
|
|
924
|
+
require: "./dist/docmedown.cjs"
|
|
925
|
+
},
|
|
926
|
+
"./iife": "./dist/docmedown.iife.js"
|
|
927
|
+
},
|
|
928
|
+
unpkg: "./dist/docmedown.iife.js",
|
|
929
|
+
jsdelivr: "./dist/docmedown.iife.js",
|
|
930
|
+
bin: {
|
|
931
|
+
docmedown: "bin/docmedown.js",
|
|
932
|
+
dmd: "bin/docmedown.js"
|
|
933
|
+
},
|
|
934
|
+
files: [
|
|
935
|
+
"dist",
|
|
936
|
+
"bin",
|
|
937
|
+
"templates",
|
|
938
|
+
"README.md",
|
|
939
|
+
"LICENSE"
|
|
940
|
+
],
|
|
941
|
+
scripts: {
|
|
942
|
+
"dev:runtime": "vite build --watch",
|
|
943
|
+
"build:runtime": "vite build",
|
|
944
|
+
"build:cli": "tsup",
|
|
945
|
+
typecheck: "tsc --noEmit",
|
|
946
|
+
"build:types": "tsc -p tsconfig.runtime.json",
|
|
947
|
+
build: "npm run build:runtime && npm run build:cli && npm run build:types",
|
|
948
|
+
docmedown: "node ./bin/docmedown.js",
|
|
949
|
+
dmd: "node ./bin/docmedown.js",
|
|
950
|
+
"init:docs": "node ./bin/docmedown.js init",
|
|
951
|
+
serve: "node ./bin/docmedown.js serve ./docs",
|
|
952
|
+
dev: "node ./bin/docmedown.js serve ./docs",
|
|
953
|
+
"build:docs": "node ./bin/docmedown.js build ./docs",
|
|
954
|
+
"build:docs:watch": "node ./bin/docmedown.js build ./docs --watch",
|
|
955
|
+
"build:offline": "node ./bin/docmedown.js build ./docs",
|
|
956
|
+
"config:docs": "node ./bin/docmedown.js config ./docs/docs.json",
|
|
957
|
+
test: "npx tsx --test tests/parser.test.ts tests/scanner.test.ts tests/runtime-integration.test.ts tests/package-smoke.test.ts",
|
|
958
|
+
"test:release": "npm run typecheck && npm run test && npm run build && npm run build:docs && npm pack --dry-run",
|
|
959
|
+
prepublishOnly: "npm run test:release"
|
|
960
|
+
},
|
|
961
|
+
keywords: [
|
|
962
|
+
"markdown",
|
|
963
|
+
"documentation",
|
|
964
|
+
"docs",
|
|
965
|
+
"docusaurus",
|
|
966
|
+
"docsify",
|
|
967
|
+
"react",
|
|
968
|
+
"spa",
|
|
969
|
+
"cli",
|
|
970
|
+
"offline-docs",
|
|
971
|
+
"github-docs"
|
|
972
|
+
],
|
|
973
|
+
author: "Gabriel M. Silva",
|
|
974
|
+
license: "MIT",
|
|
975
|
+
engines: {
|
|
976
|
+
node: ">=20.19.0"
|
|
977
|
+
},
|
|
978
|
+
repository: {
|
|
979
|
+
type: "git",
|
|
980
|
+
url: "git+https://github.com/gabrielmsilva00/docmedown.git"
|
|
981
|
+
},
|
|
982
|
+
bugs: {
|
|
983
|
+
url: "https://github.com/gabrielmsilva00/docmedown/issues"
|
|
984
|
+
},
|
|
985
|
+
homepage: "https://github.com/gabrielmsilva00/docmedown#readme",
|
|
986
|
+
packageManager: "npm@11.16.0",
|
|
987
|
+
overrides: {
|
|
988
|
+
tsup: {
|
|
989
|
+
esbuild: "0.28.2"
|
|
990
|
+
}
|
|
991
|
+
},
|
|
992
|
+
dependencies: {
|
|
993
|
+
chalk: "^4.1.2",
|
|
994
|
+
chokidar: "^3.6.0",
|
|
995
|
+
commander: "^12.1.0",
|
|
996
|
+
"gray-matter": "^4.0.3",
|
|
997
|
+
"mime-types": "^2.1.35",
|
|
998
|
+
open: "^10.1.0",
|
|
999
|
+
prompts: "^2.4.2",
|
|
1000
|
+
ws: "^8.18.0"
|
|
1001
|
+
},
|
|
1002
|
+
devDependencies: {
|
|
1003
|
+
"@types/node": "^22.5.0",
|
|
1004
|
+
"@types/prismjs": "^1.26.6",
|
|
1005
|
+
"@types/prompts": "^2.4.9",
|
|
1006
|
+
"@types/react": "^18.3.5",
|
|
1007
|
+
"@types/react-dom": "^18.3.0",
|
|
1008
|
+
"@types/ws": "^8.5.12",
|
|
1009
|
+
"@vitejs/plugin-react": "^6.1.0",
|
|
1010
|
+
katex: "^0.16.11",
|
|
1011
|
+
marked: "^14.1.1",
|
|
1012
|
+
minisearch: "^7.1.0",
|
|
1013
|
+
prismjs: "^1.29.0",
|
|
1014
|
+
react: "^18.3.1",
|
|
1015
|
+
"react-dom": "^18.3.1",
|
|
1016
|
+
tsup: "^8.2.4",
|
|
1017
|
+
tsx: "^4.19.0",
|
|
1018
|
+
typescript: "^5.5.4",
|
|
1019
|
+
vite: "^8.2.2",
|
|
1020
|
+
"vite-plugin-css-injected-by-js": "^5.0.2"
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
// src/cli/index.ts
|
|
1025
|
+
var program = new import_commander.Command();
|
|
1026
|
+
program.name("docmedown").description("DocMeDown - The simplest MarkDown documenter yet. CLI & Web Script React SPA.").version(package_default.version);
|
|
1027
|
+
program.argument("[dir]", "Documentation directory to initialize or serve", "./docs").option("-p, --port <number>", "Port for local dev server", "3000").option("--no-open", "Do not automatically open the browser").action(async (dir, options) => {
|
|
1028
|
+
const targetDir = import_path7.default.resolve(process.cwd(), dir);
|
|
1029
|
+
const hasIndex = import_fs7.default.existsSync(import_path7.default.join(targetDir, "index.html"));
|
|
1030
|
+
const hasDocs = import_fs7.default.existsSync(import_path7.default.join(targetDir, "README.md")) || import_fs7.default.existsSync(import_path7.default.join(targetDir, "docs.json"));
|
|
1031
|
+
if (!hasIndex && !hasDocs) {
|
|
1032
|
+
await initCommand(dir, { start: options.open });
|
|
1033
|
+
} else {
|
|
1034
|
+
await serveCommand(dir, { port: options.port, open: options.open });
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
program.command("init [dir]").description("Scaffold a new DocMeDown documentation site").option("--no-start", "Do not start the local preview server after initialization").action(async (dir = "./docs", options) => {
|
|
1038
|
+
await initCommand(dir, { start: options.start });
|
|
1039
|
+
});
|
|
1040
|
+
program.command("serve [dir]").alias("dev").description("Start local live-reload development server").option("-p, --port <number>", "Port to listen on", "3000").option("-H, --host <string>", "Host to bind server to", "localhost").option("--no-open", "Do not automatically open browser").action(async (dir = "./docs", options) => {
|
|
1041
|
+
await serveCommand(dir, options);
|
|
1042
|
+
});
|
|
1043
|
+
program.command("build [dir]").description("Build serveable documentation and a standalone offline bundle").option("-w, --watch", "Watch source files and rebuild both outputs after changes").option("--no-single-file", "Skip the default standalone offline index.html bundle").option("-o, --out-dir <path>", "Offline bundle output directory (default: <dir>/.dist)").action(async (dir = "./docs", options) => {
|
|
1044
|
+
if (options.watch) {
|
|
1045
|
+
await watchBuildCommand(dir, options);
|
|
1046
|
+
} else {
|
|
1047
|
+
await buildCommand(dir, options);
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
program.command("config [path]").description("Launch the interactive TUI configuration wizard for docs.json").action(async (configPath) => {
|
|
1051
|
+
await configTuiCommand(configPath);
|
|
1052
|
+
});
|
|
1053
|
+
program.parse(process.argv);
|