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 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.6",
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
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"})),j=document.createElement("script");j.src=u;j.onload=()=>URL.revokeObjectURL(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}})();`;
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 text = match[2].trim().replace(/[#*`_]/g, "");
497
- const id = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/[\s_-]+/g, "-");
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 firstSegment = relativePath.split("/")[0];
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
- return Boolean(relativePath) && !relativePath.startsWith("..") && (!relativePath.startsWith(".") || isCustomComponentModule) && !GENERATED_DOC_OUTPUTS.includes(relativePath) && firstSegment !== "node_modules" && firstSegment !== "dist";
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: { manifest, docs: docsMap, componentsSource },
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"