@t8/docsgen 0.3.32 → 0.3.34

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/bin.js CHANGED
@@ -278,14 +278,26 @@ async function setCName({ dir = "", name, cname, jsorg }) {
278
278
  }
279
279
 
280
280
  // src/bin/setContent.ts
281
- import { exec as defaultExec } from "node:child_process";
282
- import { access, cp, mkdir, writeFile as writeFile2 } from "node:fs/promises";
283
- import { dirname, join as join3 } from "node:path";
284
- import { fileURLToPath } from "node:url";
285
- import { promisify } from "node:util";
281
+ import { writeFile as writeFile2 } from "node:fs/promises";
282
+ import { join as join5 } from "node:path";
286
283
 
287
- // src/const/packageName.ts
288
- var packageName = "@t8/docsgen";
284
+ // src/bin/content/createDirs.ts
285
+ import { access, mkdir } from "node:fs/promises";
286
+ import { join as join3 } from "node:path";
287
+ async function createDirs(ctx) {
288
+ let { dir = "", contentDir = "" } = ctx;
289
+ let dirs = [contentDir];
290
+ await Promise.all(
291
+ dirs.map(async (path) => {
292
+ let dirPath = join3(dir, path);
293
+ try {
294
+ await access(dirPath);
295
+ } catch {
296
+ await mkdir(dirPath);
297
+ }
298
+ })
299
+ );
300
+ }
289
301
 
290
302
  // src/utils/escapeHTML.ts
291
303
  var htmlEntityMap = [
@@ -302,70 +314,6 @@ function escapeHTML(x) {
302
314
  return s;
303
315
  }
304
316
 
305
- // src/utils/escapeRegExp.ts
306
- function escapeRegExp(s) {
307
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
308
- }
309
-
310
- // src/bin/getCounterContent.ts
311
- function getCounterContent({ ymid }) {
312
- if (!ymid) return "";
313
- return `
314
- <script>
315
- (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
316
- m[i].l=1*new Date();
317
- for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
318
- k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
319
- (window, document, "script", "https://mc.yandex.com/metrika/tag.js", "ym");
320
- ym(${ymid}, "init", {clickmap: true, trackLinks: true, accurateTrackBounce: true});
321
- </script>
322
- <noscript><div><img src="https://mc.yandex.com/watch/${ymid}" style="position:absolute;left:-9999px;" alt=""></div></noscript>
323
- `.trim();
324
- }
325
-
326
- // src/bin/getIcon.ts
327
- var iconTypeMap = {
328
- ico: "image/x-icon",
329
- svg: "image/svg+xml",
330
- png: "image/png",
331
- gif: "image/gif",
332
- jpg: "image/jpeg",
333
- jpeg: "image/jpeg"
334
- };
335
- function getIcon({ favicon, faviconType }) {
336
- let icon = {
337
- url: favicon,
338
- type: faviconType
339
- };
340
- if (icon.url && !icon.type) {
341
- let ext = icon.url.split("/").at(-1)?.split(".").at(-1)?.toLowerCase();
342
- if (ext) icon.type = iconTypeMap[ext];
343
- }
344
- return icon;
345
- }
346
-
347
- // src/bin/getInjectedContent.ts
348
- function getInjectedContent(ctx, page, target, mode) {
349
- let injectedContent = ctx[mode]?.[target];
350
- if (!injectedContent) return "";
351
- return (Array.isArray(injectedContent) ? injectedContent : [injectedContent]).reduce((s, item) => {
352
- if (item === void 0) return s;
353
- if (typeof item === "string") return `${s}${s ? "\n" : ""}${item}`;
354
- let { content, pages } = item;
355
- if (!content || pages && !pages.includes(page)) return s;
356
- return `${s}${s ? "\n" : ""}${content}`;
357
- }, "");
358
- }
359
-
360
- // src/bin/getNav.ts
361
- import { JSDOM as JSDOM2 } from "jsdom";
362
-
363
- // src/bin/getNpmLink.ts
364
- function getNpmLink({ npm }, className) {
365
- if (!npm) return "";
366
- return `<a href="${npm}"${className ? ` class="${className}"` : ""} target="_blank">npm</a>`;
367
- }
368
-
369
317
  // src/bin/getRepoLink.ts
370
318
  function getRepoLink({ repo }, className) {
371
319
  if (!repo) return "";
@@ -373,82 +321,8 @@ function getRepoLink({ repo }, className) {
373
321
  return `<a href="${repo}"${className ? ` class="${className}"` : ""} target="_blank">${caption}</a>`;
374
322
  }
375
323
 
376
- // src/bin/getNav.ts
377
- async function getNav(ctx, navItems) {
378
- let { name, root, contentDir, backstory, nav } = ctx;
379
- let navContent = await fetchContent(nav);
380
- let s = "";
381
- if (navContent) {
382
- let navDom = new JSDOM2(navContent).window.document.body;
383
- for (let link of navDom.querySelectorAll("a")) {
384
- if (link.dataset.name === name) {
385
- let parent = link.parentElement;
386
- link.remove();
387
- while (parent && parent.innerHTML.trim() === "") {
388
- let nextParent = parent.parentElement;
389
- parent.remove();
390
- parent = nextParent;
391
- }
392
- }
393
- }
394
- navContent = navDom.innerHTML;
395
- }
396
- let navItemCount = 0;
397
- if (navItems.length === 1) {
398
- let { id, items } = navItems[0];
399
- let itemLink = `${root}${contentDir}/${encodeURIComponent(id)}`;
400
- for (let { id: id2, title } of items) {
401
- s += `
402
- <li><a href="${itemLink}#${encodeURIComponent(id2)}">${title}</a></li>`;
403
- navItemCount++;
404
- }
405
- } else {
406
- for (let { id, title, items } of navItems) {
407
- let itemLink = `${root}${contentDir}/${encodeURIComponent(id)}`;
408
- s += `
409
- <li data-id="${id}"><a href="${itemLink}">${title}</a>`;
410
- if (items.length !== 0) {
411
- s += "\n <ul>";
412
- for (let { id: id2, title: title2 } of items) {
413
- s += `
414
- <li><a href="${itemLink}#${encodeURIComponent(id2)}">${title2}</a></li>`;
415
- navItemCount++;
416
- }
417
- s += "\n </ul>\n";
418
- }
419
- s += "</li>";
420
- navItemCount++;
421
- }
422
- }
423
- if ((!s || navItemCount < 2) && !navContent) return "";
424
- s = s.trim();
425
- s = s ? `<p class="title">Contents</p>
426
- <ul>${s}
427
- </ul>
428
- ` : "";
429
- let repoLink = getRepoLink(ctx);
430
- let npmLink = getNpmLink(ctx);
431
- if (repoLink || npmLink || backstory)
432
- s += `
433
- <p class="title">Resources</p>
434
- <ul>
435
- ${repoLink ? `<li>${repoLink}</li>` : ""}
436
- ${npmLink ? `<li>${npmLink}</li>` : ""}
437
- ${backstory ? `<li><a href="${backstory}">Backstory</a></li>` : ""}
438
- </ul>
439
- `;
440
- s = s.trim();
441
- s = s ? `<section>
442
- ${s}
443
- </section>` : "";
444
- return `<nav class="aux">
445
- ${s}
446
- ${navContent}
447
- </nav>`;
448
- }
449
-
450
324
  // src/bin/parsing/getParsedContent.ts
451
- import { JSDOM as JSDOM3 } from "jsdom";
325
+ import { JSDOM as JSDOM2 } from "jsdom";
452
326
  import Markdown from "markdown-it";
453
327
 
454
328
  // src/bin/getSlug.ts
@@ -576,13 +450,15 @@ function preprocessContent(s) {
576
450
  var md = new Markdown({
577
451
  html: true
578
452
  });
453
+ var parsedContentCache = /* @__PURE__ */ new Map();
579
454
  async function getParsedContent(ctx) {
455
+ let contentLocation = getLocation(ctx, "README.md", ctx.source);
456
+ let parsedContent = parsedContentCache.get(contentLocation);
457
+ if (parsedContent) return parsedContent;
580
458
  let { singlePage, firstLineDescription, linkMap } = ctx;
581
- let rawContent = await fetchContent(
582
- getLocation(ctx, "README.md", ctx.source)
583
- );
459
+ let rawContent = await fetchContent(contentLocation);
584
460
  let content = md.render(preprocessContent(rawContent));
585
- let dom = new JSDOM3(content);
461
+ let dom = new JSDOM2(content);
586
462
  let { nav, linkMap: navLinkMap } = buildNav(ctx, dom);
587
463
  let badges = "";
588
464
  let title = "";
@@ -650,7 +526,7 @@ async function getParsedContent(ctx) {
650
526
  intro[i] = `<p><span class="li">${s}</span></p>`;
651
527
  }
652
528
  }
653
- return {
529
+ parsedContent = {
654
530
  badges,
655
531
  // postprocessBadges(joinLines(badges)),
656
532
  title,
@@ -662,25 +538,66 @@ async function getParsedContent(ctx) {
662
538
  sections: sections.map(postprocess),
663
539
  nav
664
540
  };
541
+ parsedContentCache.set(contentLocation, parsedContent);
542
+ return parsedContent;
665
543
  }
666
544
 
667
- // src/bin/toFileContent.ts
668
- function toFileContent(x) {
669
- let s = x.replace(
670
- /\s+(\r?\n *<\/?(script|link|head|body|html|meta|title)[> ])/g,
671
- "$1"
672
- ).replace(
673
- /\s+(\r?\n *<\/?(main|header|footer|nav|section|div|h\d|p|ul|ol|li|table)[> ])/g,
674
- "$1"
675
- ).trim();
676
- return `${s}
677
- `;
545
+ // src/bin/content/getCounterContent.ts
546
+ function getCounterContent({ ymid }) {
547
+ if (!ymid) return "";
548
+ return `
549
+ <script>
550
+ (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
551
+ m[i].l=1*new Date();
552
+ for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
553
+ k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
554
+ (window, document, "script", "https://mc.yandex.com/metrika/tag.js", "ym");
555
+ ym(${ymid}, "init", {clickmap: true, trackLinks: true, accurateTrackBounce: true});
556
+ </script>
557
+ <noscript><div><img src="https://mc.yandex.com/watch/${ymid}" style="position:absolute;left:-9999px;" alt=""></div></noscript>
558
+ `.trim();
678
559
  }
679
560
 
680
- // src/bin/setContent.ts
561
+ // src/bin/content/getCSSRoot.ts
562
+ import { exec as defaultExec } from "node:child_process";
563
+ import { cp } from "node:fs/promises";
564
+ import { dirname, join as join4 } from "node:path";
565
+ import { fileURLToPath } from "node:url";
566
+ import { promisify } from "node:util";
567
+
568
+ // src/const/packageName.ts
569
+ var packageName = "@t8/docsgen";
570
+
571
+ // src/bin/content/getCSSRoot.ts
681
572
  var exec = promisify(defaultExec);
682
573
  var __filename = fileURLToPath(import.meta.url);
683
574
  var __dirname = dirname(__filename);
575
+ var packageURL = "";
576
+ async function getCSSRoot(ctx, type) {
577
+ let { dir = "", assetsDir } = ctx;
578
+ let cssRoot = {
579
+ index: "",
580
+ content: ""
581
+ };
582
+ if (assetsDir) {
583
+ cssRoot.index = assetsDir;
584
+ cssRoot.content = `../${assetsDir}`;
585
+ await cp(join4(__dirname, "css"), join4(dir, cssRoot.index), {
586
+ force: true,
587
+ recursive: true
588
+ });
589
+ } else {
590
+ if (!packageURL) {
591
+ let packageVersion = (await exec(`npm view ${packageName} version`)).stdout.trim().split(".").slice(0, 2).join(".");
592
+ packageURL = `https://unpkg.com/${packageName}@${packageVersion}`;
593
+ }
594
+ cssRoot.index = `${packageURL}/dist/css`;
595
+ cssRoot.content = `${packageURL}/dist/css`;
596
+ }
597
+ return cssRoot[type];
598
+ }
599
+
600
+ // src/bin/content/getDefaultCodeStyleContent.ts
684
601
  function getDefaultCodeStyleContent(cssRoot) {
685
602
  return `
686
603
  <link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/styles/stackoverflow-light.min.css" media="(prefers-color-scheme: light)">
@@ -688,72 +605,87 @@ function getDefaultCodeStyleContent(cssRoot) {
688
605
  <link rel="stylesheet" href="${cssRoot}/code.css">
689
606
  <script src="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/highlight.min.js"></script>
690
607
  <script>hljs.highlightAll()</script>
691
- `.trim();
608
+ `.trim();
609
+ }
610
+
611
+ // src/bin/content/getIcon.ts
612
+ var iconTypeMap = {
613
+ ico: "image/x-icon",
614
+ svg: "image/svg+xml",
615
+ png: "image/png",
616
+ gif: "image/gif",
617
+ jpg: "image/jpeg",
618
+ jpeg: "image/jpeg"
619
+ };
620
+ function getIcon({ favicon, faviconType }) {
621
+ let icon = {
622
+ url: favicon,
623
+ type: faviconType
624
+ };
625
+ if (icon.url && !icon.type) {
626
+ let ext = icon.url.split("/").at(-1)?.split(".").at(-1)?.toLowerCase();
627
+ if (ext) icon.type = iconTypeMap[ext];
628
+ }
629
+ return icon;
692
630
  }
631
+
632
+ // src/bin/content/getIconTag.ts
633
+ function getIconTag(ctx) {
634
+ let { url, type } = getIcon(ctx);
635
+ return url ? `<link rel="icon"${type ? ` type="${type}"` : ""} href="${url}">` : "";
636
+ }
637
+
638
+ // src/bin/content/getInjectedContent.ts
639
+ function getInjectedContent(ctx, page, target, mode) {
640
+ let injectedContent = ctx[mode]?.[target];
641
+ if (!injectedContent) return "";
642
+ return (Array.isArray(injectedContent) ? injectedContent : [injectedContent]).reduce((s, item) => {
643
+ if (item === void 0) return s;
644
+ if (typeof item === "string") return `${s}${s ? "\n" : ""}${item}`;
645
+ let { content, pages } = item;
646
+ if (!content || pages && !pages.includes(page)) return s;
647
+ return `${s}${s ? "\n" : ""}${content}`;
648
+ }, "");
649
+ }
650
+
651
+ // src/bin/content/getPlainTitle.ts
652
+ async function getPlainTitle(ctx) {
653
+ let { name, title, htmlTitle } = ctx;
654
+ let { title: parsedTitle } = await getParsedContent(ctx);
655
+ return escapeHTML(title || stripHTML(htmlTitle || parsedTitle, true) || name);
656
+ }
657
+
658
+ // src/bin/content/toFileContent.ts
659
+ function toFileContent(x) {
660
+ let s = x.replace(
661
+ /\s+(\r?\n *<\/?(script|link|head|body|html|meta|title)[> ])/g,
662
+ "$1"
663
+ ).replace(
664
+ /\s+(\r?\n *<\/?(main|header|footer|nav|section|div|h\d|p|ul|ol|li|table)[> ])/g,
665
+ "$1"
666
+ ).trim();
667
+ return `${s}
668
+ `;
669
+ }
670
+
671
+ // src/bin/content/tweakTypography.ts
693
672
  function tweakTypography(s = "") {
694
673
  return s.replace(/\b(for|in|on|at|to|with|a|an|the|its)\s+/gi, "$1\xA0").replace(/\b(React)\s+(apps?)\b/gi, "$1\xA0$2");
695
674
  }
696
- async function setContent(ctx) {
675
+
676
+ // src/bin/content/getIndexContent.ts
677
+ async function getIndexContent(ctx) {
697
678
  let {
698
- dir = "",
699
- assetsDir,
700
679
  root,
701
680
  contentDir = "",
702
681
  name,
703
- title,
704
682
  htmlTitle,
705
683
  description: packageDescription,
706
684
  isDevDep,
707
- backstory,
708
- redirect
685
+ backstory
709
686
  } = ctx;
710
687
  let counterContent = getCounterContent(ctx);
711
688
  let escapedPackageDescription = escapeHTML(packageDescription);
712
- let cssRoot = {
713
- index: "",
714
- content: ""
715
- };
716
- if (assetsDir) {
717
- cssRoot.index = assetsDir;
718
- cssRoot.content = `../${assetsDir}`;
719
- await cp(join3(__dirname, "css"), join3(dir, cssRoot.index), {
720
- force: true,
721
- recursive: true
722
- });
723
- } else {
724
- let packageVersion = (await exec(`npm view ${packageName} version`)).stdout.trim().split(".").slice(0, 2).join(".");
725
- let packageUrl = `https://unpkg.com/${packageName}@${packageVersion}`;
726
- cssRoot.index = `${packageUrl}/dist/css`;
727
- cssRoot.content = `${packageUrl}/dist/css`;
728
- }
729
- let icon = getIcon(ctx);
730
- let iconTag = icon.url ? `<link rel="icon"${icon.type ? ` type="${icon.type}"` : ""} href="${icon.url}">` : "";
731
- if (redirect) {
732
- let escapedRedirect = escapeHTML(redirect);
733
- await writeFile2(
734
- join3(dir, "index.html"),
735
- toFileContent(`
736
- <!DOCTYPE html>
737
- <html lang="en" data-layout="redirect" class="blank">
738
- <head>
739
- ${getInjectedContent(ctx, "redirect", "head", "prepend")}
740
- <meta charset="utf-8">
741
- <meta name="viewport" content="width=device-width">
742
- <meta http-equiv="refresh" content="0; URL=${escapedRedirect}">
743
- ${iconTag}
744
- ${getInjectedContent(ctx, "redirect", "head", "append")}
745
- </head>
746
- <body>
747
- ${getInjectedContent(ctx, "redirect", "body", "prepend")}
748
- ${counterContent}
749
- ${getInjectedContent(ctx, "redirect", "body", "append")}
750
- <script>window.location.replace("${escapedRedirect}");</script>
751
- </body>
752
- </html>
753
- `)
754
- );
755
- return;
756
- }
757
689
  let {
758
690
  title: parsedTitle,
759
691
  description,
@@ -761,86 +693,15 @@ ${getInjectedContent(ctx, "redirect", "body", "append")}
761
693
  features,
762
694
  note,
763
695
  installation,
764
- sections,
765
696
  nav
766
697
  } = await getParsedContent(ctx);
767
- let plainTitle = escapeHTML(
768
- title || stripHTML(htmlTitle || parsedTitle, true) || name
769
- );
698
+ let plainTitle = await getPlainTitle(ctx);
770
699
  let coverTitle = htmlTitle || parsedTitle || plainTitle;
771
700
  let descriptionContent = tweakTypography(description) || (escapedPackageDescription ? `<p>${tweakTypography(escapedPackageDescription)}<p>` : "");
772
701
  if (!installation || isDevDep !== void 0)
773
702
  installation = `npm i${isDevDep ? " -D" : ""} ${name}`;
774
- let navContent = await getNav(ctx, nav);
775
- let dirs = [contentDir];
776
- await Promise.all(
777
- dirs.map(async (path) => {
778
- let dirPath = join3(dir, path);
779
- try {
780
- await access(dirPath);
781
- } catch {
782
- await mkdir(dirPath);
783
- }
784
- })
785
- );
786
- await Promise.all([
787
- ...sections.map(
788
- async (content, i) => writeFile2(
789
- join3(dir, contentDir, `${nav[i]?.id ?? `_untitled_${i}`}.html`),
790
- toFileContent(`
791
- <!DOCTYPE html>
792
- <html lang="en" data-layout="section">
793
- <head>
794
- ${getInjectedContent(ctx, "section", "head", "prepend")}
795
- <meta charset="utf-8">
796
- <meta name="viewport" content="width=device-width, initial-scale=1">
797
- <meta name="description" content="${plainTitle}: ${escapeHTML(stripHTML(nav[i]?.title, true))}">
798
- <title>${escapeHTML(stripHTML(nav[i]?.title, true))} | ${plainTitle}</title>
799
- <link rel="stylesheet" href="${cssRoot.content}/base.css">
800
- <link rel="stylesheet" href="${cssRoot.content}/section.css">
801
- ${iconTag}
802
- ${nav[i + 1]?.id ? `<link rel="prefetch" href="${root}${contentDir}/${nav[i + 1]?.id}">` : ""}
803
- ${nav[i - 1]?.id ? `<link rel="prefetch" href="${root}${contentDir}/${nav[i - 1]?.id}">` : ""}
804
- ${getInjectedContent(ctx, "section", "head", "append")}
805
- </head>
806
- <body>
807
- ${getInjectedContent(ctx, "section", "body", "prepend")}
808
- <div class="layout">
809
- <div class="${navContent ? "" : "no-nav "}body">
810
- <main>
811
- <h1><a href="${root}">${plainTitle}</a></h1>
812
- ${content}
813
-
814
- <p class="pagenav">
815
- <span class="prev">
816
- <span class="icon">\u2190</span>
817
- ${nav[i - 1]?.id ? `<a href="${root}${contentDir}/${nav[i - 1]?.id}">${nav[i - 1]?.title}</a>` : `<a href="${root}">Intro</a>`}
818
- </span>
819
- <span class="sep">|</span>
820
- ${nav[i + 1]?.id ? `<span class="next"><a href="${root}${contentDir}/${nav[i + 1]?.id}">${nav[i + 1]?.title}</a> <span class="icon">\u2192</span></span>` : `<span class="repo">${getRepoLink(ctx)}</span>`}
821
- </p>
822
- </main>
823
- ${navContent ? "<hr>" : ""}
824
- ${navContent.replace(
825
- new RegExp(
826
- `(<li data-id="${escapeRegExp(nav[i]?.id)}">)<a href="[^"]+">([^<]+)</a>`
827
- ),
828
- "$1<strong>$2</strong>"
829
- )}
830
- </div>
831
- </div>
832
-
833
- ${content.includes("<pre><code ") ? getInjectedContent(ctx, "section", ":has-code", "append") || getDefaultCodeStyleContent(cssRoot.content) : ""}
834
- ${counterContent}
835
- ${getInjectedContent(ctx, "section", "body", "append")}
836
- </body>
837
- </html>
838
- `)
839
- )
840
- ),
841
- writeFile2(
842
- join3(dir, "index.html"),
843
- toFileContent(`
703
+ let cssRoot = await getCSSRoot(ctx, "index");
704
+ return toFileContent(`
844
705
  <!DOCTYPE html>
845
706
  <html lang="en" data-layout="index">
846
707
  <head>
@@ -849,9 +710,9 @@ ${getInjectedContent(ctx, "section", "body", "append")}
849
710
  <meta name="viewport" content="width=device-width, initial-scale=1">
850
711
  <meta name="description" content="${plainTitle}${escapedPackageDescription ? `: ${escapedPackageDescription}` : ""}">
851
712
  <title>${plainTitle}${escapedPackageDescription ? ` | ${escapedPackageDescription}` : ""}</title>
852
- <link rel="stylesheet" href="${cssRoot.index}/base.css">
853
- <link rel="stylesheet" href="${cssRoot.index}/index.css">
854
- ${iconTag}
713
+ <link rel="stylesheet" href="${cssRoot}/base.css">
714
+ <link rel="stylesheet" href="${cssRoot}/index.css">
715
+ ${getIconTag(ctx)}
855
716
  <link rel="prefetch" href="${root}start">
856
717
  ${nav[0] ? `<link rel="prefetch" href="${root}${contentDir}/${nav[0]?.id ?? ""}">` : ""}
857
718
  ${getInjectedContent(ctx, "index", "head", "append")}
@@ -892,16 +753,216 @@ ${intro || features || note ? `
892
753
  </main>
893
754
  </div>
894
755
 
895
- ${[description, intro, features, note].some((s) => s.includes("<pre><code ")) ? getInjectedContent(ctx, "index", ":has-code", "append") || getDefaultCodeStyleContent(cssRoot.index) : ""}
756
+ ${[description, intro, features, note].some((s) => s.includes("<pre><code ")) ? getInjectedContent(ctx, "index", ":has-code", "append") || getDefaultCodeStyleContent(cssRoot) : ""}
896
757
  ${counterContent}
897
758
  ${getInjectedContent(ctx, "index", "body", "append")}
898
759
  </body>
899
760
  </html>
900
- `)
761
+ `);
762
+ }
763
+
764
+ // src/bin/content/getRedirectContent.ts
765
+ function getRedirectContent(ctx) {
766
+ let { redirect } = ctx;
767
+ let escapedRedirect = escapeHTML(redirect);
768
+ return toFileContent(`
769
+ <!DOCTYPE html>
770
+ <html lang="en" data-layout="redirect" class="blank">
771
+ <head>
772
+ ${getInjectedContent(ctx, "redirect", "head", "prepend")}
773
+ <meta charset="utf-8">
774
+ <meta name="viewport" content="width=device-width">
775
+ <meta http-equiv="refresh" content="0; URL=${escapedRedirect}">
776
+ ${getIconTag(ctx)}
777
+ ${getInjectedContent(ctx, "redirect", "head", "append")}
778
+ </head>
779
+ <body>
780
+ ${getInjectedContent(ctx, "redirect", "body", "prepend")}
781
+ ${getCounterContent(ctx)}
782
+ ${getInjectedContent(ctx, "redirect", "body", "append")}
783
+ <script>window.location.replace("${escapedRedirect}");</script>
784
+ </body>
785
+ </html>
786
+ `);
787
+ }
788
+
789
+ // src/utils/escapeRegExp.ts
790
+ function escapeRegExp(s) {
791
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
792
+ }
793
+
794
+ // src/bin/content/getNav.ts
795
+ import { JSDOM as JSDOM3 } from "jsdom";
796
+
797
+ // src/bin/getNpmLink.ts
798
+ function getNpmLink({ npm }, className) {
799
+ if (!npm) return "";
800
+ return `<a href="${npm}"${className ? ` class="${className}"` : ""} target="_blank">npm</a>`;
801
+ }
802
+
803
+ // src/bin/content/getNav.ts
804
+ var cachedNavContent = /* @__PURE__ */ new Map();
805
+ async function getNavContent({ name, nav }) {
806
+ if (!nav) return "";
807
+ let navContent = cachedNavContent.get(nav);
808
+ if (navContent !== void 0) return navContent;
809
+ navContent = await fetchContent(nav);
810
+ if (navContent) {
811
+ let navDom = new JSDOM3(navContent).window.document.body;
812
+ for (let link of navDom.querySelectorAll("a")) {
813
+ if (link.dataset.name === name) {
814
+ let parent = link.parentElement;
815
+ link.remove();
816
+ while (parent && parent.innerHTML.trim() === "") {
817
+ let nextParent = parent.parentElement;
818
+ parent.remove();
819
+ parent = nextParent;
820
+ }
821
+ }
822
+ }
823
+ navContent = navDom.innerHTML;
824
+ }
825
+ cachedNavContent.set(nav, navContent);
826
+ return navContent;
827
+ }
828
+ async function getNav(ctx, navItems) {
829
+ let { name, root, contentDir, backstory } = ctx;
830
+ let navContent = await getNavContent(ctx);
831
+ let s = "";
832
+ if (navContent) {
833
+ let navDom = new JSDOM3(navContent).window.document.body;
834
+ for (let link of navDom.querySelectorAll("a")) {
835
+ if (link.dataset.name === name) {
836
+ let parent = link.parentElement;
837
+ link.remove();
838
+ while (parent && parent.innerHTML.trim() === "") {
839
+ let nextParent = parent.parentElement;
840
+ parent.remove();
841
+ parent = nextParent;
842
+ }
843
+ }
844
+ }
845
+ navContent = navDom.innerHTML;
846
+ }
847
+ let navItemCount = 0;
848
+ if (navItems.length === 1) {
849
+ let { id, items } = navItems[0];
850
+ let itemLink = `${root}${contentDir}/${encodeURIComponent(id)}`;
851
+ for (let { id: id2, title } of items) {
852
+ s += `
853
+ <li><a href="${itemLink}#${encodeURIComponent(id2)}">${title}</a></li>`;
854
+ navItemCount++;
855
+ }
856
+ } else {
857
+ for (let { id, title, items } of navItems) {
858
+ let itemLink = `${root}${contentDir}/${encodeURIComponent(id)}`;
859
+ s += `
860
+ <li data-id="${id}"><a href="${itemLink}">${title}</a>`;
861
+ if (items.length !== 0) {
862
+ s += "\n <ul>";
863
+ for (let { id: id2, title: title2 } of items) {
864
+ s += `
865
+ <li><a href="${itemLink}#${encodeURIComponent(id2)}">${title2}</a></li>`;
866
+ navItemCount++;
867
+ }
868
+ s += "\n </ul>\n";
869
+ }
870
+ s += "</li>";
871
+ navItemCount++;
872
+ }
873
+ }
874
+ if ((!s || navItemCount < 2) && !navContent) return "";
875
+ s = s.trim();
876
+ s = s ? `<p class="title">Contents</p>
877
+ <ul>${s}
878
+ </ul>
879
+ ` : "";
880
+ let repoLink = getRepoLink(ctx);
881
+ let npmLink = getNpmLink(ctx);
882
+ if (repoLink || npmLink || backstory)
883
+ s += `
884
+ <p class="title">Resources</p>
885
+ <ul>
886
+ ${repoLink ? `<li>${repoLink}</li>` : ""}
887
+ ${npmLink ? `<li>${npmLink}</li>` : ""}
888
+ ${backstory ? `<li><a href="${backstory}">Backstory</a></li>` : ""}
889
+ </ul>
890
+ `;
891
+ s = s.trim();
892
+ s = s ? `<section>
893
+ ${s}
894
+ </section>` : "";
895
+ return `<nav class="aux">
896
+ ${s}
897
+ ${navContent}
898
+ </nav>`;
899
+ }
900
+
901
+ // src/bin/content/getSectionContent.ts
902
+ async function getSectionContent(ctx, index) {
903
+ let { root, contentDir = "" } = ctx;
904
+ let cssRoot = await getCSSRoot(ctx, "content");
905
+ let { sections, nav } = await getParsedContent(ctx);
906
+ let content = sections[index];
907
+ let navContent = await getNav(ctx, nav);
908
+ let plainTitle = await getPlainTitle(ctx);
909
+ return toFileContent(`
910
+ <!DOCTYPE html>
911
+ <html lang="en" data-layout="section">
912
+ <head>
913
+ ${getInjectedContent(ctx, "section", "head", "prepend")}
914
+ <meta charset="utf-8">
915
+ <meta name="viewport" content="width=device-width, initial-scale=1">
916
+ <meta name="description" content="${plainTitle}: ${escapeHTML(stripHTML(nav[index]?.title, true))}">
917
+ <title>${escapeHTML(stripHTML(nav[index]?.title, true))} | ${plainTitle}</title>
918
+ <link rel="stylesheet" href="${cssRoot}/base.css">
919
+ <link rel="stylesheet" href="${cssRoot}/section.css">
920
+ ${getIconTag(ctx)}
921
+ ${nav[index + 1]?.id ? `<link rel="prefetch" href="${root}${contentDir}/${nav[index + 1]?.id}">` : ""}
922
+ ${nav[index - 1]?.id ? `<link rel="prefetch" href="${root}${contentDir}/${nav[index - 1]?.id}">` : ""}
923
+ ${getInjectedContent(ctx, "section", "head", "append")}
924
+ </head>
925
+ <body>
926
+ ${getInjectedContent(ctx, "section", "body", "prepend")}
927
+ <div class="layout">
928
+ <div class="${navContent ? "" : "no-nav "}body">
929
+ <main>
930
+ <h1><a href="${root}">${plainTitle}</a></h1>
931
+ ${content}
932
+
933
+ <p class="pagenav">
934
+ <span class="prev">
935
+ <span class="icon">\u2190</span>
936
+ ${nav[index - 1]?.id ? `<a href="${root}${contentDir}/${nav[index - 1]?.id}">${nav[index - 1]?.title}</a>` : `<a href="${root}">Intro</a>`}
937
+ </span>
938
+ <span class="sep">|</span>
939
+ ${nav[index + 1]?.id ? `<span class="next"><a href="${root}${contentDir}/${nav[index + 1]?.id}">${nav[index + 1]?.title}</a> <span class="icon">\u2192</span></span>` : `<span class="repo">${getRepoLink(ctx)}</span>`}
940
+ </p>
941
+ </main>
942
+ ${navContent ? "<hr>" : ""}
943
+ ${navContent.replace(
944
+ new RegExp(
945
+ `(<li data-id="${escapeRegExp(nav[index]?.id)}">)<a href="[^"]+">([^<]+)</a>`
901
946
  ),
902
- writeFile2(
903
- join3(dir, "start.html"),
904
- toFileContent(`
947
+ "$1<strong>$2</strong>"
948
+ )}
949
+ </div>
950
+ </div>
951
+
952
+ ${content.includes("<pre><code ") ? getInjectedContent(ctx, "section", ":has-code", "append") || getDefaultCodeStyleContent(cssRoot) : ""}
953
+ ${getCounterContent(ctx)}
954
+ ${getInjectedContent(ctx, "section", "body", "append")}
955
+ </body>
956
+ </html>
957
+ `);
958
+ }
959
+
960
+ // src/bin/content/getStartContent.ts
961
+ async function getStartContent(ctx) {
962
+ let { root, contentDir = "" } = ctx;
963
+ let { nav } = await getParsedContent(ctx);
964
+ let plainTitle = await getPlainTitle(ctx);
965
+ return toFileContent(`
905
966
  <!DOCTYPE html>
906
967
  <html lang="en" data-layout="start" class="blank">
907
968
  <head>
@@ -910,8 +971,8 @@ ${getInjectedContent(ctx, "index", "body", "append")}
910
971
  <meta name="viewport" content="width=device-width">
911
972
  <meta http-equiv="refresh" content="0; URL=${root}${contentDir}/${nav[0]?.id}">
912
973
  <title>${plainTitle}</title>
913
- <link rel="stylesheet" href="${cssRoot.index}/base.css">
914
- ${iconTag}
974
+ <link rel="stylesheet" href="${await getCSSRoot(ctx, "index")}/base.css">
975
+ ${getIconTag(ctx)}
915
976
  <script>window.location.replace("${root}${contentDir}/${nav[0]?.id}");</script>
916
977
  ${getInjectedContent(ctx, "start", "head", "append")}
917
978
  </head>
@@ -921,18 +982,37 @@ ${getInjectedContent(ctx, "start", "body", "prepend")}
921
982
  <h1>${plainTitle}</h1>
922
983
  </div>
923
984
 
924
- ${counterContent}
985
+ ${getCounterContent(ctx)}
925
986
  ${getInjectedContent(ctx, "start", "body", "append")}
926
987
  </body>
927
988
  </html>
928
- `)
929
- )
989
+ `);
990
+ }
991
+
992
+ // src/bin/setContent.ts
993
+ async function setContent(ctx) {
994
+ let { dir = "", contentDir = "", redirect } = ctx;
995
+ if (redirect) {
996
+ await writeFile2(join5(dir, "index.html"), getRedirectContent(ctx));
997
+ return;
998
+ }
999
+ let { sections, nav } = await getParsedContent(ctx);
1000
+ await createDirs(ctx);
1001
+ await Promise.all([
1002
+ ...sections.map(
1003
+ async (_, i) => writeFile2(
1004
+ join5(dir, contentDir, `${nav[i]?.id ?? `_Section_${i + 1}`}.html`),
1005
+ await getSectionContent(ctx, i)
1006
+ )
1007
+ ),
1008
+ writeFile2(join5(dir, "index.html"), await getIndexContent(ctx)),
1009
+ writeFile2(join5(dir, "start.html"), await getStartContent(ctx))
930
1010
  ]);
931
1011
  }
932
1012
 
933
1013
  // src/bin/setImages.ts
934
1014
  import { writeFile as writeFile3 } from "node:fs/promises";
935
- import { join as join4 } from "node:path";
1015
+ import { join as join6 } from "node:path";
936
1016
 
937
1017
  // src/utils/getIconContent.ts
938
1018
  function getIconContent(baseColor = "gray") {
@@ -952,7 +1032,7 @@ function getIconContent(baseColor = "gray") {
952
1032
  // src/bin/setImages.ts
953
1033
  async function setImages({ dir = "", favicon }) {
954
1034
  if (favicon) return;
955
- await writeFile3(join4(dir, "./favicon.svg"), `${getIconContent()}
1035
+ await writeFile3(join6(dir, "./favicon.svg"), `${getIconContent()}
956
1036
  `);
957
1037
  }
958
1038