lildocs 0.1.15 → 0.1.17
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 +4 -4
- package/dist/cli.mjs +356 -449
- package/dist/render/Layout.tsrx +362 -0
- package/dist/render/Search.tsrx +409 -0
- package/dist/render/client.tsrx +50 -0
- package/dist/render/copy-code.ts +56 -0
- package/dist/render/heading-links.ts +50 -0
- package/dist/render/navigation.ts +16 -0
- package/dist/render/paths.ts +47 -0
- package/dist/render/renderPage.tsrx +14 -0
- package/dist/render/section-highlight.ts +63 -0
- package/dist/render/sidebar.ts +31 -0
- package/dist/render/styles.css +435 -95
- package/dist/render/table-viewer.ts +161 -0
- package/dist/render/tabler-icons.css +16 -0
- package/dist/render/types.ts +9 -0
- package/package.json +15 -11
- package/dist/render/copy-code.js +0 -54
- package/dist/render/navigation.js +0 -14
- package/dist/render/search.js +0 -3254
package/dist/cli.mjs
CHANGED
|
@@ -3,21 +3,19 @@ import { n as renderGitHubPagesWorkflow, r as repoRelativeInputPath, t as normal
|
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { command, flag, option, optional, positional, run, string, subcommands } from "cmd-ts";
|
|
6
|
-
import {
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
7
7
|
import { access, copyFile, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
8
8
|
import path from "node:path";
|
|
9
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
9
|
import matter from "gray-matter";
|
|
11
|
-
import { Marked, Parser, Renderer, marked } from "marked";
|
|
10
|
+
import { Lexer, Marked, Parser, Renderer, marked } from "marked";
|
|
11
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
12
12
|
import markedShiki from "marked-shiki";
|
|
13
13
|
import { bundledThemes, codeToHtml } from "shiki";
|
|
14
|
-
import {
|
|
14
|
+
import { decodeHTML } from "entities";
|
|
15
|
+
import { fromShikiTheme, renderMermaidSVG } from "beautiful-mermaid";
|
|
15
16
|
import os from "node:os";
|
|
16
17
|
import { generateMarkdownForModule } from "exports-md";
|
|
17
18
|
import { clampGamut, converter, formatHex, parse, wcagContrast } from "culori";
|
|
18
|
-
import { render } from "preact-render-to-string";
|
|
19
|
-
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
20
|
-
import http from "node:http";
|
|
21
19
|
//#region src/core/errors.ts
|
|
22
20
|
var LildocsError = class extends Error {
|
|
23
21
|
constructor(message) {
|
|
@@ -40,7 +38,7 @@ async function readDocsConfig(docsRoot) {
|
|
|
40
38
|
try {
|
|
41
39
|
parsed = JSON.parse(rawConfig);
|
|
42
40
|
} catch (error) {
|
|
43
|
-
throw new LildocsError(`Invalid docs config JSON at ${configPath}: ${errorMessage$
|
|
41
|
+
throw new LildocsError(`Invalid docs config JSON at ${configPath}: ${errorMessage$4(error)}`);
|
|
44
42
|
}
|
|
45
43
|
return validateDocsConfig(parsed, configPath);
|
|
46
44
|
}
|
|
@@ -180,7 +178,7 @@ function assertOptionalStringArray(value, name, configPath) {
|
|
|
180
178
|
function isMissingFileError(error) {
|
|
181
179
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
182
180
|
}
|
|
183
|
-
function errorMessage$
|
|
181
|
+
function errorMessage$4(error) {
|
|
184
182
|
return error instanceof Error ? error.message : String(error);
|
|
185
183
|
}
|
|
186
184
|
//#endregion
|
|
@@ -426,6 +424,209 @@ function ensureUniqueRoutes(pages) {
|
|
|
426
424
|
}
|
|
427
425
|
}
|
|
428
426
|
//#endregion
|
|
427
|
+
//#region src/core/frontend.ts
|
|
428
|
+
const sourceDir = path.dirname(fileURLToPath(import.meta.url));
|
|
429
|
+
const require = createRequire(import.meta.url);
|
|
430
|
+
const virtualOctaneCssId = "\0lildocs-octane-css";
|
|
431
|
+
async function readRenderAsset(name) {
|
|
432
|
+
return readFile(path.join(frontendSourceDir(), name), "utf8");
|
|
433
|
+
}
|
|
434
|
+
function frontendDevScriptPath() {
|
|
435
|
+
return `/@fs/${toVitePath(clientEntryPath())}`;
|
|
436
|
+
}
|
|
437
|
+
async function buildFrontendAssets(options) {
|
|
438
|
+
const vite = await loadVite();
|
|
439
|
+
const outDir = path.join(options.outDir, "assets", "lildocs");
|
|
440
|
+
await vite.build({
|
|
441
|
+
...await frontendViteConfig(options.cwd, "build"),
|
|
442
|
+
base: "./",
|
|
443
|
+
build: {
|
|
444
|
+
outDir,
|
|
445
|
+
emptyOutDir: true,
|
|
446
|
+
manifest: true,
|
|
447
|
+
minify: false,
|
|
448
|
+
target: "esnext",
|
|
449
|
+
rollupOptions: { input: clientEntryPath() }
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
const manifestPath = path.join(outDir, ".vite", "manifest.json");
|
|
453
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
454
|
+
const entry = Object.values(manifest).find((chunk) => chunk.isEntry === true && typeof chunk.file === "string");
|
|
455
|
+
if (!entry) throw new LildocsError("Vite did not emit a frontend entry for the documentation site.");
|
|
456
|
+
return {
|
|
457
|
+
scriptPath: toPosixPath(path.join("assets", "lildocs", entry.file)),
|
|
458
|
+
stylePaths: (entry.css ?? []).map((file) => toPosixPath(path.join("assets", "lildocs", file)))
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
async function createFrontendRenderer(options) {
|
|
462
|
+
if (options.viteServer) return {
|
|
463
|
+
renderPage: renderPageExport(await options.viteServer.ssrLoadModule(serverEntryPath())),
|
|
464
|
+
close: async () => {}
|
|
465
|
+
};
|
|
466
|
+
const server = await (await loadVite()).createServer({
|
|
467
|
+
...await frontendViteConfig(options.cwd, "ssr"),
|
|
468
|
+
appType: "custom",
|
|
469
|
+
server: {
|
|
470
|
+
middlewareMode: true,
|
|
471
|
+
fs: { allow: [frontendSourceDir(), options.cwd] }
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
return {
|
|
475
|
+
renderPage: renderPageExport(await server.ssrLoadModule(serverEntryPath())),
|
|
476
|
+
close: () => server.close()
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
async function createFrontendDevServer(options) {
|
|
480
|
+
const server = await (await loadVite()).createServer({
|
|
481
|
+
...await frontendViteConfig(options.cwd, "dev"),
|
|
482
|
+
root: options.root,
|
|
483
|
+
appType: "mpa",
|
|
484
|
+
server: {
|
|
485
|
+
host: options.host,
|
|
486
|
+
port: options.port,
|
|
487
|
+
strictPort: false,
|
|
488
|
+
fs: { allow: [
|
|
489
|
+
frontendSourceDir(),
|
|
490
|
+
options.root,
|
|
491
|
+
options.cwd
|
|
492
|
+
] }
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
await server.listen();
|
|
496
|
+
return server;
|
|
497
|
+
}
|
|
498
|
+
function frontendSourceDir() {
|
|
499
|
+
const nested = path.resolve(sourceDir, "render");
|
|
500
|
+
if (existsSync(path.join(nested, "client.tsrx"))) return nested;
|
|
501
|
+
return path.resolve(sourceDir, "../render");
|
|
502
|
+
}
|
|
503
|
+
function clientEntryPath() {
|
|
504
|
+
return path.join(frontendSourceDir(), "client.tsrx");
|
|
505
|
+
}
|
|
506
|
+
function serverEntryPath() {
|
|
507
|
+
return path.join(frontendSourceDir(), "renderPage.tsrx");
|
|
508
|
+
}
|
|
509
|
+
async function frontendViteConfig(cwd, mode) {
|
|
510
|
+
const { octane } = await loadOctaneCompiler();
|
|
511
|
+
return {
|
|
512
|
+
configFile: false,
|
|
513
|
+
root: frontendSourceDir(),
|
|
514
|
+
publicDir: false,
|
|
515
|
+
cacheDir: path.join(cwd, "node_modules", ".vite", `lildocs-${mode}`),
|
|
516
|
+
clearScreen: false,
|
|
517
|
+
logLevel: "warn",
|
|
518
|
+
plugins: [octaneRuntimeCompatibility(), octane()],
|
|
519
|
+
optimizeDeps: { exclude: ["octane"] },
|
|
520
|
+
ssr: { noExternal: [/^octane(?:$|\/)/] }
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
async function loadVite() {
|
|
524
|
+
try {
|
|
525
|
+
return await import("vite");
|
|
526
|
+
} catch (error) {
|
|
527
|
+
throw new LildocsError(`Lildocs needs Vite to build and preview its frontend. Install vite in the project using Lildocs.\n${errorMessage$3(error)}`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async function loadOctaneCompiler() {
|
|
531
|
+
try {
|
|
532
|
+
return await import("octane/compiler/vite");
|
|
533
|
+
} catch (error) {
|
|
534
|
+
throw new LildocsError(`Lildocs needs Octane to compile its frontend.\n${errorMessage$3(error)}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function renderPageExport(mod) {
|
|
538
|
+
const renderPage = mod && typeof mod === "object" && "renderPage" in mod ? mod.renderPage : void 0;
|
|
539
|
+
if (typeof renderPage !== "function") throw new LildocsError("Lildocs frontend renderer did not export renderPage.");
|
|
540
|
+
return renderPage;
|
|
541
|
+
}
|
|
542
|
+
function octaneRuntimeCompatibility() {
|
|
543
|
+
return {
|
|
544
|
+
name: "lildocs-octane-runtime-compatibility",
|
|
545
|
+
enforce: "pre",
|
|
546
|
+
resolveId(source, importer, options) {
|
|
547
|
+
if (source === "octane/server" && options.ssr) return resolveOctaneServerRuntime();
|
|
548
|
+
if (source === "./css.js" && importer && isOctaneRuntimeImport(importer)) return virtualOctaneCssId;
|
|
549
|
+
return null;
|
|
550
|
+
},
|
|
551
|
+
load(id) {
|
|
552
|
+
if (id === virtualOctaneCssId) return octaneCssHelpersSource;
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function resolveOctaneServerRuntime() {
|
|
558
|
+
const serverEntry = require.resolve("octane/server");
|
|
559
|
+
const runtimePath = [serverEntry.replace(/[/\\]server[/\\]index\.js$/, `${path.sep}runtime.server.js`), serverEntry.replace(/[/\\]server[/\\]index\.ts$/, `${path.sep}runtime.server.ts`)].find((candidate) => candidate !== serverEntry && existsSync(candidate));
|
|
560
|
+
if (!runtimePath) throw new LildocsError(`Unable to locate Octane server runtime from ${serverEntry}.`);
|
|
561
|
+
return runtimePath;
|
|
562
|
+
}
|
|
563
|
+
function isOctaneRuntimeImport(importer) {
|
|
564
|
+
const [filePath] = importer.split("?", 1);
|
|
565
|
+
const normalized = toPosixPath(filePath);
|
|
566
|
+
return /\/octane\/(?:src|dist)\/runtime(?:\.server)?\.(?:ts|js)$/.test(normalized);
|
|
567
|
+
}
|
|
568
|
+
const octaneCssHelpersSource = `
|
|
569
|
+
export function normalizeClass(value) {
|
|
570
|
+
if (typeof value === "string") return value;
|
|
571
|
+
if (typeof value !== "object") {
|
|
572
|
+
return typeof value === "number" && value ? "" + value : "";
|
|
573
|
+
}
|
|
574
|
+
if (value === null) return "";
|
|
575
|
+
let str = "";
|
|
576
|
+
if (Array.isArray(value)) {
|
|
577
|
+
for (const item of value) {
|
|
578
|
+
if (item) {
|
|
579
|
+
const inner = normalizeClass(item);
|
|
580
|
+
if (inner) str = str ? str + " " + inner : inner;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
} else {
|
|
584
|
+
for (const key in value) {
|
|
585
|
+
if (value[key]) str = str ? str + " " + key : key;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return str;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const styleNameCache = new Map();
|
|
592
|
+
|
|
593
|
+
export function styleName(name) {
|
|
594
|
+
const cached = styleNameCache.get(name);
|
|
595
|
+
if (cached !== undefined) return cached;
|
|
596
|
+
const result = hyphenateStyleName(name);
|
|
597
|
+
styleNameCache.set(name, result);
|
|
598
|
+
return result;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function hyphenateStyleName(name) {
|
|
602
|
+
if (name.charCodeAt(0) === 45) return name;
|
|
603
|
+
let hasUpper = false;
|
|
604
|
+
for (let index = 0; index < name.length; index++) {
|
|
605
|
+
const code = name.charCodeAt(index);
|
|
606
|
+
if (code >= 65 && code <= 90) {
|
|
607
|
+
hasUpper = true;
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (!hasUpper) return name;
|
|
612
|
+
let out = "";
|
|
613
|
+
for (let index = 0; index < name.length; index++) {
|
|
614
|
+
const code = name.charCodeAt(index);
|
|
615
|
+
out += code >= 65 && code <= 90 ? "-" + String.fromCharCode(code + 32) : name[index];
|
|
616
|
+
}
|
|
617
|
+
if (out.charCodeAt(0) === 109 && out.charCodeAt(1) === 115 && out.charCodeAt(2) === 45) {
|
|
618
|
+
out = "-" + out;
|
|
619
|
+
}
|
|
620
|
+
return out;
|
|
621
|
+
}
|
|
622
|
+
`;
|
|
623
|
+
function toVitePath(filePath) {
|
|
624
|
+
return path.resolve(filePath).split(path.sep).join("/");
|
|
625
|
+
}
|
|
626
|
+
function errorMessage$3(error) {
|
|
627
|
+
return error instanceof Error ? error.message : String(error);
|
|
628
|
+
}
|
|
629
|
+
//#endregion
|
|
429
630
|
//#region src/vendor/marked-alert/index.ts
|
|
430
631
|
const defaultAlertVariants = [
|
|
431
632
|
{
|
|
@@ -527,12 +728,19 @@ function buildSearchIndex(pages) {
|
|
|
527
728
|
pageTitle: page.title,
|
|
528
729
|
route: page.route,
|
|
529
730
|
headings: page.headings.map((heading) => heading.text),
|
|
530
|
-
text: normalizeSearchText(page.searchText ?? page.markdown)
|
|
731
|
+
text: normalizeSearchText(page.searchText ?? markdownToPlainText(page.markdown)),
|
|
732
|
+
excerpt: markdownToExcerpt(page.markdown)
|
|
531
733
|
}, ...buildSectionEntries(page)]);
|
|
532
734
|
}
|
|
533
735
|
function normalizeSearchText(value) {
|
|
534
736
|
return value.replace(/<[^>]+>/g, " ").replace(/[^\p{L}\p{N}]+/gu, " ").replace(/\s+/g, " ").trim();
|
|
535
737
|
}
|
|
738
|
+
function markdownToPlainText(markdown) {
|
|
739
|
+
return normalizeSearchText(markdownToExcerpt(markdown));
|
|
740
|
+
}
|
|
741
|
+
function markdownToExcerpt(markdown) {
|
|
742
|
+
return decodeHTML(Parser.parse(Lexer.lex(markdown, { gfm: true })).replace(/<br\s*\/?>/gi, "\n").replace(/<\/(?:p|h[1-6]|li|blockquote|pre|tr|div|section|article)>/gi, "\n\n").replace(/<[^>]+>/g, "")).replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
743
|
+
}
|
|
536
744
|
function buildSectionEntries(page) {
|
|
537
745
|
const entries = [];
|
|
538
746
|
const headingQueue = [...page.headings];
|
|
@@ -546,7 +754,8 @@ function buildSectionEntries(page) {
|
|
|
546
754
|
pageTitle: page.title,
|
|
547
755
|
route: `${page.route}#${activeSection.heading.id}`,
|
|
548
756
|
headings: activeSection.path.map((heading) => heading.text),
|
|
549
|
-
text:
|
|
757
|
+
text: markdownToPlainText(activeSection.lines.join("\n")),
|
|
758
|
+
excerpt: markdownToExcerpt(activeSection.lines.join("\n")),
|
|
550
759
|
depth: activeSection.heading.depth
|
|
551
760
|
});
|
|
552
761
|
};
|
|
@@ -621,7 +830,7 @@ async function renderMarkdownPage(model, page, outDir, options = {}) {
|
|
|
621
830
|
return highlightCode(code, lang, props, options.shikiTheme);
|
|
622
831
|
} })).parse(page.markdown),
|
|
623
832
|
assets,
|
|
624
|
-
text:
|
|
833
|
+
text: markdownToPlainText(page.markdown)
|
|
625
834
|
};
|
|
626
835
|
}
|
|
627
836
|
async function highlightCode(code, lang, props, theme = {}) {
|
|
@@ -705,10 +914,15 @@ function escapeHtml$1(value) {
|
|
|
705
914
|
//#endregion
|
|
706
915
|
//#region src/core/mermaid.ts
|
|
707
916
|
async function createMermaidRenderer(options) {
|
|
917
|
+
const lightColors = await loadShikiColors(options.themeConfig.light);
|
|
918
|
+
const darkColors = options.themeConfig.dark ? await loadShikiColors(options.themeConfig.dark) : void 0;
|
|
919
|
+
const colorKeys = sharedColorKeys(lightColors, darkColors);
|
|
920
|
+
const renderOptions = toRenderOptions(colorKeys, options.themeConfig.fontFamily);
|
|
708
921
|
return {
|
|
922
|
+
css: toThemeCss(lightColors, darkColors, colorKeys),
|
|
709
923
|
async render(source, idHint) {
|
|
710
924
|
try {
|
|
711
|
-
const svg = addImageRole(renderMermaidSVG(source,
|
|
925
|
+
const svg = addImageRole(renderMermaidSVG(source, renderOptions));
|
|
712
926
|
return `<figure class="mermaidDiagram" id="${escapeHtml(idHint)}">${svg}</figure>`;
|
|
713
927
|
} catch (error) {
|
|
714
928
|
throw new LildocsError(`Failed to render Mermaid diagram ${idHint}: ${errorMessage$1(error)}`);
|
|
@@ -717,17 +931,51 @@ async function createMermaidRenderer(options) {
|
|
|
717
931
|
async close() {}
|
|
718
932
|
};
|
|
719
933
|
}
|
|
720
|
-
|
|
934
|
+
const optionalColorKeys = [
|
|
935
|
+
"line",
|
|
936
|
+
"accent",
|
|
937
|
+
"muted",
|
|
938
|
+
"surface",
|
|
939
|
+
"border"
|
|
940
|
+
];
|
|
941
|
+
async function loadShikiColors(themeName) {
|
|
942
|
+
const themeLoader = bundledThemes[themeName];
|
|
943
|
+
if (!themeLoader) throw new LildocsError(`Unknown bundled Shiki theme: ${themeName}`);
|
|
944
|
+
const themeModule = await themeLoader();
|
|
945
|
+
return fromShikiTheme("default" in themeModule ? themeModule.default : themeModule);
|
|
946
|
+
}
|
|
947
|
+
function sharedColorKeys(light, dark) {
|
|
948
|
+
return optionalColorKeys.filter((key) => light[key] !== void 0 && (!dark || dark[key] !== void 0));
|
|
949
|
+
}
|
|
950
|
+
function toRenderOptions(colorKeys, fontFamily) {
|
|
721
951
|
return {
|
|
722
|
-
bg:
|
|
723
|
-
fg:
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
surface: themeConfig.surface,
|
|
727
|
-
border: themeConfig.border,
|
|
728
|
-
font: themeConfig.fontFamily
|
|
952
|
+
bg: "var(--ld-mermaid-bg)",
|
|
953
|
+
fg: "var(--ld-mermaid-fg)",
|
|
954
|
+
...Object.fromEntries(colorKeys.map((key) => [key, `var(--ld-mermaid-${key})`])),
|
|
955
|
+
font: fontFamily
|
|
729
956
|
};
|
|
730
957
|
}
|
|
958
|
+
function toThemeCss(light, dark, colorKeys) {
|
|
959
|
+
const lightVariables = toCssVariables(light, colorKeys, " ");
|
|
960
|
+
const darkVariables = dark ? toCssVariables(dark, colorKeys, " ") : void 0;
|
|
961
|
+
return `.mermaidDiagram {
|
|
962
|
+
${lightVariables}
|
|
963
|
+
}${darkVariables ? `
|
|
964
|
+
|
|
965
|
+
@media (prefers-color-scheme: dark) {
|
|
966
|
+
.mermaidDiagram {
|
|
967
|
+
${darkVariables}
|
|
968
|
+
}
|
|
969
|
+
}` : ""}
|
|
970
|
+
`;
|
|
971
|
+
}
|
|
972
|
+
function toCssVariables(colors, colorKeys, indent) {
|
|
973
|
+
return [
|
|
974
|
+
`${indent}--ld-mermaid-bg: ${colors.bg};`,
|
|
975
|
+
`${indent}--ld-mermaid-fg: ${colors.fg};`,
|
|
976
|
+
...colorKeys.map((key) => `${indent}--ld-mermaid-${key}: ${colors[key]};`)
|
|
977
|
+
].join("\n");
|
|
978
|
+
}
|
|
731
979
|
function addImageRole(svg) {
|
|
732
980
|
return svg.replace("<svg", "<svg role=\"img\"");
|
|
733
981
|
}
|
|
@@ -769,7 +1017,7 @@ function nestNavItems(items) {
|
|
|
769
1017
|
let group = byDir.get(currentPath);
|
|
770
1018
|
if (!group) {
|
|
771
1019
|
group = {
|
|
772
|
-
title: titleFromDir
|
|
1020
|
+
title: titleFromDir(part),
|
|
773
1021
|
route: `${currentPath}/index.html`,
|
|
774
1022
|
active: false,
|
|
775
1023
|
hasPage: false,
|
|
@@ -790,7 +1038,7 @@ function nestNavItems(items) {
|
|
|
790
1038
|
}
|
|
791
1039
|
return topLevel;
|
|
792
1040
|
}
|
|
793
|
-
function titleFromDir
|
|
1041
|
+
function titleFromDir(dir) {
|
|
794
1042
|
return dir.replace(/[-_]+/g, " ").replace(/\S+/g, (word) => `${word[0]?.toLocaleUpperCase() ?? ""}${word.slice(1)}`);
|
|
795
1043
|
}
|
|
796
1044
|
//#endregion
|
|
@@ -956,6 +1204,11 @@ const BACKGROUND_LIGHTNESS_STEP = .015;
|
|
|
956
1204
|
const MAX_BACKGROUND_CHROMA = .03;
|
|
957
1205
|
const toOklch = converter("oklch");
|
|
958
1206
|
const clampToRgb = clampGamut("rgb");
|
|
1207
|
+
const defaultThemeFontSet = {
|
|
1208
|
+
heading: "\"Baloo 2\", sans-serif",
|
|
1209
|
+
body: "Inter, sans-serif",
|
|
1210
|
+
code: "\"JetBrains Mono\", monospace"
|
|
1211
|
+
};
|
|
959
1212
|
const themes = {
|
|
960
1213
|
default: {
|
|
961
1214
|
color: {
|
|
@@ -968,11 +1221,7 @@ const themes = {
|
|
|
968
1221
|
codeForeground: "#24292e",
|
|
969
1222
|
sidebarBackground: "#fafafa"
|
|
970
1223
|
},
|
|
971
|
-
font: {
|
|
972
|
-
heading: "system-ui, sans-serif",
|
|
973
|
-
body: "system-ui, sans-serif",
|
|
974
|
-
code: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
|
|
975
|
-
},
|
|
1224
|
+
font: { ...defaultThemeFontSet },
|
|
976
1225
|
shiki: { theme: "github-light" }
|
|
977
1226
|
},
|
|
978
1227
|
minimal: {
|
|
@@ -998,8 +1247,9 @@ async function resolveTheme(options) {
|
|
|
998
1247
|
if (options.requestedTheme) {
|
|
999
1248
|
if (typeof options.requestedTheme === "string") return { light: await resolveThemeName(options.requestedTheme) };
|
|
1000
1249
|
return {
|
|
1001
|
-
light: await resolveThemeName(options.requestedTheme.light ?? "
|
|
1002
|
-
dark: await resolveThemeName(options.requestedTheme.dark ?? "
|
|
1250
|
+
light: await resolveThemeName(options.requestedTheme.light ?? "vitesse-light"),
|
|
1251
|
+
dark: await resolveThemeName(options.requestedTheme.dark ?? "vitesse-dark"),
|
|
1252
|
+
useDefaultFontSet: options.requestedTheme.light === void 0
|
|
1003
1253
|
};
|
|
1004
1254
|
}
|
|
1005
1255
|
const localThemePath = path.join(options.docsRoot, "theme.ts");
|
|
@@ -1010,8 +1260,9 @@ async function resolveTheme(options) {
|
|
|
1010
1260
|
return { light: validateTheme((await import(localThemeUrl.href)).default, localThemePath) };
|
|
1011
1261
|
}
|
|
1012
1262
|
return {
|
|
1013
|
-
light:
|
|
1014
|
-
dark: await resolveThemeName("
|
|
1263
|
+
light: await resolveThemeName("vitesse-light"),
|
|
1264
|
+
dark: await resolveThemeName("vitesse-dark"),
|
|
1265
|
+
useDefaultFontSet: true
|
|
1015
1266
|
};
|
|
1016
1267
|
}
|
|
1017
1268
|
async function resolveThemeName(themeName) {
|
|
@@ -1067,11 +1318,24 @@ async function resolveFontOverrides(options) {
|
|
|
1067
1318
|
themeFonts
|
|
1068
1319
|
};
|
|
1069
1320
|
}
|
|
1321
|
+
function resolveThemeFontImports(theme, overrides = {}) {
|
|
1322
|
+
if (!theme.useDefaultFontSet && theme.light !== themes.default) return "";
|
|
1323
|
+
const imports = [
|
|
1324
|
+
!overrides.heading && googleFontsCssUrl("Baloo 2", "600;700"),
|
|
1325
|
+
!overrides.body && googleFontsCssUrl("Inter", "400;500"),
|
|
1326
|
+
!overrides.code && googleFontsCssUrl("JetBrains Mono", "400")
|
|
1327
|
+
].filter((url) => Boolean(url));
|
|
1328
|
+
return imports.length > 0 ? `${imports.map((url) => `@import url("${url}");`).join("\n")}\n` : "";
|
|
1329
|
+
}
|
|
1070
1330
|
function themeToCssVariables(theme, fontOverrides = {}, linkOptions = {}, navigationOptions = {}) {
|
|
1071
1331
|
const linkDecoration = linkTextDecoration(linkOptions.underline);
|
|
1072
1332
|
const navigationDuration = navigationOptions.duration ?? 180;
|
|
1073
|
-
const
|
|
1074
|
-
|
|
1333
|
+
const effectiveFontOverrides = theme.useDefaultFontSet ? {
|
|
1334
|
+
...defaultThemeFontSet,
|
|
1335
|
+
...fontOverrides
|
|
1336
|
+
} : fontOverrides;
|
|
1337
|
+
const rootVariables = themeToCssVariableBlock(theme.light, effectiveFontOverrides);
|
|
1338
|
+
const darkVariables = theme.dark ? themeToCssVariableBlock(theme.dark, theme.useDefaultFontSet || theme.light === themes.default ? resolveThemeFonts(theme.light, effectiveFontOverrides) : effectiveFontOverrides) : void 0;
|
|
1075
1339
|
const rootBackgroundVariables = themeToBackgroundVariableBlock(theme.light);
|
|
1076
1340
|
const darkBackgroundVariables = theme.dark ? themeToBackgroundVariableBlock(theme.dark) : void 0;
|
|
1077
1341
|
return `:root {
|
|
@@ -1111,12 +1375,8 @@ function resolveBackgroundOptions(options) {
|
|
|
1111
1375
|
function themeToMermaidConfig(theme, fontOverrides = {}) {
|
|
1112
1376
|
const fonts = resolveThemeFonts(theme.light, fontOverrides);
|
|
1113
1377
|
return {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
accent: theme.light.color.link,
|
|
1117
|
-
muted: theme.light.color.mutedText,
|
|
1118
|
-
surface: theme.light.color.codeBackground,
|
|
1119
|
-
border: theme.light.color.border,
|
|
1378
|
+
light: theme.light.shiki?.theme ?? "github-light",
|
|
1379
|
+
dark: theme.dark?.shiki?.theme,
|
|
1120
1380
|
fontFamily: fonts.body
|
|
1121
1381
|
};
|
|
1122
1382
|
}
|
|
@@ -1390,233 +1650,7 @@ async function exists(filePath) {
|
|
|
1390
1650
|
}
|
|
1391
1651
|
}
|
|
1392
1652
|
//#endregion
|
|
1393
|
-
//#region src/render/Layout.tsx
|
|
1394
|
-
function Layout({ page, nav, pageNavigation, css, searchIndexJson, logo, favicon, repositoryUrl, projectName, navigation, dev }) {
|
|
1395
|
-
const transition = navigation?.transition ?? "fade";
|
|
1396
|
-
const documentTitle = projectName ? `${page.title} • ${projectName}` : page.title;
|
|
1397
|
-
const repoIconUrl = rootRelativeUrl(page.route, "assets/github-icon.svg");
|
|
1398
|
-
const issueUrl = issueUrlForRepository(repositoryUrl);
|
|
1399
|
-
return /* @__PURE__ */ jsxs("html", {
|
|
1400
|
-
lang: "en",
|
|
1401
|
-
children: [/* @__PURE__ */ jsxs("head", { children: [
|
|
1402
|
-
/* @__PURE__ */ jsx("meta", { charSet: "utf-8" }),
|
|
1403
|
-
/* @__PURE__ */ jsx("meta", {
|
|
1404
|
-
name: "viewport",
|
|
1405
|
-
content: "width=device-width, initial-scale=1"
|
|
1406
|
-
}),
|
|
1407
|
-
/* @__PURE__ */ jsx("title", { children: documentTitle }),
|
|
1408
|
-
favicon ? /* @__PURE__ */ jsx("link", {
|
|
1409
|
-
rel: "icon",
|
|
1410
|
-
href: assetSrc(page.route, favicon)
|
|
1411
|
-
}) : null,
|
|
1412
|
-
/* @__PURE__ */ jsx("link", {
|
|
1413
|
-
rel: "stylesheet",
|
|
1414
|
-
href: rootRelativeUrl(page.route, "assets/tabler-icons.css")
|
|
1415
|
-
}),
|
|
1416
|
-
/* @__PURE__ */ jsx("link", {
|
|
1417
|
-
rel: "stylesheet",
|
|
1418
|
-
href: rootRelativeUrl(page.route, "assets/lildocs.css")
|
|
1419
|
-
})
|
|
1420
|
-
] }), /* @__PURE__ */ jsxs("body", { children: [
|
|
1421
|
-
/* @__PURE__ */ jsxs("div", {
|
|
1422
|
-
className: "pageShell",
|
|
1423
|
-
children: [/* @__PURE__ */ jsxs("header", {
|
|
1424
|
-
className: "siteHeader",
|
|
1425
|
-
children: [/* @__PURE__ */ jsxs("a", {
|
|
1426
|
-
className: "brand",
|
|
1427
|
-
href: relativeUrl(page.route, "index.html"),
|
|
1428
|
-
children: [logo.image ? /* @__PURE__ */ jsx("img", {
|
|
1429
|
-
className: "brandLogo",
|
|
1430
|
-
src: assetSrc(page.route, logo.image),
|
|
1431
|
-
alt: ""
|
|
1432
|
-
}) : null, logo.text ? /* @__PURE__ */ jsx("span", { children: logo.text }) : null]
|
|
1433
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
1434
|
-
className: "headerActions",
|
|
1435
|
-
children: [repositoryUrl ? /* @__PURE__ */ jsx("a", {
|
|
1436
|
-
className: "repoButton",
|
|
1437
|
-
href: repositoryUrl,
|
|
1438
|
-
"aria-label": "View repository on GitHub",
|
|
1439
|
-
title: "View repository on GitHub",
|
|
1440
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
1441
|
-
className: "repoIcon",
|
|
1442
|
-
"aria-hidden": "true",
|
|
1443
|
-
style: `--ld-repo-icon: url(${repoIconUrl})`
|
|
1444
|
-
})
|
|
1445
|
-
}) : null, /* @__PURE__ */ jsxs("div", {
|
|
1446
|
-
id: "lildocs-search-root",
|
|
1447
|
-
className: "searchBox",
|
|
1448
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1449
|
-
className: "searchIcon ti ti-search",
|
|
1450
|
-
"aria-hidden": "true"
|
|
1451
|
-
}), /* @__PURE__ */ jsx("input", {
|
|
1452
|
-
id: "lildocs-search-input",
|
|
1453
|
-
type: "search",
|
|
1454
|
-
placeholder: "Search docs",
|
|
1455
|
-
"aria-label": "Search docs"
|
|
1456
|
-
})]
|
|
1457
|
-
})]
|
|
1458
|
-
})]
|
|
1459
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
1460
|
-
id: "swup",
|
|
1461
|
-
className: "contentGrid",
|
|
1462
|
-
children: [
|
|
1463
|
-
/* @__PURE__ */ jsx("aside", {
|
|
1464
|
-
className: "sidebar",
|
|
1465
|
-
children: /* @__PURE__ */ jsx("nav", {
|
|
1466
|
-
"aria-label": "Documentation navigation",
|
|
1467
|
-
children: /* @__PURE__ */ jsx(NavList, {
|
|
1468
|
-
items: nav,
|
|
1469
|
-
currentRoute: page.route,
|
|
1470
|
-
pageRoute: page.route
|
|
1471
|
-
})
|
|
1472
|
-
})
|
|
1473
|
-
}),
|
|
1474
|
-
/* @__PURE__ */ jsxs("main", {
|
|
1475
|
-
className: `content transition-${transition}`,
|
|
1476
|
-
children: [/* @__PURE__ */ jsxs("article", { children: [/* @__PURE__ */ jsx(GroupBreadcrumbs, { route: page.route }), /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: page.html ?? "" } })] }), /* @__PURE__ */ jsx(PageNav, { pageNavigation })]
|
|
1477
|
-
}),
|
|
1478
|
-
/* @__PURE__ */ jsx("aside", {
|
|
1479
|
-
className: `toc transition-${transition}`,
|
|
1480
|
-
children: /* @__PURE__ */ jsx(Toc, { headings: page.headings })
|
|
1481
|
-
})
|
|
1482
|
-
]
|
|
1483
|
-
})]
|
|
1484
|
-
}),
|
|
1485
|
-
/* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: `window.lildocsSearchUrl = ${JSON.stringify(rootRelativeUrl(page.route, "search-index.json"))};${issueUrl ? `window.lildocsIssueUrl = ${JSON.stringify(issueUrl)};` : ""}` } }),
|
|
1486
|
-
/* @__PURE__ */ jsx("script", {
|
|
1487
|
-
type: "application/json",
|
|
1488
|
-
id: "lildocs-search-index",
|
|
1489
|
-
dangerouslySetInnerHTML: { __html: searchIndexJson }
|
|
1490
|
-
}),
|
|
1491
|
-
/* @__PURE__ */ jsx("div", { id: "lildocs-overlay-root" }),
|
|
1492
|
-
/* @__PURE__ */ jsx("script", {
|
|
1493
|
-
type: "module",
|
|
1494
|
-
src: rootRelativeUrl(page.route, "assets/search.js")
|
|
1495
|
-
}),
|
|
1496
|
-
/* @__PURE__ */ jsx("script", { src: rootRelativeUrl(page.route, "assets/copy-code.js") }),
|
|
1497
|
-
/* @__PURE__ */ jsx("script", { src: rootRelativeUrl(page.route, "assets/swup.umd.js") }),
|
|
1498
|
-
/* @__PURE__ */ jsx("script", { src: rootRelativeUrl(page.route, "assets/navigation.js") }),
|
|
1499
|
-
dev ? /* @__PURE__ */ jsx("script", {
|
|
1500
|
-
type: "module",
|
|
1501
|
-
src: dev.clientScriptPath
|
|
1502
|
-
}) : null,
|
|
1503
|
-
/* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: css } })
|
|
1504
|
-
] })]
|
|
1505
|
-
});
|
|
1506
|
-
}
|
|
1507
|
-
function issueUrlForRepository(repositoryUrl) {
|
|
1508
|
-
if (!repositoryUrl) return;
|
|
1509
|
-
let url;
|
|
1510
|
-
try {
|
|
1511
|
-
url = new URL(repositoryUrl);
|
|
1512
|
-
} catch {
|
|
1513
|
-
return;
|
|
1514
|
-
}
|
|
1515
|
-
if (url.hostname !== "github.com") return;
|
|
1516
|
-
const [owner, repo] = url.pathname.split("/").filter(Boolean);
|
|
1517
|
-
if (!owner || !repo) return;
|
|
1518
|
-
return `https://github.com/${owner}/${repo}/issues/new`;
|
|
1519
|
-
}
|
|
1520
|
-
function assetSrc(pageRoute, image) {
|
|
1521
|
-
if (/^(?:[a-z]+:)?\/\//i.test(image) || image.startsWith("data:") || image.startsWith("/")) return image;
|
|
1522
|
-
return rootRelativeUrl(pageRoute, image);
|
|
1523
|
-
}
|
|
1524
|
-
function GroupBreadcrumbs({ route }) {
|
|
1525
|
-
const dir = route.split("/").slice(0, -1);
|
|
1526
|
-
if (dir.length === 0) return null;
|
|
1527
|
-
return /* @__PURE__ */ jsx("p", {
|
|
1528
|
-
className: "groupBreadcrumbs",
|
|
1529
|
-
children: dir.map(titleFromDir).join(" / ")
|
|
1530
|
-
});
|
|
1531
|
-
}
|
|
1532
|
-
function titleFromDir(dir) {
|
|
1533
|
-
return dir.replace(/[-_]+/g, " ").replace(/\S+/g, (word) => `${word[0]?.toLocaleUpperCase() ?? ""}${word.slice(1)}`);
|
|
1534
|
-
}
|
|
1535
|
-
function PageNav({ pageNavigation }) {
|
|
1536
|
-
if (!pageNavigation?.next) return null;
|
|
1537
|
-
return /* @__PURE__ */ jsx("nav", {
|
|
1538
|
-
className: "pageNav",
|
|
1539
|
-
"aria-label": "Page navigation",
|
|
1540
|
-
children: /* @__PURE__ */ jsxs("p", { children: [
|
|
1541
|
-
"Next:",
|
|
1542
|
-
" ",
|
|
1543
|
-
/* @__PURE__ */ jsx("a", {
|
|
1544
|
-
rel: "next",
|
|
1545
|
-
href: pageNavigation.next.href,
|
|
1546
|
-
children: pageNavigation.next.title
|
|
1547
|
-
})
|
|
1548
|
-
] })
|
|
1549
|
-
});
|
|
1550
|
-
}
|
|
1551
|
-
function NavList({ items, currentRoute, pageRoute }) {
|
|
1552
|
-
return /* @__PURE__ */ jsx("ul", {
|
|
1553
|
-
className: "navList",
|
|
1554
|
-
children: items.map((item) => /* @__PURE__ */ jsxs("li", {
|
|
1555
|
-
className: item.children.length > 0 ? "navGroup" : void 0,
|
|
1556
|
-
children: [item.children.length > 0 && item.hasPage ? /* @__PURE__ */ jsxs("details", {
|
|
1557
|
-
className: "navDisclosure",
|
|
1558
|
-
open: isActiveBranch(item, currentRoute),
|
|
1559
|
-
children: [/* @__PURE__ */ jsx("summary", {
|
|
1560
|
-
className: item.route === currentRoute ? "active" : void 0,
|
|
1561
|
-
children: item.title
|
|
1562
|
-
}), /* @__PURE__ */ jsx(NavList, {
|
|
1563
|
-
items: item.children,
|
|
1564
|
-
currentRoute,
|
|
1565
|
-
pageRoute
|
|
1566
|
-
})]
|
|
1567
|
-
}) : item.children.length > 0 ? /* @__PURE__ */ jsx("span", {
|
|
1568
|
-
className: "navFolder",
|
|
1569
|
-
children: item.title
|
|
1570
|
-
}) : /* @__PURE__ */ jsx("a", {
|
|
1571
|
-
className: item.route === currentRoute ? "active" : void 0,
|
|
1572
|
-
href: relativeUrl(pageRoute, item.route),
|
|
1573
|
-
children: item.title
|
|
1574
|
-
}), item.children.length > 0 && !item.hasPage ? /* @__PURE__ */ jsx(NavList, {
|
|
1575
|
-
items: item.children,
|
|
1576
|
-
currentRoute,
|
|
1577
|
-
pageRoute
|
|
1578
|
-
}) : null]
|
|
1579
|
-
}))
|
|
1580
|
-
});
|
|
1581
|
-
}
|
|
1582
|
-
function isActiveBranch(item, currentRoute) {
|
|
1583
|
-
return item.route === currentRoute || item.children.some((child) => isActiveBranch(child, currentRoute));
|
|
1584
|
-
}
|
|
1585
|
-
function Toc({ headings }) {
|
|
1586
|
-
const tocHeadings = headings.filter((heading) => heading.depth > 1 && heading.depth < 4);
|
|
1587
|
-
if (tocHeadings.length === 0) return null;
|
|
1588
|
-
return /* @__PURE__ */ jsxs("nav", {
|
|
1589
|
-
"aria-label": "Table of contents",
|
|
1590
|
-
children: [/* @__PURE__ */ jsx("p", { children: "On this page" }), /* @__PURE__ */ jsx("ul", { children: tocHeadings.map((heading) => /* @__PURE__ */ jsx("li", {
|
|
1591
|
-
className: `tocDepth${heading.depth}`,
|
|
1592
|
-
children: /* @__PURE__ */ jsx("a", {
|
|
1593
|
-
href: `#${heading.id}`,
|
|
1594
|
-
children: heading.text
|
|
1595
|
-
})
|
|
1596
|
-
})) })]
|
|
1597
|
-
});
|
|
1598
|
-
}
|
|
1599
|
-
//#endregion
|
|
1600
|
-
//#region src/render/renderPage.tsx
|
|
1601
|
-
function renderPage(page, nav, pageNavigation, css, searchIndexJson, logo, favicon, repositoryUrl, projectName, navigation, dev) {
|
|
1602
|
-
return `<!doctype html>${render(/* @__PURE__ */ jsx(Layout, {
|
|
1603
|
-
page,
|
|
1604
|
-
nav,
|
|
1605
|
-
pageNavigation,
|
|
1606
|
-
css,
|
|
1607
|
-
searchIndexJson,
|
|
1608
|
-
logo,
|
|
1609
|
-
favicon,
|
|
1610
|
-
repositoryUrl,
|
|
1611
|
-
projectName,
|
|
1612
|
-
navigation,
|
|
1613
|
-
dev
|
|
1614
|
-
}))}`;
|
|
1615
|
-
}
|
|
1616
|
-
//#endregion
|
|
1617
1653
|
//#region src/core/build.ts
|
|
1618
|
-
const sourceDir = path.dirname(fileURLToPath(import.meta.url));
|
|
1619
|
-
const require = createRequire(import.meta.url);
|
|
1620
1654
|
async function buildSite(options) {
|
|
1621
1655
|
const input = await resolveInput(options.input, options.cwd, { homePagePreference: options.homePagePreference });
|
|
1622
1656
|
const configOptions = mergeConfigOptions({
|
|
@@ -1654,11 +1688,7 @@ async function buildSite(options) {
|
|
|
1654
1688
|
});
|
|
1655
1689
|
const baseCss = await readRenderAsset("styles.css");
|
|
1656
1690
|
const tablerIconsCss = await readRenderAsset("tabler-icons.css");
|
|
1657
|
-
const searchScript = await readRenderAsset("search.js");
|
|
1658
|
-
const copyCodeScript = await readRenderAsset("copy-code.js");
|
|
1659
|
-
const navigationScript = await readRenderAsset("navigation.js");
|
|
1660
1691
|
const githubIcon = await readRenderAsset("github-icon.svg");
|
|
1661
|
-
const swupScript = await readSwupAsset();
|
|
1662
1692
|
const backgroundResolution = resolveBackgroundOptions({
|
|
1663
1693
|
cwd: options.cwd,
|
|
1664
1694
|
docsRoot: input.docsRoot,
|
|
@@ -1673,19 +1703,20 @@ async function buildSite(options) {
|
|
|
1673
1703
|
defaultText: packageName,
|
|
1674
1704
|
favicon: configOptions.favicon
|
|
1675
1705
|
});
|
|
1676
|
-
const
|
|
1706
|
+
const mermaid = await createMermaidRenderer({ themeConfig: themeToMermaidConfig(theme, fontResolution.themeFonts) });
|
|
1707
|
+
const css = `${resolveThemeFontImports(theme, configOptions.fonts)}${fontResolution.css}${themeToCssVariables(theme, fontResolution.themeFonts, configOptions.link, configOptions.navigation)}\n${mermaid.css}${backgroundResolution.css}${logoResolution.css}${baseCss}`;
|
|
1677
1708
|
const assets = [
|
|
1678
1709
|
...fontResolution.assets,
|
|
1679
1710
|
...backgroundResolution.assets,
|
|
1680
1711
|
...logoResolution.assets
|
|
1681
1712
|
];
|
|
1682
|
-
|
|
1683
|
-
await rm(outDir, {
|
|
1684
|
-
recursive: true,
|
|
1685
|
-
force: true
|
|
1686
|
-
});
|
|
1687
|
-
await mkdir(path.join(outDir, "assets"), { recursive: true });
|
|
1713
|
+
let frontendRenderer;
|
|
1688
1714
|
try {
|
|
1715
|
+
await rm(outDir, {
|
|
1716
|
+
recursive: true,
|
|
1717
|
+
force: true
|
|
1718
|
+
});
|
|
1719
|
+
await mkdir(path.join(outDir, "assets"), { recursive: true });
|
|
1689
1720
|
const renderedPages = await Promise.all(model.pages.map((page) => renderMarkdownPage(model, page, outDir, {
|
|
1690
1721
|
mermaid,
|
|
1691
1722
|
shikiTheme: {
|
|
@@ -1702,22 +1733,45 @@ async function buildSite(options) {
|
|
|
1702
1733
|
}
|
|
1703
1734
|
const searchIndexJson = JSON.stringify(buildSearchIndex(model.pages), null, 2);
|
|
1704
1735
|
const pageNavigation = buildPageNavigation(model.pages);
|
|
1736
|
+
const frontendAssets = options.dev ? {
|
|
1737
|
+
scriptPath: options.dev.clientScriptPath,
|
|
1738
|
+
stylePaths: []
|
|
1739
|
+
} : await buildFrontendAssets({
|
|
1740
|
+
cwd: options.cwd,
|
|
1741
|
+
outDir
|
|
1742
|
+
});
|
|
1743
|
+
const renderer = await createFrontendRenderer({
|
|
1744
|
+
cwd: options.cwd,
|
|
1745
|
+
viteServer: options.dev?.viteServer
|
|
1746
|
+
});
|
|
1747
|
+
frontendRenderer = renderer;
|
|
1705
1748
|
await Promise.all(model.pages.map(async (page) => {
|
|
1706
|
-
const
|
|
1749
|
+
const nav = buildNavigation(model, page);
|
|
1750
|
+
const html = renderer.renderPage({
|
|
1751
|
+
page,
|
|
1752
|
+
nav,
|
|
1753
|
+
pageNavigation: pageNavigation.get(page.route),
|
|
1754
|
+
css,
|
|
1755
|
+
searchIndexJson,
|
|
1756
|
+
logo: logoResolution.logo,
|
|
1757
|
+
favicon: logoResolution.favicon,
|
|
1758
|
+
repositoryUrl,
|
|
1759
|
+
projectName,
|
|
1760
|
+
navigation: configOptions.navigation,
|
|
1761
|
+
clientScriptPath: frontendAssets.scriptPath,
|
|
1762
|
+
clientStylePaths: frontendAssets.stylePaths
|
|
1763
|
+
});
|
|
1707
1764
|
const outputPath = path.join(outDir, page.outputPath);
|
|
1708
1765
|
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
1709
1766
|
await writeFile(outputPath, html);
|
|
1710
1767
|
}));
|
|
1711
1768
|
await writeFile(path.join(outDir, "assets", "lildocs.css"), css);
|
|
1712
1769
|
await writeFile(path.join(outDir, "assets", "tabler-icons.css"), tablerIconsCss);
|
|
1713
|
-
await writeFile(path.join(outDir, "assets", "search.js"), searchScript);
|
|
1714
|
-
await writeFile(path.join(outDir, "assets", "copy-code.js"), copyCodeScript);
|
|
1715
|
-
await writeFile(path.join(outDir, "assets", "swup.umd.js"), swupScript);
|
|
1716
|
-
await writeFile(path.join(outDir, "assets", "navigation.js"), navigationScript);
|
|
1717
1770
|
await writeFile(path.join(outDir, "assets", "github-icon.svg"), githubIcon);
|
|
1718
1771
|
await writeFile(path.join(outDir, "search-index.json"), searchIndexJson);
|
|
1719
1772
|
await copyAssets(assets);
|
|
1720
1773
|
} finally {
|
|
1774
|
+
await frontendRenderer?.close();
|
|
1721
1775
|
await mermaid.close();
|
|
1722
1776
|
}
|
|
1723
1777
|
return {
|
|
@@ -1768,17 +1822,6 @@ function githubRepositoryName(repositoryUrl) {
|
|
|
1768
1822
|
if (!repositoryUrl) return;
|
|
1769
1823
|
return /^https:\/\/github\.com\/([^/]+\/[^/]+)$/.exec(repositoryUrl)?.[1];
|
|
1770
1824
|
}
|
|
1771
|
-
async function readRenderAsset(name) {
|
|
1772
|
-
const bundledPath = path.resolve(sourceDir, "render", name);
|
|
1773
|
-
try {
|
|
1774
|
-
return await readFile(bundledPath, "utf8");
|
|
1775
|
-
} catch {
|
|
1776
|
-
return readFile(path.resolve(sourceDir, "../render", name), "utf8");
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
async function readSwupAsset() {
|
|
1780
|
-
return readFile(path.join(path.dirname(require.resolve("swup")), "Swup.umd.js"), "utf8");
|
|
1781
|
-
}
|
|
1782
1825
|
function buildPageNavigation(pages) {
|
|
1783
1826
|
const navigation = /* @__PURE__ */ new Map();
|
|
1784
1827
|
if (pages.length < 2) return navigation;
|
|
@@ -1799,95 +1842,18 @@ function buildPageNavigation(pages) {
|
|
|
1799
1842
|
return navigation;
|
|
1800
1843
|
}
|
|
1801
1844
|
//#endregion
|
|
1802
|
-
//#region src/core/server.ts
|
|
1803
|
-
const MIME_TYPES = new Map([
|
|
1804
|
-
[".html", "text/html; charset=utf-8"],
|
|
1805
|
-
[".css", "text/css; charset=utf-8"],
|
|
1806
|
-
[".js", "text/javascript; charset=utf-8"],
|
|
1807
|
-
[".json", "application/json; charset=utf-8"],
|
|
1808
|
-
[".svg", "image/svg+xml"],
|
|
1809
|
-
[".png", "image/png"],
|
|
1810
|
-
[".jpg", "image/jpeg"],
|
|
1811
|
-
[".jpeg", "image/jpeg"],
|
|
1812
|
-
[".gif", "image/gif"],
|
|
1813
|
-
[".webp", "image/webp"],
|
|
1814
|
-
[".avif", "image/avif"],
|
|
1815
|
-
[".ico", "image/x-icon"],
|
|
1816
|
-
[".txt", "text/plain; charset=utf-8"],
|
|
1817
|
-
[".woff", "font/woff"],
|
|
1818
|
-
[".woff2", "font/woff2"],
|
|
1819
|
-
[".ttf", "font/ttf"],
|
|
1820
|
-
[".otf", "font/otf"],
|
|
1821
|
-
[".eot", "application/vnd.ms-fontobject"]
|
|
1822
|
-
]);
|
|
1823
|
-
async function serveStaticFile(req, res, outDir) {
|
|
1824
|
-
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
1825
|
-
res.writeHead(405, { allow: "GET, HEAD" });
|
|
1826
|
-
res.end("Method Not Allowed");
|
|
1827
|
-
return;
|
|
1828
|
-
}
|
|
1829
|
-
const pathname = requestPathname$1(req);
|
|
1830
|
-
if (!pathname) {
|
|
1831
|
-
res.writeHead(400);
|
|
1832
|
-
res.end("Bad Request");
|
|
1833
|
-
return;
|
|
1834
|
-
}
|
|
1835
|
-
const relativePath = pathname === "/" ? "index.html" : pathname.slice(1);
|
|
1836
|
-
const filePath = path.resolve(outDir, relativePath);
|
|
1837
|
-
if (!isInside$1(outDir, filePath)) {
|
|
1838
|
-
res.writeHead(404);
|
|
1839
|
-
res.end("Not Found");
|
|
1840
|
-
return;
|
|
1841
|
-
}
|
|
1842
|
-
let fileStat;
|
|
1843
|
-
try {
|
|
1844
|
-
fileStat = await stat(filePath);
|
|
1845
|
-
} catch {
|
|
1846
|
-
res.writeHead(404);
|
|
1847
|
-
res.end("Not Found");
|
|
1848
|
-
return;
|
|
1849
|
-
}
|
|
1850
|
-
if (!fileStat.isFile()) {
|
|
1851
|
-
res.writeHead(404);
|
|
1852
|
-
res.end("Not Found");
|
|
1853
|
-
return;
|
|
1854
|
-
}
|
|
1855
|
-
res.writeHead(200, {
|
|
1856
|
-
"content-length": fileStat.size,
|
|
1857
|
-
"content-type": MIME_TYPES.get(path.extname(filePath).toLowerCase()) ?? "application/octet-stream"
|
|
1858
|
-
});
|
|
1859
|
-
if (req.method === "HEAD") {
|
|
1860
|
-
res.end();
|
|
1861
|
-
return;
|
|
1862
|
-
}
|
|
1863
|
-
createReadStream(filePath).pipe(res);
|
|
1864
|
-
}
|
|
1865
|
-
function requestPathname$1(req) {
|
|
1866
|
-
try {
|
|
1867
|
-
return decodeURIComponent(new URL(req.url ?? "/", "http://lildocs.local").pathname);
|
|
1868
|
-
} catch {
|
|
1869
|
-
return;
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
function isInside$1(root, candidate) {
|
|
1873
|
-
const relative = path.relative(root, candidate);
|
|
1874
|
-
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
1875
|
-
}
|
|
1876
|
-
//#endregion
|
|
1877
1845
|
//#region src/core/dev.ts
|
|
1878
|
-
const DEV_CLIENT_PATH = "/__lildocs/client.js";
|
|
1879
|
-
const DEV_EVENTS_PATH = "/__lildocs/events";
|
|
1880
|
-
const DEV_CLIENT_SCRIPT = `
|
|
1881
|
-
const events = new EventSource("${DEV_EVENTS_PATH}");
|
|
1882
|
-
events.addEventListener("reload", () => location.reload());
|
|
1883
|
-
events.onerror = () => console.debug("[lildocs] live reload disconnected");
|
|
1884
|
-
`;
|
|
1885
1846
|
async function startDevServer(options) {
|
|
1886
1847
|
const input = await resolveInput(options.input, options.cwd, { homePagePreference: "readme-first" });
|
|
1887
1848
|
const outDir = path.resolve(options.cwd, options.outDir);
|
|
1888
1849
|
validateDevOutDir(options.cwd, input.docsRoot, outDir, options.outDir);
|
|
1889
|
-
|
|
1890
|
-
|
|
1850
|
+
await mkdir(outDir, { recursive: true });
|
|
1851
|
+
const vite = await createFrontendDevServer({
|
|
1852
|
+
cwd: options.cwd,
|
|
1853
|
+
root: outDir,
|
|
1854
|
+
host: options.host,
|
|
1855
|
+
port: options.port
|
|
1856
|
+
});
|
|
1891
1857
|
let rebuildTimer;
|
|
1892
1858
|
let rebuilding = false;
|
|
1893
1859
|
let pending = false;
|
|
@@ -1909,12 +1875,14 @@ async function startDevServer(options) {
|
|
|
1909
1875
|
background: options.background,
|
|
1910
1876
|
link: options.link,
|
|
1911
1877
|
homePagePreference: "readme-first",
|
|
1912
|
-
dev: {
|
|
1878
|
+
dev: {
|
|
1879
|
+
clientScriptPath: frontendDevScriptPath(),
|
|
1880
|
+
viteServer: vite
|
|
1881
|
+
}
|
|
1913
1882
|
});
|
|
1914
|
-
await refreshWatchers();
|
|
1915
1883
|
const elapsed = Math.round(performance.now() - started);
|
|
1916
1884
|
console.log(`Rebuilt ${lastSuccessfulBuild.pages.length} page${lastSuccessfulBuild.pages.length === 1 ? "" : "s"} in ${elapsed}ms`);
|
|
1917
|
-
if (emitReload)
|
|
1885
|
+
if (emitReload) vite.ws.send({ type: "full-reload" });
|
|
1918
1886
|
} catch (error) {
|
|
1919
1887
|
if (throwOnFailure) throw error;
|
|
1920
1888
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1933,37 +1901,14 @@ async function startDevServer(options) {
|
|
|
1933
1901
|
rebuild(true);
|
|
1934
1902
|
}, 150);
|
|
1935
1903
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
if (filename && shouldIgnore(path.join(directory, filename.toString()), input.docsRoot, outDir)) return;
|
|
1942
|
-
scheduleRebuild();
|
|
1943
|
-
}));
|
|
1944
|
-
}
|
|
1945
|
-
await rebuild(false, true);
|
|
1946
|
-
const server = http.createServer((req, res) => {
|
|
1947
|
-
const pathname = requestPathname(req.url);
|
|
1948
|
-
if (pathname === DEV_CLIENT_PATH) {
|
|
1949
|
-
res.writeHead(200, { "content-type": "text/javascript; charset=utf-8" });
|
|
1950
|
-
res.end(DEV_CLIENT_SCRIPT);
|
|
1951
|
-
return;
|
|
1952
|
-
}
|
|
1953
|
-
if (pathname === DEV_EVENTS_PATH) {
|
|
1954
|
-
connectEvents(res, clients);
|
|
1955
|
-
return;
|
|
1956
|
-
}
|
|
1957
|
-
serveStaticFile(req, res, outDir);
|
|
1904
|
+
vite.watcher.add(input.docsRoot);
|
|
1905
|
+
vite.watcher.on("all", (_event, file) => {
|
|
1906
|
+
const changedPath = path.resolve(file);
|
|
1907
|
+
if (!isInside(input.docsRoot, changedPath) || shouldIgnore(changedPath, input.docsRoot, outDir)) return;
|
|
1908
|
+
scheduleRebuild();
|
|
1958
1909
|
});
|
|
1959
|
-
await
|
|
1960
|
-
|
|
1961
|
-
server.listen(options.port, options.host, () => {
|
|
1962
|
-
server.off("error", reject);
|
|
1963
|
-
resolve();
|
|
1964
|
-
});
|
|
1965
|
-
});
|
|
1966
|
-
const address = server.address();
|
|
1910
|
+
await rebuild(false, true);
|
|
1911
|
+
const address = vite.httpServer?.address();
|
|
1967
1912
|
const actualPort = typeof address === "object" && address ? address.port : options.port;
|
|
1968
1913
|
const url = `http://${options.host}:${actualPort}/`;
|
|
1969
1914
|
console.log(`lildocs dev server listening at ${url}`);
|
|
@@ -1971,11 +1916,7 @@ async function startDevServer(options) {
|
|
|
1971
1916
|
url,
|
|
1972
1917
|
close: async () => {
|
|
1973
1918
|
if (rebuildTimer) clearTimeout(rebuildTimer);
|
|
1974
|
-
|
|
1975
|
-
for (const client of clients) client.end();
|
|
1976
|
-
await new Promise((resolve, reject) => {
|
|
1977
|
-
server.close((error) => error ? reject(error) : resolve());
|
|
1978
|
-
});
|
|
1919
|
+
await vite.close();
|
|
1979
1920
|
}
|
|
1980
1921
|
};
|
|
1981
1922
|
}
|
|
@@ -1986,46 +1927,12 @@ function validateDevOutDir(cwd, docsRoot, outDir, requestedOutDir) {
|
|
|
1986
1927
|
if (outDir === docsRoot) throw new LildocsError("Dev output directory cannot be the docs root.");
|
|
1987
1928
|
if (isAncestor(outDir, docsRoot)) throw new LildocsError("Dev output directory cannot contain the docs root.");
|
|
1988
1929
|
}
|
|
1989
|
-
async function collectWatchDirectories(docsRoot, outDir) {
|
|
1990
|
-
const directories = /* @__PURE__ */ new Set();
|
|
1991
|
-
async function walk(directory) {
|
|
1992
|
-
if (shouldIgnore(directory, docsRoot, outDir)) return;
|
|
1993
|
-
directories.add(directory);
|
|
1994
|
-
const entries = await readdir(directory, { withFileTypes: true });
|
|
1995
|
-
await Promise.all(entries.map(async (entry) => {
|
|
1996
|
-
if (!entry.isDirectory()) return;
|
|
1997
|
-
await walk(path.join(directory, entry.name));
|
|
1998
|
-
}));
|
|
1999
|
-
}
|
|
2000
|
-
await walk(docsRoot);
|
|
2001
|
-
return [...directories];
|
|
2002
|
-
}
|
|
2003
1930
|
function shouldIgnore(candidate, docsRoot, outDir) {
|
|
2004
1931
|
const resolved = path.resolve(candidate);
|
|
2005
1932
|
if (resolved === outDir || isAncestor(outDir, resolved)) return true;
|
|
2006
1933
|
const relative = path.relative(docsRoot, resolved);
|
|
2007
1934
|
return relative === "dist" || relative.startsWith(`dist${path.sep}`) || relative === "node_modules" || relative.startsWith(`node_modules${path.sep}`) || isHiddenOrSystemPath(relative);
|
|
2008
1935
|
}
|
|
2009
|
-
function connectEvents(res, clients) {
|
|
2010
|
-
res.writeHead(200, {
|
|
2011
|
-
"cache-control": "no-cache",
|
|
2012
|
-
connection: "keep-alive",
|
|
2013
|
-
"content-type": "text/event-stream"
|
|
2014
|
-
});
|
|
2015
|
-
res.write(": connected\n\n");
|
|
2016
|
-
clients.add(res);
|
|
2017
|
-
res.on("close", () => clients.delete(res));
|
|
2018
|
-
}
|
|
2019
|
-
function sendReload(clients) {
|
|
2020
|
-
for (const client of clients) client.write("event: reload\ndata: {}\n\n");
|
|
2021
|
-
}
|
|
2022
|
-
function requestPathname(url) {
|
|
2023
|
-
try {
|
|
2024
|
-
return new URL(url ?? "/", "http://lildocs.local").pathname;
|
|
2025
|
-
} catch {
|
|
2026
|
-
return;
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
1936
|
function isAncestor(parent, child) {
|
|
2030
1937
|
const relative = path.relative(parent, child);
|
|
2031
1938
|
return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
|