@t8/docsgen 0.3.35 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -319,6 +319,105 @@ function escapeHTML(x) {
319
319
  return s;
320
320
  }
321
321
 
322
+ // src/bin/content/getCounterContent.ts
323
+ function getCounterContent({ ymid }) {
324
+ if (!ymid) return "";
325
+ return `
326
+ <script>
327
+ (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
328
+ m[i].l=1*new Date();
329
+ for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
330
+ k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
331
+ (window, document, "script", "https://mc.yandex.com/metrika/tag.js", "ym");
332
+ ym(${ymid}, "init", {clickmap: true, trackLinks: true, accurateTrackBounce: true});
333
+ </script>
334
+ <noscript><div><img src="https://mc.yandex.com/watch/${ymid}" style="position:absolute;left:-9999px;" alt=""></div></noscript>
335
+ `.trim();
336
+ }
337
+
338
+ // src/bin/content/getIcon.ts
339
+ var iconTypeMap = {
340
+ ico: "image/x-icon",
341
+ svg: "image/svg+xml",
342
+ png: "image/png",
343
+ gif: "image/gif",
344
+ jpg: "image/jpeg",
345
+ jpeg: "image/jpeg"
346
+ };
347
+ function getIcon({ favicon, faviconType }) {
348
+ let icon = {
349
+ url: favicon,
350
+ type: faviconType
351
+ };
352
+ if (icon.url && !icon.type) {
353
+ let ext = icon.url.split("/").at(-1)?.split(".").at(-1)?.toLowerCase();
354
+ if (ext) icon.type = iconTypeMap[ext];
355
+ }
356
+ return icon;
357
+ }
358
+
359
+ // src/bin/content/getIconTag.ts
360
+ function getIconTag(ctx) {
361
+ let { url, type } = getIcon(ctx);
362
+ return url ? `<link rel="icon"${type ? ` type="${type}"` : ""} href="${url}">` : "";
363
+ }
364
+
365
+ // src/bin/content/getInjectedContent.ts
366
+ function getInjectedContent(ctx, page, target, mode) {
367
+ let injectedContent = ctx[mode]?.[target];
368
+ if (!injectedContent) return "";
369
+ return (Array.isArray(injectedContent) ? injectedContent : [injectedContent]).reduce((s, item) => {
370
+ if (item === void 0) return s;
371
+ if (typeof item === "string") return `${s}${s ? "\n" : ""}${item}`;
372
+ let { content, pages } = item;
373
+ if (!content || pages && !pages.includes(page)) return s;
374
+ return `${s}${s ? "\n" : ""}${content}`;
375
+ }, "");
376
+ }
377
+
378
+ // src/bin/content/toFileContent.ts
379
+ function toFileContent(x) {
380
+ let s = x.replace(
381
+ /\s+(\r?\n *<\/?(script|link|head|body|html|meta|title)[> ])/g,
382
+ "$1"
383
+ ).replace(
384
+ /\s+(\r?\n *<\/?(main|header|footer|nav|section|div|h\d|p|ul|ol|li|table)[> ])/g,
385
+ "$1"
386
+ ).trim();
387
+ return `${s}
388
+ `;
389
+ }
390
+
391
+ // src/bin/content/getRedirectContent.ts
392
+ function getRedirectContent(ctx) {
393
+ let { redirect } = ctx;
394
+ let escapedRedirect = escapeHTML(redirect);
395
+ return toFileContent(`
396
+ <!DOCTYPE html>
397
+ <html lang="en" data-layout="redirect" class="blank">
398
+ <head>
399
+ ${getInjectedContent(ctx, "redirect", "head", "prepend")}
400
+ <meta charset="utf-8">
401
+ <meta name="viewport" content="width=device-width">
402
+ <meta http-equiv="refresh" content="0; URL=${escapedRedirect}">
403
+ ${getIconTag(ctx)}
404
+ ${getInjectedContent(ctx, "redirect", "head", "append")}
405
+ </head>
406
+ <body>
407
+ ${getInjectedContent(ctx, "redirect", "body", "prepend")}
408
+ ${getCounterContent(ctx)}
409
+ ${getInjectedContent(ctx, "redirect", "body", "append")}
410
+ <script>window.location.replace("${escapedRedirect}");</script>
411
+ </body>
412
+ </html>
413
+ `);
414
+ }
415
+
416
+ // src/utils/escapeRegExp.ts
417
+ function escapeRegExp(s) {
418
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
419
+ }
420
+
322
421
  // src/bin/getRepoLink.ts
323
422
  function getRepoLink({ repo }, className) {
324
423
  if (!repo) return "";
@@ -547,22 +646,6 @@ async function getParsedContent(ctx) {
547
646
  return parsedContent;
548
647
  }
549
648
 
550
- // src/bin/content/getCounterContent.ts
551
- function getCounterContent({ ymid }) {
552
- if (!ymid) return "";
553
- return `
554
- <script>
555
- (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
556
- m[i].l=1*new Date();
557
- for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
558
- k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
559
- (window, document, "script", "https://mc.yandex.com/metrika/tag.js", "ym");
560
- ym(${ymid}, "init", {clickmap: true, trackLinks: true, accurateTrackBounce: true});
561
- </script>
562
- <noscript><div><img src="https://mc.yandex.com/watch/${ymid}" style="position:absolute;left:-9999px;" alt=""></div></noscript>
563
- `.trim();
564
- }
565
-
566
649
  // src/bin/content/getCSSRoot.ts
567
650
  import { exec as defaultExec } from "node:child_process";
568
651
  import { cp } from "node:fs/promises";
@@ -578,6 +661,8 @@ var exec = promisify(defaultExec);
578
661
  var __filename = fileURLToPath(import.meta.url);
579
662
  var __dirname = dirname(__filename);
580
663
  var packageURL = "";
664
+ var packageVersionRequest = null;
665
+ var assetsCreated = /* @__PURE__ */ new Map();
581
666
  async function getCSSRoot(ctx, type) {
582
667
  let { dir = "", assetsDir } = ctx;
583
668
  let cssRoot = {
@@ -587,13 +672,18 @@ async function getCSSRoot(ctx, type) {
587
672
  if (assetsDir) {
588
673
  cssRoot.index = assetsDir;
589
674
  cssRoot.content = `../${assetsDir}`;
590
- await cp(join4(__dirname, "css"), join4(dir, cssRoot.index), {
591
- force: true,
592
- recursive: true
593
- });
675
+ let assetsDestination = join4(dir, cssRoot.index);
676
+ if (!assetsCreated.get(assetsDestination)) {
677
+ assetsCreated.set(assetsDestination, true);
678
+ await cp(join4(__dirname, "css"), assetsDestination, {
679
+ force: true,
680
+ recursive: true
681
+ });
682
+ }
594
683
  } else {
595
684
  if (!packageURL) {
596
- let packageVersion = (await exec(`npm view ${packageName} version`)).stdout.trim().split(".").slice(0, 2).join(".");
685
+ packageVersionRequest ??= exec(`npm view ${packageName} version`);
686
+ let packageVersion = (await packageVersionRequest).stdout.trim().split(".").slice(0, 2).join(".");
597
687
  packageURL = `https://unpkg.com/${packageName}@${packageVersion}`;
598
688
  }
599
689
  cssRoot.index = `${packageURL}/dist/css`;
@@ -613,44 +703,13 @@ function getDefaultCodeStyleContent(cssRoot) {
613
703
  `.trim();
614
704
  }
615
705
 
616
- // src/bin/content/getIcon.ts
617
- var iconTypeMap = {
618
- ico: "image/x-icon",
619
- svg: "image/svg+xml",
620
- png: "image/png",
621
- gif: "image/gif",
622
- jpg: "image/jpeg",
623
- jpeg: "image/jpeg"
624
- };
625
- function getIcon({ favicon, faviconType }) {
626
- let icon = {
627
- url: favicon,
628
- type: faviconType
629
- };
630
- if (icon.url && !icon.type) {
631
- let ext = icon.url.split("/").at(-1)?.split(".").at(-1)?.toLowerCase();
632
- if (ext) icon.type = iconTypeMap[ext];
633
- }
634
- return icon;
635
- }
636
-
637
- // src/bin/content/getIconTag.ts
638
- function getIconTag(ctx) {
639
- let { url, type } = getIcon(ctx);
640
- return url ? `<link rel="icon"${type ? ` type="${type}"` : ""} href="${url}">` : "";
641
- }
642
-
643
- // src/bin/content/getInjectedContent.ts
644
- function getInjectedContent(ctx, page, target, mode) {
645
- let injectedContent = ctx[mode]?.[target];
646
- if (!injectedContent) return "";
647
- return (Array.isArray(injectedContent) ? injectedContent : [injectedContent]).reduce((s, item) => {
648
- if (item === void 0) return s;
649
- if (typeof item === "string") return `${s}${s ? "\n" : ""}${item}`;
650
- let { content, pages } = item;
651
- if (!content || pages && !pages.includes(page)) return s;
652
- return `${s}${s ? "\n" : ""}${content}`;
653
- }, "");
706
+ // src/bin/content/getInstallationContent.ts
707
+ async function getInstallationContent(ctx) {
708
+ let { name, isDevDep } = ctx;
709
+ let { installation } = await getParsedContent(ctx);
710
+ if (!installation || isDevDep !== void 0)
711
+ installation = `npm i${isDevDep ? " -D" : ""} ${name}`;
712
+ return `<code>${escapeHTML(installation)}</code>`;
654
713
  }
655
714
 
656
715
  // src/bin/content/getPlainTitle.ts
@@ -660,140 +719,12 @@ async function getPlainTitle(ctx) {
660
719
  return escapeHTML(title || stripHTML(htmlTitle || parsedTitle, true) || name);
661
720
  }
662
721
 
663
- // src/bin/content/toFileContent.ts
664
- function toFileContent(x) {
665
- let s = x.replace(
666
- /\s+(\r?\n *<\/?(script|link|head|body|html|meta|title)[> ])/g,
667
- "$1"
668
- ).replace(
669
- /\s+(\r?\n *<\/?(main|header|footer|nav|section|div|h\d|p|ul|ol|li|table)[> ])/g,
670
- "$1"
671
- ).trim();
672
- return `${s}
673
- `;
674
- }
675
-
676
- // src/bin/content/tweakTypography.ts
677
- function tweakTypography(s = "") {
678
- 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");
679
- }
680
-
681
- // src/bin/content/getIndexContent.ts
682
- async function getIndexContent(ctx) {
683
- let {
684
- root,
685
- contentDir = "",
686
- name,
687
- htmlTitle,
688
- description: packageDescription,
689
- isDevDep,
690
- backstory
691
- } = ctx;
692
- let counterContent = getCounterContent(ctx);
693
- let escapedPackageDescription = escapeHTML(packageDescription);
694
- let {
695
- title: parsedTitle,
696
- description,
697
- intro,
698
- features,
699
- note,
700
- installation,
701
- nav
702
- } = await getParsedContent(ctx);
722
+ // src/bin/content/getMainTitle.ts
723
+ async function getMainTitle(ctx) {
724
+ let { htmlTitle } = ctx;
725
+ let { title: parsedTitle } = await getParsedContent(ctx);
703
726
  let plainTitle = await getPlainTitle(ctx);
704
- let coverTitle = htmlTitle || parsedTitle || plainTitle;
705
- let descriptionContent = tweakTypography(description) || (escapedPackageDescription ? `<p>${tweakTypography(escapedPackageDescription)}<p>` : "");
706
- if (!installation || isDevDep !== void 0)
707
- installation = `npm i${isDevDep ? " -D" : ""} ${name}`;
708
- let cssRoot = await getCSSRoot(ctx, "index");
709
- return toFileContent(`
710
- <!DOCTYPE html>
711
- <html lang="en" data-layout="index">
712
- <head>
713
- ${getInjectedContent(ctx, "index", "head", "prepend")}
714
- <meta charset="utf-8">
715
- <meta name="viewport" content="width=device-width, initial-scale=1">
716
- <meta name="description" content="${plainTitle}${escapedPackageDescription ? `: ${escapedPackageDescription}` : ""}">
717
- <title>${plainTitle}${escapedPackageDescription ? ` | ${escapedPackageDescription}` : ""}</title>
718
- <link rel="stylesheet" href="${cssRoot}/base.css">
719
- <link rel="stylesheet" href="${cssRoot}/index.css">
720
- ${getIconTag(ctx)}
721
- <link rel="prefetch" href="${root}start">
722
- ${nav[0] ? `<link rel="prefetch" href="${root}${contentDir}/${nav[0]?.id ?? ""}">` : ""}
723
- ${getInjectedContent(ctx, "index", "head", "append")}
724
- </head>
725
- <body>
726
- ${getInjectedContent(ctx, "index", "body", "prepend")}
727
- <div class="layout">
728
- <main>
729
- <section class="aux intro-title">
730
- <div class="section-content">
731
- ${getInjectedContent(ctx, "index", "cover", "prepend")}
732
- <h1>${coverTitle}</h1>
733
- <div class="description">
734
- ${descriptionContent}
735
- </div>
736
- <p class="actions">
737
- <a href="${root}start" class="primary">Docs</a>
738
- <span class="sep"> \u2022 </span>
739
- ${getRepoLink(ctx)}
740
- </p>
741
- ${backstory ? `<p class="ref"><a href="${backstory}">Backstory</a></p>` : ""}
742
- <p class="installation"><code>${escapeHTML(installation)}</code></p>
743
- ${getInjectedContent(ctx, "index", "cover", "append")}
744
- </div>
745
- </section>
746
- ${intro || features || note ? `
747
- <section class="intro">
748
- <div class="section-content">
749
- ${intro ? `<div class="intro">${intro}</div>` : ""}
750
- ${features ? `<div class="features">${features}</div>` : ""}
751
- ${note ? `<div class="note">${note}</div>` : ""}
752
- <p class="pagenav">
753
- <span class="next"><a href="${root}start">To the docs</a> <span class="icon">\u2192</span></span>
754
- </p>
755
- </div>
756
- </section>
757
- ` : ""}
758
- </main>
759
- </div>
760
-
761
- ${[description, intro, features, note].some((s) => s.includes("<pre><code ")) ? getInjectedContent(ctx, "index", ":has-code", "append") || getDefaultCodeStyleContent(cssRoot) : ""}
762
- ${counterContent}
763
- ${getInjectedContent(ctx, "index", "body", "append")}
764
- </body>
765
- </html>
766
- `);
767
- }
768
-
769
- // src/bin/content/getRedirectContent.ts
770
- function getRedirectContent(ctx) {
771
- let { redirect } = ctx;
772
- let escapedRedirect = escapeHTML(redirect);
773
- return toFileContent(`
774
- <!DOCTYPE html>
775
- <html lang="en" data-layout="redirect" class="blank">
776
- <head>
777
- ${getInjectedContent(ctx, "redirect", "head", "prepend")}
778
- <meta charset="utf-8">
779
- <meta name="viewport" content="width=device-width">
780
- <meta http-equiv="refresh" content="0; URL=${escapedRedirect}">
781
- ${getIconTag(ctx)}
782
- ${getInjectedContent(ctx, "redirect", "head", "append")}
783
- </head>
784
- <body>
785
- ${getInjectedContent(ctx, "redirect", "body", "prepend")}
786
- ${getCounterContent(ctx)}
787
- ${getInjectedContent(ctx, "redirect", "body", "append")}
788
- <script>window.location.replace("${escapedRedirect}");</script>
789
- </body>
790
- </html>
791
- `);
792
- }
793
-
794
- // src/utils/escapeRegExp.ts
795
- function escapeRegExp(s) {
796
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
727
+ return htmlTitle || parsedTitle || plainTitle;
797
728
  }
798
729
 
799
730
  // src/bin/content/getNav.ts
@@ -873,7 +804,7 @@ async function getNav(ctx, navItems) {
873
804
  s = s ? `<section>
874
805
  ${s}
875
806
  </section>` : "";
876
- return `<nav class="aux">
807
+ return `<nav>
877
808
  ${s}
878
809
  ${navContent}
879
810
  </nav>`;
@@ -882,10 +813,12 @@ ${navContent}
882
813
  // src/bin/content/getSectionContent.ts
883
814
  async function getSectionContent(ctx, index) {
884
815
  let { root, contentDir = "" } = ctx;
816
+ let escapedPackageDescription = escapeHTML(ctx.description);
885
817
  let cssRoot = await getCSSRoot(ctx, "content");
886
818
  let { sections, nav } = await getParsedContent(ctx);
887
819
  let content = sections[index];
888
820
  let navContent = await getNav(ctx, nav);
821
+ let mainTitle = await getMainTitle(ctx);
889
822
  let plainTitle = await getPlainTitle(ctx);
890
823
  return toFileContent(`
891
824
  <!DOCTYPE html>
@@ -906,9 +839,14 @@ async function getSectionContent(ctx, index) {
906
839
  <body>
907
840
  ${getInjectedContent(ctx, "section", "body", "prepend")}
908
841
  <div class="layout">
842
+ <header class="aux">
843
+ <h1>${mainTitle}</h1>
844
+ <div class="description">
845
+ <p>${escapedPackageDescription}</p>
846
+ </div>
847
+ </header>
909
848
  <div class="${navContent ? "" : "no-nav "}body">
910
849
  <main>
911
- <h1><a href="${root}">${plainTitle}</a></h1>
912
850
  ${content}
913
851
 
914
852
  <p class="pagenav">
@@ -920,13 +858,22 @@ ${content}
920
858
  ${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>`}
921
859
  </p>
922
860
  </main>
923
- ${navContent ? "<hr>" : ""}
861
+ <hr>
862
+ <aside class="aux">
863
+ <div class="header" hidden>
864
+ <h1>${mainTitle}</h1>
865
+ <div class="description">
866
+ <p>${escapedPackageDescription}</p>
867
+ <p class="installation">${await getInstallationContent(ctx)}</p>
868
+ </div>
869
+ </div>
924
870
  ${navContent.replace(
925
871
  new RegExp(
926
872
  `(<li data-id="${escapeRegExp(nav[index]?.id)}">)<a href="[^"]+">([^<]+)</a>`
927
873
  ),
928
874
  "$1<strong>$2</strong>"
929
875
  )}
876
+ </aside>
930
877
  </div>
931
878
  </div>
932
879
 
@@ -986,7 +933,8 @@ async function setContent(ctx) {
986
933
  await getSectionContent(ctx, i)
987
934
  )
988
935
  ),
989
- writeFile2(join5(dir, "index.html"), await getIndexContent(ctx)),
936
+ // writeFile(join(dir, "index.html"), await getIndexContent(ctx)),
937
+ writeFile2(join5(dir, "index.html"), await getStartContent(ctx)),
990
938
  writeFile2(join5(dir, "start.html"), await getStartContent(ctx))
991
939
  ]);
992
940
  }
package/dist/css/base.css CHANGED
@@ -41,9 +41,9 @@
41
41
  }
42
42
  }
43
43
  :root {
44
- --max-content-width: 45em;
44
+ --max-content-width: 720px;
45
45
  --content-padding-x: 1.75rem;
46
- --content-padding-y: 1.75rem;
46
+ --content-padding-y: 1rem;
47
47
  --block-margin-y: 1em;
48
48
  --sep-line: 0.3rem solid var(--line-color);
49
49
 
@@ -70,7 +70,7 @@ body {
70
70
  :root {
71
71
  --max-content-width: 100%;
72
72
  --content-padding-x: 0.85rem;
73
- --content-padding-y: 1rem;
73
+ --content-padding-y: 0.65rem;
74
74
  }
75
75
  body {
76
76
  font-size: 100%;
@@ -8,6 +8,25 @@ footer {
8
8
  padding: 0 var(--content-padding-x);
9
9
  }
10
10
 
11
+ header {
12
+ display: none;
13
+ text-align: center;
14
+ padding-top: 0.5em;
15
+ padding-bottom: 0.5em;
16
+ }
17
+ header h1 {
18
+ font-size: 1.25em;
19
+ font-weight: 900;
20
+ margin: 0;
21
+ }
22
+ header .description {
23
+ font-size: 0.8em;
24
+ margin-top: 0.15em;
25
+ }
26
+ header .description p {
27
+ margin: 0;
28
+ }
29
+
11
30
  .body {
12
31
  min-height: 100vh;
13
32
  display: flex;
@@ -20,19 +39,22 @@ footer {
20
39
  display: none;
21
40
  }
22
41
  @media (max-width: 840px) {
42
+ header {
43
+ display: block;
44
+ }
23
45
  .body {
24
46
  flex-direction: column;
25
47
  padding: 0;
26
48
  }
27
49
  }
28
- .body > nav {
50
+ aside {
29
51
  width: 25%;
30
52
  display: flex;
31
53
  flex-direction: column;
32
54
  align-items: flex-end;
33
55
  flex: none;
34
56
  align-self: stretch;
35
- font-size: 0.9em;
57
+ font-size: 0.87em;
36
58
  line-height: 1.25;
37
59
  text-align: end;
38
60
  background: linear-gradient(
@@ -41,65 +63,89 @@ footer {
41
63
  var(--aux-background-end)
42
64
  );
43
65
  border-right: 0.05em solid var(--aux-border-color);
44
- padding: 0.75rem var(--content-padding-x);
66
+ padding: var(--content-padding-y) var(--content-padding-x);
45
67
  margin: 0;
46
68
  box-sizing: border-box;
47
69
  }
48
- .body > nav section {
70
+ aside > .header {
71
+ display: block;
72
+ margin: 0.5rem 0 0.25rem;
73
+ }
74
+ aside > .header .description p:not(.installation) {
75
+ font-size: 0.9em;
76
+ margin: 0.35em 0 0;
77
+ }
78
+ aside > .header .description p.installation {
79
+ margin: 1em 0 0;
80
+ }
81
+ aside > .header .installation code {
82
+ display: inline-block;
83
+ background: transparent;
84
+ border: 0.05em solid;
85
+ border-radius: 0.5em;
86
+ padding: 0.35em 0.75em;
87
+ margin: 0;
88
+ }
89
+ aside h1 {
90
+ font-size: 1.35em;
91
+ font-weight: 900;
92
+ margin: 0;
93
+ }
94
+ aside section {
49
95
  width: 100%;
50
96
  max-width: 16em;
51
97
  padding: 1rem 0;
52
98
  box-sizing: border-box;
53
99
  }
54
- .body > nav section + section {
100
+ aside section + section {
55
101
  border-top: 0.05rem solid;
56
102
  }
57
- .body > nav h2,
58
- .body > nav p.title {
103
+ aside h2,
104
+ aside p.title {
59
105
  font-size: 0.7rem;
60
106
  font-weight: normal;
61
107
  text-transform: uppercase;
62
108
  margin: 1.1rem 0 0.75rem 0;
63
109
  }
64
- .body > nav h2:first-of-type,
65
- .body > nav p.title:first-of-type {
110
+ aside h2:first-of-type,
111
+ aside p.title:first-of-type {
66
112
  margin-top: 0;
67
113
  }
68
- .body > nav ul {
114
+ aside ul {
69
115
  list-style: none;
70
116
  padding: 0;
71
117
  margin: 0;
72
118
  box-sizing: border-box;
73
119
  }
74
- .body > nav > ul + ul {
120
+ aside > ul + ul {
75
121
  border-top: 0.05rem solid;
76
122
  padding-top: 1.2em;
77
123
  margin-top: 1.5em;
78
124
  }
79
- .body > nav ul ul {
125
+ aside ul ul {
80
126
  margin: 0.65em 0 0.5em;
81
127
  }
82
- .body > nav li {
128
+ aside li {
83
129
  text-align: end;
84
130
  padding-inline-end: 1em;
85
131
  margin-top: 0;
86
132
  margin-bottom: 0;
87
133
  position: relative;
88
134
  }
89
- .body > nav li::before {
135
+ aside li::before {
90
136
  content: "•";
91
137
  position: absolute;
92
138
  right: 0;
93
139
  top: 0;
94
140
  }
95
- .body > nav li + li {
141
+ aside li + li {
96
142
  margin-top: 0.55em;
97
143
  }
98
- .body > nav li > strong {
144
+ aside li > strong {
99
145
  color: var(--emphatic-color);
100
146
  }
101
147
  @media (max-width: 840px) {
102
- .body > nav {
148
+ aside {
103
149
  width: 100%;
104
150
  display: block;
105
151
  font-size: inherit;
@@ -114,30 +160,33 @@ footer {
114
160
  padding: 0.25rem var(--content-padding-x) 0.35rem;
115
161
  margin: 0;
116
162
  }
117
- .body > nav section {
163
+ aside > .header {
164
+ display: none;
165
+ }
166
+ aside section {
118
167
  max-width: 100%;
119
168
  }
120
- .body > nav li {
169
+ aside li {
121
170
  text-align: start;
122
171
  padding-inline-start: 1em;
123
172
  padding-inline-end: 0;
124
173
  }
125
- .body > nav li::before {
174
+ aside li::before {
126
175
  left: 0;
127
176
  right: auto;
128
177
  }
129
178
  }
130
- .body > nav > ul:last-child,
131
- .body > nav > ul:last-child > li:last-child {
179
+ aside > ul:last-child,
180
+ aside > ul:last-child > li:last-child {
132
181
  margin-bottom: 0;
133
182
  }
134
- .body > nav a.active {
183
+ aside a.active {
135
184
  font-weight: bold;
136
185
  text-decoration: none;
137
186
  color: inherit;
138
187
  pointer-events: none;
139
188
  }
140
- .body > nav a.anchorjs-link {
189
+ aside a.anchorjs-link {
141
190
  display: none;
142
191
  }
143
192
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t8/docsgen",
3
- "version": "0.3.35",
3
+ "version": "0.4.0",
4
4
  "description": "",
5
5
  "main": "dist/bin.js",
6
6
  "type": "module",
@@ -12,6 +12,9 @@ const __filename = fileURLToPath(import.meta.url);
12
12
  const __dirname = dirname(__filename);
13
13
 
14
14
  let packageURL = "";
15
+ let packageVersionRequest: Promise<{ stdout: string }> | null = null;
16
+
17
+ let assetsCreated = new Map<string, boolean>();
15
18
 
16
19
  export async function getCSSRoot(ctx: Context, type: "index" | "content") {
17
20
  let { dir = "", assetsDir } = ctx;
@@ -25,15 +28,21 @@ export async function getCSSRoot(ctx: Context, type: "index" | "content") {
25
28
  cssRoot.index = assetsDir;
26
29
  cssRoot.content = `../${assetsDir}`;
27
30
 
28
- await cp(join(__dirname, "css"), join(dir, cssRoot.index), {
29
- force: true,
30
- recursive: true,
31
- });
31
+ let assetsDestination = join(dir, cssRoot.index);
32
+
33
+ if (!assetsCreated.get(assetsDestination)) {
34
+ assetsCreated.set(assetsDestination, true);
35
+
36
+ await cp(join(__dirname, "css"), assetsDestination, {
37
+ force: true,
38
+ recursive: true,
39
+ });
40
+ }
32
41
  } else {
33
42
  if (!packageURL) {
34
- let packageVersion = (
35
- await exec(`npm view ${packageName} version`)
36
- ).stdout
43
+ packageVersionRequest ??= exec(`npm view ${packageName} version`);
44
+
45
+ let packageVersion = (await packageVersionRequest).stdout
37
46
  .trim()
38
47
  .split(".")
39
48
  .slice(0, 2)
@@ -0,0 +1,16 @@
1
+ import type { Context } from "../../types/Context.ts";
2
+ import { escapeHTML } from "../../utils/escapeHTML.ts";
3
+ import { getParsedContent } from "../parsing/getParsedContent.ts";
4
+ import { tweakTypography } from "./tweakTypography.ts";
5
+
6
+ export async function getDescriptionContent(ctx: Context) {
7
+ let escapedPackageDescription = escapeHTML(ctx.description);
8
+ let { description } = await getParsedContent(ctx);
9
+
10
+ return (
11
+ tweakTypography(description) ||
12
+ (escapedPackageDescription
13
+ ? `<p>${tweakTypography(escapedPackageDescription)}<p>`
14
+ : "")
15
+ );
16
+ }
@@ -5,48 +5,28 @@ import { getParsedContent } from "../parsing/getParsedContent.ts";
5
5
  import { getCounterContent } from "./getCounterContent.ts";
6
6
  import { getCSSRoot } from "./getCSSRoot.ts";
7
7
  import { getDefaultCodeStyleContent } from "./getDefaultCodeStyleContent.ts";
8
+ import { getDescriptionContent } from "./getDescriptionContent.ts";
8
9
  import { getIconTag } from "./getIconTag.ts";
9
10
  import { getInjectedContent } from "./getInjectedContent.ts";
11
+ import { getInstallationContent } from "./getInstallationContent.ts";
12
+ import { getMainTitle } from "./getMainTitle.ts";
10
13
  import { getPlainTitle } from "./getPlainTitle.ts";
11
14
  import { toFileContent } from "./toFileContent.ts";
12
- import { tweakTypography } from "./tweakTypography.ts";
13
15
 
14
16
  export async function getIndexContent(ctx: Context) {
15
17
  let {
16
18
  root,
17
19
  contentDir = "",
18
- name,
19
- htmlTitle,
20
20
  description: packageDescription,
21
- isDevDep,
22
21
  backstory,
23
22
  } = ctx;
24
23
 
25
24
  let counterContent = getCounterContent(ctx);
26
25
  let escapedPackageDescription = escapeHTML(packageDescription);
27
26
 
28
- let {
29
- title: parsedTitle,
30
- description,
31
- intro,
32
- features,
33
- note,
34
- installation,
35
- nav,
36
- } = await getParsedContent(ctx);
27
+ let { description, intro, features, note, nav } = await getParsedContent(ctx);
37
28
 
38
29
  let plainTitle = await getPlainTitle(ctx);
39
- let coverTitle = htmlTitle || parsedTitle || plainTitle;
40
-
41
- let descriptionContent =
42
- tweakTypography(description) ||
43
- (escapedPackageDescription
44
- ? `<p>${tweakTypography(escapedPackageDescription)}<p>`
45
- : "");
46
-
47
- if (!installation || isDevDep !== undefined)
48
- installation = `npm i${isDevDep ? " -D" : ""} ${name}`;
49
-
50
30
  let cssRoot = await getCSSRoot(ctx, "index");
51
31
 
52
32
  return toFileContent(`
@@ -72,9 +52,9 @@ ${getInjectedContent(ctx, "index", "body", "prepend")}
72
52
  <section class="aux intro-title">
73
53
  <div class="section-content">
74
54
  ${getInjectedContent(ctx, "index", "cover", "prepend")}
75
- <h1>${coverTitle}</h1>
55
+ <h1>${await getMainTitle(ctx)}</h1>
76
56
  <div class="description">
77
- ${descriptionContent}
57
+ ${await getDescriptionContent(ctx)}
78
58
  </div>
79
59
  <p class="actions">
80
60
  <a href="${root}start" class="primary">Docs</a>
@@ -82,7 +62,7 @@ ${getInjectedContent(ctx, "index", "body", "prepend")}
82
62
  ${getRepoLink(ctx)}
83
63
  </p>
84
64
  ${backstory ? `<p class="ref"><a href="${backstory}">Backstory</a></p>` : ""}
85
- <p class="installation"><code>${escapeHTML(installation)}</code></p>
65
+ <p class="installation">${await getInstallationContent(ctx)}</p>
86
66
  ${getInjectedContent(ctx, "index", "cover", "append")}
87
67
  </div>
88
68
  </section>
@@ -0,0 +1,13 @@
1
+ import type { Context } from "../../types/Context.ts";
2
+ import { escapeHTML } from "../../utils/escapeHTML.ts";
3
+ import { getParsedContent } from "../parsing/getParsedContent.ts";
4
+
5
+ export async function getInstallationContent(ctx: Context) {
6
+ let { name, isDevDep } = ctx;
7
+ let { installation } = await getParsedContent(ctx);
8
+
9
+ if (!installation || isDevDep !== undefined)
10
+ installation = `npm i${isDevDep ? " -D" : ""} ${name}`;
11
+
12
+ return `<code>${escapeHTML(installation)}</code>`;
13
+ }
@@ -0,0 +1,12 @@
1
+ import type { Context } from "../../types/Context.ts";
2
+ import { getParsedContent } from "../parsing/getParsedContent.ts";
3
+ import { getPlainTitle } from "./getPlainTitle.ts";
4
+
5
+ export async function getMainTitle(ctx: Context) {
6
+ let { htmlTitle } = ctx;
7
+ let { title: parsedTitle } = await getParsedContent(ctx);
8
+
9
+ let plainTitle = await getPlainTitle(ctx);
10
+
11
+ return htmlTitle || parsedTitle || plainTitle;
12
+ }
@@ -84,5 +84,5 @@ export async function getNav(ctx: Context, navItems: NavItem[]) {
84
84
  s = s.trim();
85
85
  s = s ? `<section>\n${s}\n</section>` : "";
86
86
 
87
- return `<nav class="aux">\n${s}\n${navContent}\n</nav>`;
87
+ return `<nav>\n${s}\n${navContent}\n</nav>`;
88
88
  }
@@ -9,18 +9,23 @@ import { getCSSRoot } from "./getCSSRoot.ts";
9
9
  import { getDefaultCodeStyleContent } from "./getDefaultCodeStyleContent.ts";
10
10
  import { getIconTag } from "./getIconTag.ts";
11
11
  import { getInjectedContent } from "./getInjectedContent.ts";
12
+ import { getInstallationContent } from "./getInstallationContent.ts";
13
+ import { getMainTitle } from "./getMainTitle.ts";
12
14
  import { getNav } from "./getNav.ts";
13
15
  import { getPlainTitle } from "./getPlainTitle.ts";
14
16
  import { toFileContent } from "./toFileContent.ts";
15
17
 
16
18
  export async function getSectionContent(ctx: Context, index: number) {
17
19
  let { root, contentDir = "" } = ctx;
20
+ let escapedPackageDescription = escapeHTML(ctx.description);
18
21
 
19
22
  let cssRoot = await getCSSRoot(ctx, "content");
20
23
  let { sections, nav } = await getParsedContent(ctx);
21
24
 
22
25
  let content = sections[index];
23
26
  let navContent = await getNav(ctx, nav);
27
+
28
+ let mainTitle = await getMainTitle(ctx);
24
29
  let plainTitle = await getPlainTitle(ctx);
25
30
 
26
31
  return toFileContent(`
@@ -42,9 +47,14 @@ export async function getSectionContent(ctx: Context, index: number) {
42
47
  <body>
43
48
  ${getInjectedContent(ctx, "section", "body", "prepend")}
44
49
  <div class="layout">
50
+ <header class="aux">
51
+ <h1>${mainTitle}</h1>
52
+ <div class="description">
53
+ <p>${escapedPackageDescription}</p>
54
+ </div>
55
+ </header>
45
56
  <div class="${navContent ? "" : "no-nav "}body">
46
57
  <main>
47
- <h1><a href="${root}">${plainTitle}</a></h1>
48
58
  ${content}
49
59
 
50
60
  <p class="pagenav">
@@ -56,13 +66,22 @@ ${content}
56
66
  ${nav[index + 1]?.id ? `<span class="next"><a href="${root}${contentDir}/${nav[index + 1]?.id}">${nav[index + 1]?.title}</a> <span class="icon">→</span></span>` : `<span class="repo">${getRepoLink(ctx)}</span>`}
57
67
  </p>
58
68
  </main>
59
- ${navContent ? "<hr>" : ""}
69
+ <hr>
70
+ <aside class="aux">
71
+ <div class="header" hidden>
72
+ <h1>${mainTitle}</h1>
73
+ <div class="description">
74
+ <p>${escapedPackageDescription}</p>
75
+ <p class="installation">${await getInstallationContent(ctx)}</p>
76
+ </div>
77
+ </div>
60
78
  ${navContent.replace(
61
79
  new RegExp(
62
80
  `(<li data-id="${escapeRegExp(nav[index]?.id)}">)<a href="[^"]+">([^<]+)</a>`,
63
81
  ),
64
82
  "$1<strong>$2</strong>",
65
83
  )}
84
+ </aside>
66
85
  </div>
67
86
  </div>
68
87
 
@@ -2,7 +2,7 @@ import { writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import type { Context } from "../types/Context.ts";
4
4
  import { createDirs } from "./content/createDirs.ts";
5
- import { getIndexContent } from "./content/getIndexContent.ts";
5
+ // import { getIndexContent } from "./content/getIndexContent.ts";
6
6
  import { getRedirectContent } from "./content/getRedirectContent.ts";
7
7
  import { getSectionContent } from "./content/getSectionContent.ts";
8
8
  import { getStartContent } from "./content/getStartContent.ts";
@@ -27,7 +27,8 @@ export async function setContent(ctx: Context) {
27
27
  await getSectionContent(ctx, i),
28
28
  ),
29
29
  ),
30
- writeFile(join(dir, "index.html"), await getIndexContent(ctx)),
30
+ // writeFile(join(dir, "index.html"), await getIndexContent(ctx)),
31
+ writeFile(join(dir, "index.html"), await getStartContent(ctx)),
31
32
  writeFile(join(dir, "start.html"), await getStartContent(ctx)),
32
33
  ]);
33
34
  }
package/src/css/base.css CHANGED
@@ -41,9 +41,9 @@
41
41
  }
42
42
  }
43
43
  :root {
44
- --max-content-width: 45em;
44
+ --max-content-width: 720px;
45
45
  --content-padding-x: 1.75rem;
46
- --content-padding-y: 1.75rem;
46
+ --content-padding-y: 1rem;
47
47
  --block-margin-y: 1em;
48
48
  --sep-line: 0.3rem solid var(--line-color);
49
49
 
@@ -70,7 +70,7 @@ body {
70
70
  :root {
71
71
  --max-content-width: 100%;
72
72
  --content-padding-x: 0.85rem;
73
- --content-padding-y: 1rem;
73
+ --content-padding-y: 0.65rem;
74
74
  }
75
75
  body {
76
76
  font-size: 100%;
@@ -8,6 +8,25 @@ footer {
8
8
  padding: 0 var(--content-padding-x);
9
9
  }
10
10
 
11
+ header {
12
+ display: none;
13
+ text-align: center;
14
+ padding-top: 0.5em;
15
+ padding-bottom: 0.5em;
16
+ }
17
+ header h1 {
18
+ font-size: 1.25em;
19
+ font-weight: 900;
20
+ margin: 0;
21
+ }
22
+ header .description {
23
+ font-size: 0.8em;
24
+ margin-top: 0.15em;
25
+ }
26
+ header .description p {
27
+ margin: 0;
28
+ }
29
+
11
30
  .body {
12
31
  min-height: 100vh;
13
32
  display: flex;
@@ -20,19 +39,22 @@ footer {
20
39
  display: none;
21
40
  }
22
41
  @media (max-width: 840px) {
42
+ header {
43
+ display: block;
44
+ }
23
45
  .body {
24
46
  flex-direction: column;
25
47
  padding: 0;
26
48
  }
27
49
  }
28
- .body > nav {
50
+ aside {
29
51
  width: 25%;
30
52
  display: flex;
31
53
  flex-direction: column;
32
54
  align-items: flex-end;
33
55
  flex: none;
34
56
  align-self: stretch;
35
- font-size: 0.9em;
57
+ font-size: 0.87em;
36
58
  line-height: 1.25;
37
59
  text-align: end;
38
60
  background: linear-gradient(
@@ -41,65 +63,89 @@ footer {
41
63
  var(--aux-background-end)
42
64
  );
43
65
  border-right: 0.05em solid var(--aux-border-color);
44
- padding: 0.75rem var(--content-padding-x);
66
+ padding: var(--content-padding-y) var(--content-padding-x);
45
67
  margin: 0;
46
68
  box-sizing: border-box;
47
69
  }
48
- .body > nav section {
70
+ aside > .header {
71
+ display: block;
72
+ margin: 0.5rem 0 0.25rem;
73
+ }
74
+ aside > .header .description p:not(.installation) {
75
+ font-size: 0.9em;
76
+ margin: 0.35em 0 0;
77
+ }
78
+ aside > .header .description p.installation {
79
+ margin: 1em 0 0;
80
+ }
81
+ aside > .header .installation code {
82
+ display: inline-block;
83
+ background: transparent;
84
+ border: 0.05em solid;
85
+ border-radius: 0.5em;
86
+ padding: 0.35em 0.75em;
87
+ margin: 0;
88
+ }
89
+ aside h1 {
90
+ font-size: 1.35em;
91
+ font-weight: 900;
92
+ margin: 0;
93
+ }
94
+ aside section {
49
95
  width: 100%;
50
96
  max-width: 16em;
51
97
  padding: 1rem 0;
52
98
  box-sizing: border-box;
53
99
  }
54
- .body > nav section + section {
100
+ aside section + section {
55
101
  border-top: 0.05rem solid;
56
102
  }
57
- .body > nav h2,
58
- .body > nav p.title {
103
+ aside h2,
104
+ aside p.title {
59
105
  font-size: 0.7rem;
60
106
  font-weight: normal;
61
107
  text-transform: uppercase;
62
108
  margin: 1.1rem 0 0.75rem 0;
63
109
  }
64
- .body > nav h2:first-of-type,
65
- .body > nav p.title:first-of-type {
110
+ aside h2:first-of-type,
111
+ aside p.title:first-of-type {
66
112
  margin-top: 0;
67
113
  }
68
- .body > nav ul {
114
+ aside ul {
69
115
  list-style: none;
70
116
  padding: 0;
71
117
  margin: 0;
72
118
  box-sizing: border-box;
73
119
  }
74
- .body > nav > ul + ul {
120
+ aside > ul + ul {
75
121
  border-top: 0.05rem solid;
76
122
  padding-top: 1.2em;
77
123
  margin-top: 1.5em;
78
124
  }
79
- .body > nav ul ul {
125
+ aside ul ul {
80
126
  margin: 0.65em 0 0.5em;
81
127
  }
82
- .body > nav li {
128
+ aside li {
83
129
  text-align: end;
84
130
  padding-inline-end: 1em;
85
131
  margin-top: 0;
86
132
  margin-bottom: 0;
87
133
  position: relative;
88
134
  }
89
- .body > nav li::before {
135
+ aside li::before {
90
136
  content: "•";
91
137
  position: absolute;
92
138
  right: 0;
93
139
  top: 0;
94
140
  }
95
- .body > nav li + li {
141
+ aside li + li {
96
142
  margin-top: 0.55em;
97
143
  }
98
- .body > nav li > strong {
144
+ aside li > strong {
99
145
  color: var(--emphatic-color);
100
146
  }
101
147
  @media (max-width: 840px) {
102
- .body > nav {
148
+ aside {
103
149
  width: 100%;
104
150
  display: block;
105
151
  font-size: inherit;
@@ -114,30 +160,33 @@ footer {
114
160
  padding: 0.25rem var(--content-padding-x) 0.35rem;
115
161
  margin: 0;
116
162
  }
117
- .body > nav section {
163
+ aside > .header {
164
+ display: none;
165
+ }
166
+ aside section {
118
167
  max-width: 100%;
119
168
  }
120
- .body > nav li {
169
+ aside li {
121
170
  text-align: start;
122
171
  padding-inline-start: 1em;
123
172
  padding-inline-end: 0;
124
173
  }
125
- .body > nav li::before {
174
+ aside li::before {
126
175
  left: 0;
127
176
  right: auto;
128
177
  }
129
178
  }
130
- .body > nav > ul:last-child,
131
- .body > nav > ul:last-child > li:last-child {
179
+ aside > ul:last-child,
180
+ aside > ul:last-child > li:last-child {
132
181
  margin-bottom: 0;
133
182
  }
134
- .body > nav a.active {
183
+ aside a.active {
135
184
  font-weight: bold;
136
185
  text-decoration: none;
137
186
  color: inherit;
138
187
  pointer-events: none;
139
188
  }
140
- .body > nav a.anchorjs-link {
189
+ aside a.anchorjs-link {
141
190
  display: none;
142
191
  }
143
192