serve-my-md 1.1.6 → 1.1.8
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/bin/index.js
CHANGED
|
@@ -12,12 +12,12 @@ var Logger = class {
|
|
|
12
12
|
|
|
13
13
|
// cli/src/shared.ts
|
|
14
14
|
import MarkdownIt from "markdown-it";
|
|
15
|
-
import
|
|
15
|
+
import path3 from "path";
|
|
16
16
|
import Prism from "prismjs";
|
|
17
17
|
|
|
18
18
|
// cli/src/core/index.ts
|
|
19
|
-
import
|
|
20
|
-
import
|
|
19
|
+
import fs3 from "fs/promises";
|
|
20
|
+
import path2 from "path";
|
|
21
21
|
import { minimatch } from "minimatch";
|
|
22
22
|
|
|
23
23
|
// cli/src/utils/index.ts
|
|
@@ -114,6 +114,8 @@ var config_default = {
|
|
|
114
114
|
|
|
115
115
|
// cli/src/lib/commander.ts
|
|
116
116
|
import { Command } from "commander";
|
|
117
|
+
import path from "path";
|
|
118
|
+
import fs2 from "fs";
|
|
117
119
|
var program = new Command();
|
|
118
120
|
program.name(config_default.name).description(config_default.description).version(config_default.version);
|
|
119
121
|
program.option("-d, --directory <path>", "Directory to scan for markdown files", ".");
|
|
@@ -130,6 +132,11 @@ if (options.interactive || options.directory === void 0) {
|
|
|
130
132
|
});
|
|
131
133
|
options.directory = res.trim();
|
|
132
134
|
}
|
|
135
|
+
options.directory = path.resolve(options.directory);
|
|
136
|
+
fs2.existsSync(options.directory) || (() => {
|
|
137
|
+
console.error("Directory does not exist: " + options.directory);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
})();
|
|
133
140
|
|
|
134
141
|
// cli/src/core/index.ts
|
|
135
142
|
import { readdirSync } from "fs";
|
|
@@ -143,7 +150,7 @@ var constants_default = {
|
|
|
143
150
|
var STATIC_TEMP_CONTENT_PREFIX = constants_default.STATIC_TEMP_CONTENT_PREFIX;
|
|
144
151
|
async function readConfig(filepath) {
|
|
145
152
|
try {
|
|
146
|
-
const data = JSON.parse(await
|
|
153
|
+
const data = JSON.parse(await fs3.readFile(filepath, "utf-8"));
|
|
147
154
|
return data;
|
|
148
155
|
} catch (err) {
|
|
149
156
|
Logger.log(
|
|
@@ -166,7 +173,7 @@ async function parseSmmIgnore(filePath) {
|
|
|
166
173
|
return ignored;
|
|
167
174
|
};
|
|
168
175
|
var shouldIgnore2 = shouldIgnore3;
|
|
169
|
-
const raw = await
|
|
176
|
+
const raw = await fs3.readFile(filePath, "utf8");
|
|
170
177
|
const rules = raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#")).map((line) => {
|
|
171
178
|
const negated = line.startsWith("!");
|
|
172
179
|
const pattern = negated ? line.slice(1) : line;
|
|
@@ -185,11 +192,11 @@ async function parseSmmIgnore(filePath) {
|
|
|
185
192
|
}
|
|
186
193
|
}
|
|
187
194
|
async function getMarkdownFiles(baseUrl, pairChildren) {
|
|
188
|
-
const files = await
|
|
195
|
+
const files = await fs3.readdir(baseUrl, { withFileTypes: true });
|
|
189
196
|
const routeTree = pairChildren || [];
|
|
190
197
|
const promises = [];
|
|
191
198
|
for (const file of files) {
|
|
192
|
-
const filePath =
|
|
199
|
+
const filePath = path2.join(baseUrl, file.name);
|
|
193
200
|
if (shouldIgnore(filePath.slice(options.directory.length)) || filePath.slice(options.directory.length) === finalConfig.publicPath)
|
|
194
201
|
continue;
|
|
195
202
|
if (file.isDirectory()) {
|
|
@@ -247,16 +254,16 @@ function getPath(filepath) {
|
|
|
247
254
|
return slugify(transformedPath).split("/").filter((s) => !(s.startsWith("(") && s.endsWith(")"))).join("/") || "/";
|
|
248
255
|
}
|
|
249
256
|
async function parseMD(filepath) {
|
|
250
|
-
const
|
|
257
|
+
const path5 = getPath(filepath);
|
|
251
258
|
return {
|
|
252
|
-
path:
|
|
253
|
-
content: mdParser.render(await
|
|
259
|
+
path: path5,
|
|
260
|
+
content: mdParser.render(await fs3.readFile(filepath, "utf-8"))
|
|
254
261
|
};
|
|
255
262
|
}
|
|
256
263
|
async function generateHtml(distDir, routeContent) {
|
|
257
264
|
try {
|
|
258
|
-
let htmlTemplate = await
|
|
259
|
-
|
|
265
|
+
let htmlTemplate = await fs3.readFile(
|
|
266
|
+
path2.join(import.meta.dirname, "..", "index.html"),
|
|
260
267
|
"utf-8"
|
|
261
268
|
);
|
|
262
269
|
const commentStart = htmlTemplate.indexOf("<!--");
|
|
@@ -268,20 +275,20 @@ async function generateHtml(distDir, routeContent) {
|
|
|
268
275
|
""
|
|
269
276
|
);
|
|
270
277
|
if (distDir) {
|
|
271
|
-
const files = readdirSync(
|
|
278
|
+
const files = readdirSync(path2.join(distDir, "assets"));
|
|
272
279
|
const cssFile = files.find((file) => file.endsWith(".css"));
|
|
273
280
|
const jsFile = files.find((file) => file.endsWith(".js"));
|
|
274
|
-
const prefix = distDir.slice(
|
|
281
|
+
const prefix = distDir.slice(path2.join(import.meta.dirname, options.directory).length);
|
|
275
282
|
htmlTemplate = htmlTemplate.replace(`<script type="module" src="/src/main.tsx"></script>`, "");
|
|
276
283
|
if (cssFile && jsFile) {
|
|
277
284
|
htmlTemplate = htmlTemplate.replace(
|
|
278
285
|
"{{distAssets}}",
|
|
279
|
-
`<link rel="stylesheet" href="${
|
|
286
|
+
`<link rel="stylesheet" href="${path2.join(
|
|
280
287
|
prefix,
|
|
281
288
|
"assets",
|
|
282
289
|
cssFile
|
|
283
290
|
)}" />
|
|
284
|
-
<script type="module" src="${
|
|
291
|
+
<script type="module" src="${path2.join(
|
|
285
292
|
prefix,
|
|
286
293
|
"assets",
|
|
287
294
|
jsFile
|
|
@@ -315,19 +322,19 @@ async function buildDistRoutesFromRouteTree(routeTree, groupedRoutes, distPath,
|
|
|
315
322
|
prefix
|
|
316
323
|
);
|
|
317
324
|
} else {
|
|
318
|
-
const distRoutePath =
|
|
319
|
-
await
|
|
325
|
+
const distRoutePath = path2.join(distPath, prefix, node.pathSegment.replace("/", "")) + (node.pathSegment === "" ? "/index.html" : ".html");
|
|
326
|
+
await fs3.mkdir(path2.dirname(distRoutePath), { recursive: true });
|
|
320
327
|
const html = await generateHtml(
|
|
321
328
|
distPath,
|
|
322
|
-
groupedRoutes[
|
|
329
|
+
groupedRoutes[path2.posix.join(prefix, node.pathSegment)]?.[0]?.content
|
|
323
330
|
);
|
|
324
|
-
await
|
|
331
|
+
await fs3.writeFile(distRoutePath, html, "utf-8");
|
|
325
332
|
if (node.children) {
|
|
326
333
|
await buildDistRoutesFromRouteTree(
|
|
327
334
|
node.children,
|
|
328
335
|
groupedRoutes,
|
|
329
336
|
distPath,
|
|
330
|
-
|
|
337
|
+
path2.join(prefix, node.pathSegment)
|
|
331
338
|
);
|
|
332
339
|
}
|
|
333
340
|
}
|
|
@@ -362,11 +369,11 @@ import MarkdownItFootNote from "markdown-it-footnote";
|
|
|
362
369
|
import MarkdownItTasks from "markdown-it-task-lists";
|
|
363
370
|
import loadLanguages from "prismjs/components/index.js";
|
|
364
371
|
var { shouldIgnore } = await parseSmmIgnore(
|
|
365
|
-
|
|
372
|
+
path3.join(options.directory, config_default.defaultIgnorePath)
|
|
366
373
|
);
|
|
367
374
|
var finalConfig = {
|
|
368
375
|
...smm_config_default,
|
|
369
|
-
...await readConfig(
|
|
376
|
+
...await readConfig(path3.join(options.directory, config_default.defaultConfigPath))
|
|
370
377
|
};
|
|
371
378
|
var md = new MarkdownIt({
|
|
372
379
|
...finalConfig.markdownItOptions,
|
|
@@ -383,7 +390,7 @@ var mdParser = md;
|
|
|
383
390
|
|
|
384
391
|
// cli/src/core/build.ts
|
|
385
392
|
import { cp, readdir as readdir2, rm, writeFile } from "fs/promises";
|
|
386
|
-
import
|
|
393
|
+
import path4, { resolve } from "path";
|
|
387
394
|
import { fileURLToPath } from "url";
|
|
388
395
|
import { build as viteBuild } from "vite";
|
|
389
396
|
import { mkdirSync } from "fs";
|
|
@@ -398,7 +405,7 @@ async function build(options2) {
|
|
|
398
405
|
const parsePromises = [];
|
|
399
406
|
Logger.log("Processing routes...");
|
|
400
407
|
for (const file of makeRoutesOfNestedPathsRaw(routeTree)) {
|
|
401
|
-
parsePromises.push(parseMD(
|
|
408
|
+
parsePromises.push(parseMD(path4.join(options2.directory, file)));
|
|
402
409
|
}
|
|
403
410
|
cleanNestedPaths(routeTree);
|
|
404
411
|
const groupedRoutes = Object.groupBy(
|
|
@@ -406,7 +413,13 @@ async function build(options2) {
|
|
|
406
413
|
(route) => route.path
|
|
407
414
|
);
|
|
408
415
|
const routes = makeRoutesOfNestedPaths(routeTree).reduce(
|
|
409
|
-
(acc, pth) => [
|
|
416
|
+
(acc, pth) => [
|
|
417
|
+
...acc,
|
|
418
|
+
...(groupedRoutes[pth] ?? []).map((r) => ({
|
|
419
|
+
...r,
|
|
420
|
+
path: path4.join(finalConfig.baseRoute || "/", r.path)
|
|
421
|
+
}))
|
|
422
|
+
],
|
|
410
423
|
[]
|
|
411
424
|
);
|
|
412
425
|
const out = {
|
|
@@ -429,37 +442,37 @@ async function build(options2) {
|
|
|
429
442
|
routes.forEach((o) => {
|
|
430
443
|
Logger.log(o.path);
|
|
431
444
|
});
|
|
432
|
-
const __dirname =
|
|
433
|
-
const webDir =
|
|
434
|
-
const distDir =
|
|
435
|
-
mkdirSync(
|
|
445
|
+
const __dirname = path4.dirname(fileURLToPath(import.meta.url));
|
|
446
|
+
const webDir = path4.join(__dirname, "..", WEB_DIRNAME);
|
|
447
|
+
const distDir = path4.join(webDir, DIST_DIRNAME);
|
|
448
|
+
mkdirSync(path4.join(webDir, "src", ".generated"), { recursive: true });
|
|
436
449
|
await writeFile(
|
|
437
|
-
|
|
450
|
+
path4.join(webDir, "src", ".generated", "output.json"),
|
|
438
451
|
JSON.stringify(out)
|
|
439
452
|
);
|
|
440
453
|
await writeFile(
|
|
441
|
-
|
|
454
|
+
path4.join(webDir, "src", ".generated", "paths.json"),
|
|
442
455
|
JSON.stringify(routeTree)
|
|
443
456
|
);
|
|
444
457
|
Logger.log("\nParsed MDs");
|
|
445
|
-
await writeFile(
|
|
458
|
+
await writeFile(path4.join(webDir, "index.html"), await generateHtml());
|
|
446
459
|
Logger.log("Generated HTML from template");
|
|
447
460
|
if (!skipBuild) {
|
|
448
|
-
const entries = await readdir2(
|
|
461
|
+
const entries = await readdir2(path4.join(webDir, PUBLIC_DIRNAME));
|
|
449
462
|
for (const entry of entries) {
|
|
450
|
-
await rm(
|
|
463
|
+
await rm(path4.join(webDir, PUBLIC_DIRNAME, entry), {
|
|
451
464
|
recursive: true,
|
|
452
465
|
force: true
|
|
453
466
|
});
|
|
454
467
|
}
|
|
455
468
|
if (finalConfig.publicPath) {
|
|
456
469
|
if (await FileOrDirectoryExists(
|
|
457
|
-
|
|
470
|
+
path4.join(options2.directory, finalConfig.publicPath)
|
|
458
471
|
)) {
|
|
459
472
|
Logger.log(`Copying public assets from ${finalConfig.publicPath}...`);
|
|
460
473
|
await cp(
|
|
461
|
-
|
|
462
|
-
|
|
474
|
+
path4.join(options2.directory, finalConfig.publicPath),
|
|
475
|
+
path4.join(webDir, PUBLIC_DIRNAME),
|
|
463
476
|
{ recursive: true }
|
|
464
477
|
);
|
|
465
478
|
} else {
|
|
@@ -471,7 +484,7 @@ async function build(options2) {
|
|
|
471
484
|
configFile: resolve(webDir, "vite.config.ts")
|
|
472
485
|
});
|
|
473
486
|
await buildDistRoutesFromRouteTree(routeTree, groupedRoutes, distDir);
|
|
474
|
-
const targetDist =
|
|
487
|
+
const targetDist = path4.join(options2.directory, DIST_DIRNAME);
|
|
475
488
|
await rm(targetDist, { recursive: true }).catch(() => {
|
|
476
489
|
});
|
|
477
490
|
Logger.log("Built the app, copying results...");
|
package/package.json
CHANGED
package/web/index.html
CHANGED
|
@@ -7,15 +7,14 @@
|
|
|
7
7
|
<meta name="theme-color" content="#000000" />
|
|
8
8
|
<meta
|
|
9
9
|
name="description"
|
|
10
|
-
content="
|
|
10
|
+
content="Docs for serve-my-md."
|
|
11
11
|
/>
|
|
12
|
-
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=
|
|
13
|
-
<meta property="og:title" content="
|
|
14
|
-
<meta property="og:description" content="
|
|
15
|
-
<meta property="og:
|
|
16
|
-
<meta property="og:
|
|
17
|
-
<
|
|
18
|
-
<title>SMM Ghostly Fixture</title>
|
|
12
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,100..900;1,100..900&display=swap" /><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Commissioner:wght@100..900&display=swap" /><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&display=swap" />
|
|
13
|
+
<meta property="og:title" content="Serve My MD Docs">
|
|
14
|
+
<meta property="og:description" content="Documentation for serve-my-md, a CLI tool to generate static docs sites from markdown.">
|
|
15
|
+
<meta property="og:type" content="website">
|
|
16
|
+
<meta property="og:site_name" content="Serve My MD Docs">
|
|
17
|
+
<title>Serve My MD Docs</title>
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
<style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"rootTitle":"SMM Ghostly Fixture","description":"A fixture for testing ghostly operations in SMM.","baseRoute":"/","defaultTheme":"light","name":"Ghostly Store","showNameWithLogo":true,"routes":[{"path":"/","content":"<h1>hello this is main md</h1>\n<p><a href=\"/features\">features</a></p>\n"},{"path":"/features","content":"<h1>features</h1>\n<p>browse features:</p>\n<ul>\n<li><a href=\"/features/coffee\">coffee</a></li>\n<li><a href=\"/features/tea\">tea</a></li>\n<li><a href=\"/features/choorma\">choorma</a></li>\n<li><a href=\"/features/ghee-dudh\">ghee dudh</a></li>\n</ul>\n"},{"path":"/features/choorma","content":"<h1>choorma khao body bnao</h1>\n<p>price: nani dedo 10 minute choorma bnane ke liye :moon:</p>\n"},{"path":"/features/coffee","content":"<h1>co-fee</h1>\n<p>price: $2</p>\n"},{"path":"/features/ghee-dudh","content":"<h1>ghee dudh kha ladle</h1>\n<p>price: 1 lakh de bhais le</p>\n"},{"path":"/features/tea","content":"<h1>tea-tty</h1>\n<p>price: $69</p>\n"},{"path":"/muah","content":"<h1>Markdown Feature Showcase</h1>\n<h2>Headings</h2>\n<h1>H1 Heading</h1>\n<h2>H2 Heading</h2>\n<h3>H3 Heading</h3>\n<h4>H4 Heading</h4>\n<hr />\n<h2>Text Styles</h2>\n<ul>\n<li><em>Italic</em></li>\n<li><strong>Bold</strong></li>\n<li><em><strong>Bold Italic</strong></em></li>\n<li><s>Strikethrough</s></li>\n</ul>\n<hr />\n<h2>Links</h2>\n<p><a href=\"https://openai.com\">OpenAI</a></p>\n<hr />\n<h2>Images</h2>\n<p><img src=\"https://thumbs.dreamstime.com/b/fearsome-ancient-spartan-warrior-greece-red-cloak-spartan-helmet-night-spartan-movie-scene-action-sequence-spartan-helmet-284208996.jpg?w=360\" alt=\"Placeholder\" /></p>\n<hr />\n<h2>Blockquote</h2>\n<blockquote>\n<p>This is a blockquote example.<br />\nIt spans multiple lines.</p>\n</blockquote>\n<hr />\n<h2>Code (using indentation, not backticks)</h2>\n<p>Inline code example: <code>print("Hello World")</code></p>\n<pre class=\"language-python\"><code class=\"language-python\"> <span class=\"token keyword\">def</span> <span class=\"token function\">greet</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n <span class=\"token keyword\">return</span> <span class=\"token string\">\"Hello \"</span> <span class=\"token operator\">+</span> name\n\n <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span>greet<span class=\"token punctuation\">(</span><span class=\"token string\">\"Markdown\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<hr />\n<h2>Lists</h2>\n<h3>Unordered List</h3>\n<ul>\n<li>Item A</li>\n<li>Item B\n<ul>\n<li>Subitem B1</li>\n<li>Subitem B2</li>\n</ul>\n</li>\n</ul>\n<h3>Ordered List</h3>\n<ol>\n<li>Step One</li>\n<li>Step Two</li>\n<li>Step Three</li>\n</ol>\n<hr />\n<h2>Checkboxes</h2>\n<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input class=\"task-list-item-checkbox\" checked=\"\" disabled=\"\" type=\"checkbox\"> Completed</li>\n<li class=\"task-list-item\"><input class=\"task-list-item-checkbox\" disabled=\"\" type=\"checkbox\"> Not Completed</li>\n<li class=\"task-list-item\"><input class=\"task-list-item-checkbox\" disabled=\"\" type=\"checkbox\"> Pending</li>\n</ul>\n<hr />\n<h2>Table</h2>\n<table>\n<thead>\n<tr>\n<th>Product</th>\n<th>Price</th>\n<th>In Stock</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Apple</td>\n<td>$1</td>\n<td>Yes</td>\n</tr>\n<tr>\n<td>Banana</td>\n<td>$0.5</td>\n<td>No</td>\n</tr>\n<tr>\n<td>Orange</td>\n<td>$0.8</td>\n<td>Yes</td>\n</tr>\n</tbody>\n</table>\n<hr />\n<h2>Horizontal Rule</h2>\n<hr />\n<hr />\n<h2>Emoji</h2>\n<p>😀 🚀 🎉 ❤️🔥</p>\n<hr />\n<h2>Footnotes</h2>\n<p>This is a sentence with a footnote.<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup></p>\n<hr />\n<h2>HTML Support</h2>\n<div style=\"color: green;\">This line uses HTML styling.</div>\n<hr />\n<h2>Done Checklist</h2>\n<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input class=\"task-list-item-checkbox\" checked=\"\" disabled=\"\" type=\"checkbox\"> Includes tables</li>\n<li class=\"task-list-item\"><input class=\"task-list-item-checkbox\" checked=\"\" disabled=\"\" type=\"checkbox\"> Includes checkboxes</li>\n<li class=\"task-list-item\"><input class=\"task-list-item-checkbox\" checked=\"\" disabled=\"\" type=\"checkbox\"> Includes code (indent-based)</li>\n<li class=\"task-list-item\"><input class=\"task-list-item-checkbox\" checked=\"\" disabled=\"\" type=\"checkbox\"> Includes emojis</li>\n<li class=\"task-list-item\"><input class=\"task-list-item-checkbox\" checked=\"\" disabled=\"\" type=\"checkbox\"> Includes all Markdown features</li>\n</ul>\n<hr class=\"footnotes-sep\" />\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p>This is the footnote explanation. <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n"},{"path":"/zaram-chand","content":"<h1>Mohandas Karam Chand Gandhi</h1>\n<p>He was also known as Mahatma Gandhi Thaave Aandhi.</p>\n"},{"path":"/tos","content":"<h1>Terms of Services</h1>\n<p>We only serve people with a knack for cooking. If you don't cook, you're not welcome.</p>\n"}],"outDir":"dist","fonts":{"title":"Sorts Mill Goudy","body":"Montserrat","mono":"monospace"},"version":"1.0.0 LTS"}
|
|
1
|
+
{"rootTitle":"Serve My MD Docs","description":"Docs for serve-my-md.","baseRoute":"/","defaultTheme":"dark","name":"Serve My MD","showNameWithLogo":true,"routes":[{"path":"/","content":"<h1>serve-my-md Docs</h1>\n<p>Welcome to the docs for <code>serve-my-md</code>.</p>\n<p>A tiny CLI to generate a static docs website from markdown files.</p>\n<ul>\n<li><a href=\"/why-serve-my-md\">Why serve-my-md?</a></li>\n<li><a href=\"/getting-started\">Getting Started</a></li>\n<li><a href=\"/cli-reference\">CLI Reference</a></li>\n<li><a href=\"/key-behaviors\">Key Behaviors</a></li>\n</ul>\n<h3>Guide</h3>\n<ul>\n<li><a href=\"/routing-and-sidebar-patterns\">Routing and Sidebar Patterns</a></li>\n<li><a href=\"/markdown-and-rendering\">Markdown and Rendering</a></li>\n<li><a href=\"/build-output-and-assets\">Build Output and Assets</a></li>\n</ul>\n<h3>Configuration</h3>\n<ul>\n<li><a href=\"/smm-config\">SMM Config</a></li>\n<li><a href=\"/smm-ignore\">SMM Ignore</a></li>\n</ul>\n<h3>Examples</h3>\n<ul>\n<li><a href=\"/structure-examples\">Structure Examples</a></li>\n<li><a href=\"/config-examples\">Config Examples</a></li>\n</ul>\n<h2>Future goals</h2>\n<ul>\n<li><strong>Search Indexing</strong> — structured search index for smarter results</li>\n<li><strong>Config validation</strong> — Zod-based schema validation of <code>smm.config.json</code> with JSON Schema export</li>\n<li><strong>Sitemap</strong> — automatic <code>sitemap.xml</code> generation</li>\n<li><strong>Per-page Open Graph</strong> — page-level og tags from frontmatter</li>\n<li><strong>Doctor command</strong> — health checks: config validation, route discovery, broken link detection, and more</li>\n<li><strong>Link validation</strong> — flag invalid internal links at build-time</li>\n<li><strong>Optional RSS</strong> — config-enableable RSS feed for blog/changelog content</li>\n<li><strong>SchemaStore upload</strong> — publish JSON Schema to SchemaStore once stable</li>\n</ul>\n"},{"path":"/getting-started","content":"<h1>Getting Started</h1>\n<h2>Install</h2>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> i <span class=\"token parameter variable\">-g</span> serve-my-md\n</code></pre>\n<h2>Run</h2>\n<pre class=\"language-bash\"><code class=\"language-bash\">serve-my-md <span class=\"token parameter variable\">--directory</span> <span class=\"token builtin class-name\">.</span>\n</code></pre>\n<p>The CLI scans markdown files in the target directory and generates a static website there. Every page is a standalone HTML file (SSG) — deploy to any static host or CDN.</p>\n<h2>What gets read automatically</h2>\n<ul>\n<li><code>smm.config.json</code> at docs root (if present)</li>\n<li><code>.smmignore</code> at docs root (if present)</li>\n<li>fallback to built-in defaults when these files are missing</li>\n</ul>\n<h2>Important <code>index.md</code> behavior</h2>\n<ul>\n<li><code>index.md</code> is the landing page for its directory.</li>\n<li><code>index</code> is never included in the pathname.</li>\n<li>Internally, an <code>index.md</code> entry is treated as <code>""</code> (empty string) in route tree naming.</li>\n</ul>\n<p>Example:</p>\n<pre class=\"language-text\"><code class=\"language-text\">docs/\n index.md\n guides/\n index.md\n install.md\n</code></pre>\n<p>Routes:</p>\n<ul>\n<li><code>/</code></li>\n<li><code>/guides</code></li>\n<li><code>/guides/install</code></li>\n</ul>\n<h2>Future goals</h2>\n<ul>\n<li><strong>Search Indexing</strong> — structured search index for smarter results</li>\n<li><strong>Config validation</strong> — Zod-based schema validation of <code>smm.config.json</code> with JSON Schema export</li>\n<li><strong>Sitemap</strong> — automatic <code>sitemap.xml</code> generation</li>\n<li><strong>Per-page Open Graph</strong> — page-level og tags from frontmatter</li>\n<li><strong>Doctor command</strong> — health checks: config validation, route discovery, broken link detection, and more</li>\n<li><strong>Link validation</strong> — flag invalid internal links at build-time</li>\n<li><strong>Optional RSS</strong> — config-enableable RSS feed for blog/changelog content</li>\n<li><strong>SchemaStore upload</strong> — publish JSON Schema to SchemaStore once stable</li>\n</ul>\n"},{"path":"/cli-reference","content":"<h1>CLI Reference</h1>\n<h2>Main command</h2>\n<ul>\n<li><code>serve-my-md</code>: build a static docs website from markdown files.</li>\n</ul>\n<h2>Options</h2>\n<ul>\n<li><code>-d, --directory <path></code>: docs root path (default <code>.</code>).</li>\n<li><code>-i, --interactive</code>: prompt for docs root.</li>\n</ul>\n<h2>Build output (SSG)</h2>\n<p>The CLI generates a fully static site:</p>\n<ul>\n<li>Each route gets its own <code>.html</code> file with pre-rendered markdown content in the HTML</li>\n<li>Assets (CSS, JS) are built via Vite and placed under <code>dist/assets/</code></li>\n<li>The final <code>dist/</code> directory is copied to your docs root — ready to deploy</li>\n</ul>\n<h2>Config and ignore loading</h2>\n<ul>\n<li>Loads config from <code>./smm.config.json</code> relative to target directory.</li>\n<li>Loads ignore rules from <code>./.smmignore</code> relative to target directory.</li>\n<li>If <code>publicPath</code> is set in config, copies that directory into app public assets before build.</li>\n</ul>\n<h2>Markdown pipeline</h2>\n<ul>\n<li>Uses markdown-it with task lists and footnotes plugins enabled.</li>\n<li>Prism.js syntax highlighting with auto-loading for unknown languages.</li>\n<li>Generates OG/meta/font tags in HTML from config values.</li>\n</ul>\n<h2>Runtime/navigation behaviors</h2>\n<ul>\n<li>Markdown links with non-HTTP hrefs are intercepted and navigated client-side.</li>\n<li>Intent-based link preloading for faster navigation.</li>\n<li>Keyboard shortcuts:\n<ul>\n<li>Search: <code>Ctrl+Shift+F</code></li>\n<li>Next page: <code>Alt+Enter</code></li>\n<li>Previous page: <code>Alt+Shift+Enter</code></li>\n</ul>\n</li>\n<li>Search returns matches in current page and across other pages.</li>\n</ul>\n<h2>Test-only option</h2>\n<ul>\n<li><code>--skip-build</code> exists only in vitest mode, not in normal CLI usage.</li>\n</ul>\n<h2>Future goals</h2>\n<ul>\n<li><strong>Search Indexing</strong> — structured search index for smarter results</li>\n<li><strong>Config validation</strong> — Zod-based schema validation of <code>smm.config.json</code> with JSON Schema export</li>\n<li><strong>Sitemap</strong> — automatic <code>sitemap.xml</code> generation</li>\n<li><strong>Per-page Open Graph</strong> — page-level og tags from frontmatter</li>\n<li><strong>Doctor command</strong> — health checks: config validation, route discovery, broken link detection, and more</li>\n<li><strong>Link validation</strong> — flag invalid internal links at build-time</li>\n<li><strong>Optional RSS</strong> — config-enableable RSS feed for blog/changelog content</li>\n<li><strong>SchemaStore upload</strong> — publish JSON Schema to SchemaStore once stable</li>\n</ul>\n"},{"path":"/key-behaviors","content":"<h1>Key Behaviors</h1>\n<h2>Internal links auto-prepend <code>baseRoute</code></h2>\n<p>When you set a <code>baseRoute</code> in <code>smm.config.json</code> (e.g. <code>/docs</code>), any internal markdown link (<code>[text](/some-page)</code>) automatically has that base prepended during navigation. You never need to manually prefix links with the base route — change it in one place and all links adapt.</p>\n<h2>Sorting and indexing</h2>\n<p>Pages, directories, and groupers are sorted automatically when <code>sortRoutes: true</code>:</p>\n<ol>\n<li><code>index.md</code> always comes first.</li>\n<li>Regular files and directories come next, sorted lexicographically.</li>\n<li>Groupers <code>(Name)</code> come last, after all regular items.</li>\n</ol>\n<p>Numeric prefixes on filenames (<code>1. intro.md</code>, <code>2. setup.md</code>) control ordering within each tier. The prefix is stripped from the emitted URL when <code>trimIndexFromPath: true</code>.</p>\n<p>Grouper indexes only affect order among other groupers — they never collide with file/directory indexes since groupers always sort after regular items.</p>\n<h2>SSG pre-rendering</h2>\n<p>Every page is a standalone <code>.html</code> file with pre-rendered markdown content baked into the DOM. Search engines see the full content immediately. After the page loads, the React SPA hydrates and takes over for seamless client-side navigation.</p>\n<h2>Groupers</h2>\n<p>Directories named <code>(Name)</code> act as groupers:</p>\n<ul>\n<li>Rendered as a labeled section in the sidebar (not collapsible).</li>\n<li>Skipped from URL pathnames — files inside sit at the grouper's parent URL level.</li>\n<li>Cannot contain <code>index.md</code>.</li>\n<li>Can be ordered with numeric prefixes: <code>(1. Guide)</code>, <code>(2. Reference)</code>.</li>\n</ul>\n<h2>Client-side link interception</h2>\n<p>Internal markdown links (<code>[text](/path)</code>) are intercepted by the SPA router — navigating them does not cause a full page reload. External links (<code>https://...</code>) open normally.</p>\n<h2>Intent-based preloading</h2>\n<p>When you hover over or touch a link, its content is preloaded in the background. Navigation feels instant because the data is ready before you click.</p>\n<h2>Auto-slug generation</h2>\n<p>Route path segments are automatically normalized:</p>\n<ul>\n<li>Lowercased</li>\n<li>Spaces and underscores become <code>-</code></li>\n<li>Punctuation (<code>.,;"'\\\\:<></code>?!`) is removed</li>\n<li>Grouper directory names like <code>(Group)</code> are excluded entirely</li>\n</ul>\n<p>No need to manually craft URL-safe filenames.</p>\n<h2><code>index.md</code> as landing page</h2>\n<ul>\n<li><code>index.md</code> represents the landing page for its directory.</li>\n<li>It is never included in the URL pathname — internally maps to <code>""</code>.</li>\n<li>If a directory contains only <code>index.md</code> and no other files, it collapses to a leaf node (no collapsible).</li>\n</ul>\n<h2><code>publicPath</code> auto-copy and auto-exclude</h2>\n<p>If <code>publicPath</code> is set in config, that directory is:</p>\n<ul>\n<li>Copied into the web app's public assets before the Vite build.</li>\n<li>Automatically excluded from markdown scanning — no need to also add it to <code>.smmignore</code>.</li>\n</ul>\n<h2>Config merge with sensible defaults</h2>\n<p><code>smm.config.json</code> is merged over built-in defaults. You only need to specify what you want to override. Missing options fall back to the CLI's internal defaults — the config file is entirely optional.</p>\n<h2><code>.smmignore</code> glob patterns</h2>\n<p>Create <code>.smmignore</code> at your docs root to exclude paths during markdown traversal:</p>\n<ul>\n<li>Lines starting with <code>#</code> are comments.</li>\n<li>Empty lines are ignored.</li>\n<li>Prefix with <code>!</code> to un-ignore.</li>\n<li>Patterns are evaluated top-to-bottom, last match wins.</li>\n</ul>\n"},{"path":"/why-serve-my-md","content":"<h1>Why serve-my-md?</h1>\n<h2>Zero-friction static site</h2>\n<p>serve-my-md generates a <strong>completely static site</strong> — every page is a standalone HTML file with pre-rendered content. No server, no database, no build-time backend. Deploy anywhere: CDN, GitHub Pages, Netlify, Vercel, S3, or a simple static file server.</p>\n<h2>Blazing fast</h2>\n<p>Static HTML means instant first paint. After load, the React SPA takes over for seamless client-side navigation with intent-based preloading.</p>\n<h2>Rich markdown support</h2>\n<ul>\n<li>Full markdown-it pipeline with footnotes, task lists, and configurable options</li>\n<li>Prism.js syntax highlighting for dozens of languages</li>\n<li>Configurable markdown-it options via <code>smm.config.json</code></li>\n</ul>\n<h2>Custom ordering</h2>\n<p>Control your page order with numeric filename prefixes:</p>\n<pre class=\"language-text\"><code class=\"language-text\">1. intro.md\n2. setup.md\n3. advanced.md\n</code></pre>\n<p>Set <code>trimIndexFromPath: true</code> in config and the prefixes are stripped from URLs while ordering is preserved.</p>\n<h2>Minimal yet powerful config</h2>\n<p>A single <code>smm.config.json</code> at your docs root is all you need. Override titles, theme, fonts, Open Graph tags, and more — or use the sensible defaults and get going immediately.</p>\n<h2>SEO built-in</h2>\n<p>Every page is a full HTML document with:</p>\n<ul>\n<li>Unique <code><title></code> and <code><meta description></code></li>\n<li>Open Graph tags for social previews</li>\n<li>Pre-rendered content in the HTML — search engines see everything</li>\n<li>Semantic HTML structure</li>\n</ul>\n<h2>Great user experience</h2>\n<ul>\n<li><strong>Site-wide search</strong> — instant full-text search with keyboard shortcut (Ctrl+Shift+F)</li>\n<li><strong>Accessibility</strong> — keyboard navigation, ARIA labels, semantic landmarks</li>\n<li><strong>Light/dark themes</strong> — automatic or user-toggleable</li>\n<li><strong>Responsive</strong> — works on desktop and mobile</li>\n<li><strong>Breadcrumbs</strong> — clear navigation context</li>\n<li><strong>Keyboard shortcuts</strong> — quick navigation between pages</li>\n</ul>\n"},{"path":"/routing-and-sidebar-patterns","content":"<h1>Routing and Sidebar Patterns</h1>\n<h2><code>index.md</code> is landing page</h2>\n<ul>\n<li><code>index.md</code> represents the landing page for its directory.</li>\n<li><code>index</code> is not part of URL pathnames.</li>\n<li>In nested name handling, <code>index.md</code> maps to <code>""</code> (empty string).</li>\n<li>If a directory contains only an <code>index.md</code> and no other files, the directory collapses to a leaf node (no collapsible in sidebar).</li>\n</ul>\n<h2>Grouping directories with parentheses (groupers)</h2>\n<p>Directory names wrapped in <code>()</code> act as <strong>groupers</strong>:</p>\n<ul>\n<li>shown as a labeled section in the sidebar (not collapsible)</li>\n<li>skipped from route pathname segments</li>\n<li>should not contain <code>index.md</code></li>\n<li>can be ordered with numeric prefixes: <code>(2. Category)</code></li>\n</ul>\n<p>Groupers are useful for visually grouping related pages without adding extra URL segments.</p>\n<p>Example:</p>\n<pre class=\"language-text\"><code class=\"language-text\">docs/\n (Guides)/\n setup.md\n usage.md\n</code></pre>\n<p>Routes:</p>\n<ul>\n<li><code>/setup</code></li>\n<li><code>/usage</code></li>\n</ul>\n<p>Sidebar:</p>\n<pre class=\"language-text\"><code class=\"language-text\">Guides ← grouper label (not a link)\n Setup\n Usage\n</code></pre>\n<h2>Grouper ordering and index collision rules</h2>\n<p>Numeric prefixes on groupers only control ordering among other groupers — they never collide with indexes on regular files or directories. This is because the sort algorithm always places groupers after every regular file and directory at the same level, regardless of what prefix you give them.</p>\n<p>Similarly, numeric prefixes on regular files and directories only control ordering among their own kind.</p>\n<p>In short:</p>\n<ul>\n<li><code>index.md</code> is always sorted first.</li>\n<li>Regular files and directories are sorted next (by label).</li>\n<li>Groupers are sorted last (by label), after all regular files and directories.</li>\n<li>Within each group (regulars and groupers), ordering is controlled by numeric prefixes and <code>localeCompare</code>.</li>\n</ul>\n<p>Example:</p>\n<pre class=\"language-text\"><code class=\"language-text\">docs/\n index.md\n 1. Getting Started.md\n 2. CLI Reference.md\n (1. Guide)/\n routing.md\n (2. Configuration)/\n smm-config.md\n (3. Examples)/\n structure.md\n</code></pre>\n<p>Sidebar order:</p>\n<pre class=\"language-text\"><code class=\"language-text\">Home ← index.md first\nGetting Started\nCLI Reference\nGuide ← grouper label (after all regular files)\n Routing\nConfiguration ← next grouper\n SMM Config\nExamples ← last grouper\n Structure\n</code></pre>\n<h2>Regular directories (without parentheses)</h2>\n<ul>\n<li>appear as collapsible sidebar containers</li>\n<li>can have <code>index.md</code> and be clickable landing pages</li>\n<li>are included in pathname segments of child files</li>\n</ul>\n<p>Example:</p>\n<pre class=\"language-text\"><code class=\"language-text\">docs/\n guides/\n index.md\n setup.md\n</code></pre>\n<p>Routes:</p>\n<ul>\n<li><code>/guides</code></li>\n<li><code>/guides/setup</code></li>\n</ul>\n<h2>Ordered filenames with index prefixes</h2>\n<p>If you use names like:</p>\n<ul>\n<li><code>1. blahblah.md</code></li>\n<li><code>2. blhalbhablah.md</code></li>\n</ul>\n<p>and set <code>trimIndexFromPath: true</code> in config, names can be ordered while emitted as clean paths.</p>\n<p>The index prefix in filenames does not conflict with grouper indexes — they operate in separate sort tiers.</p>\n<h2>Directories vs groupers summary</h2>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>Sidebar rendering</th>\n<th>In URL path</th>\n<th>Can have <code>index.md</code></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Regular directory</td>\n<td>Collapsible</td>\n<td>Yes</td>\n<td>Yes</td>\n</tr>\n<tr>\n<td>Grouper <code>(Name)</code></td>\n<td>Labeled section</td>\n<td>No</td>\n<td>No</td>\n</tr>\n</tbody>\n</table>\n<h2>Slug generation rules</h2>\n<p>Route path segments are normalized by the CLI:</p>\n<ul>\n<li>converted to lowercase</li>\n<li>spaces/underscores become <code>-</code></li>\n<li>punctuation like <code>.,;"'\\\\:<></code>?!` is removed</li>\n<li>grouper directory names like <code>(Group)</code> are excluded from final pathname</li>\n</ul>\n"},{"path":"/markdown-and-rendering","content":"<h1>Markdown and Rendering</h1>\n<p>The CLI uses <code>markdown-it</code> and includes:</p>\n<ul>\n<li>footnotes support</li>\n<li>task lists support</li>\n</ul>\n<p>Code highlighting is enabled through Prism with language classes like <code>language-js</code>.</p>\n<p>Useful notes:</p>\n<ul>\n<li><code>linkify</code> is enabled by default in config options.</li>\n<li><code>fuzzyEmail</code> linkification is disabled.</li>\n<li>You can override markdown-it behavior via <code>markdownItOptions</code> in <code>smm.config.json</code>.</li>\n</ul>\n<p>Additional rendering behavior from the web app:</p>\n<ul>\n<li>Heading elements are assigned generated ids for anchor navigation.</li>\n<li>Other extracted elements can receive internal labels for in-page search scroll targets.</li>\n</ul>\n"},{"path":"/build-output-and-assets","content":"<h1>Build Output and Assets</h1>\n<h2>Static site generation (SSG)</h2>\n<p>The CLI generates a fully static site. Every route gets its own <code>.html</code> file with pre-rendered markdown content baked into the DOM. The site works without JavaScript — search engines see all content, and the page is immediately readable.</p>\n<p>Once the JS bundle loads, the React SPA hydrates and takes over navigation for seamless client-side transitions.</p>\n<h2>Build flow</h2>\n<ol>\n<li>Markdown files are scanned and parsed into route/content JSON.</li>\n<li>Generated route data is written under <code>web/src/.generated/</code>.</li>\n<li>Vite builds the web app (CSS + JS bundles).</li>\n<li>Static HTML pages are generated for each route via <code>buildDistRoutesFromRouteTree</code>.</li>\n<li>The built <code>dist/</code> directory is copied to the target docs directory.</li>\n</ol>\n<h2>Generated files</h2>\n<table>\n<thead>\n<tr>\n<th>File</th>\n<th>Purpose</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>.generated/output.json</code></td>\n<td>Route metadata and parsed markdown content for all pages</td>\n</tr>\n<tr>\n<td><code>.generated/paths.json</code></td>\n<td>Nested route tree used to render sidebar groups and collapsibles</td>\n</tr>\n</tbody>\n</table>\n<h2>Public assets</h2>\n<p>If <code>publicPath</code> is configured in <code>smm.config.json</code> and the directory exists, its contents are copied into the web app's <code>public/</code> directory before the Vite build. This is useful for images, fonts, or other static files.</p>\n<h2>Output structure</h2>\n<pre class=\"language-text\"><code class=\"language-text\">dist/\n index.html ← home page (pre-rendered)\n getting-started.html ← each route gets its own .html\n cli-reference.html\n smm-config/\n index.html\n defaults-and-options.html\n trim-and-ordering.html\n assets/\n index-abc123.css ← Vite-built CSS\n index-abc123.js ← Vite-built JS (hydration)\n</code></pre>\n<h2>Defaults</h2>\n<p>If no <code>smm.config.json</code> or <code>.smmignore</code> exists, built-in defaults are used automatically.</p>\n"},{"path":"/smm-config","content":"<h1>smm.config.json</h1>\n<p>Place <code>smm.config.json</code> at docs root.</p>\n<p>Defaults are merged with your overrides.</p>\n<p>Default values come from the CLI's built-in config at <code>cli/src/smm.config.json</code>.</p>\n<p>This is the complete schema:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\">SmmConfig</span> <span class=\"token punctuation\">{</span>\n rootTitle<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n description<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n markdownItOptions<span class=\"token operator\">?</span><span class=\"token operator\">:</span> Record<span class=\"token operator\"><</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">any</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span>\n baseRoute<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n outDir<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n publicPath<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n defaultTheme<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n favicon<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n logo<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n name<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n showNameWithLogo<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n og<span class=\"token operator\">?</span><span class=\"token operator\">:</span> OpenGraph<span class=\"token punctuation\">;</span>\n sortRoutes<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n trimIndexFromPath<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n version<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n fonts<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n title<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n name<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n url<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n body<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n name<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n url<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n mono<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n name<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n url<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Read next:</p>\n<ul>\n<li><a href=\"/smm-config/defaults-and-options\">Defaults and Options</a></li>\n<li><a href=\"/smm-config/trim-and-ordering\">Trim and Ordering</a></li>\n<li><a href=\"/smm-config/opengraph\">OpenGraph</a></li>\n</ul>\n"},{"path":"/smm-config/defaults-and-options","content":"<h1>Defaults and Options</h1>\n<p><code>smm.config.json</code> supports these useful options:</p>\n<ul>\n<li><code>rootTitle</code>: document title.</li>\n<li><code>description</code>: default description.</li>\n<li><code>markdownItOptions</code>: markdown-it constructor options.</li>\n<li><code>baseRoute</code>: route base prefix.</li>\n<li><code>outDir</code>: output directory name for built artefacts (default <code>"dist"</code>).</li>\n<li><code>publicPath</code>: directory to copy as public assets.</li>\n<li><code>defaultTheme</code>: initial theme.</li>\n<li><code>favicon</code>: favicon URL/path.</li>\n<li><code>logo</code>: logo URL/path.</li>\n<li><code>name</code>: brand/app name.</li>\n<li><code>showNameWithLogo</code>: show name near logo.</li>\n<li><code>og</code>: Open Graph tags object.</li>\n<li><code>sortRoutes</code>: sort routes tree.</li>\n<li><code>trimIndexFromPath</code>: trims filename numeric prefixes from route names.</li>\n<li><code>version</code>: docs/site version string.</li>\n<li><code>fonts</code>: title/body/mono font names and optional stylesheet URLs.</li>\n</ul>\n<h2>Default values used when user config is missing</h2>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"rootTitle\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Serve My MD\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"A simple markdown to static site builder.\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"baseRoute\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"defaultTheme\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"dark\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"markdownItOptions\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"html\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"xhtmlOut\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"breaks\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"langPrefix\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"language-\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"linkify\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"typographer\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"outDir\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"dist\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"favicon\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"logo\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Serve My MD\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"showNameWithLogo\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"sortRoutes\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"trimIndexFromPath\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Example:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"rootTitle\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"My Docs\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Team documentation\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"baseRoute\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"defaultTheme\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"dark\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"sortRoutes\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"trimIndexFromPath\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"publicPath\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"public\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"outDir\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"my-site\"</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n"},{"path":"/smm-config/trim-and-ordering","content":"<h1>Trim and Ordering</h1>\n<p>Use numbered file names to force custom ordering:</p>\n<ul>\n<li><code>1. intro.md</code></li>\n<li><code>2. install.md</code></li>\n</ul>\n<p>When <code>trimIndexFromPath</code> is <code>true</code>, the numeric prefixes are removed from emitted route names while order stays controlled by your naming.</p>\n<p>Input files:</p>\n<pre class=\"language-text\"><code class=\"language-text\">1. blahblah.md\n2. blhalbhablah.md\n</code></pre>\n<p>Emitted routes:</p>\n<ul>\n<li><code>/blahblah</code></li>\n<li><code>/blhalbhablah</code></li>\n</ul>\n<p>If <code>trimIndexFromPath</code> is <code>false</code>, or not present, then the emitted routes would be:</p>\n<ul>\n<li><code>/1-blahblah</code></li>\n<li><code>/2-blhalbhablah</code></li>\n</ul>\n<p>If route sorting is enabled, routes are sorted lexicographically, with grouped <code>(Name)</code> directories ordered after regular names at the same level. Indexing can also be used for Grouper directories(directories whose names are enclosed within parenthesis) to determine their order.</p>\n"},{"path":"/smm-config/opengraph","content":"<h1>OpenGraph</h1>\n<p>The <code>og</code> field in <code>smm.config.json</code> controls Open Graph meta tags in the generated HTML. These tags control how your site appears when shared on social platforms (Twitter, Discord, Telegram, etc.) and in search results.</p>\n<h2>Schema</h2>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">interface</span> <span class=\"token class-name\">OpenGraph</span> <span class=\"token punctuation\">{</span>\n title<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n type<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n url<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n description<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n site_name<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n determiner<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token string\">\"a\"</span> <span class=\"token operator\">|</span> <span class=\"token string\">\"an\"</span> <span class=\"token operator\">|</span> <span class=\"token string\">\"the\"</span> <span class=\"token operator\">|</span> <span class=\"token string\">\"auto\"</span> <span class=\"token operator\">|</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">;</span>\n locale<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"locale:alternate\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n images<span class=\"token operator\">?</span><span class=\"token operator\">:</span> OpenGraphImage<span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n videos<span class=\"token operator\">?</span><span class=\"token operator\">:</span> OpenGraphVideo<span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n audios<span class=\"token operator\">?</span><span class=\"token operator\">:</span> OpenGraphAudio<span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\">OpenGraphImage</span> <span class=\"token punctuation\">{</span>\n image<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"image:secure_url\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"image:type\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"image:width\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"image:height\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"image:alt\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\">OpenGraphVideo</span> <span class=\"token punctuation\">{</span>\n video<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"video:secure_url\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"video:type\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"video:width\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"video:height\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\">OpenGraphAudio</span> <span class=\"token punctuation\">{</span>\n audio<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"audio:secure_url\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n <span class=\"token string\">\"audio:type\"</span><span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h2>Basic usage</h2>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"og\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"title\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"My Docs\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Documentation for my project.\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"type\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"website\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"site_name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"My Docs\"</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This generates:</p>\n<pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\"><</span>meta</span> <span class=\"token attr-name\">property</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>og:title<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">content</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>My Docs<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\"><</span>meta</span> <span class=\"token attr-name\">property</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>og:description<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">content</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>Documentation for my project.<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\"><</span>meta</span> <span class=\"token attr-name\">property</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>og:type<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">content</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>website<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\"><</span>meta</span> <span class=\"token attr-name\">property</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>og:site_name<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">content</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>My Docs<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n</code></pre>\n<h2>With images</h2>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"og\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"title\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"My Docs\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Documentation for my project.\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"images\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"image\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"https://example.com/og-image.png\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"image:width\"</span><span class=\"token operator\">:</span> <span class=\"token number\">1200</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"image:height\"</span><span class=\"token operator\">:</span> <span class=\"token number\">630</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"image:alt\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"My Docs preview\"</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">]</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h2>With videos and audio</h2>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"og\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"title\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Video Docs\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"videos\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"video\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"https://example.com/trailer.mp4\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"video:type\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"video/mp4\"</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"audios\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"audio\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"https://example.com/song.mp3\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"audio:type\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"audio/mpeg\"</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">]</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h2>Locale</h2>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"og\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"locale\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"en_US\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"locale:alternate\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"fr_FR\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"es_ES\"</span><span class=\"token punctuation\">]</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h2>How it works</h2>\n<p>The CLI reads the <code>og</code> field from <code>smm.config.json</code> and renders each entry as <code><meta property="og:key" content="value"></code> tags in the HTML <code><head></code>. Arrays (like <code>locale:alternate</code>) produce one meta tag per value. Image, video, and audio objects render their properties as flat <code>og:*</code> meta tags.</p>\n"},{"path":"/smm-ignore","content":"<h1>.smmignore</h1>\n<p>Create <code>.smmignore</code> at docs root to skip paths during markdown traversal.</p>\n<p>Rules are glob-like, processed top-to-bottom.</p>\n<ul>\n<li>Lines starting with <code>#</code> are comments.</li>\n<li>Empty lines are ignored.</li>\n<li>Prefix with <code>!</code> to un-ignore.</li>\n</ul>\n<p>Example:</p>\n<pre class=\"language-text\"><code class=\"language-text\">drafts/**\nprivate/**\n!private/keep.md\n</code></pre>\n<p>Paths are evaluated against traversal targets in the docs tree; ignored paths are skipped during route/content generation.</p>\n"},{"path":"/structure-examples","content":"<h1>Structure Examples</h1>\n<h2>Group directory (not in pathname)</h2>\n<pre class=\"language-text\"><code class=\"language-text\">docs/\n (Category)/\n tos.md\n</code></pre>\n<p>Route: <code>/tos</code></p>\n<h2>Normal directory with landing page</h2>\n<pre class=\"language-text\"><code class=\"language-text\">docs/\n features/\n index.md\n tea.md\n</code></pre>\n<p>Routes:</p>\n<ul>\n<li><code>/features</code></li>\n<li><code>/features/tea</code></li>\n</ul>\n<h2>Root landing page</h2>\n<pre class=\"language-text\"><code class=\"language-text\">docs/\n index.md\n about.md\n</code></pre>\n<p>Routes:</p>\n<ul>\n<li><code>/</code></li>\n<li><code>/about</code></li>\n</ul>\n"},{"path":"/config-examples","content":"<h1>Config Examples</h1>\n<h2>Basic customization</h2>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"rootTitle\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Company Docs\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Internal knowledge base\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"defaultTheme\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"dark\"</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"sortRoutes\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h2>Ordered file naming + trimmed paths</h2>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"sortRoutes\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n <span class=\"token property\">\"trimIndexFromPath\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h2>Public assets</h2>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"publicPath\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"public\"</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This copies <code>public</code> from docs root into build public assets, also ignores it for routes building.</p>\n"}],"outDir":"docs","fonts":{"title":"Exo","body":"Commissioner","mono":"Fira Code"}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"label":"","children":null,"pathSegment":""},{"label":"
|
|
1
|
+
[{"label":"","children":null,"pathSegment":""},{"label":"Getting Started","children":null,"pathSegment":"getting-started"},{"label":"CLI Reference","children":null,"pathSegment":"cli-reference"},{"label":"Key Behaviors","children":null,"pathSegment":"key-behaviors"},{"label":"Why serve-my-md","children":null,"pathSegment":"why-serve-my-md"},{"label":"Guide","children":[{"label":"Routing and Sidebar Patterns","children":null,"pathSegment":"routing-and-sidebar-patterns"},{"label":"Markdown and Rendering","children":null,"pathSegment":"markdown-and-rendering"},{"label":"Build Output and Assets","children":null,"pathSegment":"build-output-and-assets"}],"pathSegment":"","isGrouper":true},{"label":"Configuration","children":[{"label":"SMM Config","children":[{"label":"","children":null,"pathSegment":""},{"label":"Defaults and Options","children":null,"pathSegment":"defaults-and-options"},{"label":"Trim and Ordering","children":null,"pathSegment":"trim-and-ordering"},{"label":"OpenGraph","children":null,"pathSegment":"opengraph"}],"pathSegment":"smm-config","isGrouper":false},{"label":"SMM Ignore","children":null,"pathSegment":"smm-ignore","isGrouper":false}],"pathSegment":"","isGrouper":true},{"label":"Examples","children":[{"label":"Structure Examples","children":null,"pathSegment":"structure-examples"},{"label":"Config Examples","children":null,"pathSegment":"config-examples"}],"pathSegment":"","isGrouper":true}]
|
|
@@ -57,17 +57,18 @@ export default function Rendrer({
|
|
|
57
57
|
|
|
58
58
|
const article = articleRef.current;
|
|
59
59
|
article.querySelectorAll('a').forEach((elem: HTMLAnchorElement) => {
|
|
60
|
+
if (
|
|
61
|
+
elem.getAttribute('href')?.startsWith('http://') ||
|
|
62
|
+
elem.getAttribute('href')?.startsWith('https://')
|
|
63
|
+
)
|
|
64
|
+
return;
|
|
65
|
+
|
|
60
66
|
elem.setAttribute(
|
|
61
67
|
'href',
|
|
62
68
|
pathBrowser.join(out.baseRoute || '/', elem.getAttribute('href') || '')
|
|
63
69
|
);
|
|
64
70
|
|
|
65
71
|
elem.addEventListener('click', (e) => {
|
|
66
|
-
if (
|
|
67
|
-
elem.getAttribute('href')?.startsWith('http://') ||
|
|
68
|
-
elem.getAttribute('href')?.startsWith('https://')
|
|
69
|
-
)
|
|
70
|
-
return;
|
|
71
72
|
e.preventDefault();
|
|
72
73
|
navigate({ to: elem.getAttribute('href') || '/' });
|
|
73
74
|
});
|
package/web/public/og-image.png
DELETED
|
Binary file
|