ardo 3.2.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +49 -41
  2. package/dist/{DocPage-CIBiCAxZ.js → DocPage-Dy7OrCP2.js} +160 -31
  3. package/dist/DocPage-Dy7OrCP2.js.map +1 -0
  4. package/dist/assets/src/ui/DocPage.css.ts.vanilla-CWL92vUE.css +117 -0
  5. package/dist/assets/src/ui/ErrorBoundary.css.ts.vanilla-C4usIU4z.css +112 -0
  6. package/dist/assets/src/ui/Footer.css.ts.vanilla-DGTyff5Y.css +171 -0
  7. package/dist/assets/src/ui/{Header.css.ts.vanilla-8QL0Jzgk.css → Header.css.ts.vanilla-DEcLj8r0.css} +53 -27
  8. package/dist/assets/src/ui/{Layout.css.ts.vanilla-Bpx_-gJt.css → Layout.css.ts.vanilla-ClOa1YZm.css} +6 -10
  9. package/dist/assets/src/ui/Sidebar.css.ts.vanilla-IxNEQEBv.css +199 -0
  10. package/dist/assets/src/ui/{Toc.css.ts.vanilla-CYqcWgvD.css → Toc.css.ts.vanilla-CQbpEdTg.css} +9 -21
  11. package/dist/assets/src/ui/components/{CodeBlock.css.ts.vanilla-lNKqskjQ.css → CodeBlock.css.ts.vanilla-BxDJ2gKc.css} +38 -20
  12. package/dist/assets/src/ui/components/{CopyButton.css.ts.vanilla-DZZ5jgTM.css → CopyButton.css.ts.vanilla-CO2awD6S.css} +6 -7
  13. package/dist/assets/src/ui/components/{Features.css.ts.vanilla-D-pNXM9Q.css → Features.css.ts.vanilla-ggYasCFy.css} +7 -3
  14. package/dist/assets/src/ui/components/HeaderSearch.css.ts.vanilla-KAo_Mlc-.css +68 -0
  15. package/dist/assets/src/ui/components/{Hero.css.ts.vanilla-DHJVZ6GX.css → Hero.css.ts.vanilla-DY_BH0W6.css} +9 -6
  16. package/dist/assets/src/ui/components/{Search.css.ts.vanilla-BYpWHzky.css → Search.css.ts.vanilla-NQZH1eLo.css} +19 -7
  17. package/dist/assets/src/ui/{content.css.ts.vanilla-O_RaSPXm.css → content.css.ts.vanilla-CJnrOQNh.css} +43 -17
  18. package/dist/assets/src/ui/theme/{dark.css.ts.vanilla-2iJgcpbU.css → dark.css.ts.vanilla-CQef5pk2.css} +32 -28
  19. package/dist/assets/src/ui/theme/{light.css.ts.vanilla-CwinfWSf.css → light.css.ts.vanilla-D8gxaS1c.css} +32 -28
  20. package/dist/assets/src/ui/theme/{reset.css.ts.vanilla-0Q3pLjfC.css → reset.css.ts.vanilla-e2dF1d0f.css} +4 -0
  21. package/dist/{brand-icons-DLJKqTun.js → brand-icons-Di8w0Nu9.js} +1 -1
  22. package/dist/{brand-icons-DLJKqTun.js.map → brand-icons-Di8w0Nu9.js.map} +1 -1
  23. package/dist/config/index.d.ts +2 -2
  24. package/dist/config/index.d.ts.map +1 -1
  25. package/dist/config/index.js +1 -0
  26. package/dist/config/index.js.map +1 -1
  27. package/dist/{contract.css-DYvFVCFE.d.ts → contract.css-qPyk_asd.d.ts} +5 -1
  28. package/dist/contract.css-qPyk_asd.d.ts.map +1 -0
  29. package/dist/favicon-Cx-inut3.js +7 -0
  30. package/dist/favicon-Cx-inut3.js.map +1 -0
  31. package/dist/{generator-DPtRXxM_.js → generator-CYSyo4Vz.js} +7 -6
  32. package/dist/generator-CYSyo4Vz.js.map +1 -0
  33. package/dist/icons/index.js +1 -1
  34. package/dist/{index-BTeHvysI.d.ts → index-BcekgOfA.d.ts} +82 -12
  35. package/dist/index-BcekgOfA.d.ts.map +1 -0
  36. package/dist/{index-DySzkJlC.d.ts → index-CuMTHUxX.d.ts} +8 -2
  37. package/dist/index-CuMTHUxX.d.ts.map +1 -0
  38. package/dist/index.d.ts +4 -4
  39. package/dist/index.js +3 -3
  40. package/dist/mdx/provider.js +1 -1
  41. package/dist/runtime/index.d.ts +1 -1
  42. package/dist/runtime/index.js +1 -1
  43. package/dist/{sidebar-utils-1Skqle1Q.js → sidebar-utils-C06DJsx4.js} +15 -4
  44. package/dist/sidebar-utils-C06DJsx4.js.map +1 -0
  45. package/dist/theme/index.d.ts +16 -4
  46. package/dist/theme/index.d.ts.map +1 -1
  47. package/dist/theme/index.js +84 -59
  48. package/dist/theme/index.js.map +1 -1
  49. package/dist/typedoc/components/index.d.ts +1 -1
  50. package/dist/typedoc/index.d.ts +1 -1
  51. package/dist/typedoc/index.d.ts.map +1 -1
  52. package/dist/typedoc/index.js +1 -1
  53. package/dist/{types-CTd_mkrv.d.ts → types-B75OhnGa.d.ts} +21 -3
  54. package/dist/types-B75OhnGa.d.ts.map +1 -0
  55. package/dist/{types-BCuJBsJu.d.ts → types-Ck2Vm7NB.d.ts} +2 -2
  56. package/dist/{types-BCuJBsJu.d.ts.map → types-Ck2Vm7NB.d.ts.map} +1 -1
  57. package/dist/ui/index.d.ts +2 -2
  58. package/dist/ui/index.js +3 -3
  59. package/dist/ui/styles.css +704 -382
  60. package/dist/ui/styles.js +15 -15
  61. package/dist/{ui-3grzJSsq.js → ui-AGPGBunC.js} +729 -280
  62. package/dist/ui-AGPGBunC.js.map +1 -0
  63. package/dist/vite/index.d.ts +18 -2
  64. package/dist/vite/index.d.ts.map +1 -1
  65. package/dist/vite/index.js +446 -121
  66. package/dist/vite/index.js.map +1 -1
  67. package/package.json +14 -13
  68. package/dist/DocPage-CIBiCAxZ.js.map +0 -1
  69. package/dist/assets/src/ui/DocPage.css.ts.vanilla-CXKuz4U-.css +0 -34
  70. package/dist/assets/src/ui/Footer.css.ts.vanilla-BSzPIPt4.css +0 -100
  71. package/dist/assets/src/ui/Sidebar.css.ts.vanilla-D70qXTEr.css +0 -115
  72. package/dist/contract.css-DYvFVCFE.d.ts.map +0 -1
  73. package/dist/generator-DPtRXxM_.js.map +0 -1
  74. package/dist/index-BTeHvysI.d.ts.map +0 -1
  75. package/dist/index-DySzkJlC.d.ts.map +0 -1
  76. package/dist/sidebar-utils-1Skqle1Q.js.map +0 -1
  77. package/dist/types-CTd_mkrv.d.ts.map +0 -1
  78. package/dist/ui-3grzJSsq.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { defaultMarkdownConfig, resolveConfig } from "../config/index.js";
2
- import { n as generateApiDocs } from "../generator-DPtRXxM_.js";
2
+ import { n as ARDO_FAVICON_SVG } from "../favicon-Cx-inut3.js";
3
+ import { n as generateApiDocs } from "../generator-CYSyo4Vz.js";
3
4
  import path from "node:path";
4
5
  import matter from "gray-matter";
5
6
  import rehypeStringify from "rehype-stringify";
@@ -10,10 +11,11 @@ import remarkRehype from "remark-rehype";
10
11
  import { unified } from "unified";
11
12
  import { visit } from "unist-util-visit";
12
13
  import { createHighlighter } from "shiki";
13
- import fs from "node:fs/promises";
14
+ import fs, { readFile } from "node:fs/promises";
14
15
  import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
15
- import fsSync from "node:fs";
16
+ import fsSync, { existsSync } from "node:fs";
16
17
  import { execSync } from "node:child_process";
18
+ import { Resvg } from "@resvg/resvg-js";
17
19
  import mdx from "@mdx-js/rollup";
18
20
  import { reactRouter } from "@react-router/dev/vite";
19
21
  import rehypeShiki from "@shikijs/rehype";
@@ -219,7 +221,7 @@ function getCodeNode(node) {
219
221
  return firstChild;
220
222
  }
221
223
  function isElementNode(node) {
222
- return isRecord$5(node) && node.type === "element";
224
+ return isRecord$6(node) && node.type === "element";
223
225
  }
224
226
  function getMetaString(codeNode) {
225
227
  const properties = toRecord(codeNode.properties);
@@ -253,12 +255,12 @@ function replaceNodeWithShikiContainer(parent, index, innerHtml) {
253
255
  };
254
256
  }
255
257
  function hasChildrenArray(value) {
256
- return isRecord$5(value) && Array.isArray(value.children);
258
+ return isRecord$6(value) && Array.isArray(value.children);
257
259
  }
258
260
  function toRecord(value) {
259
- return isRecord$5(value) ? value : {};
261
+ return isRecord$6(value) ? value : {};
260
262
  }
261
- function isRecord$5(value) {
263
+ function isRecord$6(value) {
262
264
  return value != null && typeof value === "object";
263
265
  }
264
266
  //#endregion
@@ -285,9 +287,9 @@ function ensureNodeData(node) {
285
287
  return node.data;
286
288
  }
287
289
  function ensureRecord(value) {
288
- return isRecord$4(value) ? value : {};
290
+ return isRecord$5(value) ? value : {};
289
291
  }
290
- function isRecord$4(value) {
292
+ function isRecord$5(value) {
291
293
  return value != null && typeof value === "object";
292
294
  }
293
295
  /**
@@ -321,7 +323,7 @@ function ardoLineTransformer(options = {}) {
321
323
  };
322
324
  }
323
325
  function getMetaRaw(meta) {
324
- if (!isRecord$4(meta)) return "";
326
+ if (!isRecord$5(meta)) return "";
325
327
  const raw = meta.__raw;
326
328
  return typeof raw === "string" ? raw : "";
327
329
  }
@@ -407,7 +409,7 @@ function remarkExtractToc(options) {
407
409
  let headingIndex = 0;
408
410
  visit(tree, "heading", (node) => {
409
411
  if (node.depth < minLevel || node.depth > maxLevel) return;
410
- const text = getHeadingText$1(node);
412
+ const text = getHeadingText$2(node);
411
413
  const slug = slugify$1(text);
412
414
  const id = slug === "" ? `heading-${headingIndex}` : slug;
413
415
  headingIndex++;
@@ -422,10 +424,10 @@ function remarkExtractToc(options) {
422
424
  tocExtraction.toc = buildTocTree(headings);
423
425
  };
424
426
  }
425
- function getHeadingText$1(node) {
427
+ function getHeadingText$2(node) {
426
428
  const textParts = [];
427
429
  function extractText(child) {
428
- if (!isRecord$3(child)) return;
430
+ if (!isRecord$4(child)) return;
429
431
  if (child.type === "text") textParts.push(typeof child.value === "string" ? child.value : "");
430
432
  else if (child.type === "inlineCode") textParts.push(typeof child.value === "string" ? child.value : "");
431
433
  else if (Array.isArray(child.children)) child.children.forEach((nestedChild) => {
@@ -480,10 +482,10 @@ function buildTocTree(headings) {
480
482
  function ensureHProperties$1(node) {
481
483
  const data = node.data ?? {};
482
484
  node.data = data;
483
- if (!isRecord$3(data.hProperties)) data.hProperties = {};
485
+ if (!isRecord$4(data.hProperties)) data.hProperties = {};
484
486
  return data.hProperties;
485
487
  }
486
- function isRecord$3(value) {
488
+ function isRecord$4(value) {
487
489
  return value != null && typeof value === "object";
488
490
  }
489
491
  //#endregion
@@ -514,9 +516,9 @@ async function transformMarkdownToReact(content, config) {
514
516
  return transformMarkdown(content, config);
515
517
  }
516
518
  function readPageFrontmatter(data) {
517
- return isRecord$2(data) ? data : {};
519
+ return isRecord$3(data) ? data : {};
518
520
  }
519
- function isRecord$2(value) {
521
+ function isRecord$3(value) {
520
522
  return value != null && typeof value === "object";
521
523
  }
522
524
  //#endregion
@@ -641,7 +643,7 @@ function isMarkdownPage(entryName) {
641
643
  async function createDirectorySidebarItem(fullPath, relativePath, rootDir) {
642
644
  const children = await scanDirectoryForSidebar(fullPath, rootDir);
643
645
  if (children.length === 0) return null;
644
- const metadata = await readDirectoryIndexMetadata(fullPath, relativePath);
646
+ const metadata = await readDirectoryIndexMetadata$1(fullPath, relativePath);
645
647
  const title = metadata.title ?? formatTitle$2(path.basename(fullPath));
646
648
  return {
647
649
  collapsed: false,
@@ -651,7 +653,7 @@ async function createDirectorySidebarItem(fullPath, relativePath, rootDir) {
651
653
  text: title
652
654
  };
653
655
  }
654
- async function readDirectoryIndexMetadata(fullPath, relativePath) {
656
+ async function readDirectoryIndexMetadata$1(fullPath, relativePath) {
655
657
  const frontmatter = await readFrontmatter$1(path.join(fullPath, "index.md"));
656
658
  return {
657
659
  link: frontmatter == null ? void 0 : normalizePath(relativePath),
@@ -678,14 +680,14 @@ async function readFrontmatter$1(filePath) {
678
680
  }
679
681
  }
680
682
  function toSidebarFrontmatter(data) {
681
- if (!isRecord$1(data)) return {};
683
+ if (!isRecord$2(data)) return {};
682
684
  return {
683
685
  order: typeof data.order === "number" ? data.order : void 0,
684
686
  sidebar: typeof data.sidebar === "boolean" ? data.sidebar : void 0,
685
687
  title: typeof data.title === "string" ? data.title : void 0
686
688
  };
687
689
  }
688
- function isRecord$1(value) {
690
+ function isRecord$2(value) {
689
691
  return value != null && typeof value === "object";
690
692
  }
691
693
  function sortSidebarItems(items) {
@@ -1094,6 +1096,228 @@ function trimSlashes(value) {
1094
1096
  return trimmed;
1095
1097
  }
1096
1098
  //#endregion
1099
+ //#region src/vite/icons.ts
1100
+ const ICON_FILES = new Set([
1101
+ "favicon.ico",
1102
+ "icon.svg",
1103
+ "apple-touch-icon.png"
1104
+ ]);
1105
+ function createIconsPlugin(options) {
1106
+ if (options === false) return [];
1107
+ let config;
1108
+ let assetsPromise;
1109
+ const getAssets = async () => {
1110
+ assetsPromise ??= createIconAssets(config.root, options);
1111
+ return assetsPromise;
1112
+ };
1113
+ return [{
1114
+ name: "ardo:icons",
1115
+ configResolved(resolvedConfig) {
1116
+ config = resolvedConfig;
1117
+ },
1118
+ configureServer(server) {
1119
+ server.middlewares.use((request, response, next) => {
1120
+ serveIconRequest({
1121
+ config,
1122
+ getAssets,
1123
+ response,
1124
+ url: request.url
1125
+ }).then((served) => {
1126
+ if (!served) next();
1127
+ }).catch((error) => {
1128
+ if (error instanceof Error) next(error);
1129
+ else next(new Error(String(error)));
1130
+ });
1131
+ });
1132
+ },
1133
+ async generateBundle() {
1134
+ if (config.build.ssr !== false) return;
1135
+ const assets = await getAssets();
1136
+ for (const asset of assets) {
1137
+ if (hasPublicAsset(config, asset.fileName)) continue;
1138
+ this.emitFile({
1139
+ type: "asset",
1140
+ fileName: asset.fileName,
1141
+ source: asset.source
1142
+ });
1143
+ }
1144
+ }
1145
+ }];
1146
+ }
1147
+ async function serveIconRequest(input) {
1148
+ const { config, getAssets, response, url } = input;
1149
+ const fileName = getIconRequestFileName(url);
1150
+ if (fileName == null || hasPublicAsset(config, fileName)) return false;
1151
+ const asset = (await getAssets()).find((candidate) => candidate.fileName === fileName);
1152
+ if (asset == null) return false;
1153
+ response.statusCode = 200;
1154
+ response.setHeader("Content-Type", asset.contentType);
1155
+ response.setHeader("Cache-Control", "no-cache");
1156
+ response.end(asset.source);
1157
+ return true;
1158
+ }
1159
+ async function createIconAssets(root, options) {
1160
+ const svg = await resolveIconSvg(root, options?.source);
1161
+ const faviconPng = renderPng(svg, 32);
1162
+ const appleTouchIcon = renderPng(svg, 180);
1163
+ return [
1164
+ {
1165
+ fileName: "favicon.ico",
1166
+ contentType: "image/x-icon",
1167
+ source: createIco(faviconPng, 32)
1168
+ },
1169
+ {
1170
+ fileName: "icon.svg",
1171
+ contentType: "image/svg+xml",
1172
+ source: svg
1173
+ },
1174
+ {
1175
+ fileName: "apple-touch-icon.png",
1176
+ contentType: "image/png",
1177
+ source: appleTouchIcon
1178
+ }
1179
+ ];
1180
+ }
1181
+ async function resolveIconSvg(root, source) {
1182
+ if (source == null) return ARDO_FAVICON_SVG;
1183
+ const trimmedSource = source.trim();
1184
+ if (trimmedSource.startsWith("<svg")) return trimmedSource;
1185
+ return readFile(path.resolve(root, source), "utf8");
1186
+ }
1187
+ function renderPng(svg, size) {
1188
+ return new Resvg(svg, { fitTo: {
1189
+ mode: "width",
1190
+ value: size
1191
+ } }).render().asPng();
1192
+ }
1193
+ function createIco(png, size) {
1194
+ const imageOffset = 22;
1195
+ const ico = new Uint8Array(imageOffset + png.length);
1196
+ const view = new DataView(ico.buffer);
1197
+ writeIcoHeader(view);
1198
+ writeIcoDirectory({
1199
+ ico,
1200
+ imageOffset,
1201
+ pngLength: png.length,
1202
+ size,
1203
+ view
1204
+ });
1205
+ ico.set(png, imageOffset);
1206
+ return ico;
1207
+ }
1208
+ function writeIcoHeader(view) {
1209
+ view.setUint16(0, 0, true);
1210
+ view.setUint16(2, 1, true);
1211
+ view.setUint16(4, 1, true);
1212
+ }
1213
+ function writeIcoDirectory(input) {
1214
+ const { ico, imageOffset, pngLength, size, view } = input;
1215
+ ico[6] = size >= 256 ? 0 : size;
1216
+ ico[7] = size >= 256 ? 0 : size;
1217
+ ico[8] = 0;
1218
+ ico[9] = 0;
1219
+ view.setUint16(10, 1, true);
1220
+ view.setUint16(12, 32, true);
1221
+ view.setUint32(14, pngLength, true);
1222
+ view.setUint32(18, imageOffset, true);
1223
+ }
1224
+ function getIconRequestFileName(url) {
1225
+ if (url == null) return;
1226
+ const [pathname] = url.split("?", 1);
1227
+ const fileName = pathname.startsWith("/") ? pathname.slice(1) : pathname;
1228
+ return ICON_FILES.has(fileName) ? fileName : void 0;
1229
+ }
1230
+ function hasPublicAsset(config, fileName) {
1231
+ return existsSync(path.join(config.publicDir, fileName));
1232
+ }
1233
+ //#endregion
1234
+ //#region src/vite/markdown-meta.ts
1235
+ function transformMarkdownMeta(code, id, state) {
1236
+ if (!shouldInjectMeta(code, id, state)) return;
1237
+ const pageTitle = extractFrontmatterValue(code, "title");
1238
+ if (pageTitle == null || pageTitle === "") return;
1239
+ return {
1240
+ code: `${code}\nexport const meta = () => [${buildMetaEntries({
1241
+ pageTitle,
1242
+ siteTitle: state.resolvedConfig?.title ?? "Ardo",
1243
+ titleSeparator: state.resolvedConfig?.titleSeparator ?? " | ",
1244
+ description: extractFrontmatterValue(code, "description")
1245
+ }).join(", ")}];\n`,
1246
+ map: null
1247
+ };
1248
+ }
1249
+ function shouldInjectMeta(code, id, state) {
1250
+ return isMarkdownFile(id) && id.startsWith(state.routesDir) && !hasMetaExport(code);
1251
+ }
1252
+ function buildMetaEntries(input) {
1253
+ const fullTitle = `${input.pageTitle}${input.titleSeparator}${input.siteTitle}`;
1254
+ const entries = [`{ title: ${JSON.stringify(fullTitle)} }`];
1255
+ if (input.description != null && input.description !== "") entries.push(`{ name: "description", content: ${JSON.stringify(input.description)} }`);
1256
+ return entries;
1257
+ }
1258
+ function isMarkdownFile(id) {
1259
+ return id.endsWith(".md") || id.endsWith(".mdx");
1260
+ }
1261
+ function hasMetaExport(code) {
1262
+ return code.includes("export const meta") || code.includes("export function meta");
1263
+ }
1264
+ function extractFrontmatterValue(code, key) {
1265
+ const frontmatterStart = code.indexOf("export const frontmatter");
1266
+ if (frontmatterStart === -1) return;
1267
+ const valuePrefix = `${key}: "`;
1268
+ const valueStart = code.indexOf(valuePrefix, frontmatterStart);
1269
+ if (valueStart === -1) return;
1270
+ const startIndex = valueStart + valuePrefix.length;
1271
+ const endIndex = code.indexOf("\"", startIndex);
1272
+ if (endIndex === -1) return;
1273
+ return code.slice(startIndex, endIndex);
1274
+ }
1275
+ //#endregion
1276
+ //#region src/markdown/remark-callouts.ts
1277
+ const TYPE_MAP = {
1278
+ NOTE: "Note",
1279
+ TIP: "Tip",
1280
+ IMPORTANT: "Info",
1281
+ WARNING: "Warning",
1282
+ CAUTION: "Danger"
1283
+ };
1284
+ const ALERT_REGEX = /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*\r?\n?/i;
1285
+ function remarkCallouts() {
1286
+ return function(tree) {
1287
+ visit(tree, "blockquote", (node, index, parent) => {
1288
+ if (parent == null || index == null) return;
1289
+ const componentName = extractCalloutType(node);
1290
+ if (componentName == null) return;
1291
+ const replacement = {
1292
+ type: "mdxJsxFlowElement",
1293
+ name: componentName,
1294
+ attributes: [],
1295
+ children: node.children
1296
+ };
1297
+ parent.children[index] = replacement;
1298
+ });
1299
+ };
1300
+ }
1301
+ function extractCalloutType(node) {
1302
+ const firstChild = node.children[0];
1303
+ if (firstChild?.type !== "paragraph") return void 0;
1304
+ const firstParaChild = firstChild.children[0];
1305
+ if (firstParaChild?.type !== "text") return void 0;
1306
+ const match = ALERT_REGEX.exec(firstParaChild.value);
1307
+ if (match === null) return void 0;
1308
+ const calloutName = TYPE_MAP[match[1].toUpperCase()];
1309
+ firstParaChild.value = firstParaChild.value.slice(match[0].length);
1310
+ if (firstParaChild.value === "") {
1311
+ firstChild.children.shift();
1312
+ stripLeadingBreak(firstChild.children);
1313
+ }
1314
+ if (firstChild.children.length === 0) node.children.shift();
1315
+ return calloutName;
1316
+ }
1317
+ function stripLeadingBreak(children) {
1318
+ if (children[0]?.type === "break") children.shift();
1319
+ }
1320
+ //#endregion
1097
1321
  //#region ../../node_modules/.pnpm/estree-util-value-to-estree@3.5.0/node_modules/estree-util-value-to-estree/dist/estree-util-value-to-estree.js
1098
1322
  /**
1099
1323
  * Create an ESTree identifier node for a given name.
@@ -2128,6 +2352,23 @@ function define(ast, file, variables, options) {
2128
2352
  }
2129
2353
  }
2130
2354
  //#endregion
2355
+ //#region src/markdown/remark-mdx-handle.ts
2356
+ function remarkMdxHandle() {
2357
+ return function(tree, file) {
2358
+ const yamlNode = tree.children.find((node) => node.type === "yaml");
2359
+ if (yamlNode === void 0) return;
2360
+ const layout = extractLayoutValue(yamlNode.value);
2361
+ if (layout === void 0) return;
2362
+ define(tree, file, { handle: valueToEstree({ layout }) });
2363
+ };
2364
+ }
2365
+ function extractLayoutValue(yaml) {
2366
+ const match = /^layout:[ \t]+(\S.*)$/m.exec(yaml);
2367
+ if (match === null) return void 0;
2368
+ const raw = match[1].trim().replaceAll(/^["']|["']$/g, "");
2369
+ if (raw === "bare" || raw === "default") return raw;
2370
+ }
2371
+ //#endregion
2131
2372
  //#region src/markdown/remark-mdx-toc.ts
2132
2373
  function remarkMdxToc(options = {}) {
2133
2374
  const { name = "toc", levels = [2, 3] } = options;
@@ -2137,7 +2378,7 @@ function remarkMdxToc(options = {}) {
2137
2378
  let headingIndex = 0;
2138
2379
  visit(tree, "heading", (node) => {
2139
2380
  if (node.depth < minLevel || node.depth > maxLevel) return;
2140
- const text = getHeadingText(node);
2381
+ const text = getHeadingText$1(node);
2141
2382
  const slug = slugify(text);
2142
2383
  const id = slug === "" ? `heading-${String(headingIndex)}` : slug;
2143
2384
  headingIndex++;
@@ -2152,10 +2393,10 @@ function remarkMdxToc(options = {}) {
2152
2393
  define(tree, file, { [name]: valueToEstree(items) });
2153
2394
  };
2154
2395
  }
2155
- function getHeadingText(node) {
2396
+ function getHeadingText$1(node) {
2156
2397
  const parts = [];
2157
2398
  function extract(child) {
2158
- if (!isRecord(child)) return;
2399
+ if (!isRecord$1(child)) return;
2159
2400
  if (child.type === "text" || child.type === "inlineCode") parts.push(typeof child.value === "string" ? child.value : "");
2160
2401
  else if (Array.isArray(child.children)) for (const nested of child.children) extract(nested);
2161
2402
  }
@@ -2170,9 +2411,42 @@ function slugify(text) {
2170
2411
  function ensureHProperties(node) {
2171
2412
  const data = node.data ?? {};
2172
2413
  node.data = data;
2173
- if (!isRecord(data.hProperties)) data.hProperties = {};
2414
+ if (!isRecord$1(data.hProperties)) data.hProperties = {};
2174
2415
  return data.hProperties;
2175
2416
  }
2417
+ function isRecord$1(value) {
2418
+ return value != null && typeof value === "object";
2419
+ }
2420
+ //#endregion
2421
+ //#region src/markdown/remark-strip-frontmatter-h1.ts
2422
+ function remarkStripFrontmatterH1() {
2423
+ return function(tree) {
2424
+ const yamlNode = tree.children.find((node) => node.type === "yaml");
2425
+ if (yamlNode === void 0) return;
2426
+ const title = extractYamlTitle(yamlNode.value);
2427
+ if (title === "") return;
2428
+ const firstH1Index = tree.children.findIndex((node) => node.type === "heading" && node.depth === 1);
2429
+ if (firstH1Index === -1) return;
2430
+ const h1Node = tree.children[firstH1Index];
2431
+ if (h1Node.type !== "heading") return;
2432
+ if (getHeadingText(h1Node).trim() === title) tree.children.splice(firstH1Index, 1);
2433
+ };
2434
+ }
2435
+ function extractYamlTitle(yaml) {
2436
+ const match = /^title:[ \t]+(\S.*)$/m.exec(yaml);
2437
+ if (match === null) return "";
2438
+ return match[1].trim().replaceAll(/^["']|["']$/g, "");
2439
+ }
2440
+ function getHeadingText(node) {
2441
+ const parts = [];
2442
+ function extract(child) {
2443
+ if (!isRecord(child)) return;
2444
+ if (child.type === "text" || child.type === "inlineCode") parts.push(typeof child.value === "string" ? child.value : "");
2445
+ else if (Array.isArray(child.children)) for (const nested of child.children) extract(nested);
2446
+ }
2447
+ for (const child of node.children) extract(child);
2448
+ return parts.join("");
2449
+ }
2176
2450
  function isRecord(value) {
2177
2451
  return value != null && typeof value === "object";
2178
2452
  }
@@ -2327,8 +2601,11 @@ function createMdxPlugin(markdownConfig) {
2327
2601
  include: /\.(md|mdx)$/,
2328
2602
  remarkPlugins: [
2329
2603
  remarkFrontmatter,
2604
+ remarkStripFrontmatterH1,
2330
2605
  [remarkMdxFrontmatter, { name: "frontmatter" }],
2606
+ remarkMdxHandle,
2331
2607
  remarkGfm,
2608
+ remarkCallouts,
2332
2609
  remarkCodeMeta,
2333
2610
  [remarkMdxToc, { levels: markdownConfig?.toc?.level ?? [2, 3] }]
2334
2611
  ],
@@ -2692,13 +2969,38 @@ function formatTitle$1(name) {
2692
2969
  }
2693
2970
  //#endregion
2694
2971
  //#region src/vite/sidebar-index.ts
2695
- async function generateSidebar$1(routesDir) {
2972
+ async function generateSidebar$1(routesDir, options = {}) {
2696
2973
  try {
2697
- return (await scanSidebarDirectory(routesDir, routesDir)).map((node) => stripOrderFromNode(node));
2974
+ const nodes = await scanSidebarDirectory(routesDir, routesDir);
2975
+ sortNodesBySectionOrder(nodes, options.sectionOrder);
2976
+ return nodes.map((node) => stripOrderFromNode(node));
2698
2977
  } catch {
2699
2978
  return [];
2700
2979
  }
2701
2980
  }
2981
+ /**
2982
+ * Build one sidebar tree per top-level routes folder.
2983
+ *
2984
+ * The flat `generateSidebar` returns a single nested array — fine for sites
2985
+ * where everything lives in one column. For context-driven sites (Guide vs.
2986
+ * API vs. …) each top-level folder gets its own subtree, keyed by folder
2987
+ * name (`guide`, `api-reference`, …). Top-level files like `home.tsx` are
2988
+ * skipped — they belong to a bare layout, not a sidebar context.
2989
+ */
2990
+ async function generateContextSidebars(routesDir) {
2991
+ try {
2992
+ const entries = await fs.readdir(routesDir, { withFileTypes: true });
2993
+ const sidebars = {};
2994
+ for (const entry of entries) {
2995
+ if (!entry.isDirectory()) continue;
2996
+ const nodes = await scanSidebarDirectory(path.join(routesDir, entry.name), routesDir);
2997
+ if (nodes.length > 0) sidebars[entry.name] = nodes.map((node) => stripOrderFromNode(node));
2998
+ }
2999
+ return sidebars;
3000
+ } catch {
3001
+ return {};
3002
+ }
3003
+ }
2702
3004
  async function scanSidebarDirectory(dir, rootDir) {
2703
3005
  const entries = await fs.readdir(dir, { withFileTypes: true });
2704
3006
  const nodes = [];
@@ -2718,32 +3020,68 @@ async function scanSidebarDirectory(dir, rootDir) {
2718
3020
  }
2719
3021
  async function createDirectoryNode(entry, fullPath, rootDir) {
2720
3022
  const relativePath = path.relative(rootDir, fullPath);
3023
+ const metadata = await readDirectoryIndexMetadata(fullPath);
3024
+ if (metadata?.sidebar === false) return null;
3025
+ const link = metadata == null ? void 0 : `/${relativePath.replaceAll("\\", "/")}`;
3026
+ if (metadata?.sidebar === "leaf") {
3027
+ if (link === void 0) return null;
3028
+ return {
3029
+ text: metadata.title ?? formatTitle(entry.name),
3030
+ link,
3031
+ order: metadata.order,
3032
+ sectionId: entry.name
3033
+ };
3034
+ }
2721
3035
  const children = await scanSidebarDirectory(fullPath, rootDir);
2722
3036
  if (children.length === 0) return null;
2723
- const link = await fileExists(path.join(fullPath, "index.mdx")) ? `/${relativePath.replaceAll("\\", "/")}` : void 0;
2724
3037
  return {
2725
- text: formatTitle(entry.name),
3038
+ text: metadata?.title ?? formatTitle(entry.name),
2726
3039
  link,
2727
- items: children
3040
+ items: children,
3041
+ collapsed: metadata?.collapsed,
3042
+ order: metadata?.order,
3043
+ sectionId: entry.name
2728
3044
  };
2729
3045
  }
2730
3046
  async function createMarkdownNode(entry, fullPath, relativePath) {
2731
3047
  if (!isSidebarMarkdownFile(entry.name)) return null;
2732
3048
  const extension = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
2733
3049
  const frontmatter = readFrontmatter(await fs.readFile(fullPath, "utf8"));
3050
+ if (frontmatter.sidebar === false) return null;
2734
3051
  return {
2735
3052
  text: frontmatter.title ?? formatTitle(entry.name.replace(extension, "")),
2736
3053
  link: `/${relativePath.replace(extension, "").replaceAll("\\", "/")}`,
2737
- order: frontmatter.order
3054
+ order: frontmatter.order,
3055
+ sectionId: entry.name.replace(extension, "")
2738
3056
  };
2739
3057
  }
3058
+ async function readDirectoryIndexMetadata(fullPath) {
3059
+ for (const indexFileName of ["index.mdx", "index.md"]) {
3060
+ const metadata = await readFrontmatterFile(path.join(fullPath, indexFileName));
3061
+ if (metadata != null) return metadata;
3062
+ }
3063
+ return null;
3064
+ }
3065
+ async function readFrontmatterFile(filePath) {
3066
+ try {
3067
+ return readFrontmatter(await fs.readFile(filePath, "utf8"));
3068
+ } catch {
3069
+ return null;
3070
+ }
3071
+ }
2740
3072
  function readFrontmatter(fileContent) {
2741
3073
  const parsed = matter(fileContent);
2742
3074
  return {
2743
3075
  title: typeof parsed.data.title === "string" ? parsed.data.title : void 0,
2744
- order: typeof parsed.data.order === "number" ? parsed.data.order : void 0
3076
+ order: typeof parsed.data.order === "number" ? parsed.data.order : void 0,
3077
+ collapsed: typeof parsed.data.collapsed === "boolean" ? parsed.data.collapsed : void 0,
3078
+ sidebar: parseSidebarValue(parsed.data.sidebar)
2745
3079
  };
2746
3080
  }
3081
+ function parseSidebarValue(raw) {
3082
+ if (typeof raw === "boolean") return raw;
3083
+ if (raw === "leaf") return "leaf";
3084
+ }
2747
3085
  function sortNodes(nodes) {
2748
3086
  nodes.sort((leftNode, rightNode) => {
2749
3087
  if (leftNode.order != null && rightNode.order != null) return leftNode.order - rightNode.order;
@@ -2752,6 +3090,23 @@ function sortNodes(nodes) {
2752
3090
  return leftNode.text.localeCompare(rightNode.text);
2753
3091
  });
2754
3092
  }
3093
+ function sortNodesBySectionOrder(nodes, sectionOrder) {
3094
+ if (sectionOrder == null || sectionOrder.length === 0) return;
3095
+ const sectionOrderMap = /* @__PURE__ */ new Map();
3096
+ for (const [index, section] of sectionOrder.entries()) {
3097
+ const normalizedSection = normalizeSectionId(section);
3098
+ if (normalizedSection !== "" && !sectionOrderMap.has(normalizedSection)) sectionOrderMap.set(normalizedSection, index);
3099
+ }
3100
+ if (sectionOrderMap.size === 0) return;
3101
+ nodes.sort((leftNode, rightNode) => {
3102
+ const leftIndex = sectionOrderMap.get(leftNode.sectionId);
3103
+ const rightIndex = sectionOrderMap.get(rightNode.sectionId);
3104
+ if (leftIndex != null && rightIndex != null) return leftIndex - rightIndex;
3105
+ if (leftIndex != null) return -1;
3106
+ if (rightIndex != null) return 1;
3107
+ return 0;
3108
+ });
3109
+ }
2755
3110
  function isSidebarMarkdownFile(fileName) {
2756
3111
  return (fileName.endsWith(".mdx") || fileName.endsWith(".md")) && !(fileName === "index.mdx" || fileName === "index.md");
2757
3112
  }
@@ -2762,36 +3117,80 @@ function stripOrderFromNode(node) {
2762
3117
  return {
2763
3118
  text: node.text,
2764
3119
  link: node.link,
3120
+ collapsed: node.collapsed,
2765
3121
  items: node.items?.map((item) => stripOrderFromNode(item))
2766
3122
  };
2767
3123
  }
2768
- async function fileExists(filePath) {
2769
- try {
2770
- await fs.access(filePath);
2771
- return true;
2772
- } catch {
2773
- return false;
2774
- }
3124
+ function normalizeSectionId(section) {
3125
+ const normalizedSection = section.replaceAll("\\", "/");
3126
+ let startIndex = 0;
3127
+ let endIndex = normalizedSection.length;
3128
+ while (startIndex < endIndex && normalizedSection[startIndex] === "/") startIndex += 1;
3129
+ while (endIndex > startIndex && normalizedSection[endIndex - 1] === "/") endIndex -= 1;
3130
+ return normalizedSection.slice(startIndex, endIndex);
3131
+ }
3132
+ //#endregion
3133
+ //#region src/vite/typedoc-plugin.ts
3134
+ let typedocGenerated = false;
3135
+ function resolveTypedocConfig(typedoc) {
3136
+ if (typedoc == null) return;
3137
+ const packageRoot = findPackageRoot(process.cwd());
3138
+ const defaultEntryPoint = packageRoot != null ? `${packageRoot}/src/index.ts` : "./src/index.ts";
3139
+ const defaultTsconfig = packageRoot != null ? `${packageRoot}/tsconfig.json` : "./tsconfig.json";
3140
+ const defaults = {
3141
+ enabled: true,
3142
+ entryPoints: [defaultEntryPoint],
3143
+ tsconfig: defaultTsconfig,
3144
+ out: "api-reference",
3145
+ excludePrivate: true,
3146
+ excludeInternal: true
3147
+ };
3148
+ return typedoc === true ? defaults : {
3149
+ ...defaults,
3150
+ ...typedoc
3151
+ };
3152
+ }
3153
+ function createTypeDocPlugin(typedocConfig, routesDir) {
3154
+ return {
3155
+ name: "ardo:typedoc",
3156
+ async buildStart() {
3157
+ if (!typedocConfig.enabled || typedocGenerated) return;
3158
+ typedocGenerated = true;
3159
+ console.log("[ardo] Generating API documentation with TypeDoc...");
3160
+ const startTime = Date.now();
3161
+ try {
3162
+ const docs = await generateApiDocs(typedocConfig, routesDir);
3163
+ const duration = Date.now() - startTime;
3164
+ console.log(`[ardo] Generated ${docs.length} API documentation pages in ${duration}ms`);
3165
+ } catch (error) {
3166
+ console.warn("[ardo] TypeDoc generation failed. API documentation will not be available.");
3167
+ console.warn("[ardo] Check your typedoc.entryPoints configuration.");
3168
+ if (error instanceof Error) console.warn(`[ardo] Error: ${error.message}`);
3169
+ }
3170
+ }
3171
+ };
2775
3172
  }
2776
3173
  //#endregion
2777
3174
  //#region src/vite/plugin.ts
2778
3175
  const VIRTUAL_MODULE_ID = "virtual:ardo/config";
2779
3176
  const VIRTUAL_SIDEBAR_ID = "virtual:ardo/sidebar";
3177
+ const VIRTUAL_SIDEBARS_ID = "virtual:ardo/sidebars";
2780
3178
  const VIRTUAL_SEARCH_ID = "virtual:ardo/search-index";
2781
3179
  const RESOLVED_IDS = {
2782
3180
  [VIRTUAL_MODULE_ID]: `\0${VIRTUAL_MODULE_ID}`,
2783
3181
  [VIRTUAL_SIDEBAR_ID]: `\0${VIRTUAL_SIDEBAR_ID}`,
3182
+ [VIRTUAL_SIDEBARS_ID]: `\0${VIRTUAL_SIDEBARS_ID}`,
2784
3183
  [VIRTUAL_SEARCH_ID]: `\0${VIRTUAL_SEARCH_ID}`
2785
3184
  };
2786
- let typedocGenerated = false;
2787
3185
  function ardoPlugin(options = {}) {
2788
- const { routes, typedoc, githubPages = true, routesDir: routesDirOption, ...pressConfig } = options;
3186
+ const { icons = {}, routes, typedoc, githubPages = true, routesDir: routesDirOption, ...pressConfig } = options;
2789
3187
  const state = { routesDir: resolveRoutesDir(process.cwd(), routesDirOption) };
2790
3188
  const plugins = [createMainPlugin(state, {
2791
3189
  githubPages,
2792
3190
  pressConfig,
2793
3191
  routesDirOption
2794
3192
  })];
3193
+ plugins.push(...createIconsPlugin(icons));
2795
3194
  addRoutesPlugin(plugins, routes, routesDirOption);
2796
3195
  addTypeDocPlugin(plugins, typedoc, state);
2797
3196
  plugins.push(ardoCodeBlockPlugin(pressConfig.markdown));
@@ -2811,7 +3210,7 @@ function addRoutesPlugin(plugins, routes, routesDirOption) {
2811
3210
  }
2812
3211
  function addTypeDocPlugin(plugins, typedoc, state) {
2813
3212
  const typedocConfig = resolveTypedocConfig(typedoc);
2814
- if (typedocConfig != null) plugins.unshift(createTypeDocPlugin(typedocConfig, state));
3213
+ if (typedocConfig != null) plugins.unshift(createTypeDocPlugin(typedocConfig, state.routesDir));
2815
3214
  }
2816
3215
  function createMainPlugin(state, options) {
2817
3216
  return {
@@ -2889,92 +3288,18 @@ async function loadVirtualModule(id, state) {
2889
3288
  return `export default ${JSON.stringify(clientConfig)}`;
2890
3289
  }
2891
3290
  if (id === RESOLVED_IDS[VIRTUAL_SIDEBAR_ID]) {
2892
- const sidebar = await generateSidebar$1(state.routesDir);
3291
+ const sidebar = await generateSidebar$1(state.routesDir, state.resolvedConfig.sidebar);
2893
3292
  return `export default ${JSON.stringify(sidebar)}`;
2894
3293
  }
3294
+ if (id === RESOLVED_IDS[VIRTUAL_SIDEBARS_ID]) {
3295
+ const sidebars = await generateContextSidebars(state.routesDir);
3296
+ return `export default ${JSON.stringify(sidebars)}`;
3297
+ }
2895
3298
  if (id === RESOLVED_IDS[VIRTUAL_SEARCH_ID]) {
2896
3299
  const searchIndex = await generateSearchIndex(state.routesDir);
2897
3300
  return `export default ${JSON.stringify(searchIndex)}`;
2898
3301
  }
2899
3302
  }
2900
- function transformMarkdownMeta(code, id, state) {
2901
- if (!shouldInjectMeta(code, id, state)) return;
2902
- const pageTitle = extractFrontmatterValue(code, "title");
2903
- if (pageTitle == null || pageTitle === "") return;
2904
- return {
2905
- code: `${code}\nexport const meta = () => [${buildMetaEntries({
2906
- pageTitle,
2907
- siteTitle: state.resolvedConfig?.title ?? "Ardo",
2908
- titleSeparator: state.resolvedConfig?.titleSeparator ?? " | ",
2909
- description: extractFrontmatterValue(code, "description")
2910
- }).join(", ")}];\n`,
2911
- map: null
2912
- };
2913
- }
2914
- function shouldInjectMeta(code, id, state) {
2915
- return isMarkdownFile(id) && id.startsWith(state.routesDir) && !hasMetaExport(code);
2916
- }
2917
- function buildMetaEntries(input) {
2918
- const fullTitle = `${input.pageTitle}${input.titleSeparator}${input.siteTitle}`;
2919
- const entries = [`{ title: ${JSON.stringify(fullTitle)} }`];
2920
- if (input.description != null && input.description !== "") entries.push(`{ name: "description", content: ${JSON.stringify(input.description)} }`);
2921
- return entries;
2922
- }
2923
- function isMarkdownFile(id) {
2924
- return id.endsWith(".md") || id.endsWith(".mdx");
2925
- }
2926
- function hasMetaExport(code) {
2927
- return code.includes("export const meta") || code.includes("export function meta");
2928
- }
2929
- function extractFrontmatterValue(code, key) {
2930
- const frontmatterStart = code.indexOf("export const frontmatter");
2931
- if (frontmatterStart === -1) return;
2932
- const valuePrefix = `${key}: "`;
2933
- const valueStart = code.indexOf(valuePrefix, frontmatterStart);
2934
- if (valueStart === -1) return;
2935
- const startIndex = valueStart + valuePrefix.length;
2936
- const endIndex = code.indexOf("\"", startIndex);
2937
- if (endIndex === -1) return;
2938
- return code.slice(startIndex, endIndex);
2939
- }
2940
- function resolveTypedocConfig(typedoc) {
2941
- if (typedoc == null) return;
2942
- const packageRoot = findPackageRoot(process.cwd());
2943
- const defaultEntryPoint = packageRoot != null ? `${packageRoot}/src/index.ts` : "./src/index.ts";
2944
- const defaultTsconfig = packageRoot != null ? `${packageRoot}/tsconfig.json` : "./tsconfig.json";
2945
- const defaults = {
2946
- enabled: true,
2947
- entryPoints: [defaultEntryPoint],
2948
- tsconfig: defaultTsconfig,
2949
- out: "api-reference",
2950
- excludePrivate: true,
2951
- excludeInternal: true
2952
- };
2953
- return typedoc === true ? defaults : {
2954
- ...defaults,
2955
- ...typedoc
2956
- };
2957
- }
2958
- function createTypeDocPlugin(typedocConfig, state) {
2959
- return {
2960
- name: "ardo:typedoc",
2961
- async buildStart() {
2962
- if (!typedocConfig.enabled || typedocGenerated) return;
2963
- typedocGenerated = true;
2964
- console.log("[ardo] Generating API documentation with TypeDoc...");
2965
- const startTime = Date.now();
2966
- try {
2967
- const docs = await generateApiDocs(typedocConfig, state.routesDir);
2968
- const duration = Date.now() - startTime;
2969
- console.log(`[ardo] Generated ${docs.length} API documentation pages in ${duration}ms`);
2970
- } catch (error) {
2971
- console.warn("[ardo] TypeDoc generation failed. API documentation will not be available.");
2972
- console.warn("[ardo] Check your typedoc.entryPoints configuration.");
2973
- if (error instanceof Error) console.warn(`[ardo] Error: ${error.message}`);
2974
- }
2975
- }
2976
- };
2977
- }
2978
3303
  function resolveRoutesDir(root, routesDirOption) {
2979
3304
  return routesDirOption ?? path.join(root, "app", "routes");
2980
3305
  }