docmedown 0.1.6 → 0.1.9
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/dist/cli.js +111 -9
- package/dist/docmedown.cjs +53 -50
- package/dist/docmedown.iife.js +294 -291
- package/dist/docmedown.mjs +7484 -5019
- package/dist/types/runtime/components/Builtins.d.ts +149 -4
- package/dist/types/runtime/components/OfflineNotice.d.ts +6 -0
- package/dist/types/runtime/config-schema.d.ts +9 -0
- package/dist/types/runtime/markdown/component-body.d.ts +7 -0
- package/dist/types/runtime/markdown/highlighter.d.ts +18 -0
- package/dist/types/runtime/markdown/inline-text.d.ts +11 -0
- package/dist/types/runtime/markdown/renderer.d.ts +1 -1
- package/dist/types/runtime/offline-export.d.ts +25 -0
- package/dist/types/runtime/types.d.ts +3 -0
- package/package.json +2 -1
- package/schemas/docs.schema.json +11 -0
- package/templates/docs.json +3 -0
- package/templates/guides/custom-components.md +20 -0
package/dist/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ var import_commander = require("commander");
|
|
|
31
31
|
// package.json
|
|
32
32
|
var package_default = {
|
|
33
33
|
name: "docmedown",
|
|
34
|
-
version: "0.1.
|
|
34
|
+
version: "0.1.9",
|
|
35
35
|
description: "The simplest MarkDown documenter yet. CLI & Web Script React SPA documentation engine.",
|
|
36
36
|
main: "./dist/docmedown.cjs",
|
|
37
37
|
module: "./dist/docmedown.mjs",
|
|
@@ -80,6 +80,7 @@ var package_default = {
|
|
|
80
80
|
test: "npx tsx --test tests/parser.test.ts tests/scanner.test.ts tests/theme-schema.test.ts tests/runtime-integration.test.ts tests/package-smoke.test.ts tests/hosting.test.ts tests/deploy.test.ts tests/home.test.ts",
|
|
81
81
|
"test:artifacts": "npx tsx --test tests/offline-artifact.test.ts",
|
|
82
82
|
"test:release": "npm run typecheck && npm run lint && npm run test && npm run build && npm run build:docs && npm run test:artifacts && npm pack --dry-run",
|
|
83
|
+
login: "npm login",
|
|
83
84
|
deploy: "node ./scripts/deploy.mjs",
|
|
84
85
|
"deploy:dry-run": "npm run deploy -- --dry-run",
|
|
85
86
|
prepublishOnly: "npm run test:release"
|
|
@@ -240,6 +241,9 @@ var docSearchConfigSchema = import_zod.z.object({
|
|
|
240
241
|
placeholder: import_zod.z.string().optional(),
|
|
241
242
|
maxResults: import_zod.z.number().int().positive().max(100).optional()
|
|
242
243
|
}).strict();
|
|
244
|
+
var docOfflineConfigSchema = import_zod.z.object({
|
|
245
|
+
embedNestedDocs: import_zod.z.boolean().optional()
|
|
246
|
+
}).strict();
|
|
243
247
|
var docConfigSchema = import_zod.z.object({
|
|
244
248
|
$schema: import_zod.z.string().url("Must be an absolute URL.").optional(),
|
|
245
249
|
name: nonEmptyString.optional(),
|
|
@@ -260,6 +264,7 @@ var docConfigSchema = import_zod.z.object({
|
|
|
260
264
|
defaultCollapsed: import_zod.z.boolean().optional()
|
|
261
265
|
}).strict().optional(),
|
|
262
266
|
search: docSearchConfigSchema.optional(),
|
|
267
|
+
offline: docOfflineConfigSchema.optional(),
|
|
263
268
|
footer: import_zod.z.object({
|
|
264
269
|
copyright: import_zod.z.string().optional(),
|
|
265
270
|
links: import_zod.z.array(navLinkSchema).optional(),
|
|
@@ -314,6 +319,9 @@ var DEFAULT_CONFIG = {
|
|
|
314
319
|
placeholder: "Search documentation...",
|
|
315
320
|
maxResults: 10
|
|
316
321
|
},
|
|
322
|
+
offline: {
|
|
323
|
+
embedNestedDocs: true
|
|
324
|
+
},
|
|
317
325
|
footer: {
|
|
318
326
|
copyright: `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} DocMeDown. All rights reserved.`,
|
|
319
327
|
showBuiltWith: true
|
|
@@ -345,6 +353,10 @@ function normalizeConfig(userConfig = {}) {
|
|
|
345
353
|
...DEFAULT_CONFIG.search,
|
|
346
354
|
...parsedConfig.search
|
|
347
355
|
},
|
|
356
|
+
offline: {
|
|
357
|
+
...DEFAULT_CONFIG.offline,
|
|
358
|
+
...parsedConfig.offline
|
|
359
|
+
},
|
|
348
360
|
footer: {
|
|
349
361
|
...DEFAULT_CONFIG.footer,
|
|
350
362
|
...parsedConfig.footer
|
|
@@ -366,7 +378,7 @@ function escapeHtml(value) {
|
|
|
366
378
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
367
379
|
}
|
|
368
380
|
function createCompressedOfflineHtml(encodedGzip, title) {
|
|
369
|
-
const bootstrap = `(async()=>{try{const b=Uint8Array.from(atob(document.getElementById("d").textContent.trim()),c=>c.charCodeAt(0)),s=await new Response(new Blob([b]).stream().pipeThrough(new DecompressionStream("gzip"))).text(),e=JSON.parse(s);if(e.version!==${OFFLINE_FORMAT_VERSION})throw new Error("Unsupported offline format");window.__DOCMEDOWN_OFFLINE__=true;window.__DOCMEDOWN_DATA__=e.data;window.__DOCMEDOWN_CONFIG__=e.data.manifest.config;const u=URL.createObjectURL(new Blob([e.runtime],{type:"text/javascript"}))
|
|
381
|
+
const bootstrap = `(async()=>{try{const b=Uint8Array.from(atob(document.getElementById("d").textContent.trim()),c=>c.charCodeAt(0)),s=await new Response(new Blob([b]).stream().pipeThrough(new DecompressionStream("gzip"))).text(),e=JSON.parse(s);if(e.version!==${OFFLINE_FORMAT_VERSION})throw new Error("Unsupported offline format");window.__DOCMEDOWN_OFFLINE__=true;window.__DOCMEDOWN_DATA__=e.data;window.__DOCMEDOWN_CONFIG__=e.data.manifest.config;const u=URL.createObjectURL(new Blob([e.runtime],{type:"text/javascript"}));window.__DOCMEDOWN_RUNTIME_URL__=u;const j=document.createElement("script");j.src=u;j.onerror=()=>{URL.revokeObjectURL(u);throw new Error("Runtime bootstrap failed")};document.head.appendChild(j)}catch(e){document.getElementById("dmd-app").textContent="Could not open this offline documentation copy: "+e.message}})();`;
|
|
370
382
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="data:,"><title>${escapeHtml(title)}</title></head><body><div id="dmd-app">Opening offline documentation\u2026</div><script id="d" type="application/octet-stream">${encodedGzip}</script><script>${bootstrap}</script></body></html>`;
|
|
371
383
|
}
|
|
372
384
|
|
|
@@ -485,6 +497,49 @@ function mapManualSidebar(items) {
|
|
|
485
497
|
});
|
|
486
498
|
}
|
|
487
499
|
|
|
500
|
+
// src/runtime/markdown/inline-text.ts
|
|
501
|
+
var NAMED_HTML_ENTITIES = {
|
|
502
|
+
amp: "&",
|
|
503
|
+
lt: "<",
|
|
504
|
+
gt: ">",
|
|
505
|
+
quot: '"',
|
|
506
|
+
apos: "'",
|
|
507
|
+
nbsp: " ",
|
|
508
|
+
copy: "\xA9",
|
|
509
|
+
reg: "\xAE",
|
|
510
|
+
trade: "\u2122",
|
|
511
|
+
middot: "\xB7",
|
|
512
|
+
hellip: "\u2026",
|
|
513
|
+
mdash: "\u2014",
|
|
514
|
+
ndash: "\u2013",
|
|
515
|
+
bull: "\u2022",
|
|
516
|
+
deg: "\xB0",
|
|
517
|
+
times: "\xD7",
|
|
518
|
+
plusmn: "\xB1",
|
|
519
|
+
laquo: "\xAB",
|
|
520
|
+
raquo: "\xBB",
|
|
521
|
+
sect: "\xA7",
|
|
522
|
+
para: "\xB6"
|
|
523
|
+
};
|
|
524
|
+
function codePointToChar(code) {
|
|
525
|
+
return Number.isInteger(code) && code > 0 && code <= 1114111 ? String.fromCodePoint(code) : "";
|
|
526
|
+
}
|
|
527
|
+
function decodeHtmlEntities(value) {
|
|
528
|
+
return value.replace(/&#x([0-9a-f]+);/gi, (_match, hex) => codePointToChar(parseInt(hex, 16))).replace(/&#([0-9]+);/g, (_match, dec) => codePointToChar(Number(dec))).replace(/&([a-z][a-z0-9]*);/gi, (match, name) => NAMED_HTML_ENTITIES[name.toLowerCase()] ?? match);
|
|
529
|
+
}
|
|
530
|
+
var IMAGE_PATTERN = /!\[([^\]]*)\]\([^)]*\)/g;
|
|
531
|
+
var LINK_PATTERN = /\[([^\]]+)\]\([^)]*\)/g;
|
|
532
|
+
var CODE_PATTERN = /`([^`]+)`/g;
|
|
533
|
+
var BOLD_PATTERN = /\*\*([^*\n]+)\*\*/g;
|
|
534
|
+
var UNDERSCORE_BOLD_PATTERN = /__([^_\n]+)__/g;
|
|
535
|
+
var ITALIC_PATTERN = /\*([^*\n]+)\*/g;
|
|
536
|
+
var STRIKETHROUGH_PATTERN = /~~([^~\n]+)~~/g;
|
|
537
|
+
function inlineHeadingToPlainText(raw) {
|
|
538
|
+
return decodeHtmlEntities(
|
|
539
|
+
raw.replace(IMAGE_PATTERN, "$1").replace(LINK_PATTERN, "$1").replace(CODE_PATTERN, "$1").replace(BOLD_PATTERN, "$1").replace(UNDERSCORE_BOLD_PATTERN, "$1").replace(ITALIC_PATTERN, "$1").replace(STRIKETHROUGH_PATTERN, "$1")
|
|
540
|
+
).replace(/\s+/g, " ").trim();
|
|
541
|
+
}
|
|
542
|
+
|
|
488
543
|
// src/cli/utils/scanner.ts
|
|
489
544
|
function extractHeadings(content) {
|
|
490
545
|
const headings = [];
|
|
@@ -493,8 +548,9 @@ function extractHeadings(content) {
|
|
|
493
548
|
const match = line.match(/^(#{1,4})\s+(.+)$/);
|
|
494
549
|
if (match) {
|
|
495
550
|
const level = match[1].length;
|
|
496
|
-
const
|
|
497
|
-
const
|
|
551
|
+
const rawHeading = match[2].trim();
|
|
552
|
+
const text = inlineHeadingToPlainText(rawHeading);
|
|
553
|
+
const id = rawHeading.replace(/[#*`_]/g, "").toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/[\s_-]+/g, "-");
|
|
498
554
|
headings.push({ level, text, id });
|
|
499
555
|
}
|
|
500
556
|
}
|
|
@@ -615,11 +671,40 @@ function findNestedDocumentationRoots(targetDir) {
|
|
|
615
671
|
visit(targetDir);
|
|
616
672
|
return roots;
|
|
617
673
|
}
|
|
674
|
+
function collectNestedOfflineSites(targetDir) {
|
|
675
|
+
const sites = {};
|
|
676
|
+
for (const nestedRoot of findNestedDocumentationRoots(targetDir)) {
|
|
677
|
+
const key = import_node_path2.default.relative(targetDir, nestedRoot).replace(/\\/g, "/");
|
|
678
|
+
const nestedConfigPath = import_node_path2.default.join(nestedRoot, "docs.json");
|
|
679
|
+
let nestedConfig = normalizeConfig();
|
|
680
|
+
if (import_node_fs2.default.existsSync(nestedConfigPath)) {
|
|
681
|
+
nestedConfig = normalizeConfig(parseDocConfigJson(import_node_fs2.default.readFileSync(nestedConfigPath, "utf-8"), nestedConfigPath));
|
|
682
|
+
}
|
|
683
|
+
const nestedDocs = {};
|
|
684
|
+
for (const file of scanDirectory(nestedRoot)) {
|
|
685
|
+
const raw = import_node_fs2.default.readFileSync(import_node_path2.default.join(nestedRoot, file), "utf-8");
|
|
686
|
+
const slug = file.replace(/\.(md|mdx)$/i, "");
|
|
687
|
+
const cleanSlug = slug.toLowerCase() === "readme" || slug.toLowerCase() === "index" ? "README" : slug;
|
|
688
|
+
nestedDocs[cleanSlug] = raw;
|
|
689
|
+
nestedDocs[file] = raw;
|
|
690
|
+
}
|
|
691
|
+
sites[key] = {
|
|
692
|
+
name: nestedConfig.name || key,
|
|
693
|
+
manifest: generateManifest(nestedRoot, nestedConfig),
|
|
694
|
+
docs: nestedDocs,
|
|
695
|
+
componentsSource: bundleCustomComponents(import_node_path2.default.join(nestedRoot, ".dmd", "components.js"))
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
return sites;
|
|
699
|
+
}
|
|
618
700
|
function shouldWatchDocumentationSource(rootDir, changedPath) {
|
|
619
701
|
const relativePath = import_node_path2.default.relative(rootDir, changedPath).replace(/\\/g, "/");
|
|
620
|
-
const
|
|
702
|
+
const segments = relativePath.split("/");
|
|
703
|
+
const firstSegment = segments[0];
|
|
704
|
+
const baseName = segments[segments.length - 1];
|
|
621
705
|
const isCustomComponentModule = /(^|\/)\.dmd\/(components|index)\.js$/i.test(relativePath);
|
|
622
|
-
|
|
706
|
+
const isGeneratedOutput = baseName === ".nojekyll" || GENERATED_DOC_OUTPUTS.includes(baseName) || segments.includes(".dist");
|
|
707
|
+
return Boolean(relativePath) && !relativePath.startsWith("..") && (!relativePath.startsWith(".") || isCustomComponentModule) && !isGeneratedOutput && !GENERATED_DOC_OUTPUTS.includes(relativePath) && firstSegment !== "node_modules" && firstSegment !== "dist";
|
|
623
708
|
}
|
|
624
709
|
async function buildCommand(targetDirArg = "./docs", options = {}) {
|
|
625
710
|
const targetDir = import_node_path2.default.resolve(process.cwd(), targetDirArg);
|
|
@@ -682,9 +767,24 @@ async function buildCommand(targetDirArg = "./docs", options = {}) {
|
|
|
682
767
|
} else {
|
|
683
768
|
console.warn(import_chalk.default.yellow(" \u26A0 Local runtime bundle not found, linking CDN script."));
|
|
684
769
|
}
|
|
770
|
+
const embedNestedDocs = config.offline?.embedNestedDocs !== false;
|
|
771
|
+
const nestedSites = embedNestedDocs && options.buildNested !== false ? collectNestedOfflineSites(targetDir) : void 0;
|
|
772
|
+
const nestedCount = nestedSites ? Object.keys(nestedSites).length : 0;
|
|
773
|
+
if (nestedCount > 0) {
|
|
774
|
+
console.log(
|
|
775
|
+
import_chalk.default.green(
|
|
776
|
+
` \u2714 Embedded ${nestedCount} nested documentation site${nestedCount === 1 ? "" : "s"} into the offline bundle`
|
|
777
|
+
)
|
|
778
|
+
);
|
|
779
|
+
}
|
|
685
780
|
const envelope = {
|
|
686
781
|
version: OFFLINE_FORMAT_VERSION,
|
|
687
|
-
data: {
|
|
782
|
+
data: {
|
|
783
|
+
manifest,
|
|
784
|
+
docs: docsMap,
|
|
785
|
+
componentsSource,
|
|
786
|
+
...nestedSites && nestedCount > 0 ? { nestedSites } : {}
|
|
787
|
+
},
|
|
688
788
|
runtime: bundleJs
|
|
689
789
|
};
|
|
690
790
|
const encodedEnvelope = encodeCompressedOfflineEnvelope(envelope);
|
|
@@ -728,10 +828,11 @@ async function watchBuildCommand(targetDirArg = "./docs", options = {}) {
|
|
|
728
828
|
console.log(import_chalk.default.dim(" Source changes rebuild serveable files and .dist/index.html. Press Ctrl+C to stop.\n"));
|
|
729
829
|
const watcher = import_chokidar.default.watch(targetDir, {
|
|
730
830
|
ignored: [
|
|
731
|
-
"**/node_modules/**",
|
|
732
|
-
"**/.git/**",
|
|
733
831
|
"**/dist/**",
|
|
734
832
|
"**/.dist/**",
|
|
833
|
+
"**/node_modules/**",
|
|
834
|
+
"**/.git/**",
|
|
835
|
+
"**/.nojekyll",
|
|
735
836
|
"**/_manifest.json",
|
|
736
837
|
"**/_docs.js",
|
|
737
838
|
"**/docmedown.iife.js"
|
|
@@ -1027,6 +1128,7 @@ function startDevServer(options) {
|
|
|
1027
1128
|
"**/.git/**",
|
|
1028
1129
|
"**/dist/**",
|
|
1029
1130
|
"**/.dist/**",
|
|
1131
|
+
"**/.nojekyll",
|
|
1030
1132
|
"**/_manifest.json",
|
|
1031
1133
|
"**/_docs.js",
|
|
1032
1134
|
"**/docmedown.iife.js"
|