@t8/docsgen 0.3.32 → 0.3.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +384 -304
- package/package.json +1 -1
- package/src/bin/content/createDirs.ts +21 -0
- package/src/bin/content/getCSSRoot.ts +50 -0
- package/src/bin/{getCounterContent.ts → content/getCounterContent.ts} +1 -1
- package/src/bin/content/getDefaultCodeStyleContent.ts +9 -0
- package/src/bin/{getIcon.ts → content/getIcon.ts} +1 -1
- package/src/bin/content/getIconTag.ts +10 -0
- package/src/bin/content/getIndexContent.ts +119 -0
- package/src/bin/{getInjectedContent.ts → content/getInjectedContent.ts} +3 -3
- package/src/bin/{getNav.ts → content/getNav.ts} +44 -7
- package/src/bin/content/getPlainTitle.ts +12 -0
- package/src/bin/content/getRedirectContent.ts +31 -0
- package/src/bin/content/getSectionContent.ts +80 -0
- package/src/bin/content/getStartContent.ts +40 -0
- package/src/bin/content/tweakTypography.ts +5 -0
- package/src/bin/parsing/getParsedContent.ts +28 -5
- package/src/bin/setContent.ts +16 -300
- package/.lintincludes +0 -2
- package/biome.json +0 -35
- /package/src/bin/{toFileContent.ts → content/toFileContent.ts} +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { access, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import type { Context } from "../../types/Context.ts";
|
|
4
|
+
|
|
5
|
+
export async function createDirs(ctx: Context) {
|
|
6
|
+
let { dir = "", contentDir = "" } = ctx;
|
|
7
|
+
|
|
8
|
+
let dirs = [contentDir];
|
|
9
|
+
|
|
10
|
+
await Promise.all(
|
|
11
|
+
dirs.map(async (path) => {
|
|
12
|
+
let dirPath = join(dir, path);
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
await access(dirPath);
|
|
16
|
+
} catch {
|
|
17
|
+
await mkdir(dirPath);
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { exec as defaultExec } from "node:child_process";
|
|
2
|
+
import { cp } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { packageName } from "../../const/packageName.ts";
|
|
7
|
+
import type { Context } from "../../types/Context.ts";
|
|
8
|
+
|
|
9
|
+
const exec = promisify(defaultExec);
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
|
|
14
|
+
let packageURL = "";
|
|
15
|
+
|
|
16
|
+
export async function getCSSRoot(ctx: Context, type: "index" | "content") {
|
|
17
|
+
let { dir = "", assetsDir } = ctx;
|
|
18
|
+
|
|
19
|
+
let cssRoot = {
|
|
20
|
+
index: "",
|
|
21
|
+
content: "",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (assetsDir) {
|
|
25
|
+
cssRoot.index = assetsDir;
|
|
26
|
+
cssRoot.content = `../${assetsDir}`;
|
|
27
|
+
|
|
28
|
+
await cp(join(__dirname, "css"), join(dir, cssRoot.index), {
|
|
29
|
+
force: true,
|
|
30
|
+
recursive: true,
|
|
31
|
+
});
|
|
32
|
+
} else {
|
|
33
|
+
if (!packageURL) {
|
|
34
|
+
let packageVersion = (
|
|
35
|
+
await exec(`npm view ${packageName} version`)
|
|
36
|
+
).stdout
|
|
37
|
+
.trim()
|
|
38
|
+
.split(".")
|
|
39
|
+
.slice(0, 2)
|
|
40
|
+
.join(".");
|
|
41
|
+
|
|
42
|
+
packageURL = `https://unpkg.com/${packageName}@${packageVersion}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
cssRoot.index = `${packageURL}/dist/css`;
|
|
46
|
+
cssRoot.content = `${packageURL}/dist/css`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return cssRoot[type];
|
|
50
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function getDefaultCodeStyleContent(cssRoot: string) {
|
|
2
|
+
return `
|
|
3
|
+
<link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/styles/stackoverflow-light.min.css" media="(prefers-color-scheme: light)">
|
|
4
|
+
<link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/styles/base16/material.min.css" media="(prefers-color-scheme: dark)">
|
|
5
|
+
<link rel="stylesheet" href="${cssRoot}/code.css">
|
|
6
|
+
<script src="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/highlight.min.js"></script>
|
|
7
|
+
<script>hljs.highlightAll()</script>
|
|
8
|
+
`.trim();
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from "../../types/Context.ts";
|
|
2
|
+
import { getIcon } from "./getIcon.ts";
|
|
3
|
+
|
|
4
|
+
export function getIconTag(ctx: Context) {
|
|
5
|
+
let { url, type } = getIcon(ctx);
|
|
6
|
+
|
|
7
|
+
return url
|
|
8
|
+
? `<link rel="icon"${type ? ` type="${type}"` : ""} href="${url}">`
|
|
9
|
+
: "";
|
|
10
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { Context } from "../../types/Context.ts";
|
|
2
|
+
import { escapeHTML } from "../../utils/escapeHTML.ts";
|
|
3
|
+
import { getRepoLink } from "../getRepoLink.ts";
|
|
4
|
+
import { getParsedContent } from "../parsing/getParsedContent.ts";
|
|
5
|
+
import { getCounterContent } from "./getCounterContent.ts";
|
|
6
|
+
import { getCSSRoot } from "./getCSSRoot.ts";
|
|
7
|
+
import { getDefaultCodeStyleContent } from "./getDefaultCodeStyleContent.ts";
|
|
8
|
+
import { getIconTag } from "./getIconTag.ts";
|
|
9
|
+
import { getInjectedContent } from "./getInjectedContent.ts";
|
|
10
|
+
import { getPlainTitle } from "./getPlainTitle.ts";
|
|
11
|
+
import { toFileContent } from "./toFileContent.ts";
|
|
12
|
+
import { tweakTypography } from "./tweakTypography.ts";
|
|
13
|
+
|
|
14
|
+
export async function getIndexContent(ctx: Context) {
|
|
15
|
+
let {
|
|
16
|
+
root,
|
|
17
|
+
contentDir = "",
|
|
18
|
+
name,
|
|
19
|
+
htmlTitle,
|
|
20
|
+
description: packageDescription,
|
|
21
|
+
isDevDep,
|
|
22
|
+
backstory,
|
|
23
|
+
} = ctx;
|
|
24
|
+
|
|
25
|
+
let counterContent = getCounterContent(ctx);
|
|
26
|
+
let escapedPackageDescription = escapeHTML(packageDescription);
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
title: parsedTitle,
|
|
30
|
+
description,
|
|
31
|
+
intro,
|
|
32
|
+
features,
|
|
33
|
+
note,
|
|
34
|
+
installation,
|
|
35
|
+
nav,
|
|
36
|
+
} = await getParsedContent(ctx);
|
|
37
|
+
|
|
38
|
+
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
|
+
let cssRoot = await getCSSRoot(ctx, "index");
|
|
51
|
+
|
|
52
|
+
return toFileContent(`
|
|
53
|
+
<!DOCTYPE html>
|
|
54
|
+
<html lang="en" data-layout="index">
|
|
55
|
+
<head>
|
|
56
|
+
${getInjectedContent(ctx, "index", "head", "prepend")}
|
|
57
|
+
<meta charset="utf-8">
|
|
58
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
59
|
+
<meta name="description" content="${plainTitle}${escapedPackageDescription ? `: ${escapedPackageDescription}` : ""}">
|
|
60
|
+
<title>${plainTitle}${escapedPackageDescription ? ` | ${escapedPackageDescription}` : ""}</title>
|
|
61
|
+
<link rel="stylesheet" href="${cssRoot}/base.css">
|
|
62
|
+
<link rel="stylesheet" href="${cssRoot}/index.css">
|
|
63
|
+
${getIconTag(ctx)}
|
|
64
|
+
<link rel="prefetch" href="${root}start">
|
|
65
|
+
${nav[0] ? `<link rel="prefetch" href="${root}${contentDir}/${nav[0]?.id ?? ""}">` : ""}
|
|
66
|
+
${getInjectedContent(ctx, "index", "head", "append")}
|
|
67
|
+
</head>
|
|
68
|
+
<body>
|
|
69
|
+
${getInjectedContent(ctx, "index", "body", "prepend")}
|
|
70
|
+
<div class="layout">
|
|
71
|
+
<main>
|
|
72
|
+
<section class="aux intro-title">
|
|
73
|
+
<div class="section-content">
|
|
74
|
+
${getInjectedContent(ctx, "index", "cover", "prepend")}
|
|
75
|
+
<h1>${coverTitle}</h1>
|
|
76
|
+
<div class="description">
|
|
77
|
+
${descriptionContent}
|
|
78
|
+
</div>
|
|
79
|
+
<p class="actions">
|
|
80
|
+
<a href="${root}start" class="primary">Docs</a>
|
|
81
|
+
<span class="sep"> • </span>
|
|
82
|
+
${getRepoLink(ctx)}
|
|
83
|
+
</p>
|
|
84
|
+
${backstory ? `<p class="ref"><a href="${backstory}">Backstory</a></p>` : ""}
|
|
85
|
+
<p class="installation"><code>${escapeHTML(installation)}</code></p>
|
|
86
|
+
${getInjectedContent(ctx, "index", "cover", "append")}
|
|
87
|
+
</div>
|
|
88
|
+
</section>
|
|
89
|
+
${
|
|
90
|
+
intro || features || note
|
|
91
|
+
? `
|
|
92
|
+
<section class="intro">
|
|
93
|
+
<div class="section-content">
|
|
94
|
+
${intro ? `<div class="intro">${intro}</div>` : ""}
|
|
95
|
+
${features ? `<div class="features">${features}</div>` : ""}
|
|
96
|
+
${note ? `<div class="note">${note}</div>` : ""}
|
|
97
|
+
<p class="pagenav">
|
|
98
|
+
<span class="next"><a href="${root}start">To the docs</a> <span class="icon">→</span></span>
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
</section>
|
|
102
|
+
`
|
|
103
|
+
: ""
|
|
104
|
+
}
|
|
105
|
+
</main>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
${
|
|
109
|
+
[description, intro, features, note].some((s) => s.includes("<pre><code "))
|
|
110
|
+
? getInjectedContent(ctx, "index", ":has-code", "append") ||
|
|
111
|
+
getDefaultCodeStyleContent(cssRoot)
|
|
112
|
+
: ""
|
|
113
|
+
}
|
|
114
|
+
${counterContent}
|
|
115
|
+
${getInjectedContent(ctx, "index", "body", "append")}
|
|
116
|
+
</body>
|
|
117
|
+
</html>
|
|
118
|
+
`);
|
|
119
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ContentInjectionTarget } from "
|
|
2
|
-
import type { Context } from "
|
|
3
|
-
import type { Page } from "
|
|
1
|
+
import type { ContentInjectionTarget } from "../../types/ContentInjectionTarget.ts";
|
|
2
|
+
import type { Context } from "../../types/Context.ts";
|
|
3
|
+
import type { Page } from "../../types/Page.ts";
|
|
4
4
|
|
|
5
5
|
export function getInjectedContent(
|
|
6
6
|
ctx: Context,
|
|
@@ -1,13 +1,50 @@
|
|
|
1
1
|
import { JSDOM } from "jsdom";
|
|
2
|
-
import type { Context } from "
|
|
3
|
-
import type { NavItem } from "
|
|
4
|
-
import { fetchContent } from "
|
|
5
|
-
import { getNpmLink } from "
|
|
6
|
-
import { getRepoLink } from "
|
|
2
|
+
import type { Context } from "../../types/Context.ts";
|
|
3
|
+
import type { NavItem } from "../../types/NavItem.ts";
|
|
4
|
+
import { fetchContent } from "../fetchContent.ts";
|
|
5
|
+
import { getNpmLink } from "../getNpmLink.ts";
|
|
6
|
+
import { getRepoLink } from "../getRepoLink.ts";
|
|
7
|
+
|
|
8
|
+
let cachedNavContent = new Map<string, string>();
|
|
9
|
+
|
|
10
|
+
async function getNavContent({ name, nav }: Context) {
|
|
11
|
+
if (!nav) return "";
|
|
12
|
+
|
|
13
|
+
let navContent = cachedNavContent.get(nav);
|
|
14
|
+
|
|
15
|
+
if (navContent !== undefined) return navContent;
|
|
16
|
+
|
|
17
|
+
navContent = await fetchContent(nav);
|
|
18
|
+
|
|
19
|
+
if (navContent) {
|
|
20
|
+
let navDom = new JSDOM(navContent).window.document.body;
|
|
21
|
+
|
|
22
|
+
for (let link of navDom.querySelectorAll("a")) {
|
|
23
|
+
if (link.dataset.name === name) {
|
|
24
|
+
let parent = link.parentElement;
|
|
25
|
+
|
|
26
|
+
link.remove();
|
|
27
|
+
|
|
28
|
+
while (parent && parent.innerHTML.trim() === "") {
|
|
29
|
+
let nextParent = parent.parentElement;
|
|
30
|
+
|
|
31
|
+
parent.remove();
|
|
32
|
+
parent = nextParent;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
navContent = navDom.innerHTML;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
cachedNavContent.set(nav, navContent);
|
|
41
|
+
|
|
42
|
+
return navContent;
|
|
43
|
+
}
|
|
7
44
|
|
|
8
45
|
export async function getNav(ctx: Context, navItems: NavItem[]) {
|
|
9
|
-
let { name, root, contentDir, backstory
|
|
10
|
-
let navContent = await
|
|
46
|
+
let { name, root, contentDir, backstory } = ctx;
|
|
47
|
+
let navContent = await getNavContent(ctx);
|
|
11
48
|
let s = "";
|
|
12
49
|
|
|
13
50
|
if (navContent) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Context } from "../../types/Context.ts";
|
|
2
|
+
import { escapeHTML } from "../../utils/escapeHTML.ts";
|
|
3
|
+
import { getParsedContent } from "../parsing/getParsedContent.ts";
|
|
4
|
+
import { stripHTML } from "../stripHTML.ts";
|
|
5
|
+
|
|
6
|
+
export async function getPlainTitle(ctx: Context) {
|
|
7
|
+
let { name, title, htmlTitle } = ctx;
|
|
8
|
+
|
|
9
|
+
let { title: parsedTitle } = await getParsedContent(ctx);
|
|
10
|
+
|
|
11
|
+
return escapeHTML(title || stripHTML(htmlTitle || parsedTitle, true) || name);
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Context } from "../../types/Context.ts";
|
|
2
|
+
import { escapeHTML } from "../../utils/escapeHTML.ts";
|
|
3
|
+
import { getCounterContent } from "./getCounterContent.ts";
|
|
4
|
+
import { getIconTag } from "./getIconTag.ts";
|
|
5
|
+
import { getInjectedContent } from "./getInjectedContent.ts";
|
|
6
|
+
import { toFileContent } from "./toFileContent.ts";
|
|
7
|
+
|
|
8
|
+
export function getRedirectContent(ctx: Context) {
|
|
9
|
+
let { redirect } = ctx;
|
|
10
|
+
let escapedRedirect = escapeHTML(redirect);
|
|
11
|
+
|
|
12
|
+
return toFileContent(`
|
|
13
|
+
<!DOCTYPE html>
|
|
14
|
+
<html lang="en" data-layout="redirect" class="blank">
|
|
15
|
+
<head>
|
|
16
|
+
${getInjectedContent(ctx, "redirect", "head", "prepend")}
|
|
17
|
+
<meta charset="utf-8">
|
|
18
|
+
<meta name="viewport" content="width=device-width">
|
|
19
|
+
<meta http-equiv="refresh" content="0; URL=${escapedRedirect}">
|
|
20
|
+
${getIconTag(ctx)}
|
|
21
|
+
${getInjectedContent(ctx, "redirect", "head", "append")}
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
${getInjectedContent(ctx, "redirect", "body", "prepend")}
|
|
25
|
+
${getCounterContent(ctx)}
|
|
26
|
+
${getInjectedContent(ctx, "redirect", "body", "append")}
|
|
27
|
+
<script>window.location.replace("${escapedRedirect}");</script>
|
|
28
|
+
</body>
|
|
29
|
+
</html>
|
|
30
|
+
`);
|
|
31
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Context } from "../../types/Context.ts";
|
|
2
|
+
import { escapeHTML } from "../../utils/escapeHTML.ts";
|
|
3
|
+
import { escapeRegExp } from "../../utils/escapeRegExp.ts";
|
|
4
|
+
import { getRepoLink } from "../getRepoLink.ts";
|
|
5
|
+
import { getParsedContent } from "../parsing/getParsedContent.ts";
|
|
6
|
+
import { stripHTML } from "../stripHTML.ts";
|
|
7
|
+
import { getCounterContent } from "./getCounterContent.ts";
|
|
8
|
+
import { getCSSRoot } from "./getCSSRoot.ts";
|
|
9
|
+
import { getDefaultCodeStyleContent } from "./getDefaultCodeStyleContent.ts";
|
|
10
|
+
import { getIconTag } from "./getIconTag.ts";
|
|
11
|
+
import { getInjectedContent } from "./getInjectedContent.ts";
|
|
12
|
+
import { getNav } from "./getNav.ts";
|
|
13
|
+
import { getPlainTitle } from "./getPlainTitle.ts";
|
|
14
|
+
import { toFileContent } from "./toFileContent.ts";
|
|
15
|
+
|
|
16
|
+
export async function getSectionContent(ctx: Context, index: number) {
|
|
17
|
+
let { root, contentDir = "" } = ctx;
|
|
18
|
+
|
|
19
|
+
let cssRoot = await getCSSRoot(ctx, "content");
|
|
20
|
+
let { sections, nav } = await getParsedContent(ctx);
|
|
21
|
+
|
|
22
|
+
let content = sections[index];
|
|
23
|
+
let navContent = await getNav(ctx, nav);
|
|
24
|
+
let plainTitle = await getPlainTitle(ctx);
|
|
25
|
+
|
|
26
|
+
return toFileContent(`
|
|
27
|
+
<!DOCTYPE html>
|
|
28
|
+
<html lang="en" data-layout="section">
|
|
29
|
+
<head>
|
|
30
|
+
${getInjectedContent(ctx, "section", "head", "prepend")}
|
|
31
|
+
<meta charset="utf-8">
|
|
32
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
33
|
+
<meta name="description" content="${plainTitle}: ${escapeHTML(stripHTML(nav[index]?.title, true))}">
|
|
34
|
+
<title>${escapeHTML(stripHTML(nav[index]?.title, true))} | ${plainTitle}</title>
|
|
35
|
+
<link rel="stylesheet" href="${cssRoot}/base.css">
|
|
36
|
+
<link rel="stylesheet" href="${cssRoot}/section.css">
|
|
37
|
+
${getIconTag(ctx)}
|
|
38
|
+
${nav[index + 1]?.id ? `<link rel="prefetch" href="${root}${contentDir}/${nav[index + 1]?.id}">` : ""}
|
|
39
|
+
${nav[index - 1]?.id ? `<link rel="prefetch" href="${root}${contentDir}/${nav[index - 1]?.id}">` : ""}
|
|
40
|
+
${getInjectedContent(ctx, "section", "head", "append")}
|
|
41
|
+
</head>
|
|
42
|
+
<body>
|
|
43
|
+
${getInjectedContent(ctx, "section", "body", "prepend")}
|
|
44
|
+
<div class="layout">
|
|
45
|
+
<div class="${navContent ? "" : "no-nav "}body">
|
|
46
|
+
<main>
|
|
47
|
+
<h1><a href="${root}">${plainTitle}</a></h1>
|
|
48
|
+
${content}
|
|
49
|
+
|
|
50
|
+
<p class="pagenav">
|
|
51
|
+
<span class="prev">
|
|
52
|
+
<span class="icon">←</span>
|
|
53
|
+
${nav[index - 1]?.id ? `<a href="${root}${contentDir}/${nav[index - 1]?.id}">${nav[index - 1]?.title}</a>` : `<a href="${root}">Intro</a>`}
|
|
54
|
+
</span>
|
|
55
|
+
<span class="sep">|</span>
|
|
56
|
+
${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
|
+
</p>
|
|
58
|
+
</main>
|
|
59
|
+
${navContent ? "<hr>" : ""}
|
|
60
|
+
${navContent.replace(
|
|
61
|
+
new RegExp(
|
|
62
|
+
`(<li data-id="${escapeRegExp(nav[index]?.id)}">)<a href="[^"]+">([^<]+)</a>`,
|
|
63
|
+
),
|
|
64
|
+
"$1<strong>$2</strong>",
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
${
|
|
70
|
+
content.includes("<pre><code ")
|
|
71
|
+
? getInjectedContent(ctx, "section", ":has-code", "append") ||
|
|
72
|
+
getDefaultCodeStyleContent(cssRoot)
|
|
73
|
+
: ""
|
|
74
|
+
}
|
|
75
|
+
${getCounterContent(ctx)}
|
|
76
|
+
${getInjectedContent(ctx, "section", "body", "append")}
|
|
77
|
+
</body>
|
|
78
|
+
</html>
|
|
79
|
+
`);
|
|
80
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Context } from "../../types/Context.ts";
|
|
2
|
+
import { getParsedContent } from "../parsing/getParsedContent.ts";
|
|
3
|
+
import { getCounterContent } from "./getCounterContent.ts";
|
|
4
|
+
import { getCSSRoot } from "./getCSSRoot.ts";
|
|
5
|
+
import { getIconTag } from "./getIconTag.ts";
|
|
6
|
+
import { getInjectedContent } from "./getInjectedContent.ts";
|
|
7
|
+
import { getPlainTitle } from "./getPlainTitle.ts";
|
|
8
|
+
import { toFileContent } from "./toFileContent.ts";
|
|
9
|
+
|
|
10
|
+
export async function getStartContent(ctx: Context) {
|
|
11
|
+
let { root, contentDir = "" } = ctx;
|
|
12
|
+
let { nav } = await getParsedContent(ctx);
|
|
13
|
+
let plainTitle = await getPlainTitle(ctx);
|
|
14
|
+
|
|
15
|
+
return toFileContent(`
|
|
16
|
+
<!DOCTYPE html>
|
|
17
|
+
<html lang="en" data-layout="start" class="blank">
|
|
18
|
+
<head>
|
|
19
|
+
${getInjectedContent(ctx, "start", "head", "prepend")}
|
|
20
|
+
<meta charset="utf-8">
|
|
21
|
+
<meta name="viewport" content="width=device-width">
|
|
22
|
+
<meta http-equiv="refresh" content="0; URL=${root}${contentDir}/${nav[0]?.id}">
|
|
23
|
+
<title>${plainTitle}</title>
|
|
24
|
+
<link rel="stylesheet" href="${await getCSSRoot(ctx, "index")}/base.css">
|
|
25
|
+
${getIconTag(ctx)}
|
|
26
|
+
<script>window.location.replace("${root}${contentDir}/${nav[0]?.id}");</script>
|
|
27
|
+
${getInjectedContent(ctx, "start", "head", "append")}
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
${getInjectedContent(ctx, "start", "body", "prepend")}
|
|
31
|
+
<div class="layout">
|
|
32
|
+
<h1>${plainTitle}</h1>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
${getCounterContent(ctx)}
|
|
36
|
+
${getInjectedContent(ctx, "start", "body", "append")}
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
39
|
+
`);
|
|
40
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { JSDOM } from "jsdom";
|
|
2
2
|
import Markdown from "markdown-it";
|
|
3
3
|
import type { Context } from "../../types/Context.ts";
|
|
4
|
+
import type { NavItem } from "../../types/NavItem.ts";
|
|
4
5
|
import { fetchContent } from "../fetchContent.ts";
|
|
5
6
|
import { getLocation } from "../getLocation.ts";
|
|
6
7
|
import { buildNav } from "./buildNav.ts";
|
|
@@ -10,15 +11,33 @@ import { isBadgeContainer } from "./isBadgeContainer.ts";
|
|
|
10
11
|
import { joinLines } from "./joinLines.ts";
|
|
11
12
|
import { preprocessContent } from "./preprocessContent.ts";
|
|
12
13
|
|
|
14
|
+
export type ParsedContent = {
|
|
15
|
+
badges: string;
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
intro: string;
|
|
19
|
+
features: string;
|
|
20
|
+
note: string;
|
|
21
|
+
installation: string;
|
|
22
|
+
sections: string[];
|
|
23
|
+
nav: NavItem[];
|
|
24
|
+
};
|
|
25
|
+
|
|
13
26
|
const md = new Markdown({
|
|
14
27
|
html: true,
|
|
15
28
|
});
|
|
16
29
|
|
|
17
|
-
|
|
30
|
+
const parsedContentCache = new Map<string, ParsedContent>();
|
|
31
|
+
|
|
32
|
+
export async function getParsedContent(ctx: Context): Promise<ParsedContent> {
|
|
33
|
+
let contentLocation = getLocation(ctx, "README.md", ctx.source);
|
|
34
|
+
let parsedContent = parsedContentCache.get(contentLocation);
|
|
35
|
+
|
|
36
|
+
if (parsedContent) return parsedContent;
|
|
37
|
+
|
|
18
38
|
let { singlePage, firstLineDescription, linkMap } = ctx;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
);
|
|
39
|
+
|
|
40
|
+
let rawContent = await fetchContent(contentLocation);
|
|
22
41
|
let content = md.render(preprocessContent(rawContent));
|
|
23
42
|
let dom = new JSDOM(content);
|
|
24
43
|
|
|
@@ -123,7 +142,7 @@ export async function getParsedContent(ctx: Context) {
|
|
|
123
142
|
}
|
|
124
143
|
}
|
|
125
144
|
|
|
126
|
-
|
|
145
|
+
parsedContent = {
|
|
127
146
|
badges, // postprocessBadges(joinLines(badges)),
|
|
128
147
|
title,
|
|
129
148
|
description: joinLines(description),
|
|
@@ -134,4 +153,8 @@ export async function getParsedContent(ctx: Context) {
|
|
|
134
153
|
sections: sections.map(postprocess),
|
|
135
154
|
nav,
|
|
136
155
|
};
|
|
156
|
+
|
|
157
|
+
parsedContentCache.set(contentLocation, parsedContent);
|
|
158
|
+
|
|
159
|
+
return parsedContent;
|
|
137
160
|
}
|