@t8/docsgen 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/dist/bin.js +756 -0
- package/dist/css/base.css +232 -0
- package/dist/css/code.css +30 -0
- package/dist/css/code.lightbulb.css +278 -0
- package/dist/css/index.css +212 -0
- package/dist/css/section.css +200 -0
- package/package.json +36 -0
- package/src/bin/cleanup.ts +11 -0
- package/src/bin/createFiles.ts +8 -0
- package/src/bin/fetchText.ts +16 -0
- package/src/bin/getConfig.ts +44 -0
- package/src/bin/getCounterContent.ts +17 -0
- package/src/bin/getNav.ts +64 -0
- package/src/bin/getParsedContent.ts +180 -0
- package/src/bin/getRepoLink.ts +9 -0
- package/src/bin/getSlug.ts +21 -0
- package/src/bin/getTitle.ts +49 -0
- package/src/bin/run.ts +31 -0
- package/src/bin/runEntry.ts +55 -0
- package/src/bin/setCName.ts +13 -0
- package/src/bin/setContent.ts +251 -0
- package/src/bin/setImages.ts +8 -0
- package/src/bin/toConfig.ts +14 -0
- package/src/bin/toFileContent.ts +3 -0
- package/src/bin/toRepoURL.ts +22 -0
- package/src/const/packageName.ts +1 -0
- package/src/css/base.css +232 -0
- package/src/css/code.css +30 -0
- package/src/css/code.lightbulb.css +278 -0
- package/src/css/index.css +212 -0
- package/src/css/section.css +200 -0
- package/src/types/BinConfig.ts +6 -0
- package/src/types/Context.ts +4 -0
- package/src/types/EntryConfig.ts +57 -0
- package/src/types/NavItem.ts +8 -0
- package/src/types/PackageMetadata.ts +11 -0
- package/src/types/Theme.ts +1 -0
- package/src/utils/escapeHTML.ts +17 -0
- package/src/utils/escapeRegExp.ts +4 -0
- package/src/utils/getIcon.ts +16 -0
- package/src/utils/getSVGDataURL.ts +9 -0
- package/tsconfig.json +14 -0
package/dist/bin.js
ADDED
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// node_modules/args-json/dist/index.js
|
|
27
|
+
function d(e) {
|
|
28
|
+
let i = e.replace(/^[-_.\s~+]|[-_.\s~+]$/g, "");
|
|
29
|
+
return /[-_.\s~+]/.test(i) ? i.toLowerCase().replace(/[-_.\s~+](\S)/g, (r, o) => o.toUpperCase()) : i.slice(0, 1).toLowerCase() + i.slice(1);
|
|
30
|
+
}
|
|
31
|
+
function p(e) {
|
|
32
|
+
if (e) {
|
|
33
|
+
if (e.startsWith("--") && e.length > 2) return d(e.slice(2));
|
|
34
|
+
if (e.startsWith("-") && e.length === 2) return d(e.slice(1));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function h(e) {
|
|
38
|
+
let i = [], r = "", o = false, n = false;
|
|
39
|
+
for (let s = 0; s < e.length; s++) {
|
|
40
|
+
let t = e[s];
|
|
41
|
+
if (/^\s/.test(t) && !o && !n) {
|
|
42
|
+
r && i.push(r), r = "";
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
t === "'" && e[s - 1] !== "\\" && (o = !o), t === '"' && e[s - 1] !== "\\" && (n = !n), r += t;
|
|
46
|
+
}
|
|
47
|
+
return r && i.push(r), i;
|
|
48
|
+
}
|
|
49
|
+
function c() {
|
|
50
|
+
return typeof process === void 0 ? [] : process.argv;
|
|
51
|
+
}
|
|
52
|
+
function A(e, i) {
|
|
53
|
+
let r, o;
|
|
54
|
+
e === void 0 ? r = c() : typeof e == "string" ? r = h(e) : Array.isArray(e) ? r = e.map((t) => String(t)) : e !== null && typeof e == "object" ? (r = c(), o = e) : r = [], r = r.map((t) => {
|
|
55
|
+
let f = t.trim(), l = f.indexOf("=");
|
|
56
|
+
return l === -1 ? f : [f.slice(0, l), f.slice(l + 1)];
|
|
57
|
+
}).flat(), i && (o = i);
|
|
58
|
+
let n = "", s = {};
|
|
59
|
+
for (let t of r) {
|
|
60
|
+
t = t.trim(), (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) && (t = t.slice(1, -1));
|
|
61
|
+
let f = p(t);
|
|
62
|
+
if (f !== void 0) {
|
|
63
|
+
let g = o?.[f] ?? f;
|
|
64
|
+
n && g !== n && s[n] === void 0 && (s[n] = true), n = g;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
let l;
|
|
68
|
+
if (t) try {
|
|
69
|
+
l = JSON.parse(t);
|
|
70
|
+
} catch {
|
|
71
|
+
l = t;
|
|
72
|
+
}
|
|
73
|
+
else l = true;
|
|
74
|
+
let a = s[n], u;
|
|
75
|
+
a === void 0 ? u = l : Array.isArray(a) ? u = [...a, l] : u = [a, l], s[n] = u;
|
|
76
|
+
}
|
|
77
|
+
return n && s[n] === void 0 && (s[n] = true), s;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/bin/fetchText.ts
|
|
81
|
+
var import_promises = require("node:fs/promises");
|
|
82
|
+
async function fetchText(location) {
|
|
83
|
+
if (!location) return "";
|
|
84
|
+
if (/^https?:\/\//.test(location)) {
|
|
85
|
+
try {
|
|
86
|
+
return await (await fetch(location)).text();
|
|
87
|
+
} catch {
|
|
88
|
+
console.warn(`Failed to fetch content from '${location}'`);
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return (await (0, import_promises.readFile)(location)).toString();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/bin/toRepoURL.ts
|
|
96
|
+
function toRepoURL(x) {
|
|
97
|
+
if (!x) return "";
|
|
98
|
+
let s = typeof x === "string" ? x : x.url;
|
|
99
|
+
if (!s) return;
|
|
100
|
+
if (/^https?:\/\//.test(s)) return s;
|
|
101
|
+
if (/^git\+https?:\/\/.*\.git$/.test(s))
|
|
102
|
+
return s.replace(/^git\+(https?:\/\/.*)\.git$/, "$1");
|
|
103
|
+
if (/^git:\/\/.*\.git$/.test(s))
|
|
104
|
+
return s.replace(/^git(:\/\/.*)\.git$/, "https$1");
|
|
105
|
+
if (/^github:/.test(s))
|
|
106
|
+
return `https://github.com/${s.replace(/^github:/, "")}`;
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/bin/toConfig.ts
|
|
111
|
+
function toConfig(metadata) {
|
|
112
|
+
let { name, description, version, repository } = metadata;
|
|
113
|
+
return {
|
|
114
|
+
name,
|
|
115
|
+
description,
|
|
116
|
+
version,
|
|
117
|
+
repo: toRepoURL(repository)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/bin/getConfig.ts
|
|
122
|
+
var config = null;
|
|
123
|
+
async function getConfig() {
|
|
124
|
+
if (config) return config;
|
|
125
|
+
let metadata = {};
|
|
126
|
+
try {
|
|
127
|
+
metadata = JSON.parse(await fetchText("./package.json"));
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
let localConfig = {};
|
|
131
|
+
try {
|
|
132
|
+
localConfig = JSON.parse(
|
|
133
|
+
await fetchText("./docsgen.config.json")
|
|
134
|
+
);
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
let targetId;
|
|
138
|
+
let args = process.argv.slice(2);
|
|
139
|
+
if (!args[0].startsWith("--")) targetId = args.shift();
|
|
140
|
+
config = {
|
|
141
|
+
targetId,
|
|
142
|
+
mainBranch: "main",
|
|
143
|
+
root: "/",
|
|
144
|
+
contentDir: "x",
|
|
145
|
+
...toConfig(metadata),
|
|
146
|
+
...localConfig,
|
|
147
|
+
...A(args)
|
|
148
|
+
};
|
|
149
|
+
if (!config.root?.endsWith("/")) config.root = `${config.root ?? ""}/`;
|
|
150
|
+
return config;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/bin/runEntry.ts
|
|
154
|
+
var import_node_child_process2 = require("node:child_process");
|
|
155
|
+
var import_promises6 = require("node:fs/promises");
|
|
156
|
+
var import_node_util2 = require("node:util");
|
|
157
|
+
|
|
158
|
+
// src/bin/cleanup.ts
|
|
159
|
+
var import_promises2 = require("node:fs/promises");
|
|
160
|
+
var import_node_path = require("node:path");
|
|
161
|
+
async function cleanup({ dir = "" }) {
|
|
162
|
+
await Promise.all(
|
|
163
|
+
["x", "index.html", "start.html"].map((path) => {
|
|
164
|
+
return (0, import_promises2.rm)((0, import_node_path.join)(dir, path), { force: true, recursive: true });
|
|
165
|
+
})
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/bin/setCName.ts
|
|
170
|
+
var import_promises3 = require("node:fs/promises");
|
|
171
|
+
var import_node_path2 = require("node:path");
|
|
172
|
+
async function setCName({ dir = "", name, cname, jsorg }) {
|
|
173
|
+
let domain = "";
|
|
174
|
+
if (cname) domain = cname;
|
|
175
|
+
else if (typeof jsorg === "string") domain = jsorg ? `${jsorg}.js.org` : "";
|
|
176
|
+
else if (jsorg === true) domain = name ? `${name}.js.org` : "";
|
|
177
|
+
if (domain !== "") await (0, import_promises3.writeFile)((0, import_node_path2.join)(dir, "CNAME"), domain);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/bin/setContent.ts
|
|
181
|
+
var import_node_child_process = require("node:child_process");
|
|
182
|
+
var import_promises4 = require("node:fs/promises");
|
|
183
|
+
var import_node_path3 = require("node:path");
|
|
184
|
+
var import_node_util = require("node:util");
|
|
185
|
+
|
|
186
|
+
// src/const/packageName.ts
|
|
187
|
+
var packageName = "@t8/docsgen";
|
|
188
|
+
|
|
189
|
+
// src/utils/escapeHTML.ts
|
|
190
|
+
var htmlEntityMap = [
|
|
191
|
+
["&", "&"],
|
|
192
|
+
["<", "<"],
|
|
193
|
+
[">", ">"],
|
|
194
|
+
['"', """]
|
|
195
|
+
];
|
|
196
|
+
function escapeHTML(x) {
|
|
197
|
+
let s = String(x);
|
|
198
|
+
if (!x) return "";
|
|
199
|
+
for (let [character, htmlEntity] of htmlEntityMap)
|
|
200
|
+
s = s.replaceAll(character, htmlEntity);
|
|
201
|
+
return s;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/utils/escapeRegExp.ts
|
|
205
|
+
function escapeRegExp(s) {
|
|
206
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/bin/getCounterContent.ts
|
|
210
|
+
function getCounterContent({ ymid }) {
|
|
211
|
+
if (!ymid) return "";
|
|
212
|
+
return `
|
|
213
|
+
<script>
|
|
214
|
+
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
|
215
|
+
m[i].l=1*new Date();
|
|
216
|
+
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
|
217
|
+
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
|
|
218
|
+
(window, document, "script", "https://mc.yandex.com/metrika/tag.js", "ym");
|
|
219
|
+
ym(${ymid}, "init", {clickmap: true, trackLinks: true, accurateTrackBounce: true});
|
|
220
|
+
</script>
|
|
221
|
+
<noscript><div><img src="https://mc.yandex.com/watch/${ymid}" style="position:absolute;left:-9999px;" alt=""></div></noscript>
|
|
222
|
+
`.trim();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/bin/getNav.ts
|
|
226
|
+
var import_jsdom = require("jsdom");
|
|
227
|
+
|
|
228
|
+
// src/bin/getRepoLink.ts
|
|
229
|
+
function getRepoLink({ repo }, className) {
|
|
230
|
+
if (!repo) return "";
|
|
231
|
+
let caption = /\bgithub\.com\//.test(repo) ? "GitHub" : "Repository";
|
|
232
|
+
return `<a href="${repo}"${className ? ` class="${className}"` : ""} target="_blank">${caption}</a>`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/bin/getNav.ts
|
|
236
|
+
async function getNav(ctx, navItems) {
|
|
237
|
+
let { name, root, contentDir, backstory, nav } = ctx;
|
|
238
|
+
let navContent = await fetchText(nav);
|
|
239
|
+
let s = "";
|
|
240
|
+
if (navContent) {
|
|
241
|
+
let navDom = new import_jsdom.JSDOM(navContent).window.document.body;
|
|
242
|
+
for (let link of navDom.querySelectorAll("a")) {
|
|
243
|
+
if (link.dataset.name === name) {
|
|
244
|
+
let parent = link.parentElement;
|
|
245
|
+
link.remove();
|
|
246
|
+
while (parent && parent.innerHTML.trim() === "") {
|
|
247
|
+
let nextParent = parent.parentElement;
|
|
248
|
+
parent.remove();
|
|
249
|
+
parent = nextParent;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
navContent = navDom.innerHTML;
|
|
254
|
+
}
|
|
255
|
+
if (navItems.length > 1) {
|
|
256
|
+
for (let { id, title, items } of navItems) {
|
|
257
|
+
s += `
|
|
258
|
+
<li data-id="${id}"><a href="${root}${contentDir}/${id}">${title}</a></li>`;
|
|
259
|
+
if (items.length !== 0) {
|
|
260
|
+
s += "\n <ul>";
|
|
261
|
+
for (let { title: title2 } of items) s += `
|
|
262
|
+
<li>${title2}</li>`;
|
|
263
|
+
s += "\n </ul>\n";
|
|
264
|
+
}
|
|
265
|
+
s += "</li>";
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (!s && !navContent) return "";
|
|
269
|
+
let repoLink = getRepoLink(ctx);
|
|
270
|
+
s = s.trim() ? `<section><ul>${s}
|
|
271
|
+
</ul></section>` : "";
|
|
272
|
+
s = `${s}
|
|
273
|
+
<section class="ref">
|
|
274
|
+
<ul>
|
|
275
|
+
<li>${repoLink}</li>
|
|
276
|
+
${backstory ? `<li><a href="${backstory}">Backstory</a></li>` : ""}
|
|
277
|
+
</ul>
|
|
278
|
+
</section>
|
|
279
|
+
${navContent}`;
|
|
280
|
+
return `<nav>
|
|
281
|
+
${s}
|
|
282
|
+
</nav>`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/bin/getParsedContent.ts
|
|
286
|
+
var import_jsdom2 = require("jsdom");
|
|
287
|
+
var import_markdown_it = __toESM(require("markdown-it"));
|
|
288
|
+
|
|
289
|
+
// src/bin/getSlug.ts
|
|
290
|
+
function getSlug(title) {
|
|
291
|
+
if (!title) return "";
|
|
292
|
+
let slug = title.trim();
|
|
293
|
+
if (slug.startsWith("<") && slug.endsWith(">") || slug.startsWith("`") && slug.endsWith("`"))
|
|
294
|
+
slug = slug.slice(1, -1);
|
|
295
|
+
slug = slug.replace(/[<>`:?!.,]+/g, " ").trim().replace(/\s+/g, "_").replace(/'/g, "");
|
|
296
|
+
if (/^\w+\(\)$/.test(slug)) slug = slug.slice(0, -2);
|
|
297
|
+
return slug;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/bin/getParsedContent.ts
|
|
301
|
+
var md = new import_markdown_it.default({
|
|
302
|
+
html: true
|
|
303
|
+
});
|
|
304
|
+
function joinLines(x) {
|
|
305
|
+
return x.join("\n").trim();
|
|
306
|
+
}
|
|
307
|
+
function buildNav(ctx, dom) {
|
|
308
|
+
let { root, contentDir, singlePage } = ctx;
|
|
309
|
+
let linkMap = {};
|
|
310
|
+
let navItem = null;
|
|
311
|
+
let nav = [];
|
|
312
|
+
if (singlePage)
|
|
313
|
+
nav.push({
|
|
314
|
+
id: "Overview",
|
|
315
|
+
title: "Overview",
|
|
316
|
+
items: []
|
|
317
|
+
});
|
|
318
|
+
else {
|
|
319
|
+
let headings = dom.window.document.body.querySelectorAll("h2, h3, h4, h5, h6");
|
|
320
|
+
for (let element of headings) {
|
|
321
|
+
let tagName = element.tagName.toLowerCase();
|
|
322
|
+
let isSectionTitle = tagName === "h2";
|
|
323
|
+
let isSubsectionTitle = tagName === "h3";
|
|
324
|
+
let sectionId = isSectionTitle ? getSlug(element.textContent) : navItem?.id ?? "";
|
|
325
|
+
let elementId = element.id;
|
|
326
|
+
if (!elementId)
|
|
327
|
+
elementId = getSlug(element.textContent).toLowerCase().replace(/_/g, "-");
|
|
328
|
+
if (elementId) {
|
|
329
|
+
element.id = elementId;
|
|
330
|
+
let link = `${root}${contentDir}/${sectionId}`;
|
|
331
|
+
if (!isSectionTitle) link += `#${elementId}`;
|
|
332
|
+
linkMap[`#${elementId}`] = link;
|
|
333
|
+
}
|
|
334
|
+
if (isSectionTitle) {
|
|
335
|
+
if (navItem) nav.push(navItem);
|
|
336
|
+
navItem = {
|
|
337
|
+
id: getSlug(element.textContent),
|
|
338
|
+
title: element.innerHTML.trim(),
|
|
339
|
+
items: []
|
|
340
|
+
};
|
|
341
|
+
} else if (isSubsectionTitle) {
|
|
342
|
+
if (navItem)
|
|
343
|
+
navItem.items.push({
|
|
344
|
+
id: getSlug(element.textContent),
|
|
345
|
+
title: element.innerHTML.trim()
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (navItem) nav.push(navItem);
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
nav,
|
|
353
|
+
linkMap
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function getInstallationCode(element) {
|
|
357
|
+
return element.querySelector("code")?.innerHTML.trim().match(/(\S\s*)?(npm (i|install) .*)/)?.[2];
|
|
358
|
+
}
|
|
359
|
+
function getSectionPostprocess(linkMap) {
|
|
360
|
+
return (content) => {
|
|
361
|
+
let s = content;
|
|
362
|
+
s = s.replace(/<a href="([^"]+)">/g, (_, url) => {
|
|
363
|
+
if (url?.startsWith("#")) return `<a href="${linkMap[url] ?? url}">`;
|
|
364
|
+
return `<a href="${url}" target="_blank">`;
|
|
365
|
+
});
|
|
366
|
+
return s;
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
async function getParsedContent(ctx) {
|
|
370
|
+
let { source = "README.md", singlePage } = ctx;
|
|
371
|
+
let content = md.render(await fetchText(source));
|
|
372
|
+
let dom = new import_jsdom2.JSDOM(content);
|
|
373
|
+
let { nav, linkMap } = buildNav(ctx, dom);
|
|
374
|
+
let badges = [];
|
|
375
|
+
let title = "";
|
|
376
|
+
let description = [];
|
|
377
|
+
let features = [];
|
|
378
|
+
let note = [];
|
|
379
|
+
let installation = "";
|
|
380
|
+
let section = [];
|
|
381
|
+
let sections = [];
|
|
382
|
+
let hasTitle = false;
|
|
383
|
+
let hasFeatures = false;
|
|
384
|
+
let indexComplete = false;
|
|
385
|
+
let element = dom.window.document.body.firstElementChild;
|
|
386
|
+
while (element !== null) {
|
|
387
|
+
if (element.matches("h1")) hasTitle = true;
|
|
388
|
+
else {
|
|
389
|
+
if (element.matches("h2")) {
|
|
390
|
+
if (!indexComplete) indexComplete = true;
|
|
391
|
+
if (!singlePage && section.length !== 0) {
|
|
392
|
+
sections.push(joinLines(section));
|
|
393
|
+
section = [];
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
let { outerHTML } = element;
|
|
397
|
+
if (indexComplete) section.push(outerHTML);
|
|
398
|
+
else if (!hasTitle) {
|
|
399
|
+
badges.push(outerHTML);
|
|
400
|
+
} else if (!hasFeatures) {
|
|
401
|
+
if (element.matches("ul")) {
|
|
402
|
+
hasFeatures = true;
|
|
403
|
+
features.push(outerHTML);
|
|
404
|
+
} else {
|
|
405
|
+
let installationCode = getInstallationCode(element);
|
|
406
|
+
if (installationCode) installation = installationCode;
|
|
407
|
+
else description.push(outerHTML);
|
|
408
|
+
}
|
|
409
|
+
} else {
|
|
410
|
+
let installationCode = getInstallationCode(element);
|
|
411
|
+
if (installationCode) installation = installationCode;
|
|
412
|
+
else note.push(outerHTML);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
element = element.nextElementSibling;
|
|
416
|
+
}
|
|
417
|
+
if (section.length !== 0) sections.push(joinLines(section));
|
|
418
|
+
let postprocess = getSectionPostprocess(linkMap);
|
|
419
|
+
return {
|
|
420
|
+
badges: joinLines(badges),
|
|
421
|
+
title,
|
|
422
|
+
description: joinLines(description),
|
|
423
|
+
features: joinLines(features),
|
|
424
|
+
note: joinLines(note),
|
|
425
|
+
installation,
|
|
426
|
+
sections: sections.map(postprocess),
|
|
427
|
+
nav
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/bin/getTitle.ts
|
|
432
|
+
function getTitle(ctx, { cover, originalContent, withPackageURL } = {}) {
|
|
433
|
+
let { root, name, title: packageTitle, scope, theme } = ctx;
|
|
434
|
+
if (originalContent && ![name, packageTitle].includes(originalContent.trim()))
|
|
435
|
+
return originalContent;
|
|
436
|
+
if (packageTitle) {
|
|
437
|
+
let escapedTitle = escapeHTML(packageTitle);
|
|
438
|
+
if (cover && theme === "t8" && packageTitle.startsWith("T8 "))
|
|
439
|
+
return `<a href="/">T8</a> <span class="name">${packageTitle.replace(/^T8 /, "")}</span>`;
|
|
440
|
+
return withPackageURL ? `<a href="${root}" class="name">${escapedTitle}</a>` : `<span class="name">${escapedTitle}</span>`;
|
|
441
|
+
}
|
|
442
|
+
let scopeMatches = name?.match(/^(@[^/]+)\/?(.*)/);
|
|
443
|
+
if (!scope || !scopeMatches) {
|
|
444
|
+
let escapedName = escapeHTML(name);
|
|
445
|
+
return withPackageURL ? `<a href="${root}" class="name">${escapedName}</a>` : `<span class="name">${escapedName}</span>`;
|
|
446
|
+
}
|
|
447
|
+
let title = `<a href="${scope}" class="scope">${scopeMatches[1]}</a><span class="sep">/</span>`;
|
|
448
|
+
title += withPackageURL ? `<a href="${root}" class="name">${scopeMatches[2]}</a>` : `<span class="name">${scopeMatches[2]}</span>`;
|
|
449
|
+
return title;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/bin/toFileContent.ts
|
|
453
|
+
function toFileContent(x) {
|
|
454
|
+
return `${x.trim()}
|
|
455
|
+
`;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/bin/setContent.ts
|
|
459
|
+
var exec = (0, import_node_util.promisify)(import_node_child_process.exec);
|
|
460
|
+
async function setContent(ctx) {
|
|
461
|
+
let {
|
|
462
|
+
dir = "",
|
|
463
|
+
colorScheme,
|
|
464
|
+
theme,
|
|
465
|
+
root,
|
|
466
|
+
contentDir = "",
|
|
467
|
+
name,
|
|
468
|
+
title,
|
|
469
|
+
description: packageDescription,
|
|
470
|
+
backstory,
|
|
471
|
+
redirect
|
|
472
|
+
} = ctx;
|
|
473
|
+
let counterContent = getCounterContent(ctx);
|
|
474
|
+
let escapedName = escapeHTML(name);
|
|
475
|
+
let escapedTitle = title ? escapeHTML(title) : escapedName;
|
|
476
|
+
let packageVersion = (await exec(`npm view ${packageName} version`)).stdout.trim().split(".").slice(0, 2).join(".");
|
|
477
|
+
let packageUrl = `https://unpkg.com/${packageName}@${packageVersion}`;
|
|
478
|
+
let rootAttrs = "";
|
|
479
|
+
let icon = {
|
|
480
|
+
url: `${root}favicon.svg`,
|
|
481
|
+
type: "image/svg+xml"
|
|
482
|
+
};
|
|
483
|
+
if (theme) rootAttrs += ` data-theme="${escapeHTML(theme)}"`;
|
|
484
|
+
if (colorScheme)
|
|
485
|
+
rootAttrs += ` style="--color-scheme: ${escapeHTML(colorScheme)}"`;
|
|
486
|
+
if (theme === "t8")
|
|
487
|
+
icon = {
|
|
488
|
+
url: "/assets/t8.png",
|
|
489
|
+
type: "image/png"
|
|
490
|
+
};
|
|
491
|
+
if (redirect) {
|
|
492
|
+
let escapedRedirect = escapeHTML(redirect);
|
|
493
|
+
await (0, import_promises4.writeFile)(
|
|
494
|
+
(0, import_node_path3.join)(dir, "index.html"),
|
|
495
|
+
toFileContent(`
|
|
496
|
+
<!DOCTYPE html>
|
|
497
|
+
<html lang="en" class="blank"${rootAttrs}>
|
|
498
|
+
<head>
|
|
499
|
+
<meta charset="utf-8">
|
|
500
|
+
<meta name="viewport" content="width=device-width">
|
|
501
|
+
<meta http-equiv="refresh" content="0; URL=${escapedRedirect}">
|
|
502
|
+
</head>
|
|
503
|
+
<body>
|
|
504
|
+
${counterContent}
|
|
505
|
+
<script>window.location.replace("${escapedRedirect}");</script>
|
|
506
|
+
</body>
|
|
507
|
+
</html>
|
|
508
|
+
`)
|
|
509
|
+
);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
let { badges, description, features, installation, sections, nav } = await getParsedContent(ctx);
|
|
513
|
+
let navContent = await getNav(ctx, nav);
|
|
514
|
+
let dirs = [contentDir];
|
|
515
|
+
await Promise.all(
|
|
516
|
+
dirs.map(async (path) => {
|
|
517
|
+
let dirPath = (0, import_node_path3.join)(dir, path);
|
|
518
|
+
try {
|
|
519
|
+
await (0, import_promises4.access)(dirPath);
|
|
520
|
+
} catch {
|
|
521
|
+
await (0, import_promises4.mkdir)(dirPath);
|
|
522
|
+
}
|
|
523
|
+
})
|
|
524
|
+
);
|
|
525
|
+
await Promise.all([
|
|
526
|
+
...sections.map(
|
|
527
|
+
async (content, i) => (0, import_promises4.writeFile)(
|
|
528
|
+
(0, import_node_path3.join)(dir, contentDir, `${nav[i]?.id ?? `_untitled_${i}`}.html`),
|
|
529
|
+
toFileContent(`
|
|
530
|
+
<!DOCTYPE html>
|
|
531
|
+
<html lang="en"${rootAttrs}>
|
|
532
|
+
<head>
|
|
533
|
+
<meta charset="utf-8">
|
|
534
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
535
|
+
<title>${escapeHTML(nav[i]?.title)} | ${escapedTitle}</title>
|
|
536
|
+
<link rel="stylesheet" href="${packageUrl}/dist/css/base.css">
|
|
537
|
+
<link rel="stylesheet" href="${packageUrl}/dist/css/section.css">
|
|
538
|
+
<link rel="icon" type="${icon.type}" href="${icon.url}">
|
|
539
|
+
${nav[i + 1]?.id ? `<link rel="prefetch" href="${root}${contentDir}/${nav[i + 1]?.id}">` : ""}
|
|
540
|
+
${nav[i - 1]?.id ? `<link rel="prefetch" href="${root}${contentDir}/${nav[i - 1]?.id}">` : ""}
|
|
541
|
+
</head>
|
|
542
|
+
<body>
|
|
543
|
+
<div class="layout">
|
|
544
|
+
<div class="${navContent ? "" : "no-nav "}body">
|
|
545
|
+
<main>
|
|
546
|
+
<h1>${getTitle(ctx, { withPackageURL: true })}</h1>
|
|
547
|
+
${content}
|
|
548
|
+
|
|
549
|
+
<p class="pagenav">
|
|
550
|
+
<span class="prev">
|
|
551
|
+
<span class="icon">\u2190</span>
|
|
552
|
+
${nav[i - 1]?.id ? `<a href="${root}${contentDir}/${nav[i - 1]?.id}">${nav[i - 1]?.title}</a>` : `<a href="${root}">Intro</a>`}
|
|
553
|
+
</span>
|
|
554
|
+
<span class="sep">|</span>
|
|
555
|
+
${nav[i + 1]?.id ? `<span class="next"><a href="${root}${contentDir}/${nav[i + 1]?.id}">${nav[i + 1]?.title}</a> <span class="icon">\u2192</span></span>` : `<span class="repo next">${getRepoLink(ctx)} <span class="icon">\u2726</span></span>`}
|
|
556
|
+
</p>
|
|
557
|
+
</main>
|
|
558
|
+
${navContent ? "<hr>" : ""}
|
|
559
|
+
${navContent.replace(
|
|
560
|
+
new RegExp(
|
|
561
|
+
`(<li data-id="${escapeRegExp(nav[i]?.id)}">)<a href="[^"]+">([^<]+)</a>(</li>)`
|
|
562
|
+
),
|
|
563
|
+
"$1<strong>$2</strong>$3"
|
|
564
|
+
)}
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
567
|
+
|
|
568
|
+
${content.includes("<pre><code ") ? `
|
|
569
|
+
<link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/styles/base16/material.min.css">
|
|
570
|
+
<link rel="stylesheet" href="${packageUrl}/dist/css/code.css">
|
|
571
|
+
<script src="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/highlight.min.js"></script>
|
|
572
|
+
<script>hljs.highlightAll()</script>
|
|
573
|
+
`.trim() : ""}
|
|
574
|
+
${counterContent}
|
|
575
|
+
</body>
|
|
576
|
+
</html>
|
|
577
|
+
`)
|
|
578
|
+
)
|
|
579
|
+
),
|
|
580
|
+
(0, import_promises4.writeFile)(
|
|
581
|
+
(0, import_node_path3.join)(dir, "index.html"),
|
|
582
|
+
toFileContent(`
|
|
583
|
+
<!DOCTYPE html>
|
|
584
|
+
<html lang="en"${rootAttrs}>
|
|
585
|
+
<head>
|
|
586
|
+
<meta charset="utf-8">
|
|
587
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
588
|
+
<title>${escapedTitle}${packageDescription ? ` | ${escapeHTML(packageDescription)}` : ""}</title>
|
|
589
|
+
<link rel="stylesheet" href="${packageUrl}/dist/css/base.css">
|
|
590
|
+
<link rel="stylesheet" href="${packageUrl}/dist/css/index.css">
|
|
591
|
+
<link rel="icon" type="${icon.type}" href="${icon.url}">
|
|
592
|
+
<link rel="prefetch" href="${root}start">
|
|
593
|
+
${nav[0] ? `<link rel="prefetch" href="${root}${contentDir}/${nav[0]?.id ?? ""}">` : ""}
|
|
594
|
+
</head>
|
|
595
|
+
<body>
|
|
596
|
+
<div class="layout">
|
|
597
|
+
<main>
|
|
598
|
+
<section class="intro-title">
|
|
599
|
+
<div class="badges">
|
|
600
|
+
${badges}
|
|
601
|
+
</div>
|
|
602
|
+
<h1>${getTitle(ctx, { cover: true })}</h1>
|
|
603
|
+
<div class="description">
|
|
604
|
+
${description}
|
|
605
|
+
</div>
|
|
606
|
+
<p class="actions">
|
|
607
|
+
<a href="${root}start" class="primary button">Docs</a>
|
|
608
|
+
<span class="sep"> \u2022 </span>
|
|
609
|
+
${getRepoLink(ctx, "button")}
|
|
610
|
+
</p>
|
|
611
|
+
${backstory ? `<p class="ref"><a href="${backstory}">Backstory</a></p>` : ""}
|
|
612
|
+
<p class="installation"><code>${installation}</code></p>
|
|
613
|
+
</section>
|
|
614
|
+
${features ? `
|
|
615
|
+
<section class="intro">
|
|
616
|
+
<div class="features">
|
|
617
|
+
<h2>Features</h2>
|
|
618
|
+
${features}
|
|
619
|
+
</div>
|
|
620
|
+
</section>
|
|
621
|
+
` : ""}
|
|
622
|
+
</main>
|
|
623
|
+
</div>
|
|
624
|
+
|
|
625
|
+
${[description, features].some((s) => s.includes("<pre><code ")) ? `
|
|
626
|
+
<link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/styles/base16/material.min.css">
|
|
627
|
+
<link rel="stylesheet" href="${packageUrl}/dist/css/code.css">
|
|
628
|
+
<script src="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/highlight.min.js"></script>
|
|
629
|
+
<script>hljs.highlightAll()</script>
|
|
630
|
+
`.trim() : ""}
|
|
631
|
+
${counterContent}
|
|
632
|
+
</body>
|
|
633
|
+
</html>
|
|
634
|
+
`)
|
|
635
|
+
),
|
|
636
|
+
(0, import_promises4.writeFile)(
|
|
637
|
+
(0, import_node_path3.join)(dir, "start.html"),
|
|
638
|
+
toFileContent(`
|
|
639
|
+
<!DOCTYPE html>
|
|
640
|
+
<html lang="en" class="blank"${rootAttrs}>
|
|
641
|
+
<head>
|
|
642
|
+
<meta charset="utf-8">
|
|
643
|
+
<meta name="viewport" content="width=device-width">
|
|
644
|
+
<meta http-equiv="refresh" content="0; URL=${root}${contentDir}/${nav[0]?.id}">
|
|
645
|
+
<title>${escapedTitle}</title>
|
|
646
|
+
<link rel="stylesheet" href="${packageUrl}/dist/css/base.css">
|
|
647
|
+
<link rel="icon" type="${icon.type}" href="${icon.url}">
|
|
648
|
+
<script>window.location.replace("${root}${contentDir}/${nav[0]?.id}");</script>
|
|
649
|
+
</head>
|
|
650
|
+
<body>
|
|
651
|
+
<div class="layout">
|
|
652
|
+
<h1>${escapedTitle}</h1>
|
|
653
|
+
</div>
|
|
654
|
+
|
|
655
|
+
${counterContent}
|
|
656
|
+
</body>
|
|
657
|
+
</html>
|
|
658
|
+
`)
|
|
659
|
+
)
|
|
660
|
+
]);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/bin/setImages.ts
|
|
664
|
+
var import_promises5 = require("node:fs/promises");
|
|
665
|
+
var import_node_path4 = require("node:path");
|
|
666
|
+
|
|
667
|
+
// src/utils/getIcon.ts
|
|
668
|
+
function getIcon(baseColor = "gray") {
|
|
669
|
+
return `
|
|
670
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
671
|
+
<style>.b{fill:${baseColor};}.c0{fill:oklch(from ${baseColor} calc(100*(.78 - l)) 0 0 / .5);}.c1{fill:oklch(from ${baseColor} calc(100*(.78 - l)) 0 0 / .3);}.c2{fill:none;}</style>
|
|
672
|
+
<g stroke="none">
|
|
673
|
+
<path d="M0,15 L50,0 L100,15 L100,78 L50,100 L0,81z" stroke="none" class="b"/>
|
|
674
|
+
<path d="M0,15 L50,30 L100,15 L50,0z" stroke="none" class="c1"/>
|
|
675
|
+
<path d="M0,15 L50,30 L50,100 L0,81z" stroke="none" class="c0"/>
|
|
676
|
+
<path d="M50,30 L100,15 L100,78 L50,100z" stroke="none" class="c2"/>
|
|
677
|
+
</g>
|
|
678
|
+
</svg>
|
|
679
|
+
`.trim().replace(/\s+/g, " ").replace(/> </g, "><");
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// src/bin/setImages.ts
|
|
683
|
+
async function setImages({ dir = "", colorScheme }) {
|
|
684
|
+
await (0, import_promises5.writeFile)((0, import_node_path4.join)(dir, "./favicon.svg"), `${getIcon(colorScheme)}
|
|
685
|
+
`);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/bin/createFiles.ts
|
|
689
|
+
async function createFiles(ctx) {
|
|
690
|
+
await Promise.all([setCName(ctx), setContent(ctx), setImages(ctx)]);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/bin/runEntry.ts
|
|
694
|
+
var exec2 = (0, import_node_util2.promisify)(import_node_child_process2.exec);
|
|
695
|
+
var stdout = async (cmd) => (await exec2(cmd)).stdout.trim();
|
|
696
|
+
async function isGitDir() {
|
|
697
|
+
try {
|
|
698
|
+
await (0, import_promises6.access)("./.git");
|
|
699
|
+
return true;
|
|
700
|
+
} catch {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
async function runEntry(ctx) {
|
|
705
|
+
let { targetBranch, mainBranch, remove } = ctx;
|
|
706
|
+
if (!targetBranch || !await isGitDir()) {
|
|
707
|
+
await cleanup(ctx);
|
|
708
|
+
await createFiles(ctx);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
let originalBranch = await stdout("git rev-parse --abbrev-ref HEAD");
|
|
712
|
+
if (originalBranch === targetBranch) await exec2(`git checkout ${mainBranch}`);
|
|
713
|
+
try {
|
|
714
|
+
await exec2(`git branch -D ${targetBranch}`);
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
try {
|
|
718
|
+
await exec2(`git push origin ${targetBranch} --delete`);
|
|
719
|
+
} catch {
|
|
720
|
+
}
|
|
721
|
+
if (remove) return;
|
|
722
|
+
await exec2(`git checkout -b ${targetBranch}`);
|
|
723
|
+
await createFiles(ctx);
|
|
724
|
+
await exec2("git add *");
|
|
725
|
+
let updated = await stdout("git diff --cached --name-only") !== "";
|
|
726
|
+
if (updated) {
|
|
727
|
+
await exec2('git commit -m "release gh-pages"');
|
|
728
|
+
await exec2(`git push -u origin ${targetBranch} -f`);
|
|
729
|
+
}
|
|
730
|
+
if (originalBranch && originalBranch !== targetBranch)
|
|
731
|
+
await exec2(`git checkout ${originalBranch}`);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/bin/run.ts
|
|
735
|
+
async function run() {
|
|
736
|
+
let { targetId, entries, ...rootCtx } = await getConfig();
|
|
737
|
+
if (!entries) {
|
|
738
|
+
if (!targetId || targetId === rootCtx.id) return await runEntry(rootCtx);
|
|
739
|
+
console.warn(`Specified config entry not found: '${targetId}'`);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
if (targetId) {
|
|
743
|
+
let entryCtx = entries.find(({ id }) => id === targetId);
|
|
744
|
+
if (entryCtx) runEntry(entryCtx);
|
|
745
|
+
else console.warn(`Specified config entry not found: '${targetId}'`);
|
|
746
|
+
} else {
|
|
747
|
+
await Promise.all(
|
|
748
|
+
entries.map((entryCtx) => {
|
|
749
|
+
return runEntry({ ...rootCtx, ...entryCtx });
|
|
750
|
+
})
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
(async () => {
|
|
755
|
+
await run();
|
|
756
|
+
})();
|