lildocs 0.1.14 → 0.1.16
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 +304 -434
- 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 +390 -137
- package/dist/render/table-viewer.ts +98 -0
- package/dist/render/tabler-icons.css +16 -0
- package/dist/render/types.ts +9 -0
- package/package.json +16 -12
- 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 { decodeHTML } from "entities";
|
|
14
15
|
import { 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 = {}) {
|
|
@@ -769,7 +978,7 @@ function nestNavItems(items) {
|
|
|
769
978
|
let group = byDir.get(currentPath);
|
|
770
979
|
if (!group) {
|
|
771
980
|
group = {
|
|
772
|
-
title: titleFromDir
|
|
981
|
+
title: titleFromDir(part),
|
|
773
982
|
route: `${currentPath}/index.html`,
|
|
774
983
|
active: false,
|
|
775
984
|
hasPage: false,
|
|
@@ -790,7 +999,7 @@ function nestNavItems(items) {
|
|
|
790
999
|
}
|
|
791
1000
|
return topLevel;
|
|
792
1001
|
}
|
|
793
|
-
function titleFromDir
|
|
1002
|
+
function titleFromDir(dir) {
|
|
794
1003
|
return dir.replace(/[-_]+/g, " ").replace(/\S+/g, (word) => `${word[0]?.toLocaleUpperCase() ?? ""}${word.slice(1)}`);
|
|
795
1004
|
}
|
|
796
1005
|
//#endregion
|
|
@@ -956,6 +1165,11 @@ const BACKGROUND_LIGHTNESS_STEP = .015;
|
|
|
956
1165
|
const MAX_BACKGROUND_CHROMA = .03;
|
|
957
1166
|
const toOklch = converter("oklch");
|
|
958
1167
|
const clampToRgb = clampGamut("rgb");
|
|
1168
|
+
const defaultThemeFontSet = {
|
|
1169
|
+
heading: "\"Baloo 2\", sans-serif",
|
|
1170
|
+
body: "Inter, sans-serif",
|
|
1171
|
+
code: "\"JetBrains Mono\", monospace"
|
|
1172
|
+
};
|
|
959
1173
|
const themes = {
|
|
960
1174
|
default: {
|
|
961
1175
|
color: {
|
|
@@ -968,11 +1182,7 @@ const themes = {
|
|
|
968
1182
|
codeForeground: "#24292e",
|
|
969
1183
|
sidebarBackground: "#fafafa"
|
|
970
1184
|
},
|
|
971
|
-
font: {
|
|
972
|
-
heading: "system-ui, sans-serif",
|
|
973
|
-
body: "system-ui, sans-serif",
|
|
974
|
-
code: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
|
|
975
|
-
},
|
|
1185
|
+
font: { ...defaultThemeFontSet },
|
|
976
1186
|
shiki: { theme: "github-light" }
|
|
977
1187
|
},
|
|
978
1188
|
minimal: {
|
|
@@ -998,8 +1208,9 @@ async function resolveTheme(options) {
|
|
|
998
1208
|
if (options.requestedTheme) {
|
|
999
1209
|
if (typeof options.requestedTheme === "string") return { light: await resolveThemeName(options.requestedTheme) };
|
|
1000
1210
|
return {
|
|
1001
|
-
light: await resolveThemeName(options.requestedTheme.light ?? "
|
|
1002
|
-
dark: await resolveThemeName(options.requestedTheme.dark ?? "
|
|
1211
|
+
light: await resolveThemeName(options.requestedTheme.light ?? "vitesse-light"),
|
|
1212
|
+
dark: await resolveThemeName(options.requestedTheme.dark ?? "vitesse-dark"),
|
|
1213
|
+
useDefaultFontSet: options.requestedTheme.light === void 0
|
|
1003
1214
|
};
|
|
1004
1215
|
}
|
|
1005
1216
|
const localThemePath = path.join(options.docsRoot, "theme.ts");
|
|
@@ -1010,8 +1221,9 @@ async function resolveTheme(options) {
|
|
|
1010
1221
|
return { light: validateTheme((await import(localThemeUrl.href)).default, localThemePath) };
|
|
1011
1222
|
}
|
|
1012
1223
|
return {
|
|
1013
|
-
light:
|
|
1014
|
-
dark: await resolveThemeName("
|
|
1224
|
+
light: await resolveThemeName("vitesse-light"),
|
|
1225
|
+
dark: await resolveThemeName("vitesse-dark"),
|
|
1226
|
+
useDefaultFontSet: true
|
|
1015
1227
|
};
|
|
1016
1228
|
}
|
|
1017
1229
|
async function resolveThemeName(themeName) {
|
|
@@ -1067,11 +1279,24 @@ async function resolveFontOverrides(options) {
|
|
|
1067
1279
|
themeFonts
|
|
1068
1280
|
};
|
|
1069
1281
|
}
|
|
1282
|
+
function resolveThemeFontImports(theme, overrides = {}) {
|
|
1283
|
+
if (!theme.useDefaultFontSet && theme.light !== themes.default) return "";
|
|
1284
|
+
const imports = [
|
|
1285
|
+
!overrides.heading && googleFontsCssUrl("Baloo 2", "600;700"),
|
|
1286
|
+
!overrides.body && googleFontsCssUrl("Inter", "400;500"),
|
|
1287
|
+
!overrides.code && googleFontsCssUrl("JetBrains Mono", "400")
|
|
1288
|
+
].filter((url) => Boolean(url));
|
|
1289
|
+
return imports.length > 0 ? `${imports.map((url) => `@import url("${url}");`).join("\n")}\n` : "";
|
|
1290
|
+
}
|
|
1070
1291
|
function themeToCssVariables(theme, fontOverrides = {}, linkOptions = {}, navigationOptions = {}) {
|
|
1071
1292
|
const linkDecoration = linkTextDecoration(linkOptions.underline);
|
|
1072
1293
|
const navigationDuration = navigationOptions.duration ?? 180;
|
|
1073
|
-
const
|
|
1074
|
-
|
|
1294
|
+
const effectiveFontOverrides = theme.useDefaultFontSet ? {
|
|
1295
|
+
...defaultThemeFontSet,
|
|
1296
|
+
...fontOverrides
|
|
1297
|
+
} : fontOverrides;
|
|
1298
|
+
const rootVariables = themeToCssVariableBlock(theme.light, effectiveFontOverrides);
|
|
1299
|
+
const darkVariables = theme.dark ? themeToCssVariableBlock(theme.dark, theme.useDefaultFontSet || theme.light === themes.default ? resolveThemeFonts(theme.light, effectiveFontOverrides) : effectiveFontOverrides) : void 0;
|
|
1075
1300
|
const rootBackgroundVariables = themeToBackgroundVariableBlock(theme.light);
|
|
1076
1301
|
const darkBackgroundVariables = theme.dark ? themeToBackgroundVariableBlock(theme.dark) : void 0;
|
|
1077
1302
|
return `:root {
|
|
@@ -1390,235 +1615,7 @@ async function exists(filePath) {
|
|
|
1390
1615
|
}
|
|
1391
1616
|
}
|
|
1392
1617
|
//#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?.previous && !pageNavigation?.next) return null;
|
|
1537
|
-
return /* @__PURE__ */ jsxs("nav", {
|
|
1538
|
-
className: "pageNav",
|
|
1539
|
-
"aria-label": "Page navigation",
|
|
1540
|
-
children: [pageNavigation.previous ? /* @__PURE__ */ jsxs("a", {
|
|
1541
|
-
className: "pageNavLink pageNavPrevious",
|
|
1542
|
-
rel: "prev",
|
|
1543
|
-
href: pageNavigation.previous.href,
|
|
1544
|
-
children: [/* @__PURE__ */ jsx("span", { children: "Previous" }), pageNavigation.previous.title]
|
|
1545
|
-
}) : /* @__PURE__ */ jsx("span", {}), pageNavigation.next ? /* @__PURE__ */ jsxs("a", {
|
|
1546
|
-
className: "pageNavLink pageNavNext",
|
|
1547
|
-
rel: "next",
|
|
1548
|
-
href: pageNavigation.next.href,
|
|
1549
|
-
children: [/* @__PURE__ */ jsx("span", { children: "Next" }), pageNavigation.next.title]
|
|
1550
|
-
}) : null]
|
|
1551
|
-
});
|
|
1552
|
-
}
|
|
1553
|
-
function NavList({ items, currentRoute, pageRoute }) {
|
|
1554
|
-
return /* @__PURE__ */ jsx("ul", {
|
|
1555
|
-
className: "navList",
|
|
1556
|
-
children: items.map((item) => /* @__PURE__ */ jsxs("li", {
|
|
1557
|
-
className: item.children.length > 0 ? "navGroup" : void 0,
|
|
1558
|
-
children: [item.children.length > 0 && item.hasPage ? /* @__PURE__ */ jsxs("details", {
|
|
1559
|
-
className: "navDisclosure",
|
|
1560
|
-
open: isActiveBranch(item, currentRoute),
|
|
1561
|
-
children: [/* @__PURE__ */ jsx("summary", {
|
|
1562
|
-
className: item.route === currentRoute ? "active" : void 0,
|
|
1563
|
-
children: item.title
|
|
1564
|
-
}), /* @__PURE__ */ jsx(NavList, {
|
|
1565
|
-
items: item.children,
|
|
1566
|
-
currentRoute,
|
|
1567
|
-
pageRoute
|
|
1568
|
-
})]
|
|
1569
|
-
}) : item.children.length > 0 ? /* @__PURE__ */ jsx("span", {
|
|
1570
|
-
className: "navFolder",
|
|
1571
|
-
children: item.title
|
|
1572
|
-
}) : /* @__PURE__ */ jsx("a", {
|
|
1573
|
-
className: item.route === currentRoute ? "active" : void 0,
|
|
1574
|
-
href: relativeUrl(pageRoute, item.route),
|
|
1575
|
-
children: item.title
|
|
1576
|
-
}), item.children.length > 0 && !item.hasPage ? /* @__PURE__ */ jsx(NavList, {
|
|
1577
|
-
items: item.children,
|
|
1578
|
-
currentRoute,
|
|
1579
|
-
pageRoute
|
|
1580
|
-
}) : null]
|
|
1581
|
-
}))
|
|
1582
|
-
});
|
|
1583
|
-
}
|
|
1584
|
-
function isActiveBranch(item, currentRoute) {
|
|
1585
|
-
return item.route === currentRoute || item.children.some((child) => isActiveBranch(child, currentRoute));
|
|
1586
|
-
}
|
|
1587
|
-
function Toc({ headings }) {
|
|
1588
|
-
const tocHeadings = headings.filter((heading) => heading.depth > 1 && heading.depth < 4);
|
|
1589
|
-
if (tocHeadings.length === 0) return null;
|
|
1590
|
-
return /* @__PURE__ */ jsxs("nav", {
|
|
1591
|
-
"aria-label": "Table of contents",
|
|
1592
|
-
children: [/* @__PURE__ */ jsx("p", { children: "On this page" }), /* @__PURE__ */ jsx("ul", { children: tocHeadings.map((heading) => /* @__PURE__ */ jsx("li", {
|
|
1593
|
-
className: `tocDepth${heading.depth}`,
|
|
1594
|
-
children: /* @__PURE__ */ jsx("a", {
|
|
1595
|
-
href: `#${heading.id}`,
|
|
1596
|
-
children: heading.text
|
|
1597
|
-
})
|
|
1598
|
-
})) })]
|
|
1599
|
-
});
|
|
1600
|
-
}
|
|
1601
|
-
//#endregion
|
|
1602
|
-
//#region src/render/renderPage.tsx
|
|
1603
|
-
function renderPage(page, nav, pageNavigation, css, searchIndexJson, logo, favicon, repositoryUrl, projectName, navigation, dev) {
|
|
1604
|
-
return `<!doctype html>${render(/* @__PURE__ */ jsx(Layout, {
|
|
1605
|
-
page,
|
|
1606
|
-
nav,
|
|
1607
|
-
pageNavigation,
|
|
1608
|
-
css,
|
|
1609
|
-
searchIndexJson,
|
|
1610
|
-
logo,
|
|
1611
|
-
favicon,
|
|
1612
|
-
repositoryUrl,
|
|
1613
|
-
projectName,
|
|
1614
|
-
navigation,
|
|
1615
|
-
dev
|
|
1616
|
-
}))}`;
|
|
1617
|
-
}
|
|
1618
|
-
//#endregion
|
|
1619
1618
|
//#region src/core/build.ts
|
|
1620
|
-
const sourceDir = path.dirname(fileURLToPath(import.meta.url));
|
|
1621
|
-
const require = createRequire(import.meta.url);
|
|
1622
1619
|
async function buildSite(options) {
|
|
1623
1620
|
const input = await resolveInput(options.input, options.cwd, { homePagePreference: options.homePagePreference });
|
|
1624
1621
|
const configOptions = mergeConfigOptions({
|
|
@@ -1656,11 +1653,7 @@ async function buildSite(options) {
|
|
|
1656
1653
|
});
|
|
1657
1654
|
const baseCss = await readRenderAsset("styles.css");
|
|
1658
1655
|
const tablerIconsCss = await readRenderAsset("tabler-icons.css");
|
|
1659
|
-
const searchScript = await readRenderAsset("search.js");
|
|
1660
|
-
const copyCodeScript = await readRenderAsset("copy-code.js");
|
|
1661
|
-
const navigationScript = await readRenderAsset("navigation.js");
|
|
1662
1656
|
const githubIcon = await readRenderAsset("github-icon.svg");
|
|
1663
|
-
const swupScript = await readSwupAsset();
|
|
1664
1657
|
const backgroundResolution = resolveBackgroundOptions({
|
|
1665
1658
|
cwd: options.cwd,
|
|
1666
1659
|
docsRoot: input.docsRoot,
|
|
@@ -1675,19 +1668,20 @@ async function buildSite(options) {
|
|
|
1675
1668
|
defaultText: packageName,
|
|
1676
1669
|
favicon: configOptions.favicon
|
|
1677
1670
|
});
|
|
1678
|
-
const css = `${fontResolution.css}${themeToCssVariables(theme, fontResolution.themeFonts, configOptions.link, configOptions.navigation)}\n${backgroundResolution.css}${logoResolution.css}${baseCss}`;
|
|
1671
|
+
const css = `${resolveThemeFontImports(theme, configOptions.fonts)}${fontResolution.css}${themeToCssVariables(theme, fontResolution.themeFonts, configOptions.link, configOptions.navigation)}\n${backgroundResolution.css}${logoResolution.css}${baseCss}`;
|
|
1679
1672
|
const assets = [
|
|
1680
1673
|
...fontResolution.assets,
|
|
1681
1674
|
...backgroundResolution.assets,
|
|
1682
1675
|
...logoResolution.assets
|
|
1683
1676
|
];
|
|
1684
1677
|
const mermaid = await createMermaidRenderer({ themeConfig: themeToMermaidConfig(theme, fontResolution.themeFonts) });
|
|
1685
|
-
|
|
1686
|
-
recursive: true,
|
|
1687
|
-
force: true
|
|
1688
|
-
});
|
|
1689
|
-
await mkdir(path.join(outDir, "assets"), { recursive: true });
|
|
1678
|
+
let frontendRenderer;
|
|
1690
1679
|
try {
|
|
1680
|
+
await rm(outDir, {
|
|
1681
|
+
recursive: true,
|
|
1682
|
+
force: true
|
|
1683
|
+
});
|
|
1684
|
+
await mkdir(path.join(outDir, "assets"), { recursive: true });
|
|
1691
1685
|
const renderedPages = await Promise.all(model.pages.map((page) => renderMarkdownPage(model, page, outDir, {
|
|
1692
1686
|
mermaid,
|
|
1693
1687
|
shikiTheme: {
|
|
@@ -1704,22 +1698,45 @@ async function buildSite(options) {
|
|
|
1704
1698
|
}
|
|
1705
1699
|
const searchIndexJson = JSON.stringify(buildSearchIndex(model.pages), null, 2);
|
|
1706
1700
|
const pageNavigation = buildPageNavigation(model.pages);
|
|
1701
|
+
const frontendAssets = options.dev ? {
|
|
1702
|
+
scriptPath: options.dev.clientScriptPath,
|
|
1703
|
+
stylePaths: []
|
|
1704
|
+
} : await buildFrontendAssets({
|
|
1705
|
+
cwd: options.cwd,
|
|
1706
|
+
outDir
|
|
1707
|
+
});
|
|
1708
|
+
const renderer = await createFrontendRenderer({
|
|
1709
|
+
cwd: options.cwd,
|
|
1710
|
+
viteServer: options.dev?.viteServer
|
|
1711
|
+
});
|
|
1712
|
+
frontendRenderer = renderer;
|
|
1707
1713
|
await Promise.all(model.pages.map(async (page) => {
|
|
1708
|
-
const
|
|
1714
|
+
const nav = buildNavigation(model, page);
|
|
1715
|
+
const html = renderer.renderPage({
|
|
1716
|
+
page,
|
|
1717
|
+
nav,
|
|
1718
|
+
pageNavigation: pageNavigation.get(page.route),
|
|
1719
|
+
css,
|
|
1720
|
+
searchIndexJson,
|
|
1721
|
+
logo: logoResolution.logo,
|
|
1722
|
+
favicon: logoResolution.favicon,
|
|
1723
|
+
repositoryUrl,
|
|
1724
|
+
projectName,
|
|
1725
|
+
navigation: configOptions.navigation,
|
|
1726
|
+
clientScriptPath: frontendAssets.scriptPath,
|
|
1727
|
+
clientStylePaths: frontendAssets.stylePaths
|
|
1728
|
+
});
|
|
1709
1729
|
const outputPath = path.join(outDir, page.outputPath);
|
|
1710
1730
|
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
1711
1731
|
await writeFile(outputPath, html);
|
|
1712
1732
|
}));
|
|
1713
1733
|
await writeFile(path.join(outDir, "assets", "lildocs.css"), css);
|
|
1714
1734
|
await writeFile(path.join(outDir, "assets", "tabler-icons.css"), tablerIconsCss);
|
|
1715
|
-
await writeFile(path.join(outDir, "assets", "search.js"), searchScript);
|
|
1716
|
-
await writeFile(path.join(outDir, "assets", "copy-code.js"), copyCodeScript);
|
|
1717
|
-
await writeFile(path.join(outDir, "assets", "swup.umd.js"), swupScript);
|
|
1718
|
-
await writeFile(path.join(outDir, "assets", "navigation.js"), navigationScript);
|
|
1719
1735
|
await writeFile(path.join(outDir, "assets", "github-icon.svg"), githubIcon);
|
|
1720
1736
|
await writeFile(path.join(outDir, "search-index.json"), searchIndexJson);
|
|
1721
1737
|
await copyAssets(assets);
|
|
1722
1738
|
} finally {
|
|
1739
|
+
await frontendRenderer?.close();
|
|
1723
1740
|
await mermaid.close();
|
|
1724
1741
|
}
|
|
1725
1742
|
return {
|
|
@@ -1770,17 +1787,6 @@ function githubRepositoryName(repositoryUrl) {
|
|
|
1770
1787
|
if (!repositoryUrl) return;
|
|
1771
1788
|
return /^https:\/\/github\.com\/([^/]+\/[^/]+)$/.exec(repositoryUrl)?.[1];
|
|
1772
1789
|
}
|
|
1773
|
-
async function readRenderAsset(name) {
|
|
1774
|
-
const bundledPath = path.resolve(sourceDir, "render", name);
|
|
1775
|
-
try {
|
|
1776
|
-
return await readFile(bundledPath, "utf8");
|
|
1777
|
-
} catch {
|
|
1778
|
-
return readFile(path.resolve(sourceDir, "../render", name), "utf8");
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
async function readSwupAsset() {
|
|
1782
|
-
return readFile(path.join(path.dirname(require.resolve("swup")), "Swup.umd.js"), "utf8");
|
|
1783
|
-
}
|
|
1784
1790
|
function buildPageNavigation(pages) {
|
|
1785
1791
|
const navigation = /* @__PURE__ */ new Map();
|
|
1786
1792
|
if (pages.length < 2) return navigation;
|
|
@@ -1801,95 +1807,18 @@ function buildPageNavigation(pages) {
|
|
|
1801
1807
|
return navigation;
|
|
1802
1808
|
}
|
|
1803
1809
|
//#endregion
|
|
1804
|
-
//#region src/core/server.ts
|
|
1805
|
-
const MIME_TYPES = new Map([
|
|
1806
|
-
[".html", "text/html; charset=utf-8"],
|
|
1807
|
-
[".css", "text/css; charset=utf-8"],
|
|
1808
|
-
[".js", "text/javascript; charset=utf-8"],
|
|
1809
|
-
[".json", "application/json; charset=utf-8"],
|
|
1810
|
-
[".svg", "image/svg+xml"],
|
|
1811
|
-
[".png", "image/png"],
|
|
1812
|
-
[".jpg", "image/jpeg"],
|
|
1813
|
-
[".jpeg", "image/jpeg"],
|
|
1814
|
-
[".gif", "image/gif"],
|
|
1815
|
-
[".webp", "image/webp"],
|
|
1816
|
-
[".avif", "image/avif"],
|
|
1817
|
-
[".ico", "image/x-icon"],
|
|
1818
|
-
[".txt", "text/plain; charset=utf-8"],
|
|
1819
|
-
[".woff", "font/woff"],
|
|
1820
|
-
[".woff2", "font/woff2"],
|
|
1821
|
-
[".ttf", "font/ttf"],
|
|
1822
|
-
[".otf", "font/otf"],
|
|
1823
|
-
[".eot", "application/vnd.ms-fontobject"]
|
|
1824
|
-
]);
|
|
1825
|
-
async function serveStaticFile(req, res, outDir) {
|
|
1826
|
-
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
1827
|
-
res.writeHead(405, { allow: "GET, HEAD" });
|
|
1828
|
-
res.end("Method Not Allowed");
|
|
1829
|
-
return;
|
|
1830
|
-
}
|
|
1831
|
-
const pathname = requestPathname$1(req);
|
|
1832
|
-
if (!pathname) {
|
|
1833
|
-
res.writeHead(400);
|
|
1834
|
-
res.end("Bad Request");
|
|
1835
|
-
return;
|
|
1836
|
-
}
|
|
1837
|
-
const relativePath = pathname === "/" ? "index.html" : pathname.slice(1);
|
|
1838
|
-
const filePath = path.resolve(outDir, relativePath);
|
|
1839
|
-
if (!isInside$1(outDir, filePath)) {
|
|
1840
|
-
res.writeHead(404);
|
|
1841
|
-
res.end("Not Found");
|
|
1842
|
-
return;
|
|
1843
|
-
}
|
|
1844
|
-
let fileStat;
|
|
1845
|
-
try {
|
|
1846
|
-
fileStat = await stat(filePath);
|
|
1847
|
-
} catch {
|
|
1848
|
-
res.writeHead(404);
|
|
1849
|
-
res.end("Not Found");
|
|
1850
|
-
return;
|
|
1851
|
-
}
|
|
1852
|
-
if (!fileStat.isFile()) {
|
|
1853
|
-
res.writeHead(404);
|
|
1854
|
-
res.end("Not Found");
|
|
1855
|
-
return;
|
|
1856
|
-
}
|
|
1857
|
-
res.writeHead(200, {
|
|
1858
|
-
"content-length": fileStat.size,
|
|
1859
|
-
"content-type": MIME_TYPES.get(path.extname(filePath).toLowerCase()) ?? "application/octet-stream"
|
|
1860
|
-
});
|
|
1861
|
-
if (req.method === "HEAD") {
|
|
1862
|
-
res.end();
|
|
1863
|
-
return;
|
|
1864
|
-
}
|
|
1865
|
-
createReadStream(filePath).pipe(res);
|
|
1866
|
-
}
|
|
1867
|
-
function requestPathname$1(req) {
|
|
1868
|
-
try {
|
|
1869
|
-
return decodeURIComponent(new URL(req.url ?? "/", "http://lildocs.local").pathname);
|
|
1870
|
-
} catch {
|
|
1871
|
-
return;
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
function isInside$1(root, candidate) {
|
|
1875
|
-
const relative = path.relative(root, candidate);
|
|
1876
|
-
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
1877
|
-
}
|
|
1878
|
-
//#endregion
|
|
1879
1810
|
//#region src/core/dev.ts
|
|
1880
|
-
const DEV_CLIENT_PATH = "/__lildocs/client.js";
|
|
1881
|
-
const DEV_EVENTS_PATH = "/__lildocs/events";
|
|
1882
|
-
const DEV_CLIENT_SCRIPT = `
|
|
1883
|
-
const events = new EventSource("${DEV_EVENTS_PATH}");
|
|
1884
|
-
events.addEventListener("reload", () => location.reload());
|
|
1885
|
-
events.onerror = () => console.debug("[lildocs] live reload disconnected");
|
|
1886
|
-
`;
|
|
1887
1811
|
async function startDevServer(options) {
|
|
1888
1812
|
const input = await resolveInput(options.input, options.cwd, { homePagePreference: "readme-first" });
|
|
1889
1813
|
const outDir = path.resolve(options.cwd, options.outDir);
|
|
1890
1814
|
validateDevOutDir(options.cwd, input.docsRoot, outDir, options.outDir);
|
|
1891
|
-
|
|
1892
|
-
|
|
1815
|
+
await mkdir(outDir, { recursive: true });
|
|
1816
|
+
const vite = await createFrontendDevServer({
|
|
1817
|
+
cwd: options.cwd,
|
|
1818
|
+
root: outDir,
|
|
1819
|
+
host: options.host,
|
|
1820
|
+
port: options.port
|
|
1821
|
+
});
|
|
1893
1822
|
let rebuildTimer;
|
|
1894
1823
|
let rebuilding = false;
|
|
1895
1824
|
let pending = false;
|
|
@@ -1911,12 +1840,14 @@ async function startDevServer(options) {
|
|
|
1911
1840
|
background: options.background,
|
|
1912
1841
|
link: options.link,
|
|
1913
1842
|
homePagePreference: "readme-first",
|
|
1914
|
-
dev: {
|
|
1843
|
+
dev: {
|
|
1844
|
+
clientScriptPath: frontendDevScriptPath(),
|
|
1845
|
+
viteServer: vite
|
|
1846
|
+
}
|
|
1915
1847
|
});
|
|
1916
|
-
await refreshWatchers();
|
|
1917
1848
|
const elapsed = Math.round(performance.now() - started);
|
|
1918
1849
|
console.log(`Rebuilt ${lastSuccessfulBuild.pages.length} page${lastSuccessfulBuild.pages.length === 1 ? "" : "s"} in ${elapsed}ms`);
|
|
1919
|
-
if (emitReload)
|
|
1850
|
+
if (emitReload) vite.ws.send({ type: "full-reload" });
|
|
1920
1851
|
} catch (error) {
|
|
1921
1852
|
if (throwOnFailure) throw error;
|
|
1922
1853
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1935,37 +1866,14 @@ async function startDevServer(options) {
|
|
|
1935
1866
|
rebuild(true);
|
|
1936
1867
|
}, 150);
|
|
1937
1868
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
if (filename && shouldIgnore(path.join(directory, filename.toString()), input.docsRoot, outDir)) return;
|
|
1944
|
-
scheduleRebuild();
|
|
1945
|
-
}));
|
|
1946
|
-
}
|
|
1947
|
-
await rebuild(false, true);
|
|
1948
|
-
const server = http.createServer((req, res) => {
|
|
1949
|
-
const pathname = requestPathname(req.url);
|
|
1950
|
-
if (pathname === DEV_CLIENT_PATH) {
|
|
1951
|
-
res.writeHead(200, { "content-type": "text/javascript; charset=utf-8" });
|
|
1952
|
-
res.end(DEV_CLIENT_SCRIPT);
|
|
1953
|
-
return;
|
|
1954
|
-
}
|
|
1955
|
-
if (pathname === DEV_EVENTS_PATH) {
|
|
1956
|
-
connectEvents(res, clients);
|
|
1957
|
-
return;
|
|
1958
|
-
}
|
|
1959
|
-
serveStaticFile(req, res, outDir);
|
|
1869
|
+
vite.watcher.add(input.docsRoot);
|
|
1870
|
+
vite.watcher.on("all", (_event, file) => {
|
|
1871
|
+
const changedPath = path.resolve(file);
|
|
1872
|
+
if (!isInside(input.docsRoot, changedPath) || shouldIgnore(changedPath, input.docsRoot, outDir)) return;
|
|
1873
|
+
scheduleRebuild();
|
|
1960
1874
|
});
|
|
1961
|
-
await
|
|
1962
|
-
|
|
1963
|
-
server.listen(options.port, options.host, () => {
|
|
1964
|
-
server.off("error", reject);
|
|
1965
|
-
resolve();
|
|
1966
|
-
});
|
|
1967
|
-
});
|
|
1968
|
-
const address = server.address();
|
|
1875
|
+
await rebuild(false, true);
|
|
1876
|
+
const address = vite.httpServer?.address();
|
|
1969
1877
|
const actualPort = typeof address === "object" && address ? address.port : options.port;
|
|
1970
1878
|
const url = `http://${options.host}:${actualPort}/`;
|
|
1971
1879
|
console.log(`lildocs dev server listening at ${url}`);
|
|
@@ -1973,11 +1881,7 @@ async function startDevServer(options) {
|
|
|
1973
1881
|
url,
|
|
1974
1882
|
close: async () => {
|
|
1975
1883
|
if (rebuildTimer) clearTimeout(rebuildTimer);
|
|
1976
|
-
|
|
1977
|
-
for (const client of clients) client.end();
|
|
1978
|
-
await new Promise((resolve, reject) => {
|
|
1979
|
-
server.close((error) => error ? reject(error) : resolve());
|
|
1980
|
-
});
|
|
1884
|
+
await vite.close();
|
|
1981
1885
|
}
|
|
1982
1886
|
};
|
|
1983
1887
|
}
|
|
@@ -1988,46 +1892,12 @@ function validateDevOutDir(cwd, docsRoot, outDir, requestedOutDir) {
|
|
|
1988
1892
|
if (outDir === docsRoot) throw new LildocsError("Dev output directory cannot be the docs root.");
|
|
1989
1893
|
if (isAncestor(outDir, docsRoot)) throw new LildocsError("Dev output directory cannot contain the docs root.");
|
|
1990
1894
|
}
|
|
1991
|
-
async function collectWatchDirectories(docsRoot, outDir) {
|
|
1992
|
-
const directories = /* @__PURE__ */ new Set();
|
|
1993
|
-
async function walk(directory) {
|
|
1994
|
-
if (shouldIgnore(directory, docsRoot, outDir)) return;
|
|
1995
|
-
directories.add(directory);
|
|
1996
|
-
const entries = await readdir(directory, { withFileTypes: true });
|
|
1997
|
-
await Promise.all(entries.map(async (entry) => {
|
|
1998
|
-
if (!entry.isDirectory()) return;
|
|
1999
|
-
await walk(path.join(directory, entry.name));
|
|
2000
|
-
}));
|
|
2001
|
-
}
|
|
2002
|
-
await walk(docsRoot);
|
|
2003
|
-
return [...directories];
|
|
2004
|
-
}
|
|
2005
1895
|
function shouldIgnore(candidate, docsRoot, outDir) {
|
|
2006
1896
|
const resolved = path.resolve(candidate);
|
|
2007
1897
|
if (resolved === outDir || isAncestor(outDir, resolved)) return true;
|
|
2008
1898
|
const relative = path.relative(docsRoot, resolved);
|
|
2009
1899
|
return relative === "dist" || relative.startsWith(`dist${path.sep}`) || relative === "node_modules" || relative.startsWith(`node_modules${path.sep}`) || isHiddenOrSystemPath(relative);
|
|
2010
1900
|
}
|
|
2011
|
-
function connectEvents(res, clients) {
|
|
2012
|
-
res.writeHead(200, {
|
|
2013
|
-
"cache-control": "no-cache",
|
|
2014
|
-
connection: "keep-alive",
|
|
2015
|
-
"content-type": "text/event-stream"
|
|
2016
|
-
});
|
|
2017
|
-
res.write(": connected\n\n");
|
|
2018
|
-
clients.add(res);
|
|
2019
|
-
res.on("close", () => clients.delete(res));
|
|
2020
|
-
}
|
|
2021
|
-
function sendReload(clients) {
|
|
2022
|
-
for (const client of clients) client.write("event: reload\ndata: {}\n\n");
|
|
2023
|
-
}
|
|
2024
|
-
function requestPathname(url) {
|
|
2025
|
-
try {
|
|
2026
|
-
return new URL(url ?? "/", "http://lildocs.local").pathname;
|
|
2027
|
-
} catch {
|
|
2028
|
-
return;
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
1901
|
function isAncestor(parent, child) {
|
|
2032
1902
|
const relative = path.relative(parent, child);
|
|
2033
1903
|
return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
|