lildocs 0.1.6 → 0.1.8

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 CHANGED
@@ -60,7 +60,7 @@ The project uses:
60
60
  - `cmd-ts` for CLI parsing
61
61
  - Preact SSR for rendering
62
62
  - `marked` and `gray-matter` for Markdown and frontmatter
63
- - `jiti` for theme loading
63
+ - native dynamic `import()` for local theme loading
64
64
  - `tsdown` for bundling
65
65
  - `oxlint` for linting
66
66
  - `oxfmt` for formatting
package/dist/cli.mjs CHANGED
@@ -15,7 +15,6 @@ import { renderMermaidSVG } from "beautiful-mermaid";
15
15
  import os from "node:os";
16
16
  import { generateMarkdownForModule } from "exports-md";
17
17
  import { clampGamut, converter, formatHex, parse, wcagContrast } from "culori";
18
- import { createJiti } from "jiti";
19
18
  import { render } from "preact-render-to-string";
20
19
  import { jsx, jsxs } from "preact/jsx-runtime";
21
20
  import http from "node:http";
@@ -322,8 +321,10 @@ function inferTitle(metadata, markdown, sourcePath) {
322
321
  if (h1?.[1]) return stripMarkdownInline(h1[1]).trim();
323
322
  return formatFilename(path.basename(sourcePath, path.extname(sourcePath)));
324
323
  }
325
- function slugify(value) {
326
- return value.toLowerCase().trim().replace(/[`*_~[\]()]/g, "").replace(/&/g, " and ").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "section";
324
+ function slugify(value, options = {}) {
325
+ const punctuation = options.preserveUnderscores ? /[`*~[\]()]/g : /[`*_~[\]()]/g;
326
+ const nonSlugCharacters = options.preserveUnderscores ? /[^a-z0-9_]+/g : /[^a-z0-9]+/g;
327
+ return value.toLowerCase().trim().replace(punctuation, "").replace(/&/g, " and ").replace(nonSlugCharacters, "-").replace(/^-+|-+$/g, "") || "section";
327
328
  }
328
329
  function extractHeadings(markdown) {
329
330
  const seen = /* @__PURE__ */ new Map();
@@ -331,7 +332,7 @@ function extractHeadings(markdown) {
331
332
  for (const line of markdown.split(/\r?\n/)) {
332
333
  const match = line.match(/^(#{1,6})\s+(.+)$/);
333
334
  if (!match?.[1] || !match[2]) continue;
334
- const baseId = slugify(stripMarkdownInline(match[2]));
335
+ const baseId = slugify(stripMarkdownInline(match[2]), { preserveUnderscores: true });
335
336
  const count = seen.get(baseId) ?? 0;
336
337
  seen.set(baseId, count + 1);
337
338
  headings.push({
@@ -343,7 +344,7 @@ function extractHeadings(markdown) {
343
344
  return headings;
344
345
  }
345
346
  function stripMarkdownInline(value) {
346
- return value.replace(/[`*_~]/g, "").replace(/\[(.*?)\]\(.*?\)/g, "$1");
347
+ return value.replace(/\[(.*?)\]\(.*?\)/g, "$1").replace(/`([^`]*)`/g, "$1").replace(/~~([\s\S]*?)~~/g, "$1").replace(/\*\*([\s\S]*?)\*\*/g, "$1").replace(/__([\s\S]*?)__/g, "$1").replace(/\*([^*]*?)\*/g, "$1").replace(/(^|[\s([{])_([^\s_](?:[\s\S]*?[^\s_])?)_(?=$|[\s)\]}.,;:!?])/g, "$1$2");
347
348
  }
348
349
  function formatFilename(value) {
349
350
  return value.replace(/[-_]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase()).trim();
@@ -965,8 +966,7 @@ const themes = {
965
966
  body: "system-ui, sans-serif",
966
967
  code: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
967
968
  },
968
- shiki: { theme: "github-light" },
969
- background: { gradient: "linear-gradient(180deg, rgb(37 99 235 / 0.045), transparent 220px)" }
969
+ shiki: { theme: "github-light" }
970
970
  },
971
971
  minimal: {
972
972
  color: {
@@ -984,8 +984,7 @@ const themes = {
984
984
  body: "Arial, sans-serif",
985
985
  code: "Menlo, Consolas, monospace"
986
986
  },
987
- shiki: { theme: "github-light" },
988
- background: { gradient: "linear-gradient(180deg, rgb(15 118 110 / 0.04), transparent 220px)" }
987
+ shiki: { theme: "github-light" }
989
988
  }
990
989
  };
991
990
  async function resolveTheme(options) {
@@ -997,7 +996,12 @@ async function resolveTheme(options) {
997
996
  };
998
997
  }
999
998
  const localThemePath = path.join(options.docsRoot, "theme.ts");
1000
- if (await exists(localThemePath)) return { light: validateTheme(await createJiti(pathToFileURL(options.cwd).href).import(localThemePath, { default: true }), localThemePath) };
999
+ if (await exists(localThemePath)) {
1000
+ const localThemeUrl = pathToFileURL(localThemePath);
1001
+ const localThemeStats = await stat(localThemePath);
1002
+ localThemeUrl.searchParams.set("mtime", String(localThemeStats.mtimeMs));
1003
+ return { light: validateTheme((await import(localThemeUrl.href)).default, localThemePath) };
1004
+ }
1001
1005
  return {
1002
1006
  light: themes.default,
1003
1007
  dark: await resolveThemeName("github-dark")
@@ -1174,30 +1178,27 @@ function mapShikiThemeToTheme(shikiTheme, themeName) {
1174
1178
  "foreground",
1175
1179
  "sideBar.foreground"
1176
1180
  ], shikiTheme.fg ?? (shikiTheme.type === "dark" ? "#f5f5f5" : "#111111"));
1177
- const mutedText = pickColor(colors, [
1178
- "descriptionForeground",
1179
- "breadcrumb.foreground",
1180
- "editorLineNumber.foreground",
1181
- "sideBar.foreground"
1182
- ], tokenForeground(tokenColors, "comment") ?? text);
1183
- const border = pickColor(colors, [
1184
- "panel.border",
1185
- "sideBar.border",
1186
- "editorGroup.border",
1187
- "tab.border"
1188
- ], shikiTheme.type === "dark" ? "#333333" : "#e5e5e5");
1189
- const link = pickColor(colors, [
1190
- "textLink.foreground",
1191
- "terminal.ansiBlue",
1192
- "activityBarBadge.background"
1193
- ], tokenForeground(tokenColors, "markup.inline.raw") ?? (shikiTheme.type === "dark" ? "#79b8ff" : "#2563eb"));
1194
1181
  return {
1195
1182
  color: {
1196
1183
  background,
1197
1184
  text,
1198
- mutedText,
1199
- border,
1200
- link,
1185
+ mutedText: pickColor(colors, [
1186
+ "descriptionForeground",
1187
+ "breadcrumb.foreground",
1188
+ "editorLineNumber.foreground",
1189
+ "sideBar.foreground"
1190
+ ], tokenForeground(tokenColors, "comment") ?? text),
1191
+ border: pickColor(colors, [
1192
+ "panel.border",
1193
+ "sideBar.border",
1194
+ "editorGroup.border",
1195
+ "tab.border"
1196
+ ], shikiTheme.type === "dark" ? "#333333" : "#e5e5e5"),
1197
+ link: pickColor(colors, [
1198
+ "textLink.foreground",
1199
+ "terminal.ansiBlue",
1200
+ "activityBarBadge.background"
1201
+ ], tokenForeground(tokenColors, "markup.inline.raw") ?? (shikiTheme.type === "dark" ? "#79b8ff" : "#2563eb")),
1201
1202
  codeBackground: ensureBackgroundContrast({
1202
1203
  background,
1203
1204
  candidate: pickColor(colors, [
@@ -1220,8 +1221,7 @@ function mapShikiThemeToTheme(shikiTheme, themeName) {
1220
1221
  body: "system-ui, sans-serif",
1221
1222
  code: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
1222
1223
  },
1223
- shiki: { theme: themeName },
1224
- background: { gradient: shikiTheme.type === "dark" ? `linear-gradient(180deg, color-mix(in srgb, ${link} 16%, transparent), transparent 240px)` : `linear-gradient(180deg, color-mix(in srgb, ${link} 9%, transparent), transparent 220px)` }
1224
+ shiki: { theme: themeName }
1225
1225
  };
1226
1226
  }
1227
1227
  function pickColor(colors, keys, fallback) {
@@ -1623,7 +1623,6 @@ async function buildSite(options) {
1623
1623
  const packageName = await resolvePackageName(packagePath);
1624
1624
  const projectName = configOptions.projectName ?? packageName;
1625
1625
  const theme = await resolveTheme({
1626
- cwd: options.cwd,
1627
1626
  docsRoot: input.docsRoot,
1628
1627
  requestedTheme: configOptions.theme
1629
1628
  });
@@ -1634,7 +1633,7 @@ async function buildSite(options) {
1634
1633
  fonts: configOptions.fonts
1635
1634
  });
1636
1635
  const baseCss = await readRenderAsset("styles.css");
1637
- const tablerIconsCss = await readTablerIconsCss();
1636
+ const tablerIconsCss = await readRenderAsset("tabler-icons.css");
1638
1637
  const searchScript = await readRenderAsset("search.js");
1639
1638
  const copyCodeScript = await readRenderAsset("copy-code.js");
1640
1639
  const navigationScript = await readRenderAsset("navigation.js");
@@ -1656,7 +1655,6 @@ async function buildSite(options) {
1656
1655
  });
1657
1656
  const css = `${fontResolution.css}${themeToCssVariables(theme, fontResolution.themeFonts, configOptions.link, configOptions.navigation)}\n${backgroundResolution.css}${logoResolution.css}${baseCss}`;
1658
1657
  const assets = [
1659
- ...tablerIconFontAssets(outDir),
1660
1658
  ...fontResolution.assets,
1661
1659
  ...backgroundResolution.assets,
1662
1660
  ...logoResolution.assets
@@ -1757,20 +1755,6 @@ async function readRenderAsset(name) {
1757
1755
  async function readSwupAsset() {
1758
1756
  return readFile(path.join(path.dirname(require.resolve("swup")), "Swup.umd.js"), "utf8");
1759
1757
  }
1760
- async function readTablerIconsCss() {
1761
- return readFile(require.resolve("@tabler/icons-webfont/dist/tabler-icons.css"), "utf8");
1762
- }
1763
- function tablerIconFontAssets(outDir) {
1764
- const fontsDir = path.join(path.dirname(require.resolve("@tabler/icons-webfont/dist/tabler-icons.css")), "fonts");
1765
- return [
1766
- "tabler-icons.woff2",
1767
- "tabler-icons.woff",
1768
- "tabler-icons.ttf"
1769
- ].map((name) => ({
1770
- from: path.join(fontsDir, name),
1771
- to: path.join(outDir, "assets", "fonts", name)
1772
- }));
1773
- }
1774
1758
  function buildPageNavigation(pages) {
1775
1759
  const navigation = /* @__PURE__ */ new Map();
1776
1760
  if (pages.length < 2) return navigation;
@@ -2119,7 +2103,7 @@ const fontCodeOption = option({
2119
2103
  const backgroundImageOption = option({
2120
2104
  type: optional(string),
2121
2105
  long: "background.image",
2122
- description: "Background image URL or docs-relative image path."
2106
+ description: "Background image URL, CSS image function, or docs-relative image path."
2123
2107
  });
2124
2108
  const backgroundGradientOption = option({
2125
2109
  type: optional(string),
@@ -149,6 +149,10 @@ html.is-animating .transition-scale {
149
149
  margin-block: 2em 0.667em;
150
150
  }
151
151
 
152
+ .content article h1 {
153
+ font-size: 2.2rem;
154
+ }
155
+
152
156
  .content article p {
153
157
  margin-block: 1.25em;
154
158
  }
@@ -160,11 +164,11 @@ html.is-animating .transition-scale {
160
164
 
161
165
  .content article {
162
166
  max-width: 60ch;
163
- font-size: 110%;
167
+ font-size: 105%;
164
168
  }
165
169
 
166
170
  .groupBreadcrumbs {
167
- margin: 0 0 -4px;
171
+ margin-block-end: -4px !important;
168
172
  color: var(--ld-color-accent);
169
173
  font-size: 80%;
170
174
  font-weight: 700;
@@ -187,6 +191,18 @@ html.is-animating .transition-scale {
187
191
  color: var(--ld-color-muted-text);
188
192
  }
189
193
 
194
+ .content article h1 + blockquote {
195
+ margin: 0 0 1.5em;
196
+ margin-top: -1.1em;
197
+ padding: 0;
198
+ border-left: 0;
199
+ border-radius: 0;
200
+ background: transparent;
201
+ color: color-mix(in srgb, var(--ld-color-muted-text) 88%, var(--ld-color-text));
202
+ font-size: 1.15em;
203
+ line-height: 1.45;
204
+ }
205
+
190
206
  .content blockquote > :first-child {
191
207
  margin-top: 0;
192
208
  }
@@ -0,0 +1,46 @@
1
+ /*
2
+ * Tabler Icons 3.44.0, MIT License.
3
+ * Bundles only the icon masks used by lildocs.
4
+ */
5
+
6
+ .ti {
7
+ display: inline-block;
8
+ width: 1em;
9
+ height: 1em;
10
+ background: currentColor;
11
+ vertical-align: -0.125em;
12
+ mask: var(--ld-tabler-icon) center / contain no-repeat;
13
+ -webkit-mask: var(--ld-tabler-icon) center / contain no-repeat;
14
+ }
15
+
16
+ .ti-search {
17
+ --ld-tabler-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%20%2F%3E%3Cpath%20d%3D%22M3%2010a7%207%200%201%200%2014%200a7%207%200%201%200%20-14%200%22%20%2F%3E%3Cpath%20d%3D%22M21%2021l-6%20-6%22%20%2F%3E%3C%2Fsvg%3E");
18
+ }
19
+
20
+ .ti-copy {
21
+ --ld-tabler-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%20%2F%3E%3Cpath%20d%3D%22M7%209.667a2.667%202.667%200%200%201%202.667%20-2.667h8.666a2.667%202.667%200%200%201%202.667%202.667v8.666a2.667%202.667%200%200%201%20-2.667%202.667h-8.666a2.667%202.667%200%200%201%20-2.667%20-2.667l0%20-8.666%22%20%2F%3E%3Cpath%20d%3D%22M4.012%2016.737a2.005%202.005%200%200%201%20-1.012%20-1.737v-10c0%20-1.1%20.9%20-2%202%20-2h10c.75%200%201.158%20.385%201.5%201%22%20%2F%3E%3C%2Fsvg%3E");
22
+ }
23
+
24
+ .ti-copy-check {
25
+ --ld-tabler-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%20%2F%3E%3Cpath%20d%3D%22M7%209.667a2.667%202.667%200%200%201%202.667%20-2.667h8.666a2.667%202.667%200%200%201%202.667%202.667v8.666a2.667%202.667%200%200%201%20-2.667%202.667h-8.666a2.667%202.667%200%200%201%20-2.667%20-2.667l0%20-8.666%22%20%2F%3E%3Cpath%20d%3D%22M4.012%2016.737a2%202%200%200%201%20-1.012%20-1.737v-10c0%20-1.1%20.9%20-2%202%20-2h10c.75%200%201.158%20.385%201.5%201%22%20%2F%3E%3Cpath%20d%3D%22M11%2014l2%202l4%20-4%22%20%2F%3E%3C%2Fsvg%3E");
26
+ }
27
+
28
+ .ti-alert-circle {
29
+ --ld-tabler-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%20%2F%3E%3Cpath%20d%3D%22M3%2012a9%209%200%201%200%2018%200a9%209%200%200%200%20-18%200%22%20%2F%3E%3Cpath%20d%3D%22M12%208v4%22%20%2F%3E%3Cpath%20d%3D%22M12%2016h.01%22%20%2F%3E%3C%2Fsvg%3E");
30
+ }
31
+
32
+ .ti-alert-triangle {
33
+ --ld-tabler-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%20%2F%3E%3Cpath%20d%3D%22M12%209v4%22%20%2F%3E%3Cpath%20d%3D%22M10.363%203.591l-8.106%2013.534a1.914%201.914%200%200%200%201.636%202.871h16.214a1.914%201.914%200%200%200%201.636%20-2.87l-8.106%20-13.536a1.914%201.914%200%200%200%20-3.274%200%22%20%2F%3E%3Cpath%20d%3D%22M12%2016h.01%22%20%2F%3E%3C%2Fsvg%3E");
34
+ }
35
+
36
+ .ti-info-circle {
37
+ --ld-tabler-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%20%2F%3E%3Cpath%20d%3D%22M3%2012a9%209%200%201%200%2018%200a9%209%200%200%200%20-18%200%22%20%2F%3E%3Cpath%20d%3D%22M12%209h.01%22%20%2F%3E%3Cpath%20d%3D%22M11%2012h1v4h1%22%20%2F%3E%3C%2Fsvg%3E");
38
+ }
39
+
40
+ .ti-bulb {
41
+ --ld-tabler-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%20%2F%3E%3Cpath%20d%3D%22M3%2012h1m8%20-9v1m8%208h1m-15.4%20-6.4l.7%20.7m12.1%20-.7l-.7%20.7%22%20%2F%3E%3Cpath%20d%3D%22M9%2016a5%205%200%201%201%206%200a3.5%203.5%200%200%200%20-1%203a2%202%200%200%201%20-4%200a3.5%203.5%200%200%200%20-1%20-3%22%20%2F%3E%3Cpath%20d%3D%22M9.7%2017l4.6%200%22%20%2F%3E%3C%2Fsvg%3E");
42
+ }
43
+
44
+ .ti-message-report {
45
+ --ld-tabler-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%20%2F%3E%3Cpath%20d%3D%22M18%204a3%203%200%200%201%203%203v8a3%203%200%200%201%20-3%203h-5l-5%203v-3h-2a3%203%200%200%201%20-3%20-3v-8a3%203%200%200%201%203%20-3h12%22%20%2F%3E%3Cpath%20d%3D%22M12%208v3%22%20%2F%3E%3Cpath%20d%3D%22M12%2014v.01%22%20%2F%3E%3C%2Fsvg%3E");
46
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lildocs",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "A lightweight CLI that turns Markdown docs into a static searchable documentation site.",
5
5
  "homepage": "https://aleclarson.github.io/lildocs/",
6
6
  "repository": {
@@ -26,13 +26,11 @@
26
26
  "dependencies": {
27
27
  "@goddard-ai/ui-primitives": "^0.1.1",
28
28
  "@preact/signals": "^2.9.2",
29
- "@tabler/icons-webfont": "^3.44.0",
30
29
  "beautiful-mermaid": "^1.1.3",
31
30
  "cmd-ts": "^0.15.0",
32
31
  "culori": "^4.0.2",
33
32
  "exports-md": "^0.1.7",
34
33
  "gray-matter": "^4.0.3",
35
- "jiti": "^2.7.0",
36
34
  "marked": "^18.0.3",
37
35
  "marked-shiki": "^1.2.1",
38
36
  "preact": "^10.29.2",
@@ -50,7 +48,7 @@
50
48
  "typescript": "^5.9.0"
51
49
  },
52
50
  "engines": {
53
- "node": "^22.18.0 || >=24.2.0"
51
+ "node": "^22.18.0 || >=24.3.0"
54
52
  },
55
53
  "packageManager": "pnpm@10.29.3"
56
54
  }