Skip to content

Instantly share code, notes, and snippets.

@nberlette
Last active April 7, 2024 09:13
Show Gist options
  • Save nberlette/62b636b50602bd5f5ce940b58a493de6 to your computer and use it in GitHub Desktop.
Save nberlette/62b636b50602bd5f5ce940b58a493de6 to your computer and use it in GitHub Desktop.
`DenoHTML`: enhances Deno's builtin HTML documentation generator (v1.38.0+)

Deno HTML Documentation Enhancer

This is a simple tool to enhance the HTML documentation generated by the Deno CLI's builtin command, deno doc --html (Deno v1.38.0+). It improves the CSS styling, adding support for dark mode and persisted color scheme preferences, along with some other usability improvements.

Usage

deno run --allow-read --allow-write --allow-run https://gist.github.com/nberlette/62b636b50602bd5f5ce940b58a493de6/raw/cli.ts
#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run --unstable
import { DenoHTML } from "./docs.ts";
if (import.meta.main) {
await DenoHTML.run(...Deno.args);
Deno.exit(0);
}
export const dark = `
color-scheme: dark;
/* text colors */
--color-fg-default: #adbac7;
--color-fg-muted: #768390;
--color-fg-subtle: #636e7b;
--color-fg-on-emphasis: #cdd9e5;
/* background colors */
--color-canvas-default: #22272e;
--color-canvas-overlay: #2d333b;
--color-canvas-inset: #1c2128;
--color-canvas-subtle: #2d333b;
/* border colors */
--color-border-default: #444c56;
--color-border-muted: #373e47;
--color-border-subtle: #cdd9e51a;
/* shadow colors */
--color-shadow-small: 0 0 transparent;
--color-shadow-medium: 0 3px 6px #1c2128;
--color-shadow-large: 0 8px 24px #1c2128;
--color-shadow-extra-large: 0 12px 48px #1c2128;
/* neutral colors */
--color-neutral-emphasis-plus: #636e7b;
--color-neutral-emphasis: #636e7b;
--color-neutral-muted: #636e7b66;
--color-neutral-subtle: #636e7b1a;
/*** kind colors ***/
/* (c)lass */
--color-kind-class: #6dca70;
--color-kind-class-muted: #347d39;
--bg-kind-class: #6dca701a;
--bg-kind-class-muted: #46954a66;
/* (E)num */
--color-kind-enum: #6cb6ff;
--color-kind-enum-muted: #539bf5;
--bg-kind-enum: #6cb6ff26;
--bg-kind-enum-muted: #6cb6ff66;
/* (f)unction */
--color-kind-function: #539bf5;
--color-kind-function-muted: #316dca;
--bg-kind-function: #4184e426;
--bg-kind-function-muted: #4184e466;
/* (i)nterface */
--color-kind-interface: #dda027;
--color-kind-interface-muted: #966600;
--bg-kind-interface: #ae7c141a;
--bg-kind-interface-muted: #ae7c1466;
/* (N)amespace */
--color-kind-namespace: #dc681b;
--color-kind-namespace-muted: #ae5622;
--bg-kind-namespace: #cc6b2c1a;
--bg-kind-namespace-muted: #cc6b2c66;
/* (T)ype Alias */
--color-kind-type-alias: #dd61a3;
--color-kind-type-alias-muted: #ae4c82;
--bg-kind-type-alias: #c961981a;
--bg-kind-type-alias-muted: #c9619866;
/* (v)ariable */
--color-kind-variable: #986ee2;
--color-kind-variable-muted: #8256d0;
--bg-kind-variable: #986ee21a;
--bg-kind-variable-muted: #986ee266;
/*** other colors ***/
--color-black: #1c2128;
--color-white: #cdd9e5;
--color-gray-0: #cdd9e5;
--color-gray-1: #adbac7;
--color-gray-2: #909dab;
--color-gray-3: #768390;
--color-gray-4: #636e7b;
--color-gray-5: #545d68;
--color-gray-6: #444c56;
--color-gray-7: #373e47;
--color-gray-8: #2d333b;
--color-gray-9: #22272e;
--color-blue-0: #c6e6ff;
--color-blue-1: #96d0ff;
--color-blue-2: #6cb6ff;
--color-blue-3: #539bf5;
--color-blue-4: #4184e4;
--color-blue-5: #316dca;
--color-blue-6: #255ab2;
--color-blue-7: #1b4b91;
--color-blue-8: #143d79;
--color-blue-9: #0f2d5c;
--color-yellow-0: #fbe090;
--color-yellow-1: #eac55f;
--color-yellow-2: #daaa3f;
--color-yellow-3: #c69026;
--color-yellow-4: #ae7c14;
--color-yellow-5: #966600;
--color-yellow-6: #805400;
--color-yellow-7: #6c4400;
--color-yellow-8: #593600;
--color-yellow-9: #452700;
--color-coral-0: #ffdacf;
--color-coral-1: #ffb9a5;
--color-coral-2: #f79981;
--color-coral-3: #ec775c;
--color-coral-4: #de5b41;
--color-coral-5: #c2442d;
--color-coral-6: #a93524;
--color-coral-7: #8d291b;
--color-coral-8: #771d13;
--color-coral-9: #5d1008;
`;
export const light = `
color-scheme: light;
/* text colors */
--color-fg-default: #1F2328;
--color-fg-muted: #656d76;
--color-fg-subtle: #6e7781;
--color-fg-on-emphasis: #ffffff;
/* background colors */
--color-canvas-default: #ffffff;
--color-canvas-overlay: #ffffff;
--color-canvas-inset: #f6f8fa;
--color-canvas-subtle: #f6f8fa;
/* border colors */
--color-border-default: #d0d7de;
--color-border-muted: #d8dee4;
--color-border-subtle: #1f232826;
/* shadow colors */
--color-shadow-small: 0 1px 0 #1f23280a;
--color-shadow-medium: 0 3px 6px #8c959f26;
--color-shadow-large: 0 8px 24px #8c959f33;
--color-shadow-extra-large: 0 12px 28px #8c959f4d;
/* neutral colors */
--color-neutral-emphasis-plus: #24292f;
--color-neutral-emphasis: #6e7781;
--color-neutral-muted: #afb8c133;
--color-neutral-subtle: #eaeef280;
/*** kind colors ***/
/* (c)lass */
--color-kind-class: #146f2e;
--color-kind-class-muted: #1f883d;
--bg-kind-class: #4ac26b1a;
--bg-kind-class-muted: #4ac26b66;
/* (E)num */
--color-kind-enum: #11a2c7;
--color-kind-enum-muted: #47c3e2;
--bg-kind-enum: #11a2c71a;
--bg-kind-enum-muted: #47c3e266;
/* (f)unction */
--color-kind-function: #0969da;
--color-kind-function-muted: #0550ae;
--bg-kind-function: #0969da1a;
--bg-kind-function-muted: #54aeff66;
/* (i)nterface */
--color-kind-interface: #c98805;
--color-kind-interface-muted: #7d4e00;
--bg-kind-interface: #c9880526;
--bg-kind-interface-muted: #d4a72c66;
/* (N)amespace */
--color-kind-namespace: #bc4c00;
--color-kind-namespace-muted: #953800;
--bg-kind-namespace: #fb8f4426;
--bg-kind-namespace-muted: #fb8f4466;
/* (T)ype Alias */
--color-kind-type-alias: #bf3989;
--color-kind-type-alias-muted: #99286e;
--bg-kind-type-alias: #ff80c826;
--bg-kind-type-alias-muted: #ff80c866;
/* (v)ariable */
--color-kind-variable: #8250df;
--color-kind-variable-muted: #6639ba;
--bg-kind-variable: #c297ff26;
--bg-kind-variable-muted: #c297ff66;
/*** other colors ***/
--color-black: #1F2328;
--color-white: #ffffff;
--color-gray-0: #f6f8fa;
--color-gray-1: #eaeef2;
--color-gray-2: #d0d7de;
--color-gray-3: #afb8c1;
--color-gray-4: #8c959f;
--color-gray-5: #6e7781;
--color-gray-6: #57606a;
--color-gray-7: #424a53;
--color-gray-8: #32383f;
--color-gray-9: #24292f;
--color-blue-0: #ddf4ff;
--color-blue-1: #b6e3ff;
--color-blue-2: #80ccff;
--color-blue-3: #54aeff;
--color-blue-4: #218bff;
--color-blue-5: #0969da;
--color-blue-6: #0550ae;
--color-blue-7: #033d8b;
--color-blue-8: #0a3069;
--color-blue-9: #002155;
--color-yellow-0: #fff8c5;
--color-yellow-1: #fae17d;
--color-yellow-2: #eac54f;
--color-yellow-3: #d4a72c;
--color-yellow-4: #bf8700;
--color-yellow-5: #9a6700;
--color-yellow-6: #7d4e00;
--color-yellow-7: #633c01;
--color-yellow-8: #4d2d00;
--color-yellow-9: #3b2300;
--color-coral-0: #fff0eb;
--color-coral-1: #ffd6cc;
--color-coral-2: #ffb4a1;
--color-coral-3: #fd8c73;
--color-coral-4: #ec6547;
--color-coral-5: #c4432b;
--color-coral-6: #9e2f1c;
--color-coral-7: #801f0f;
--color-coral-8: #691105;
--color-coral-9: #510901;
`;
export const normalize = `
/*! normalize.css v8.0.1 | MIT | github.com/necolas/normalize.css */
html{-webkit-text-size-adjust:100%}body{margin:0}main{display:block}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
`;
/**
* Variables that are used between the `styles.css` file and `page.css` file.
* For simplicity, these variables are only included in `styles.css`.
*/
export const variables = `
/* #region variables */
/* light color scheme */
.light,
:not(.dark) {
${light.replaceAll(/^/gm, " ")}
}
/* dark color scheme */
.dark,
:root:not(.light),
:not(.light) {
${dark.replaceAll(/^/gm, " ")}
}
/* muted color scheme variants */
.light.muted,
:not(.dark).muted {
${
[...light.matchAll(/--((?:color|bg)-[\w\-]+?)-muted: ?(.+?);/g)].map(
([, name, value]) => `--${name}: ${value};`
).join("\n ")
}
}
.dark.muted,
:not(.light).muted {
${
[...dark.matchAll(/--(color-[\w\-]+?)-muted: ?(.+?);/g)].map(
([, name, value]) => `--${name}: ${value};`
).join("\n ")
}
}
@media (prefer-color-scheme: dark) {
:root {
${dark.replaceAll(/^/gm, " ")}
}
}
@media (prefer-color-scheme: light) {
:root,
:not(.dark) {
${light.replaceAll(/^/gm, " ")}
}
}
/* base styles */
:root {
/* background colors */
--bg-body: var(--color-canvas-subtle);
--bg-content: var(--color-canvas-default);
--bg-highlight: var(--color-canvas-default);
--bg-searchbar: var(--color-canvas-subtle);
--bg-search-results-hover: var(--color-canvas-accent);
--bg-code: var(--color-canvas-subtle);
--bg-overload-label: var(--color-canvas-subtle);
/* foreground colors (text) */
--color-link: var(--color-blue-4);
--color-link-hover: var(--color-blue-3);
--color-link-underline: currentColor;
--color-text-accent: var(--color-fg-subtle);
--color-text-doc: var(--color-fg-default);
--color-text-subtitle: var(--color-fg-muted);
/* border colors */
--border-color-base: var(--color-border-default);
--border-radius-kind: var(--border-radius-half);
/* font families */
--font-mono: 'Operator Mono', 'Operator Mono SSm', 'Dank Mono', 'IBM Plex Mono', 'Fira Code', 'Roboto Mono', 'Monaco', 'Menlo', 'Souurce Code Pro', 'Lucida Console', 'Courier New', ui-monospace, monospace;
--font-sans: 'Gotham Narrow', 'IBM Plex Sans', 'Roboto', 'Inter', 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica', 'Arial', 'AppleColorEmoji', ui-sans-serif, sans-serif;
--font-serif: 'Sentinel SSm', 'Sentinel', 'IBM Plex Serif', 'Georgia', 'Times New Roman', ui-serif, serif;
/* font weights */
--font-thin: 100;
--font-extralight: 200;
--font-light: 300;
--font-book: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
--font-extrabold: 800;
--font-black: 900;
/* font sizes */
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-md: 1.125rem;
--text-lg: 1.25rem;
--text-xl: 1.5rem;
--text-2xl: 1.75rem;
--text-3xl: 2rem;
--text-4xl: 2.5rem;
--text-5xl: 3rem;
--text-6xl: 4rem;
/* line heights */
--leading-5: 1rem;
--leading-6: 1.125rem;
--leading-8: 1.25rem;
--leading-10: 1.5rem;
--leading-12: 1.625rem;
--leading-14: 1.750rem;
--leading-16: 1.875rem;
--leading-18: 2rem;
--leading-20: 2.25rem;
/* font styles, text decorations, alignments */
--font-style-subtitle: italic;
--text-align-table-header: center;
--text-decoration-link: underline 1px solid transparent;
/* border styles */
--border-overload-label: 1px solid var(--color-border-default);
--border-results-divider: 1px solid var(--color-border-subtle);
--border-table-cell: 1px solid var(--color-border-default);
--border-searchbar: 1px solid var(--color-border-muted)wws
--border-overload-label: 1px solid var(--color-border-default);
/* border radius */
--border-radius-sm: 0.25rem;
--border-radius-base: 0.5rem;
--border-radius-md: 0.625rem;
--border-radius-lg: 0.75rem;
--border-radius-xl: 1rem;
--border-radius-2xl: 1.5rem;
--border-radius-3xl: 2rem;
--border-radius-full: 9999px;
--border-radius-none: 0px;
--border-radius-half: 50%;
--border-radius-overload-label: var(--border-radius-sm);
/* dimensions */
--width-content: 100%;
--width-kind: 1.5rem;
--height-kind: 1.5rem;
--min-width-sidepanel: 12%;
--max-width-sidepanel: 22%;
--min-width-symbol-section: 13rem;
/* margins */
--margin-markdown-between: 1rem;
/* paddings */
--padding-code: 0.25rem 0.375rem;
--padding-content: 2rem;
--padding-section-title: 0.5rem 0;
--padding-sidepanel: 0.75rem 0 0.75rem;
--padding-table-cell: 0.5rem;
--padding-symbol-section: 0.5rem 0.75rem 0.5rem 0;
--padding-overload-label: 0.25rem 0.5rem;
/* transitions */
--transition-color: color 75ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-underline: text-decoration 150ms ease-in-out;
/* other */
--cursor-overload-label: pointer;
}
/* #endregion variables */
`;
export const animation = `
@keyframes pulse {
0% { transform: scale(1) }
50% { transform: scale(1.1) }
100% { transform: scale(1) }
}
@keyframes spin {
0% { transform: rotate(0deg) }
100% { transform: rotate(360deg) }
}
@keyframes spin-reverse {
0% { transform: rotate(0deg) }
100% { transform: rotate(-360deg) }
}
@keyframes spin-pulse {
0% { transform: rotate(0deg) scale(1) }
50% { transform: rotate(180deg) scale(1.1) }
100% { transform: rotate(360deg) scale(1) }
}
@keyframes spin-pulse-alt {
0% { transform: rotate(0deg) scale(1) }
50% { transform: rotate(-180deg) scale(1.1) }
100% { transform: rotate(-360deg) scale(1) }
}
@keyframes rock {
00.0% { transform: rotate(0deg) }
33.3% { transform: rotate(15deg) }
66.6% { transform: rotate(0deg) }
99.9% { transform: rotate(-15deg) }
}
@keyframes rock-pulse {
0% { transform: rotate(0deg) scale(1) }
33.3% { transform: rotate(15deg) scale(1.1) }
66.6% { transform: rotate(0deg) scale(1) }
99.9% { transform: rotate(-15deg) scale(1.1) }
}
@keyframes bounce {
0% { transform: translateY(0) }
25% { transform: translateY(-10px) }
50% { transform: translateY(0) }
75% { transform: translateY(-5px) }
100% { transform: translateY(0) }
}
@keyframes bounce-pulse {
0% { transform: translateY(0) scale(1) }
25% { transform: translateY(-10px) scale(1.1) }
50% { transform: translateY(0) scale(1) }
75% { transform: translateY(-5px) scale(1.1) }
100% { transform: translateY(0) scale(1) }
}
@keyframes bounce-pulse-alt {
0% { transform: translateY(0) scale(1) }
25% { transform: translateY(10px) scale(1.1) }
50% { transform: translateY(0) scale(1) }
75% { transform: translateY(5px) scale(1.1) }
100% { transform: translateY(0) scale(1) }
}
@keyframes beat {
0% { transform: scale(1) }
50% { transform: scale(1.05) }
100% { transform: scale(1) }
}
@keyframes swell {
0% { transform: scale(0) }
50% { transform: scale(1) }
100% { transform: scale(0) }
}
@keyframes jelly {
0% { transform: scaleX(1) scaleY(1) }
25% { transform: scaleX(1.25) scaleY(0.75) }
50% { transform: scaleX(0.75) scaleY(1.25) }
75% { transform: scaleX(1.15) scaleY(0.85) }
100% { transform: scaleX(0.95) scaleY(1.05) }
}
@keyframes jelly-alt {
0% { transform: scaleX(1) scaleY(1) }
25% { transform: scaleX(0.75) scaleY(1.25) }
50% { transform: scaleX(1.25) scaleY(0.75) }
75% { transform: scaleX(0.85) scaleY(1.15) }
100% { transform: scaleX(1.05) scaleY(0.95) }
}
@keyframes float {
0% { transform: translateY(0) }
50% { transform: translateY(-5px) }
100% { transform: translateY(0) }
}
/* animations */
.animate {
animation-name: none;
animation-delay: 0s;
animation-direction: normal;
animation-duration: 2s;
animation-fill-mode: both;
animation-iteration-count: infinite;
animation-play-stateee: running;
animation-timing-function: ease-in-out;
}
.fast {
animation-duration: 1250ms;
}
.faster {
animation-duration: 750ms;
}
.fastest {
animation-duration: 500ms;
}
.slow {
animation-duration: 3s;
}
.slower {
animation-duration: 4s;
}
.slowest {
animation-duration: 5s;
}
.ease-in {
animation-timing-function: ease-in;
transition-timing-function: ease-in;
}
.ease-out {
animation-timing-function: ease-out;
transition-timing-function: ease-out;
}
.ease-in-out {
animation-timing-function: ease-in-out;
transition-timing-function: ease-in-out;
}
.linear {
animation-timing-function: linear;
transition-timing-function: linear;
}
.ease {
animation-timing-function: ease;
transition-timing-function: ease;
}
.timing-bounce {
animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.timing-bounce-alt {
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
/* animation names */
.pulse {
animation-name: pulse;
}
.spin {
animation-name: spin;
}
.spin-reverse {
animation-name: spin-reverse;
}
.spin-pulse {
animation-name: spin-pulse;
}
.spin-pulse-alt {
animation-name: spin-pulse-alt;
}
.rock {
animation-name: rock;
}
.rock-pulse {
animation-name: rock-pulse;
}
.bounce {
animation-name: bounce;
}
.bounce-pulse {
animation-name: bounce-pulse;
}
.bounce-pulse-alt {
animation-name: bounce-pulse-alt;
}
.beat {
animation-name: beat;
}
.swell {
animation-name: swell;
}
.jelly {
animation-name: jelly;
}
.jelly-alt {
animation-name: jelly-alt;
}
.float {
animation-name: float;
}
`;
/**
* Represents the stylesheet content of the `page.css` file that the module
* provides in place of the `page.css` file that the original documentation
* generator would have provided. This stylesheet is used to style the page
* itself, and not the documentation.
*/
export const page = `
/* @import "./normalize.css";
@import "./variables.css";
@import "./animation.css";
*/
html,
body {
width: 100vw;
height: 100vh;
font-family: var(--font-sans);
font-size: var(--text-base);
line-height: var(--leading-10);
margin: 0;
padding: 0;
background-color: var(--bg-body);
transition: background-color 150ms ease-in-out;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
body {
display: flex;
}
:root:not(:is(.light, .dark)) {
color-scheme: light dark;
}
.dark {
color-scheme: dark;
}
.light {
color-scheme: light;
}
.color-scheme-toggle {
content: "";
width: 2rem;
height: 2rem;
margin: 0;
padding: 0;
position: fixed;
right: 2rem;
bottom: 2rem;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
z-index: 99999;
border: none;
opacity: 0.8;
transition: all 250ms ease-out;
transform: scale(1);
transform-origin: 50% 50%;
outline: none;
appearance: none;
-webkit-mask: var(--icon) no-repeat;
mask: var(--icon) no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
background-color: currentColor;
color: inherit;
}
.color-scheme-toggle:hover {
opacity: 1;
}
.color-scheme-toggle:focus {
outline: none;
}
.color-scheme-toggle.active {
background-color: transparent;
}
.i-sun,[i-sun=""] {
--icon: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='1em' height='1em'%3E%3Cpath fill='currentColor' d='M12 18a6 6 0 1 1 0-12a6 6 0 0 1 0 12Zm0-2a4 4 0 1 0 0-8a4 4 0 0 0 0 8ZM11 1h2v3h-2V1Zm0 19h2v3h-2v-3ZM3.515 4.929l1.414-1.414L7.05 5.636L5.636 7.05L3.515 4.93ZM16.95 18.364l1.414-1.414l2.121 2.121l-1.414 1.414l-2.121-2.121Zm2.121-14.85l1.414 1.415l-2.121 2.121l-1.414-1.414l2.121-2.121ZM5.636 16.95l1.414 1.414l-2.121 2.121l-1.414-1.414l2.121-2.121ZM23 11v2h-3v-2h3ZM4 11v2H1v-2h3Z'/%3E%3C/svg%3E");
}
.dark .i-moon,
.dark [i-moon=""] {
--icon: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='1em' height='1em'%3E%3Cpath fill='currentColor' d='M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2h.1A6.98 6.98 0 0 0 10 7Zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938A7.999 7.999 0 0 0 4 12Z'/%3E%3C/svg%3E");
}
.dark .i-moon-fog,
.dark [i-moon-fog=""] {
--icon: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='1em' height='1em'%3E%3Cg fill='none'%3E%3Cpath stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M8 22h8'/%3E%3Cpath stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M5 19h14' opacity='.5'/%3E%3Cpath stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M2 16h20'/%3E%3Cpath fill='currentColor' d='m21.067 11.857l-.642-.388l.642.388Zm-8.924-8.924l-.388-.642l.388.642ZM2.75 12A9.25 9.25 0 0 1 12 2.75v-1.5C6.063 1.25 1.25 6.063 1.25 12h1.5Zm12.75 2.25A5.75 5.75 0 0 1 9.75 8.5h-1.5a7.25 7.25 0 0 0 7.25 7.25v-1.5Zm4.925-2.781A5.746 5.746 0 0 1 15.5 14.25v1.5a7.247 7.247 0 0 0 6.21-3.505l-1.285-.776ZM9.75 8.5a5.747 5.747 0 0 1 2.781-4.925l-.776-1.284A7.246 7.246 0 0 0 8.25 8.5h1.5Zm11.5 3.5a9.216 9.216 0 0 1-.77 3.7l1.375.6c.576-1.318.895-2.773.895-4.3h-1.5ZM3.52 15.7a9.216 9.216 0 0 1-.77-3.7h-1.5c0 1.527.319 2.982.895 4.3l1.374-.6ZM12 2.75a.384.384 0 0 1-.268-.118a.285.285 0 0 1-.082-.155c-.004-.031-.002-.121.105-.186l.776 1.284c.503-.304.665-.861.606-1.299c-.062-.455-.42-1.026-1.137-1.026v1.5Zm9.71 9.495c-.066.107-.156.109-.187.105a.285.285 0 0 1-.155-.082a.384.384 0 0 1-.118-.268h1.5c0-.717-.571-1.075-1.026-1.137c-.438-.059-.995.103-1.299.606l1.284.776Z' opacity='.5'/%3E%3Cpath stroke='currentColor' d='M19.9 2.307a.483.483 0 0 0-.9 0l-.43 1.095a.484.484 0 0 1-.272.274l-1.091.432a.486.486 0 0 0 0 .903l1.091.432a.48.48 0 0 1 .272.273L19 6.81c.162.41.74.41.9 0l.43-1.095a.484.484 0 0 1 .273-.273l1.091-.432a.486.486 0 0 0 0-.903l-1.091-.432a.484.484 0 0 1-.273-.274l-.43-1.095ZM16.033 8.13a.483.483 0 0 0-.9 0l-.157.399a.484.484 0 0 1-.272.273l-.398.158a.486.486 0 0 0 0 .903l.398.157c.125.05.223.148.272.274l.157.399c.161.41.739.41.9 0l.157-.4a.484.484 0 0 1 .272-.273l.398-.157a.486.486 0 0 0 0-.903l-.398-.158a.484.484 0 0 1-.272-.273l-.157-.4Z'/%3E%3C/g%3E%3C/svg%3E");
}
.i-sun, .dark .i-moon {
opacity: 1;
}
.dark .i-sun,
.light .i-moon {
opacity: 0;
}
.unicon {
-webkit-mask: var(--icon) no-repeat;
mask: var(--icon) no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
background-color: currentColor;
color: inherit;
width: 1em;
height: 1em
}
table {
border-color: inherit;
border-collapse: collapse;
}
#content {
padding: var(--padding-content, 2rem);
width: var(--width-content);
box-sizing: border-box;
margin: 0;
background-color: var(--bg-content);
border-radius: var(--border-radius-md) 0 0 var(--border-radius-md);
overflow-y: auto;
}
#content a > h2 {
margin: 0;
}
p {
margin: 0;
}
a {
color: inherit;
text-decoration: inherit;
}
.link {
color: var(--color-link);
text-decoration: var(--text-decoration-link);
transition: var(--transition-color), var(--transition-underline);
}
.link:hover {
color: var(--color-link-hover);
text-decoration-color: var(--color-link-underline);
}
#sidepanel {
flex-shrink: 0;
margin: 0;
padding: 0.75rem 0 0.75rem;
height: 100vh;
width: fit-content;
min-width: var(--min-width-sidepanel);
max-width: var(--max-width-sidepanel);
position: sticky;
top: 0;
left: 0;
bottom: 0;
overflow-y: scroll;
box-sizing: border-box;
overscroll-behavior-y: auto;
scrollbar-width: thin;
}
#sidepanel ul {
list-style: none;
padding-left: 1rem;
}
#sidepanel li {
display: block;
margin: -0.125rem 0;
}
#sidepanel a {
display: flex;
padding: 0.25rem 1rem 0.25rem;
border-radius: var(--border-radius-md) 0 0 var(--border-radius-md);
position: relative;
background-color: var(--bg-content);
transition: all 0ms;
}
#sidepanel a span {
display: block;
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
background-color: inherit;
}
#sidepanel :is(a:target, a:hover, a.active) {
background-color: var(--bg-highlight) !important;
}
#sidepanel a::before,
#sidepanel a::after {
content: "";
position: absolute;
background-color: #f000;
right: 0;
width: var(--border-radius-md);
height: 0;
padding: calc(var(--border-radius-md)) 0;
opacity: 0;
/* transition: box-shadow 60ms 1ms ease; */
}
#sidepanel a::before {
top: calc(var(--border-radius-md) * -2);
border-bottom-right-radius: var(--border-radius-md);
box-shadow: 0.5rem 0.5rem 0 0.5rem var(--bg-highlight);
}
#sidepanel a::after {
bottom: calc(var(--border-radius-md) * -2);
border-top-right-radius: var(--border-radius-md);
box-shadow: 0.5rem -0.5rem 0 0.5rem var(--bg-highlight);
}
#sidepanel :is(a:target, a:hover, a.active)::before,
#sidepanel :is(a:target, a:hover, a.active)::after {
opacity: 1;
}
#sidepanel .section_title {
display: flex;
align-items: center;
gap: 0.625rem;
margin: 0.625rem 1.25rem 0.625rem;
}
#sidepanel .section_title h3 {
margin: 0;
line-height: 1;
}
#searchbar {
width: 100%;
margin: 1.25rem 0;
padding: 0.575rem 1.125rem;
box-sizing: border-box;
border: 1.5px solid var(--border-searchbar);
background-color: var(--bg-searchbar);
border-radius: var(--border-radius-full);
font-size: var(--text-base);
font-weight: var(--font-medium, 500);
}
#searchResults ul {
list-style: none;
margin: 0;
padding: 0;
}
#searchResults ul li {
display: block;
}
#searchResults ul a {
display: flex;
gap: 1rem;
align-items: center;
padding: 0.625rem 0.75rem;
justify-content: space-between;
line-height: 1;
background-color: var(--bg-search-results);
transition: background-color 0.125s ease-in-out;
}
#searchResults ul a div:first-child {
display: flex;
gap: 0.625rem;
align-items: center;
}
#searchResults ul a div:last-child {
color: var(--color-text-subtitle);
font-style: var(--font-style-subtitle);
}
#searchResults ul a:hover {
background-color: var(--bg-search-results-hover);
}
#searchResults ul > * + * {
border-top: var(--border-results-divider);
}
`;
/**
* Represents the stylesheet content of the `styles.css` file that the module
* provides in place of the `styles.css` file that the original documentation
* generator would have provided.
*/
export const styles = `
/* @import "./normalize.css";
@import "./variables.css";
@import "./animation.css"; */
/* #region styles */
.symbol_group {
margin-top: 1rem;
}
/* doc classes */
.symbol_group > * + * {
margin-top: 3rem;
}
.symbol > * + * {
margin-top: 2rem;
}
.symbol_header {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.doc_block_items > * + * {
margin-top: 1.75rem;
}
.doc_block_title {
font-weight: var(--font-medium, 500);
background-color: unset !important;
}
.doc_block_title > * + * {
margin-top: 0.25rem;
}
.doc_block_title > *:first-child {
font-size: var(--text-xl);
line-height: var(--leading-14);
}
.doc_block_subtitle {
font-size: var(--text-lg);
line-height: var(--leading-12);
font-weight: var(--font-medium, 500);
}
.doc_block_subtitle > * + * {
margin-top: 0.125rem;
}
.doc_block_subtitle_text {
color: var(--color-text-subtitle);
font-style: var(--font-style-subtitle);
}
.section_title {
font-size: var(--text-base);
line-height: var(--leading-10);
font-weight: var(--font-medium);
color: var(--color-text-subtitle);
padding: var(--padding-section-title);
}
.section {
margin-top: 0.5rem;
}
.section > * + * {
margin-top: 1.75rem;
}
.doc_item {
/* TODO: group */
position: relative;
}
.doc_item:hover .anchor {
opacity: 1;
}
.doc_entry {
display: flex;
justify-content: space-between;
}
.doc_entry_children {
display: flex;
align-items: center;
overflow-wrap: break-word;
gap: 0.5rem;
}
.doc_entry + .markdown {
margin-top: 1rem;
}
.function_overload_selectors {
gap: 0.5rem;
margin: 1rem 0;
}
.function_overload_selectors > * + * {
margin-top: 0.5rem;
}
.function_overload_label {
display: block;
padding: var(--padding-overload-label);
border-radius: var(--border-radius-overload-label);
border: var(--border-overload-label);
cursor: var(--cursor-overload-label);
}
.function_overload_label:hover {
border-color: var(--bg-overload-label);
}
.symbol_section,
.symbol_section > tr {
display: block;
}
.symbol_section_symbol {
display: block;
padding: var(--padding-symbol-section);
font-weight: var(--font-bold);
}
.symbol_section_symbol > div {
min-width: var(--min-width-symbol-section);
display: flex;
align-items: center;
}
.symbol_section_symbol > div > * + * {
margin-left: 0.5rem;
}
.symbol_section_doc {
display: block;
padding: 0.25rem 0;
font-size: var(--text-wa);
line-height: var(--leading-8);
color: var(--color-text-doc);
}
@media (min-width: 1024px) {
.symbol_section {
display: table;
}
.symbol_section > tr {
display: table-row;
}
.symbol_section_symbol {
display: table-cell;
}
.symbol_section_doc {
display: table-cell;
}
}
.anchor {
float: left;
line-height: 1;
display: block;
opacity: 0;
color: var(--color-text-accent);
margin-left: -1.25rem;
padding-right: 0.5rem;
transition: opacity 200ms ease-in-out;
}
.anchor:hover,
*:hover > .anchor,
*:target > .anchor {
opacity: 1 !important;
}
/*.muted div[class^="kind_"] {
--color-kind-function: var(--color-kind-function-muted);
--bg-kind-function: var(--bg-kind-function-muted);
--color-kind-enum: var(--color-kind-enum-muted);
--bg-kind-enum: var(--bg-kind-enum-muted);
--color-kind-class: var(--color-kind-class-muted);
--bg-kind-class: var(--bg-kind-class-muted);
--color-kind-interface: var(--color-kind-interface-muted);
--bg-kind-interface: var(--bg-kind-interface-muted);
--color-kind-type-alias: var(--color-kind-type-alias-muted);
--bg-kind-type-alias: var(--bg-kind-type-alias-muted);
--color-kind-namespace: var(--color-kind-namespace-muted);
--bg-kind-namespace: var(--bg-kind-namespace-muted);
--color-kind-variable: var(--color-kind-variable-muted);
--bg-kind-variable: var(--bg-kind-variable-muted);
}*/
.kind_Function_text {
color: var(--color-kind-function);
}
.kind_Function_bg {
background-color: var(--bg-kind-function);
}
.kind_Variable_text {
color: var(--color-kind-variable);
}
.kind_Variable_bg {
background-color: var(--bg-kind-variable);
}
.kind_Class_text {
color: var(--color-kind-class);
}
.kind_Class_bg {
background-color: var(--bg-kind-class);
}
.kind_Enum_text {
color: var(--color-kind-enum);
}
.kind_Enum_bg {
background-color: var(--bg-kind-enum);
}
.kind_Interface_text {
color: var(--color-kind-interface);
}
.kind_Interface_bg {
background-color: var(--bg-kind-interface);
}
.kind_TypeAlias_text {
color: var(--color-kind-type-alias);
}
.kind_TypeAlias_bg {
background-color: var(--bg-kind-type-alias);
}
.kind_Namespace_text {
color: var(--color-kind-namespace);
}
.kind_Namespace_bg {
background-color: var(--bg-kind-namespace);
}
.symbol_kind {
border-radius: var(--border-radius-kind);
width: var(--width-kind);
height: var(--height-kind);
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: var(--font-semibold);
font-size: var(--text-sm);
line-height: 1;
flex-shrink: 0;
user-select: none;
}
/* markdown */
.markdown > * + * {
margin-top: var(--margin-markdown-between);
}
.markdown :not(pre) > code {
font-family: var(--font-mono);
font-size: var(--text-sm, 0.875rem);
line-height: var(--leading-8);
padding: 0.25rem 0.375rem;
border-radius: var(--border-radius-sm);
background-color: var(--bg-code);
}
.markdown pre {
font-family: var(--font-mono);
font-size: var(--text-sm);
line-height: var(--leading-8);
padding: 0.625rem;
border-radius: var(--border-radius-base);
color: var(--color-fg-muted);
background-color: var(--bg-code);
overflow-x: auto;
}
.markdown a {
color: var(--color-link);
transition: var(--transition-color);
}
.markdown a:hover {
color: var(--color-link-hover);
}
.markdown p {
margin: 0.25rem 0;
text-align: left;
}
.markdown table {
table-layout: auto;
}
.markdown td {
padding: var(--padding-table-cell);
border: var(--border-table-cell);
}
.markdown th {
font-weight: var(--font-bold, 700);
text-align: var(--text-align-table-header);
}
.markdown_summary {
display: flex;
color: var(--color-text-accent);
padding: 0.5rem 0;
}
.markdown_summary p {
display: block;
}
.markdown_summary a {
color: var(--color-link);
transition: var(--transition-color);
}
.markdown_summary a:hover {
color: var(--color-link-hover);
}
.markdown_summary :not(pre) > code, .markdown_summary > code {
font-family: var(--font-mono);
font-size: var(--text-sm, 0.875rem);
line-height: var(--leading-8, 1.25rem);
padding: var(--padding-code, 0.25rem 0.375rem);
border-radius: var(--border-radius-sm, 0.25rem);
background-color: var(--bg-code);
}
.example > details .arrow_toggle {
color: var(--color-text-accent);
}
.example > details[open] .arrow_toggle {
transform: rotate(90deg);
}
.example > details > summary {
list-style: none;
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
border-radius: var(--border-radius-md);
width: 100%;
line-height: var(--leading-10);
}
.example > details > summary::-webkit-details-marker {
display: none;
}
/* #endregion styles */
`;
export default {
light,
dark,
animation,
normalize,
variables,
styles,
page,
}
export { outdent } from "https://gist.githubusercontent.com/nberlette/664884d3e9cf67fbc14ad67428e75fa7/raw/c1976f86dd45a0cf551455c6800f8a4f1be7d931/outdent.ts";
export { XMLFormatter } from "https://gist.githubusercontent.com/nberlette/d00faf7253a5f76ec2893cb7a51647ec/raw/dc856bf8aac454289f4fbfdea471cbb34ecb7811/xml-formatter.ts";
export { parseArgs } from "https://deno.land/std@0.209.0/cli/parse_args.ts";
// deno-lint-ignore-file ban-types
import { outdent, parseArgs, XMLFormatter } from "./deps.ts";
import * as $path from "node:path";
import { minify as minifyHtml } from "npm:html-minifier-terser";
import CleanCSS from "npm:clean-css";
import css from "./css.ts";
import js from "./js.ts";
import usage from "./usage.ts";
export interface Options {
/** The title of the generated documentation. */
title: string;
/** The output directory for the generated documentation. */
output: string;
/** The files to generate documentation for. */
files: string[];
/** The timeout for formatting the generated HTML and JavaScript. */
timeout?: number;
/** The path to the Deno executable. */
denoExecPath?: string;
/** Whether to format the generated HTML and JavaScript. */
format?: boolean;
/** Whether to minify the generated HTML and JavaScript. */
minify?: boolean;
/** Fix broken relative paths in the generated HTML. */
fixPaths?: boolean;
/** Add preloads for the generated CSS and JS files. */
preloads?: boolean;
/** Format the generated CSS files. */
formatCSS?: boolean;
/** Minify the generated CSS files. */
minifyCSS?: boolean;
/** Format the generated JS files. */
formatJS?: boolean;
/** Minify the generated JS files. */
minifyJS?: boolean;
/** Format the generated HTML files. */
formatHTML?: boolean;
/** Minify the generated HTML files. */
minifyHTML?: boolean;
/** Operate in debug mode. */
debug?: boolean;
/** Whether to continue running on errors. */
continueOnError?: boolean;
/** Whether to reload remote modules. */
reload?: boolean;
/** Whether to use unstable APIs. */
unstable?: boolean;
/** Whether to allow remote imports. */
noRemote?: boolean;
}
interface CommandOutput {
code: number;
stderr: string;
stderrBytes: Uint8Array;
stdout: string;
stdoutBytes: Uint8Array;
success: boolean;
}
export type ResolvedOptions = Readonly<Required<Options>>;
type Printable = string | number | bigint | boolean | null | undefined;
export class DenoHTML implements ResolvedOptions {
static readonly default: DenoHTML = new DenoHTML();
/** Runs the docs generator with a given set of {@link args|arguments}. */
static async run(...args: string[]) {
const argv = parseArgs(args, {
alias: {
title: ["T", "t", "name"],
output: ["O", "o", "outdir"],
format: ["F", "f"],
minify: ["M", "m"],
debug: ["d"],
reload: ["r"],
noRemote: ["no-remote", "nr"],
noNpm: ["no-npm", "nn"],
private: ["p"],
help: ["H", "h", "?"],
},
boolean: [
"format",
"minify",
"debug",
"reload",
"private",
"noRemote",
"noNpm",
"help",
],
string: ["title", "output"],
default: {
title: "Docs",
output: "docs",
format: false,
minify: false,
debug: false,
reload: false,
noRemote: false,
noNpm: false,
private: false,
help: false,
},
});
const {
title,
output,
format,
minify,
debug,
noNpm,
noRemote,
reload,
private: withPrivate,
help,
_: files,
} = argv;
// show the help page, exit code 1.
if (help || !(title && output && files?.length)) {
DenoHTML.showHelp();
}
const doc = new DenoHTML(files, {
title,
output,
format,
minify,
debug,
noRemote,
noNpm,
withPrivate,
reload,
});
return await doc.run();
}
/** Shows a CLI usage page and then exits with status code 1. */
static showHelp(): void {
const basename = import.meta.url.split(/\/|\\/).pop() ?? "docs.ts";
console.error(usage(basename));
Deno.exit(1);
}
#tmpdir = Deno.makeTempDirSync({ prefix: "deno-doc-html-" });
/** Operate in debug mode. */
debug = false;
/** The timeout for formatting the generated HTML and JavaScript. */
timeout = 5e3;
/** The path to the Deno executable. */
denoExecPath = Deno.execPath();
/** Whether to continue running on errors. */
continueOnError = false;
/** Whether to reload remote modules. */
reload = false;
/** Whether to use unstable APIs. */
unstable = true;
/** Whether to allow remote imports. */
noRemote = true;
/** The title of the generated documentation. */
title = "Docs";
/** The output directory for the generated documentation. */
output = "./docs";
/** The files to generate documentation for. */
files: string[] = [];
/** Fix broken relative paths in the generated HTML. */
fixPaths = true;
/** Add preloads for the generated CSS and JS files. */
preloads = true;
/** Format **all** generated HTML, CSS, and JavaScript. */
format = false;
/** Format the generated HTML files. */
formatHTML = true;
/** Format the generated CSS files. */
formatCSS = true;
/** Format the generated JS files. */
formatJS = true;
/** Minify **all** generated HTML, CSS, and JavaScript. */
minify = false;
/** Minify the generated HTML files. */
minifyHTML = false;
/** Minify the generated CSS files. */
minifyCSS = false;
/** Minify the generated JS files. */
minifyJS = false;
readonly customStyleSheets: string[] = [
"normalize.css",
"variables.css",
"animation.css",
];
readonly builtinStyleSheets: string[] = [
"styles.css",
"page.css",
];
readonly customScripts: string[] = [];
readonly builtinScripts: string[] = [
"search.js",
];
constructor(files?: string[], options?: Partial<Options>) {
if (files) this.files.push(...files);
if (options) Object.assign(this, options);
}
/** The options used for minifying the HTML. */
#htmlMinifyOptions = {
caseSensitive: false,
collapseBooleanAttributes: true,
// collapseInlineTagWhitespace: true,
collapseWhitespace: true,
// conservativeCollapse: true,
continueOnParseError: true,
keepClosingSlash: true,
get minifyCSS() {
return this.__minifyCSS()
? {
level: {
1: {
all: true,
normalizeUrls: false,
},
},
}
: false;
},
get minifyJS() {
return this.__minifyJS();
},
noNewlinesBeforeTagClose: true,
quoteCharacter: "'",
removeAttributeQuotes: false,
removeComments: true,
// removeEmptyAttributes: true,
// removeOptionalTags: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
sortAttributes: true,
// sortClassName: true,
trimCustomFragments: true,
useShortDoctype: true,
__minifyCSS: () => Boolean(this.minify || this.minifyCSS),
__minifyJS: () => Boolean(this.minify || this.minifyJS),
};
#cleanCSS = new CleanCSS({
level: {
1: {
all: true,
normalizeUrls: false,
},
},
});
/** Run a command in the Deno executable. */
protected async $deno<const A extends readonly Printable[]>(
...args: A
): Promise<CommandOutput>;
protected async $deno<A extends readonly unknown[]>(
template: TemplateStringsArray,
...values: A
): Promise<CommandOutput>;
protected async $deno(...params: readonly unknown[]): Promise<CommandOutput> {
// @ts-ignore no overload compatible with this usage (but that's fine)
return await this.$(this.denoExecPath ??= Deno.execPath(), ...params);
}
/** Run a shell command using the Deno.Command API. */
protected async $<
const A extends readonly Printable[],
>(...args: A): Promise<CommandOutput>;
protected async $<
const A extends readonly Printable[],
const O extends Deno.CommandOptions,
>(...args: readonly [...A, O]): Promise<CommandOutput>;
protected async $<
const A extends readonly Printable[],
>(...args: A): Promise<CommandOutput>;
protected async $<
A extends readonly unknown[],
>(template: TemplateStringsArray, ...values: A): Promise<CommandOutput>;
protected async $(
...params: readonly unknown[]
): Promise<CommandOutput> {
const [argv0, ..._argv] = params;
// @ts-ignore no overload compatible with this usage (but that's fine)
const cmd = this.$$(...params);
const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
const decode = decoder.decode.bind(decoder);
const output = await cmd.output();
const { code, stderr: stderrBytes, stdout: stdoutBytes, success } = output;
const stderr = decode(stderrBytes), stdout = decode(stdoutBytes);
if (!success) {
const error = new Error(
`Failed to run command '${argv0}', exit code ${code}.\nStderr: ${stderr}\nStdout: ${stdout}`,
) as Error & {
readonly code: number;
readonly stderr: string | undefined;
readonly stdout: string | undefined;
};
Object.assign(error, { code, stderr, stdout });
this.#fail(error, this.$, this.continueOnError);
}
return { code, stderr, stderrBytes, stdout, stdoutBytes, success };
}
/** Spawns a shell subprocess for a given command using the Deno.ChildProcess API. */
protected $$<S extends string, const O extends Deno.CommandOptions>(
command: S,
options?: O,
): Deno.ChildProcess;
protected $$<
const A extends readonly Printable[],
const O extends Deno.CommandOptions,
>(...args: readonly [...A, O]): Deno.ChildProcess;
protected $$<const A extends readonly Printable[]>(
...args: A
): Deno.ChildProcess;
protected $$<A extends readonly unknown[]>(
template: TemplateStringsArray,
...values: A
): Deno.ChildProcess;
protected $$(...params: readonly unknown[]): Deno.ChildProcess {
const [argv0, ...argv] = params;
const options: Deno.CommandOptions = {};
if (
argv.at(-1) != null && typeof argv.at(-1) === "object" &&
!Array.isArray(argv.at(-1))
) {
Object.assign(options, argv.pop());
}
let args: string[] = [];
if (typeof argv0 === "object" && argv0 != null && "raw" in argv0) {
args = String.raw(
argv0 as TemplateStringsArray,
...argv.map((arg, i, args) => {
if (typeof arg === "string") return arg;
if (arg == null) return "";
if (Array.isArray(arg)) return arg.join(" ");
if (typeof arg === "object") {
return Object.entries(arg).map(
([k, v]) =>
`-${k.length === 1 ? k : "-" + k}${
typeof v === "boolean" && v
? ""
: typeof v === "string"
? "=" + v
: ` ${v ?? ""}`
}`,
).join(" ");
}
if (typeof arg === "function") {
return Reflect.apply(arg, this, args.slice(0, i));
}
return String(arg);
}),
).split(/\s+/g).flat().filter(Boolean).map((a) => a.trim());
} else if (typeof argv0 === "string") {
if (
argv[0] != null && typeof argv[0] === "object" &&
!Array.isArray(argv[0])
) {
Object.assign(options, argv.shift()) as Deno.CommandOptions;
}
args = [argv0, ...argv].flat().map((a) =>
Array.isArray(a) ? a.join(" ") : String(a)
);
} else {
throw new TypeError(
`Expected a string or template literal, got ${typeof argv0}`,
);
}
while (!args.at(0)?.trim?.()) args.shift();
return new Deno.Command(args.shift()!, {
args,
stdin: "null",
stderr: "piped",
stdout: "piped",
...options,
}).spawn();
}
/**
* 1. Generate HTML docs using `deno doc` with custom {@link options} for the
* given {@link files}. The docs are generated in the {@link output} folder
* with the {@link title} as the page title.
* 2. Write the custom CSS and JS to the {@link output} directory.
* 3. If {@link format} is enabled, formats the JavaScript and HTML files.
* 4. If {@link minify} is enabled, it then minifies the syntax and whitespace
* in the HTML/CSS/JS files to reduce file size.
* 5. ????
* 6. Profit!
*/
async run() {
await this.document();
await this.writeCode();
await this.formatCode();
}
/**
* Generate HTML docs using `deno doc` with custom {@link options} for the
* given {@link files}.
*/
async document(options: Options, ...files: string[]): Promise<CommandOutput>;
/** Generate HTML docs with `deno doc` for the given {@link files}. */
async document(...files: string[]): Promise<CommandOutput>;
async document(...args: unknown[]): Promise<CommandOutput> {
let options: Options = { ...this };
let files: string[] = [];
if (args[0] != null && typeof args[0] === "object") {
const opts = args.shift()!;
if (typeof opts === "object") options = { ...options, ...opts };
}
files = args.filter((a): a is string => a != null && typeof a === "string");
this.files = [...new Set([...this.files, ...files])];
return await this.#document();
}
async #document() {
const args = [
"doc",
"--html",
this.reload ? "-r" : "",
this.unstable ? "--unstable" : "",
this.noRemote ? "--no-remote" : "",
`--name=${this.title}`,
`--output=${this.output}`,
...this.files,
].filter(Boolean);
const {
code,
success = false,
stderr: stderrBytes,
stdout: stdoutBytes,
} = await this.$$(Deno.execPath(), ...args).output();
const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
const decode = decoder.decode.bind(decoder);
const stderr = decode(stderrBytes), stdout = decode(stdoutBytes);
if (!success) {
const error = new Error(
`Failed to run command 'deno doc', exit code ${code}.\nStderr: ${stderr}\nStdout: ${stdout}`,
) as Error & {
readonly code: number;
readonly stderr: string | undefined;
readonly stdout: string | undefined;
};
Object.assign(error, { code, stderr, stdout });
this.#fail(error, this.#document, this.continueOnError);
}
return { code, stderr, stderrBytes, stdout, stdoutBytes, success };
}
/** Write the custom CSS and JS to the {@link output} directory. */
async writeCode(): Promise<void> {
const { output } = this;
const _styles = await Promise.allSettled([
["animation.css", css.animation],
["normalize.css", css.normalize],
["variables.css", css.variables],
["styles.css", css.styles],
["page.css", css.page],
].map(async ([name, content]) => {
const path = `${output}/${name}`;
if (this.format || this.formatCSS) {
content = this.#minifyCSS(name, path, content);
}
await Deno.writeTextFile(path, content);
return [path, content] as const;
}));
const searchJs = `${output}/search.js`;
let content = await Deno.readTextFile(searchJs);
content = content.replace(/^(?=const SEARCH_INDEX = )/m, `${js}\n\n`);
await Deno.writeTextFile(searchJs, content);
}
/**
* If {@link format} is enabled, formats the JavaScript and HTML files
* generated for the documentation. If {@link minify} is enabled, it then
* minifies the whitespace in those files to reduce file size.
*/
async formatCode(): Promise<void> {
const { output } = this;
// format the generated JavaScript
if (this.format || this.formatJS) {
await this.$`deno fmt --ext=js --ignore=fuse.js --unstable -q ${output}`;
}
const files: (Deno.DirEntry & { path: string })[] = [
...Array.from(
Deno.readDirSync(output),
({ name, ...rest }) => ({ name, path: `${output}/${name}`, ...rest }),
),
...Array.from(
Deno.readDirSync(`${output}/~`),
({ name, ...rest }) => ({ name, path: `${output}/~/${name}`, ...rest }),
),
].filter(({ isFile, name }) => isFile && /\.(?:html|css|js)$/.test(name));
for (const { name, path } of files) {
// deno-lint-ignore no-unused-vars
let prefix = "", content = await Deno.readTextFile(path);
if (name.endsWith(".html")) {
content = this.#fixSyntaxErrors(content);
// fix awfully generated relative paths
if (this.fixPaths) {
prefix = "../";
content = this.#fixRelativePaths(path, content);
}
// inject stylesheets and preloads into the head
content = this.#injectPreloadsAndLinks(name, path, content);
// format and/or minify the HTML/CSS/JS
if (
(this.minify || this.minifyHTML) ||
(this.format || this.formatHTML)
) {
// we minify the HTML first, and then format it, to ensure a more
// consistent formatting result. It also brings benefits like attr
// sorting and other helpful cleanups.
content = await this.#minifyHTML(name, path, content);
// now format it with the XMLFormatter singleton instance
if (
(this.format || this.formatHTML) &&
!(this.minify || this.minifyHTML)
) {
content = this.#formatHTML(name, path, content);
}
}
} else if (name.endsWith(".css")) {
// minify the CSS
if (this.minify || this.minifyCSS) {
try {
content = this.#minifyCSS(name, path, content);
} catch (cause) {
// this.#fail(
// new Error(`Minifying '${name}' (${path}) failed.`, { cause }),
// this.formatCode,
// );
// noop
}
}
}
// write the formatted and/or minified code back to its original path
await Deno.writeTextFile(path, content);
}
}
#fail(
error: string | Error,
stackCrawlMark?: Function,
continueOnError?: false | undefined,
): never;
#fail(
error: string | Error,
stackCrawlMark: Function,
continueOnError: true,
): Error;
#fail(
error: string | Error,
stackCrawlMark?: Function,
continueOnError?: boolean,
): Error | never;
#fail(
error: string | Error,
stackCrawlMark?: Function,
continueOnError?: boolean,
) {
error = error instanceof Error ? error : new Error(String(error));
Error.captureStackTrace?.(error, stackCrawlMark ?? this.#fail);
error.stack?.slice();
if (!continueOnError) throw error;
return error;
}
#fixRelativePaths(path: string, content: string): string {
if (!this.fixPaths) return content;
let prefix = "";
if (path.includes("~/")) prefix = "../";
if (prefix) content = content.replaceAll("../.././/~/", "./");
content = content.replaceAll(/\.\.\/\.\.\/\.\//g, "./");
content = content.replaceAll(/\.\.\/\.\.\//g, prefix);
return content;
}
/**
* Currently the Deno Documentation generator produces shitty code like this:
* ```html
* Methods
* <div class="section">
* <div class="doc_item" id="method_[Symbol.for("nodejs.util.inspect.custom")]_0">
* <a
* href="#method_[Symbol.for("nodejs.util.inspect.custom")]_0"
* class="anchor"
* aria-label="Anchor"
* tabIndex=-1
* >
* <div style="width: 14px; height: 14px; display: inline-block;">&#128279;</div>
* </a>
* ```
*
* Notice the `id="method_[Symbol.for("nodejs.util.inspect.custom")]_0"` and the
* `href="#method_[Symbol.for("nodejs.util.inspect.custom")]_0"` attributes? Yeah,
* that's not valid HTML. So we need to fix that and fully escape/remove any invalid
* characters from the output. One thing we need to keep in mind while doing this, is
* that all such cases of ID's and their links must be kept **in sync**! Otherwise we
* will just cause a bunch of broken links.
*/
#fixSyntaxErrors(content: string): string {
const errors = content.matchAll(/(?<=<div .*?\bid=").+?(?="[ >])/g);
if (!errors) return content;
const fixes: [bad: string, good: string][] = [];
for (const [id] of errors) {
const good = id.replaceAll(/[^a-z0-9\-_$.]/gi, "-").replaceAll(
/-+/g,
"-",
);
fixes.push([id, good]);
content = content
.replaceAll(`id="${id}"`, `id="${good}"`)
.replaceAll(`href="#${id}"`, `href="#${good}"`);
}
if (this.debug && fixes.length) {
console.log(`Fixed ${fixes.length} syntax errors in the HTML:`);
for (const [bad, good] of fixes) {
console.log(` - \x1b[91m${bad}\x1b[0m`);
console.log(` + \x1b[92m${good}\x1b[0m\n`);
}
}
// janky , but just in case...
return content.replaceAll(
/\b(id|href)="(.*?)Symbol(?:\.(for))\("(.+?)"\)(.*?)"/g,
(_, name, pre, for_, symbol, post) =>
`${name}="${pre}symbol${for_ ? "-for" : ""}-${
symbol.replace(/\W+/g, "")
}${post}"`,
);
}
#injectPreloadsAndLinks(
_name: string,
path: string,
content: string,
): string {
let prefix = "";
const stylesheets = this.customStyleSheets;
const before = `<!-- This section was auto-generated. Do not edit. -->\n`;
const after = `<!-- End of auto-generated section. -->\n`;
if (this.preloads) {
if (this.fixPaths && path.includes("~/")) prefix = "../";
// stylesheet preloads
const stylesheetsToPreload = [...stylesheets, "styles.css", "page.css"].map((href) => `${prefix}${href}`);
const preloads = [before, ...stylesheetsToPreload.map(
(href) => `<link rel="preload" href="${href}" as="style" />`,
), after].join("\n");
// inject preloads before the title opening tag
content = content.replace(/(?=<title>)/, `${preloads}\n`);
if (this.debug) {
console.debug(
`\x1b[1;92m+ ${stylesheetsToPreload.length} stylesheet preloads added to '${path}'.\x1b[0m`,
);
}
}
// stylesheet links
const links = stylesheets.map(
(href) => `<link rel="stylesheet" href="${prefix}${href}" />`,
).concat(after).reverse().concat(before).reverse().join("\n");
// inject the actual stylesheet links after the title closing tag
content = content.replace(/(?<=<\/title>)/, `\n${links}`);
if (this.debug) {
console.debug(
`\x1b[1;92m+ ${stylesheets.length} stylesheet links added to '${path}'.\x1b[0m`,
);
}
return content;
}
#minifyCSS(name: string, path: string, content: string) {
const min = this.#cleanCSS.minify(content);
const { styles, stats, errors, warnings } = min;
if (errors?.length) {
console.warn(`Minifying '${name}' (${path}) produced errors.`, {
errors,
});
// this.#fail(
// Object.assign(new Error(`Minifying '${name}' (${path}) failed.`), {
// errors,
// warnings,
// }),
// this.#minifyCSS,
// );
}
if (warnings?.length) {
console.warn(`\x1b[1;38;2;Minifying '${name}' (${path}) produced warnings.`, {
warnings,
});
}
if (this.debug) {
console.log(
`Minifying '${name}' (${path}) produced the following stats:`,
{ stats },
);
}
return styles ?? content;
}
#formatHTML(name: string, path: string, content: string): string {
try {
content = XMLFormatter.format(content);
return content;
} catch (cause) {
this.#fail(
new Error(
`Formatting the file '${name}' (${path}) failed.`,
{ cause },
),
this.#formatHTML,
);
}
}
async #minifyHTML(
name: string,
path: string,
content: string,
): Promise<string> {
try {
// normalize the content to NFKC
content = String(content).normalize("NFKC");
// // minify the HTML syntax
// content = XMLFormatter.minify(content);
// // remove excessive line breaks and spaces
// content = content.replaceAll(/\n{3,}/g, "\n\n");
// content = content.replaceAll(/[ ]{2,}/g, " ")
content = await minifyHtml(content, this.#htmlMinifyOptions);
return content;
} catch (cause) {
this.#fail(
new Error(
`Minifying the file '${name}' (${path}) failed.`,
{ cause },
),
this.#minifyHTML,
);
}
}
}
export default DenoHTML.run;
export const icons = {
"letter-c": `<path stroke-width="1.5" d="M15.5 9.5v-.312C15.5 7.979 14.52 7 13.313 7H10.5A2.5 2.5 0 0 0 8 9.5v5a2.5 2.5 0 0 0 2.5 2.5h2.813c1.208 0 2.187-.98 2.187-2.187V14.5"/>`,
"letter-c-circle": `<g stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="M15 10v-.25A1.75 1.75 0 0 0 13.25 8H11a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2.25A1.75 1.75 0 0 0 15 14.25V14"/></g>`,
"letter-c-diamond": `<g stroke-width="1.5"><path d="M15 10v-.25A1.75 1.75 0 0 0 13.25 8H11a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2.25A1.75 1.75 0 0 0 15 14.25V14"/><path d="M2.707 10.295a2.41 2.41 0 0 0 0 3.41l7.588 7.588a2.409 2.409 0 0 0 3.41 0l7.588-7.588a2.409 2.409 0 0 0 0-3.41l-7.588-7.588a2.41 2.41 0 0 0-3.41 0z"/></g>`,
"letter-c-hexagon": `<g stroke-width="1.5"><path d="M15 10v-.25A1.75 1.75 0 0 0 13.25 8H11a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2.25A1.75 1.75 0 0 0 15 14.25V14"/><path d="M20.5 15.8V8.2a1.91 1.91 0 0 0-.944-1.645l-6.612-3.8a1.88 1.88 0 0 0-1.888 0l-6.612 3.8A1.895 1.895 0 0 0 3.5 8.2v7.602a1.91 1.91 0 0 0 .944 1.644l6.612 3.8a1.88 1.88 0 0 0 1.888 0l6.612-3.8A1.895 1.895 0 0 0 20.5 15.8"/></g>`,
"letter-c-octagon": `<g stroke-width="1.5"><path d="M15 10v-.25A1.75 1.75 0 0 0 13.25 8H11a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2.25A1.75 1.75 0 0 0 15 14.25V14"/><path d="M7.805 3.469C8.16 3.115 8.451 3 8.937 3h6.126c.486 0 .778.115 1.132.469l4.336 4.336c.354.354.469.646.469 1.132v6.126c0 .5-.125.788-.469 1.132l-4.336 4.336c-.354.354-.646.469-1.132.469H8.937c-.5 0-.788-.125-1.132-.469L3.47 16.195c-.355-.355-.47-.646-.47-1.132V8.937c0-.5.125-.788.469-1.132z"/></g>`,
"letter-c-square": `<g stroke-width="1.5"><path d="M15 10v-.25A1.75 1.75 0 0 0 13.25 8H11a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2.25A1.75 1.75 0 0 0 15 14.25V14"/><path d="M3 12c0-4.243 0-6.364 1.318-7.682C5.636 3 7.758 3 12 3c4.243 0 6.364 0 7.682 1.318C21 5.636 21 7.758 21 12c0 4.243 0 6.364-1.318 7.682C18.364 21 16.242 21 12 21c-4.243 0-6.364 0-7.682-1.318C3 18.364 3 16.242 3 12"/></g>`,
"letter-c-waves": `<g stroke-width="1.5"><path d="M15 10v-.25A1.75 1.75 0 0 0 13.25 8H11a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2.25A1.75 1.75 0 0 0 15 14.25V14"/><path d="M9.713 3.64c.581-.495.872-.743 1.176-.888a2.577 2.577 0 0 1 2.222 0c.304.145.595.393 1.176.888c.599.51 1.207.768 2.007.831c.761.061 1.142.092 1.46.204c.734.26 1.312.837 1.571 1.572c.112.317.143.698.204 1.46c.063.8.32 1.407.83 2.006c.496.581.744.872.889 1.176c.336.703.336 1.52 0 2.222c-.145.304-.393.595-.888 1.176a3.306 3.306 0 0 0-.831 2.007c-.061.761-.092 1.142-.204 1.46a2.577 2.577 0 0 1-1.572 1.571c-.317.112-.698.143-1.46.204c-.8.063-1.407.32-2.006.83c-.581.496-.872.744-1.176.889a2.577 2.577 0 0 1-2.222 0c-.304-.145-.595-.393-1.176-.888a3.306 3.306 0 0 0-2.007-.831c-.761-.061-1.142-.092-1.46-.204a2.577 2.577 0 0 1-1.571-1.572c-.112-.317-.143-.698-.204-1.46a3.305 3.305 0 0 0-.83-2.006c-.496-.581-.744-.872-.89-1.176a2.577 2.577 0 0 1 .001-2.222c.145-.304.393-.595.888-1.176c.52-.611.769-1.223.831-2.007c.061-.761.092-1.142.204-1.46a2.577 2.577 0 0 1 1.572-1.571c.317-.112.698-.143 1.46-.204a3.305 3.305 0 0 0 2.006-.83"/></g>`,
"letter-e": `<path stroke-width="1.5" d="M15.25 7H9v5m6.25 5H9v-5m0 0h5"/>`,
"letter-e-circle": `<g stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="M14.5 8h-5v4m5 4h-5v-4m0 0h4"/></g>`,
"letter-e-diamond": `<g stroke-width="1.5"><path d="M14.5 8h-5v4m5 4h-5v-4m0 0h4"/><path d="M2.707 10.295a2.41 2.41 0 0 0 0 3.41l7.588 7.588a2.409 2.409 0 0 0 3.41 0l7.588-7.588a2.409 2.409 0 0 0 0-3.41l-7.588-7.588a2.41 2.41 0 0 0-3.41 0z"/></g>`,
"letter-e-hexagon": `<g stroke-width="1.5"><path d="M14.5 8h-5v4m5 4h-5v-4m0 0h4"/><path d="M20.5 15.8V8.2a1.91 1.91 0 0 0-.944-1.645l-6.612-3.8a1.88 1.88 0 0 0-1.888 0l-6.612 3.8A1.895 1.895 0 0 0 3.5 8.2v7.602a1.91 1.91 0 0 0 .944 1.644l6.612 3.8a1.88 1.88 0 0 0 1.888 0l6.612-3.8A1.895 1.895 0 0 0 20.5 15.8"/></g>`,
"letter-e-octagon": `<g stroke-width="1.5"><path d="M14.5 8h-5v4m5 4h-5v-4m0 0h4"/><path d="M7.805 3.469C8.16 3.115 8.451 3 8.937 3h6.126c.486 0 .778.115 1.132.469l4.336 4.336c.354.354.469.646.469 1.132v6.126c0 .5-.125.788-.469 1.132l-4.336 4.336c-.354.354-.646.469-1.132.469H8.937c-.5 0-.788-.125-1.132-.469L3.47 16.195c-.355-.355-.47-.646-.47-1.132V8.937c0-.5.125-.788.469-1.132z"/></g>`,
"letter-e-square": `<g stroke-width="1.5"><path d="M14.5 8h-5v4m5 4h-5v-4m0 0h4"/><path d="M3 12c0-4.243 0-6.364 1.318-7.682C5.636 3 7.758 3 12 3c4.243 0 6.364 0 7.682 1.318C21 5.636 21 7.758 21 12c0 4.243 0 6.364-1.318 7.682C18.364 21 16.242 21 12 21c-4.243 0-6.364 0-7.682-1.318C3 18.364 3 16.242 3 12"/></g>`,
"letter-e-waves": `<g stroke-width="1.5"><path d="M14.5 8h-5v4m5 4h-5v-4m0 0h4"/><path d="M9.713 3.64c.581-.495.872-.743 1.176-.888a2.577 2.577 0 0 1 2.222 0c.304.145.595.393 1.176.888c.599.51 1.207.768 2.007.831c.761.061 1.142.092 1.46.204c.734.26 1.312.837 1.571 1.572c.112.317.143.698.204 1.46c.063.8.32 1.407.83 2.006c.496.581.744.872.889 1.176c.336.703.336 1.52 0 2.222c-.145.304-.393.595-.888 1.176a3.306 3.306 0 0 0-.831 2.007c-.061.761-.092 1.142-.204 1.46a2.577 2.577 0 0 1-1.572 1.571c-.317.112-.698.143-1.46.204c-.8.063-1.407.32-2.006.83c-.581.496-.872.744-1.176.889a2.577 2.577 0 0 1-2.222 0c-.304-.145-.595-.393-1.176-.888a3.306 3.306 0 0 0-2.007-.831c-.761-.061-1.142-.092-1.46-.204a2.577 2.577 0 0 1-1.571-1.572c-.112-.317-.143-.698-.204-1.46a3.305 3.305 0 0 0-.83-2.006c-.496-.581-.744-.872-.89-1.176a2.577 2.577 0 0 1 .001-2.222c.145-.304.393-.595.888-1.176c.52-.611.769-1.223.831-2.007c.061-.761.092-1.142.204-1.46a2.577 2.577 0 0 1 1.572-1.571c.317-.112.698-.143 1.46-.204a3.305 3.305 0 0 0 2.006-.83"/></g>`,
"letter-f": `<path stroke-width="1.5" d="M15.5 7H9.25v5m0 0v5m0-5h5"/>`,
"letter-f-circle": `<g stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="M14.75 8h-5v4m0 0v4m0-4h4"/></g>`,
"letter-f-diamond": `<g stroke-width="1.5"><path d="M14.75 8h-5v4m0 0v4m0-4h4"/><path d="M2.707 10.295a2.41 2.41 0 0 0 0 3.41l7.588 7.588a2.409 2.409 0 0 0 3.41 0l7.588-7.588a2.409 2.409 0 0 0 0-3.41l-7.588-7.588a2.41 2.41 0 0 0-3.41 0z"/></g>`,
"letter-f-hexagon": `<g stroke-width="1.5"><path d="M14.75 8h-5v4m0 0v4m0-4h4"/><path d="M20.5 15.8V8.2a1.91 1.91 0 0 0-.944-1.645l-6.612-3.8a1.88 1.88 0 0 0-1.888 0l-6.612 3.8A1.895 1.895 0 0 0 3.5 8.2v7.602a1.91 1.91 0 0 0 .944 1.644l6.612 3.8a1.88 1.88 0 0 0 1.888 0l6.612-3.8A1.895 1.895 0 0 0 20.5 15.8"/></g>`,
"letter-f-octagon": `<g stroke-width="1.5"><path d="M14.75 8h-5v4m0 0v4m0-4h4"/><path d="M7.805 3.469C8.16 3.115 8.451 3 8.937 3h6.126c.486 0 .778.115 1.132.469l4.336 4.336c.354.354.469.646.469 1.132v6.126c0 .5-.125.788-.469 1.132l-4.336 4.336c-.354.354-.646.469-1.132.469H8.937c-.5 0-.788-.125-1.132-.469L3.47 16.195c-.355-.355-.47-.646-.47-1.132V8.937c0-.5.125-.788.469-1.132z"/></g>`,
"letter-f-square": `<g stroke-width="1.5"><path d="M14.75 8h-5v4m0 0v4m0-4h4"/><path d="M3 12c0-4.243 0-6.364 1.318-7.682C5.636 3 7.758 3 12 3c4.243 0 6.364 0 7.682 1.318C21 5.636 21 7.758 21 12c0 4.243 0 6.364-1.318 7.682C18.364 21 16.242 21 12 21c-4.243 0-6.364 0-7.682-1.318C3 18.364 3 16.242 3 12"/></g>`,
"letter-f-waves": `<g stroke-width="1.5"><path d="M14.75 8h-5v4m0 0v4m0-4h4"/><path d="M9.713 3.64c.581-.495.872-.743 1.176-.888a2.577 2.577 0 0 1 2.222 0c.304.145.595.393 1.176.888c.599.51 1.207.768 2.007.831c.761.061 1.142.092 1.46.204c.734.26 1.312.837 1.571 1.572c.112.317.143.698.204 1.46c.063.8.32 1.407.83 2.006c.496.581.744.872.889 1.176c.336.703.336 1.52 0 2.222c-.145.304-.393.595-.888 1.176a3.306 3.306 0 0 0-.831 2.007c-.061.761-.092 1.142-.204 1.46a2.577 2.577 0 0 1-1.572 1.571c-.317.112-.698.143-1.46.204c-.8.063-1.407.32-2.006.83c-.581.496-.872.744-1.176.889a2.577 2.577 0 0 1-2.222 0c-.304-.145-.595-.393-1.176-.888a3.306 3.306 0 0 0-2.007-.831c-.761-.061-1.142-.092-1.46-.204a2.577 2.577 0 0 1-1.571-1.572c-.112-.317-.143-.698-.204-1.46a3.305 3.305 0 0 0-.83-2.006c-.496-.581-.744-.872-.89-1.176a2.577 2.577 0 0 1 .001-2.222c.145-.304.393-.595.888-1.176c.52-.611.769-1.223.831-2.007c.061-.761.092-1.142.204-1.46a2.577 2.577 0 0 1 1.572-1.571c.317-.112.698-.143 1.46-.204a3.305 3.305 0 0 0 2.006-.83"/></g>`,
"letter-i": `<path stroke-width="1.5" d="M9 7h3.125m0 0h3.125m-3.125 0v10m3.125 0h-3.125m0 0H9"/>`,
"letter-i-circle": `<g stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="M9.5 8H12m0 0h2.5M12 8v8m2.5 0H12m0 0H9.5"/></g>`,
"letter-i-diamond": `<path stroke-width="1.5" d="M9.5 8H12m0 0h2.5M12 8v8m2.5 0H12m0 0H9.5m-6.793-5.705a2.41 2.41 0 0 0 0 3.41l7.588 7.588a2.409 2.409 0 0 0 3.41 0l7.588-7.588a2.409 2.409 0 0 0 0-3.41l-7.588-7.588a2.41 2.41 0 0 0-3.41 0z"/>`,
"letter-i-hexagon": `<path stroke-width="1.5" d="M9.5 8H12m0 0h2.5M12 8v8m2.5 0H12m0 0H9.5m11-.2V8.2a1.91 1.91 0 0 0-.944-1.645l-6.612-3.8a1.88 1.88 0 0 0-1.888 0l-6.612 3.8A1.895 1.895 0 0 0 3.5 8.2v7.602a1.91 1.91 0 0 0 .944 1.644l6.612 3.8a1.88 1.88 0 0 0 1.888 0l6.612-3.8A1.895 1.895 0 0 0 20.5 15.8"/>`,
"letter-i-octagon": `<path stroke-width="1.5" d="M9.5 8H12m0 0h2.5M12 8v8m2.5 0H12m0 0H9.5M7.805 3.469C8.16 3.115 8.451 3 8.937 3h6.126c.486 0 .778.115 1.132.469l4.336 4.336c.354.354.469.646.469 1.132v6.126c0 .5-.125.788-.469 1.132l-4.336 4.336c-.354.354-.646.469-1.132.469H8.937c-.5 0-.788-.125-1.132-.469L3.47 16.195c-.355-.355-.47-.646-.47-1.132V8.937c0-.5.125-.788.469-1.132z"/>`,
"letter-i-square": `<path stroke-width="1.5" d="M9.5 8H12m0 0h2.5M12 8v8m2.5 0H12m0 0H9.5M3 12c0-4.243 0-6.364 1.318-7.682C5.636 3 7.758 3 12 3c4.243 0 6.364 0 7.682 1.318C21 5.636 21 7.758 21 12c0 4.243 0 6.364-1.318 7.682C18.364 21 16.242 21 12 21c-4.243 0-6.364 0-7.682-1.318C3 18.364 3 16.242 3 12"/>`,
"letter-i-waves": `<path stroke-width="1.5" d="M9.5 8H12m0 0h2.5M12 8v8m2.5 0H12m0 0H9.5m.213-12.36c.581-.495.872-.743 1.176-.888a2.577 2.577 0 0 1 2.222 0c.304.145.595.393 1.176.888c.599.51 1.207.768 2.007.831c.761.061 1.142.092 1.46.204c.734.26 1.312.837 1.571 1.572c.112.317.143.698.204 1.46c.063.8.32 1.407.83 2.006c.496.581.744.872.889 1.176c.336.703.336 1.52 0 2.222c-.145.304-.393.595-.888 1.176a3.306 3.306 0 0 0-.831 2.007c-.061.761-.092 1.142-.204 1.46a2.577 2.577 0 0 1-1.572 1.571c-.317.112-.698.143-1.46.204c-.8.063-1.407.32-2.006.83c-.581.496-.872.744-1.176.889a2.577 2.577 0 0 1-2.222 0c-.304-.145-.595-.393-1.176-.888a3.306 3.306 0 0 0-2.007-.831c-.761-.061-1.142-.092-1.46-.204a2.577 2.577 0 0 1-1.571-1.572c-.112-.317-.143-.698-.204-1.46a3.305 3.305 0 0 0-.83-2.006c-.496-.581-.744-.872-.89-1.176a2.577 2.577 0 0 1 .001-2.222c.145-.304.393-.595.888-1.176c.52-.611.769-1.223.831-2.007c.061-.761.092-1.142.204-1.46a2.577 2.577 0 0 1 1.572-1.571c.317-.112.698-.143 1.46-.204a3.305 3.305 0 0 0 2.006-.83"/>`,
"letter-m": `<path stroke-width="1.5" d="M8 17V7l3.75 5l3.75-5v10"/>`,
"letter-m-circle": `<g stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="M9 16V8l3 4l3-4v8"/></g>`,
"letter-m-diamond": `<g stroke-width="1.5"><path d="M9 16V8l3 4l3-4v8"/><path d="M2.707 10.295a2.41 2.41 0 0 0 0 3.41l7.588 7.588a2.409 2.409 0 0 0 3.41 0l7.588-7.588a2.409 2.409 0 0 0 0-3.41l-7.588-7.588a2.41 2.41 0 0 0-3.41 0z"/></g>`,
"letter-m-hexagon": `<g stroke-width="1.5"><path d="M9 16V8l3 4l3-4v8"/><path d="M20.5 15.8V8.2a1.91 1.91 0 0 0-.944-1.645l-6.612-3.8a1.88 1.88 0 0 0-1.888 0l-6.612 3.8A1.895 1.895 0 0 0 3.5 8.2v7.602a1.91 1.91 0 0 0 .944 1.644l6.612 3.8a1.88 1.88 0 0 0 1.888 0l6.612-3.8A1.895 1.895 0 0 0 20.5 15.8"/></g>`,
"letter-m-octagon": `<g stroke-width="1.5"><path d="M9 16V8l3 4l3-4v8"/><path d="M7.805 3.469C8.16 3.115 8.451 3 8.937 3h6.126c.486 0 .778.115 1.132.469l4.336 4.336c.354.354.469.646.469 1.132v6.126c0 .5-.125.788-.469 1.132l-4.336 4.336c-.354.354-.646.469-1.132.469H8.937c-.5 0-.788-.125-1.132-.469L3.47 16.195c-.355-.355-.47-.646-.47-1.132V8.937c0-.5.125-.788.469-1.132z"/></g>`,
"letter-m-square": `<g stroke-width="1.5"><path d="M9 16V8l3 4l3-4v8"/><path d="M3 12c0-4.243 0-6.364 1.318-7.682C5.636 3 7.758 3 12 3c4.243 0 6.364 0 7.682 1.318C21 5.636 21 7.758 21 12c0 4.243 0 6.364-1.318 7.682C18.364 21 16.242 21 12 21c-4.243 0-6.364 0-7.682-1.318C3 18.364 3 16.242 3 12"/></g>`,
"letter-m-waves": `<g stroke-width="1.5"><path d="M9 16V8l3 4l3-4v8"/><path d="M9.713 3.64c.581-.495.872-.743 1.176-.888a2.577 2.577 0 0 1 2.222 0c.304.145.595.393 1.176.888c.599.51 1.207.768 2.007.831c.761.061 1.142.092 1.46.204c.734.26 1.312.837 1.571 1.572c.112.317.143.698.204 1.46c.063.8.32 1.407.83 2.006c.496.581.744.872.889 1.176c.336.703.336 1.52 0 2.222c-.145.304-.393.595-.888 1.176a3.306 3.306 0 0 0-.831 2.007c-.061.761-.092 1.142-.204 1.46a2.577 2.577 0 0 1-1.572 1.571c-.317.112-.698.143-1.46.204c-.8.063-1.407.32-2.006.83c-.581.496-.872.744-1.176.889a2.577 2.577 0 0 1-2.222 0c-.304-.145-.595-.393-1.176-.888a3.306 3.306 0 0 0-2.007-.831c-.761-.061-1.142-.092-1.46-.204a2.577 2.577 0 0 1-1.571-1.572c-.112-.317-.143-.698-.204-1.46a3.305 3.305 0 0 0-.83-2.006c-.496-.581-.744-.872-.89-1.176a2.577 2.577 0 0 1 .001-2.222c.145-.304.393-.595.888-1.176c.52-.611.769-1.223.831-2.007c.061-.761.092-1.142.204-1.46a2.577 2.577 0 0 1 1.572-1.571c.317-.112.698-.143 1.46-.204a3.305 3.305 0 0 0 2.006-.83"/></g>`,
"letter-n": `<path stroke-width="1.5" d="M8 17V7l7.5 10V7"/>`,
"letter-n-circle": `<g stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="M9 16V8l6 8V8"/></g>`,
"letter-n-diamond": `<g stroke-width="1.5"><path d="M9 16V8l6 8V8"/><path d="M2.707 10.295a2.41 2.41 0 0 0 0 3.41l7.588 7.588a2.409 2.409 0 0 0 3.41 0l7.588-7.588a2.409 2.409 0 0 0 0-3.41l-7.588-7.588a2.41 2.41 0 0 0-3.41 0z"/></g>`,
"letter-n-hexagon": `<g stroke-width="1.5"><path d="M9 16V8l6 8V8"/><path d="M20.5 15.8V8.2a1.91 1.91 0 0 0-.944-1.645l-6.612-3.8a1.88 1.88 0 0 0-1.888 0l-6.612 3.8A1.895 1.895 0 0 0 3.5 8.2v7.602a1.91 1.91 0 0 0 .944 1.644l6.612 3.8a1.88 1.88 0 0 0 1.888 0l6.612-3.8A1.895 1.895 0 0 0 20.5 15.8"/></g>`,
"letter-n-octagon": `<g stroke-width="1.5"><path d="M9 16V8l6 8V8"/><path d="M7.805 3.469C8.16 3.115 8.451 3 8.937 3h6.126c.486 0 .778.115 1.132.469l4.336 4.336c.354.354.469.646.469 1.132v6.126c0 .5-.125.788-.469 1.132l-4.336 4.336c-.354.354-.646.469-1.132.469H8.937c-.5 0-.788-.125-1.132-.469L3.47 16.195c-.355-.355-.47-.646-.47-1.132V8.937c0-.5.125-.788.469-1.132z"/></g>`,
"letter-n-square": `<g stroke-width="1.5"><path d="M9 16V8l6 8V8"/><path d="M3 12c0-4.243 0-6.364 1.318-7.682C5.636 3 7.758 3 12 3c4.243 0 6.364 0 7.682 1.318C21 5.636 21 7.758 21 12c0 4.243 0 6.364-1.318 7.682C18.364 21 16.242 21 12 21c-4.243 0-6.364 0-7.682-1.318C3 18.364 3 16.242 3 12"/></g>`,
"letter-n-waves": `<g stroke-width="1.5"><path d="M9 16V8l6 8V8"/><path d="M9.713 3.64c.581-.495.872-.743 1.176-.888a2.577 2.577 0 0 1 2.222 0c.304.145.595.393 1.176.888c.599.51 1.207.768 2.007.831c.761.061 1.142.092 1.46.204c.734.26 1.312.837 1.571 1.572c.112.317.143.698.204 1.46c.063.8.32 1.407.83 2.006c.496.581.744.872.889 1.176c.336.703.336 1.52 0 2.222c-.145.304-.393.595-.888 1.176a3.306 3.306 0 0 0-.831 2.007c-.061.761-.092 1.142-.204 1.46a2.577 2.577 0 0 1-1.572 1.571c-.317.112-.698.143-1.46.204c-.8.063-1.407.32-2.006.83c-.581.496-.872.744-1.176.889a2.577 2.577 0 0 1-2.222 0c-.304-.145-.595-.393-1.176-.888a3.306 3.306 0 0 0-2.007-.831c-.761-.061-1.142-.092-1.46-.204a2.577 2.577 0 0 1-1.571-1.572c-.112-.317-.143-.698-.204-1.46a3.305 3.305 0 0 0-.83-2.006c-.496-.581-.744-.872-.89-1.176a2.577 2.577 0 0 1 .001-2.222c.145-.304.393-.595.888-1.176c.52-.611.769-1.223.831-2.007c.061-.761.092-1.142.204-1.46a2.577 2.577 0 0 1 1.572-1.571c.317-.112.698-.143 1.46-.204a3.305 3.305 0 0 0 2.006-.83"/></g>`,
"letter-t": `<path stroke-width="1.5" d="M8 7h3.75m0 0h3.75m-3.75 0v10"/>`,
"letter-t-circle": `<g stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="M9 8.25h3m0 0h3m-3 0v8"/></g>`,
"letter-t-diamond": `<path stroke-width="1.5" d="M9 8.25h3m0 0h3m-3 0v8m-9.293-5.955a2.41 2.41 0 0 0 0 3.41l7.588 7.588a2.409 2.409 0 0 0 3.41 0l7.588-7.588a2.409 2.409 0 0 0 0-3.41l-7.588-7.588a2.41 2.41 0 0 0-3.41 0z"/>`,
"letter-t-hexagon": `<path stroke-width="1.5" d="M9 8.25h3m0 0h3m-3 0v8m8.5-.45V8.2a1.91 1.91 0 0 0-.944-1.645l-6.612-3.8a1.88 1.88 0 0 0-1.888 0l-6.612 3.8A1.895 1.895 0 0 0 3.5 8.2v7.602a1.91 1.91 0 0 0 .944 1.644l6.612 3.8a1.88 1.88 0 0 0 1.888 0l6.612-3.8A1.895 1.895 0 0 0 20.5 15.8"/>`,
"letter-t-octagon": `<path stroke-width="1.5" d="M9 8.25h3m0 0h3m-3 0v8M7.805 3.469C8.16 3.115 8.451 3 8.937 3h6.126c.486 0 .778.115 1.132.469l4.336 4.336c.354.354.469.646.469 1.132v6.126c0 .5-.125.788-.469 1.132l-4.336 4.336c-.354.354-.646.469-1.132.469H8.937c-.5 0-.788-.125-1.132-.469L3.47 16.195c-.355-.355-.47-.646-.47-1.132V8.937c0-.5.125-.788.469-1.132z"/>`,
"letter-t-square": `<path stroke-width="1.5" d="M9 8.25h3m0 0h3m-3 0v8M3 12c0-4.243 0-6.364 1.318-7.682C5.636 3 7.758 3 12 3c4.243 0 6.364 0 7.682 1.318C21 5.636 21 7.758 21 12c0 4.243 0 6.364-1.318 7.682C18.364 21 16.242 21 12 21c-4.243 0-6.364 0-7.682-1.318C3 18.364 3 16.242 3 12"/>`,
"letter-t-waves": `<path stroke-width="1.5" d="M9 8.25h3m0 0h3m-3 0v8M9.713 3.64c.581-.495.872-.743 1.176-.888a2.577 2.577 0 0 1 2.222 0c.304.145.595.393 1.176.888c.599.51 1.207.768 2.007.831c.761.061 1.142.092 1.46.204c.734.26 1.312.837 1.571 1.572c.112.317.143.698.204 1.46c.063.8.32 1.407.83 2.006c.496.581.744.872.889 1.176c.336.703.336 1.52 0 2.222c-.145.304-.393.595-.888 1.176a3.306 3.306 0 0 0-.831 2.007c-.061.761-.092 1.142-.204 1.46a2.577 2.577 0 0 1-1.572 1.571c-.317.112-.698.143-1.46.204c-.8.063-1.407.32-2.006.83c-.581.496-.872.744-1.176.889a2.577 2.577 0 0 1-2.222 0c-.304-.145-.595-.393-1.176-.888a3.306 3.306 0 0 0-2.007-.831c-.761-.061-1.142-.092-1.46-.204a2.577 2.577 0 0 1-1.571-1.572c-.112-.317-.143-.698-.204-1.46a3.305 3.305 0 0 0-.83-2.006c-.496-.581-.744-.872-.89-1.176a2.577 2.577 0 0 1 .001-2.222c.145-.304.393-.595.888-1.176c.52-.611.769-1.223.831-2.007c.061-.761.092-1.142.204-1.46a2.577 2.577 0 0 1 1.572-1.571c.317-.112.698-.143 1.46-.204a3.305 3.305 0 0 0 2.006-.83"/>`,
"letter-u-hexagon": `<g stroke-width="1.5"><path d="M9 8v6a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2V8"/><path d="M20.5 15.8V8.2a1.91 1.91 0 0 0-.944-1.645l-6.612-3.8a1.88 1.88 0 0 0-1.888 0l-6.612 3.8A1.895 1.895 0 0 0 3.5 8.2v7.602a1.91 1.91 0 0 0 .944 1.644l6.612 3.8a1.88 1.88 0 0 0 1.888 0l6.612-3.8A1.895 1.895 0 0 0 20.5 15.8"/></g>`,
"letter-v": `<path stroke-width="1.5" d="m8 7l3.75 10L15.5 7"/>`,
"letter-v-circle": `<g stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="m9 8.25l3 8l3-8"/></g>`,
"letter-v-diamond": `<g stroke-width="1.5"><path d="m9 8.25l3 8l3-8"/><path d="M2.707 10.295a2.41 2.41 0 0 0 0 3.41l7.588 7.588a2.409 2.409 0 0 0 3.41 0l7.588-7.588a2.409 2.409 0 0 0 0-3.41l-7.588-7.588a2.41 2.41 0 0 0-3.41 0z"/></g>`,
"letter-v-hexagon": `<g stroke-width="1.5"><path d="m9 8.25l3 8l3-8"/><path d="M20.5 15.8V8.2a1.91 1.91 0 0 0-.944-1.645l-6.612-3.8a1.88 1.88 0 0 0-1.888 0l-6.612 3.8A1.895 1.895 0 0 0 3.5 8.2v7.602a1.91 1.91 0 0 0 .944 1.644l6.612 3.8a1.88 1.88 0 0 0 1.888 0l6.612-3.8A1.895 1.895 0 0 0 20.5 15.8"/></g>`,
"letter-v-octagon": `<g stroke-width="1.5"><path d="m9 8.25l3 8l3-8"/><path d="M7.805 3.469C8.16 3.115 8.451 3 8.937 3h6.126c.486 0 .778.115 1.132.469l4.336 4.336c.354.354.469.646.469 1.132v6.126c0 .5-.125.788-.469 1.132l-4.336 4.336c-.354.354-.646.469-1.132.469H8.937c-.5 0-.788-.125-1.132-.469L3.47 16.195c-.355-.355-.47-.646-.47-1.132V8.937c0-.5.125-.788.469-1.132z"/></g>`,
"letter-v-square": `<g stroke-width="1.5"><path d="m9 8.25l3 8l3-8"/><path d="M3 12c0-4.243 0-6.364 1.318-7.682C5.636 3 7.758 3 12 3c4.243 0 6.364 0 7.682 1.318C21 5.636 21 7.758 21 12c0 4.243 0 6.364-1.318 7.682C18.364 21 16.242 21 12 21c-4.243 0-6.364 0-7.682-1.318C3 18.364 3 16.242 3 12"/></g>`,
"letter-v-waves": `<g stroke-width="1.5"><path d="m9 8.25l3 8l3-8"/><path d="M9.713 3.64c.581-.495.872-.743 1.176-.888a2.577 2.577 0 0 1 2.222 0c.304.145.595.393 1.176.888c.599.51 1.207.768 2.007.831c.761.061 1.142.092 1.46.204c.734.26 1.312.837 1.571 1.572c.112.317.143.698.204 1.46c.063.8.32 1.407.83 2.006c.496.581.744.872.889 1.176c.336.703.336 1.52 0 2.222c-.145.304-.393.595-.888 1.176a3.306 3.306 0 0 0-.831 2.007c-.061.761-.092 1.142-.204 1.46a2.577 2.577 0 0 1-1.572 1.571c-.317.112-.698.143-1.46.204c-.8.063-1.407.32-2.006.83c-.581.496-.872.744-1.176.889a2.577 2.577 0 0 1-2.222 0c-.304-.145-.595-.393-1.176-.888a3.306 3.306 0 0 0-2.007-.831c-.761-.061-1.142-.092-1.46-.204a2.577 2.577 0 0 1-1.571-1.572c-.112-.317-.143-.698-.204-1.46a3.305 3.305 0 0 0-.83-2.006c-.496-.581-.744-.872-.89-1.176a2.577 2.577 0 0 1 .001-2.222c.145-.304.393-.595.888-1.176c.52-.611.769-1.223.831-2.007c.061-.761.092-1.142.204-1.46a2.577 2.577 0 0 1 1.572-1.571c.317-.112.698-.143 1.46-.204a3.305 3.305 0 0 0 2.006-.83"/></g>`,
"t-letter-c": `<path stroke-width="1.5" d="M18 9a5 5 0 0 0-5-5h-2a5 5 0 0 0-5 5v6a5 5 0 0 0 5 5h2a5 5 0 0 0 5-5"/>`,
"t-letter-c-sm": `<path stroke-width="1.5" d="M14 10a2 2 0 1 0-4 0v4a2 2 0 1 0 4 0"/>`,
"t-letter-e": `<path stroke-width="1.5" d="M17 4H7v16h10M7 12h8"/>`,
"t-letter-e-sm": `<path stroke-width="1.5" d="M14 8h-4v8h4m-4-4h2.5"/>`,
"t-letter-f": `<path stroke-width="1.5" d="M17 4H7v16m0-8h8"/>`,
"t-letter-f-sm": `<path stroke-width="1.5" d="M10 12h3m1-4h-4v8"/>`,
"t-letter-i": `<path stroke-width="1.5" d="M12 4v16"/>`,
"t-letter-i-sm": `<path stroke-width="1.5" d="M12 8v8"/>`,
"t-letter-m": `<path stroke-width="1.5" d="M6 20V4l6 14l6-14v16"/>`,
"t-letter-m-sm": `<path stroke-width="1.5" d="M9 16V8l3 5l3-5v8"/>`,
"t-letter-n": `<path stroke-width="1.5" d="M7 20V4l10 16V4"/>`,
"t-letter-n-sm": `<path stroke-width="1.5" d="M10 16V8l4 8V8"/>`,
"t-letter-t": `<path stroke-width="1.5" d="M6 4h12m-6 0v16"/>`,
"t-letter-t-sm": `<path stroke-width="1.5" d="M10 8h4m-2 0v8"/>`,
"t-letter-v": `<path stroke-width="1.5" d="m6 4l6 16l6-16"/>`,
"t-letter-v-sm": `<path stroke-width="1.5" d="m10 8l2 8l2-8"/>`,
"td-letter-c": `<path fill="currentColor" d="M17 4H9a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8v-2H9V6h8z"/>`,
"td-letter-e": `<path fill="currentColor" d="M7 6a2 2 0 0 1 2-2h8v2H9v5h8v2H9v5h8v2H9a2 2 0 0 1-2-2z"/>`,
"td-letter-f": `<path fill="currentColor" d="M8 6a2 2 0 0 1 2-2h7v2h-7v5h7v2h-7v7H8z"/>`,
"td-letter-i": `<path fill="currentColor" d="M8.5 4h7v2H13v12h2.5v2h-7v-2H11V6H8.5z"/>`,
"td-letter-m": `<path fill="currentColor" d="M18 20V6h-5v14h-2V6H6v14H4V4h14a2 2 0 0 1 2 2v14z"/>`,
"td-letter-n": `<path fill="currentColor" d="M8.5 20V6h7v14h2V6a2 2 0 0 0-2-2h-9v16z"/>`,
"td-letter-t": `<path fill="currentColor" d="M6.5 4h11v2H13v14h-2V6H6.5z"/>`,
"td-letter-v": `<path fill="currentColor" d="M15 4v10.273l-3 3.652l-3-3.652V4H7v10.273a2 2 0 0 0 .455 1.27l3.772 4.592h1.546l3.772-4.592a2 2 0 0 0 .455-1.27V4z"/>`,
get<K extends Exclude<string & keyof typeof icons, "get">>(
id: K,
size: string | number = "1rem",
fill: string | undefined = "none",
stroke: string | undefined = "currentColor",
color: string | undefined = "inherit"
) {
if (!(id in this && id !== "get") || typeof this[id] !== "string") {
throw new Error(`Icon ${id} not found`);
}
const body = this[id];
return `<svg viewBox="0 0 24 24" id="${id}" color="${color}" fill="${fill}" stroke="${stroke}" stroke-linecap="round" stroke-linejoin="round" width="${size ??= "1rem"}" height="${size}">${body}</symbol>`;
},
};
export type icons = Omit<typeof icons, "get">;
export default icons;
export default `
/* AUTO-GENERATED CONTENT BELOW. DO NOT EDIT. */
// wrapped in IIFE to avoid collisions or global scope pollution
(function () {
const disposables = [];
window.addEventListener("unload", () => {
while (disposables.length) {
const fn = disposables.pop();
fn?.();
};
});
const COLOR_SCHEME_STORAGE_KEY = "deno-doc-color-scheme";
// #region current symbol highlighting
// we pull the last 2 segments of the URL to get the current symbol name
// (since the normal method of doing this is broken in Deno v1.39.0, and
// the meta element in the head is empty for all pages)
const basename = window.location.href.split(/\//).slice(-2).join("/");
const currentLink = document.querySelector(
\`#sidepanel li a[href$="\${basename}"]\`,
);
if (currentLink) {
currentLink.classList.add("active");
const kind = currentLink.querySelector("div[class^=])
const span = currentLink.querySelector("span");
if (span) span.classList.add("active");
// trigger the scroll event in the next microtask
queueMicrotask(() => {
currentLink.parentNode.scrollIntoView({ behavior: "smooth" });
});
}
// #endregion current symbol highlighting
// #region color-scheme switching
const darkMode = window.matchMedia("(prefers-color-scheme: dark)");
const rootClassList = document.documentElement.classList;
// toggle color scheme class names
function toggleClassNames(matches = false) {
const current = matches ? "dark" : "light";
const other = matches ? "light" : "dark";
rootClassList.add(current);
rootClassList.remove(other);
}
// get user color scheme preference
function getUserColorScheme() {
let scheme = localStorage.getItem(COLOR_SCHEME_STORAGE_KEY);
if (!["light", "dark", "auto"].includes(scheme)) {
localStorage.setItem(COLOR_SCHEME_STORAGE_KEY, scheme = "auto");
}
return scheme;
}
// set user color scheme preference
function setColorScheme(scheme) {
if (!["light", "dark", "auto"].includes(scheme)) scheme = "auto";
localStorage.setItem(COLOR_SCHEME_STORAGE_KEY, scheme);
if (scheme === "auto") {
toggleClassNames(darkMode.matches)
} else {
toggleClassNames(scheme === "dark");
}
}
// toggle user color scheme preference
function cycleColorScheme(scheme = getUserColorScheme()) {
if (scheme === "auto") return setColorScheme("dark");
if (scheme === "dark") return setColorScheme("light");
/* else */ return setColorScheme("auto");
}
// switch color scheme based on user preference
function switchColorScheme({ matches = false } = {}) {
const scheme = getUserColorScheme();
if (scheme === "auto") {
return toggleClassNames(matches);
} else if (scheme === "dark") {
return setColorScheme("dark");
} else {
return setColorScheme("light");
}
}
// check user preference on page load
const initDarkMode = () => switchColorScheme(darkMode);
addEventListener("DOMContentLoaded", initDarkMode, { once: true });
// change color scheme when user preference changes, if
// user preference is "auto"
darkMode.addEventListener("change", switchColorScheme);
// cleanup
disposables.push(
() => darkMode.removeEventListener("change", switchColorScheme),
() => removeEventListener("DOMContentLoaded", initDarkMode),
);
// #endregion color-scheme switching
// #region color-scheme toggle button
let toggle = document.querySelector(".color-scheme-toggle");
// create the button if it does not already exist
if (!toggle) {
toggle = document.createElement("button");
document.body.appendChild(toggle);
}
// set initial state
toggle.classList.add("color-scheme-toggle", "active");
toggle.setAttribute("aria-label", "Switch color scheme");
toggle.setAttribute("title", "Switch color scheme");
toggle.setAttribute("type", "button");
toggle.innerHTML = \`<span hidden class="sr-only">Toggle Dark Mode</span>\`;
// cycle through color schemes on click/tap/enter
const colorEventHandler = (e) => {
e.preventDefault();
if (!("key" in e) || e.key === "Enter") cycleColorScheme();
};
// add event listeners
toggle.addEventListener("touchstart", colorEventHandler, { passive: true });
toggle.addEventListener("click", colorEventHandler);
toggle.addEventListener("keydown", colorEventHandler);
// cleanup
disposables.push(
() => toggle.removeEventListener("touchstart", colorEventHandler, { passive: true }),
() => toggle.removeEventListener("click", colorEventHandler),
() => toggle.removeEventListener("keydown", colorEventHandler),
)
// #endregion color-scheme toggle button
})();
/* AUTO-GENERATED CONTENT ABOVE. DO NOT EDIT. */
`;

The MIT License (MIT)

Copyright © 2023+ Nicholas Berlette (https://github.com/nberlette)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

export default function usage(basename = "docs.ts"): string {
return `
\x1b[1m🦕 \x1b[4mDeno HTML Documentation Tool\x1b[0m
\x1b[2;3mGenerates a custom HTML documentation site with \`deno doc --html\`,
patching the CSS and JS to improve the UI/UX, support dark mode, and more.\x1b[m
\x1b[4mUsage\x1b[0m
\x1b[2m$ \x1b[3m${basename}\x1b[m \x1b[95m<options>\x1b[m \x1b[96m<files...>\x1b[m
\x1b[4mOptions\x1b[0m
\x1b[93m-t, --title \x1b[2m"docs"\x1b[m Title for the generated documentation site. †
\x1b[93m-o, --output \x1b[2m./path\x1b[m Output directory for generated files, relative to the CWD. †
\x1b[94m-f, --format\x1b[m Format the generated HTML and JS.
\x1b[94m-m, --minify\x1b[m Minify the generated HTML.
\x1b[95m--unstable\x1b[m Enable unstable APIs.
\x1b[95m--no-remote\x1b[m Disable documentation for remote modules.
\x1b[95m-r, --reload\x1b[m Reload cached source files prior to generating documentation.
\x1b[95m-d, --debug\x1b[m Show debug information while running.
\x1b[95m-h, --help\x1b[m Show this help message.
\x1b[1;93m†\x1b[0;2;3m the 'title' and 'output' options are \x1b[4mrequired\x1b[24m.\x1b[m
\x1b[2mMIT © Nicholas Berlette · <\x1b[4mhttps://github.com/nberlette\x1b[24m>\x1b[m
`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment