ardo 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{Features-C_06EvGb.d.ts → Features-DEndhnwl.d.ts} +10 -4
  2. package/dist/{chunk-OTUACKCQ.js → chunk-4IRLOOXV.js} +20 -9
  3. package/dist/chunk-4IRLOOXV.js.map +1 -0
  4. package/dist/{chunk-N5CEHG2F.js → chunk-CGFRUWNJ.js} +2 -1
  5. package/dist/chunk-CGFRUWNJ.js.map +1 -0
  6. package/dist/{chunk-UWAVET45.js → chunk-LSNTX2BA.js} +8 -6
  7. package/dist/chunk-LSNTX2BA.js.map +1 -0
  8. package/dist/{chunk-G5L4ZUTS.js → chunk-O64PX2AK.js} +2 -2
  9. package/dist/{chunk-ZXPAEM3M.js → chunk-QC2JA663.js} +14 -6
  10. package/dist/{chunk-ZXPAEM3M.js.map → chunk-QC2JA663.js.map} +1 -1
  11. package/dist/{chunk-LUOUBO3L.js → chunk-SWER4H2H.js} +540 -394
  12. package/dist/chunk-SWER4H2H.js.map +1 -0
  13. package/dist/config/index.d.ts +2 -2
  14. package/dist/config/index.js +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +6 -6
  17. package/dist/mdx/provider.js +14 -6
  18. package/dist/mdx/provider.js.map +1 -1
  19. package/dist/runtime/index.d.ts +1 -1
  20. package/dist/typedoc/index.d.ts +6 -0
  21. package/dist/typedoc/index.js +2 -2
  22. package/dist/{types-DchPWkJl.d.ts → types-tjxB5eh7.d.ts} +22 -2
  23. package/dist/ui/index.d.ts +2 -2
  24. package/dist/ui/index.js +2 -2
  25. package/dist/ui/styles.css +232 -66
  26. package/dist/vite/index.d.ts +1 -1
  27. package/dist/vite/index.js +3 -3
  28. package/package.json +9 -9
  29. package/dist/chunk-LUOUBO3L.js.map +0 -1
  30. package/dist/chunk-N5CEHG2F.js.map +0 -1
  31. package/dist/chunk-OTUACKCQ.js.map +0 -1
  32. package/dist/chunk-UWAVET45.js.map +0 -1
  33. /package/dist/{chunk-G5L4ZUTS.js.map → chunk-O64PX2AK.js.map} +0 -0
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  resolveConfig
3
- } from "./chunk-N5CEHG2F.js";
3
+ } from "./chunk-CGFRUWNJ.js";
4
4
  import {
5
5
  generateApiDocs
6
- } from "./chunk-OTUACKCQ.js";
6
+ } from "./chunk-4IRLOOXV.js";
7
7
 
8
8
  // src/vite/plugin.ts
9
9
  import { reactRouter } from "@react-router/dev/vite";
@@ -13,10 +13,406 @@ import remarkMdxFrontmatter from "remark-mdx-frontmatter";
13
13
  import remarkGfm from "remark-gfm";
14
14
  import remarkDirective from "remark-directive";
15
15
  import rehypeShiki from "@shikijs/rehype";
16
+
17
+ // src/markdown/shiki.ts
18
+ import {
19
+ createHighlighter
20
+ } from "shiki";
21
+ import { visit } from "unist-util-visit";
22
+ async function createShikiHighlighter(config) {
23
+ const themeConfig = config.theme ?? {
24
+ light: "github-light",
25
+ dark: "github-dark"
26
+ };
27
+ const themes = typeof themeConfig === "string" ? [themeConfig] : [themeConfig.light, themeConfig.dark];
28
+ const highlighter = await createHighlighter({
29
+ themes,
30
+ langs: [
31
+ "javascript",
32
+ "typescript",
33
+ "jsx",
34
+ "tsx",
35
+ "json",
36
+ "html",
37
+ "css",
38
+ "markdown",
39
+ "bash",
40
+ "shell",
41
+ "yaml",
42
+ "python",
43
+ "rust",
44
+ "go",
45
+ "sql",
46
+ "diff"
47
+ ]
48
+ });
49
+ return highlighter;
50
+ }
51
+ function rehypeShikiFromHighlighter(options) {
52
+ const { highlighter, config } = options;
53
+ const themeConfig = config.theme ?? {
54
+ light: "github-light",
55
+ dark: "github-dark"
56
+ };
57
+ return function(tree) {
58
+ visit(tree, "element", (node, index, parent) => {
59
+ if (node.tagName !== "pre" || !node.children[0] || node.children[0].tagName !== "code") {
60
+ return;
61
+ }
62
+ const codeNode = node.children[0];
63
+ const className = codeNode.properties?.className || [];
64
+ const langClass = className.find((c) => c.startsWith("language-"));
65
+ const lang = langClass ? langClass.replace("language-", "") : "text";
66
+ const codeContent = getTextContent(codeNode);
67
+ if (!codeContent.trim()) {
68
+ return;
69
+ }
70
+ try {
71
+ let html;
72
+ if (typeof themeConfig === "string") {
73
+ html = highlighter.codeToHtml(codeContent, {
74
+ lang,
75
+ theme: themeConfig
76
+ });
77
+ } else {
78
+ html = highlighter.codeToHtml(codeContent, {
79
+ lang,
80
+ themes: {
81
+ light: themeConfig.light,
82
+ dark: themeConfig.dark
83
+ },
84
+ defaultColor: false
85
+ });
86
+ }
87
+ const metaString = codeNode.properties?.metastring || "";
88
+ const lineNumbers = config.lineNumbers || metaString.includes("showLineNumbers");
89
+ const highlightLines = parseHighlightLines(metaString);
90
+ const title = parseTitle(metaString);
91
+ const wrapperHtml = buildCodeBlockHtml(html, {
92
+ lang,
93
+ lineNumbers,
94
+ highlightLines,
95
+ title
96
+ });
97
+ if (parent && typeof index === "number") {
98
+ const newNode = {
99
+ type: "element",
100
+ tagName: "div",
101
+ properties: {
102
+ className: ["ardo-code-block"],
103
+ "data-lang": lang
104
+ },
105
+ children: [
106
+ {
107
+ type: "raw",
108
+ value: wrapperHtml
109
+ }
110
+ ]
111
+ };
112
+ parent.children[index] = newNode;
113
+ }
114
+ } catch {
115
+ }
116
+ });
117
+ };
118
+ }
119
+ function getTextContent(node) {
120
+ if (node.type === "text") {
121
+ return node.value;
122
+ }
123
+ if ("children" in node) {
124
+ return node.children.map((child) => getTextContent(child)).join("");
125
+ }
126
+ return "";
127
+ }
128
+ function parseHighlightLines(meta) {
129
+ const match = meta.match(/\{([\d,-]+)\}/);
130
+ if (!match) return [];
131
+ const ranges = match[1].split(",");
132
+ const lines = [];
133
+ for (const range of ranges) {
134
+ if (range.includes("-")) {
135
+ const [start, end] = range.split("-").map(Number);
136
+ for (let i = start; i <= end; i++) {
137
+ lines.push(i);
138
+ }
139
+ } else {
140
+ lines.push(Number(range));
141
+ }
142
+ }
143
+ return lines;
144
+ }
145
+ function parseTitle(meta) {
146
+ const match = meta.match(/title="([^"]+)"/);
147
+ return match ? match[1] : void 0;
148
+ }
149
+ function buildCodeBlockHtml(shikiHtml, options) {
150
+ const { lang, lineNumbers, highlightLines, title } = options;
151
+ let html = "";
152
+ if (title) {
153
+ html += `<div class="ardo-code-title">${escapeHtml(title)}</div>`;
154
+ }
155
+ html += `<div class="ardo-code-wrapper" data-lang="${lang}">`;
156
+ if (lineNumbers || highlightLines.length > 0) {
157
+ const lines = shikiHtml.split("\n");
158
+ const processedHtml = lines.map((line, i) => {
159
+ const lineNum = i + 1;
160
+ const isHighlighted = highlightLines.includes(lineNum);
161
+ const classes = ["ardo-code-line"];
162
+ if (isHighlighted) classes.push("highlighted");
163
+ let prefix = "";
164
+ if (lineNumbers) {
165
+ prefix = `<span class="ardo-line-number">${lineNum}</span>`;
166
+ }
167
+ return `<span class="${classes.join(" ")}">${prefix}${line}</span>`;
168
+ }).join("\n");
169
+ html += processedHtml;
170
+ } else {
171
+ html += shikiHtml;
172
+ }
173
+ html += `<button class="ardo-copy-button" data-code="${encodeURIComponent(extractCodeFromHtml(shikiHtml))}">
174
+ <span class="ardo-copy-icon">Copy</span>
175
+ <span class="ardo-copied-icon" style="display:none">Copied!</span>
176
+ </button>`;
177
+ html += "</div>";
178
+ return html;
179
+ }
180
+ function extractCodeFromHtml(html) {
181
+ return html.replace(/<[^>]+>/g, "").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
182
+ }
183
+ function escapeHtml(text) {
184
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
185
+ }
186
+ function remarkCodeMeta() {
187
+ return function(tree) {
188
+ visit(tree, "code", (node) => {
189
+ if (!node.meta) return;
190
+ const meta = node.meta;
191
+ const data = node.data || (node.data = {});
192
+ const hProperties = data.hProperties || {};
193
+ hProperties.metastring = meta;
194
+ data.hProperties = hProperties;
195
+ node.meta = null;
196
+ });
197
+ };
198
+ }
199
+ function ardoLineTransformer(options = {}) {
200
+ let highlightLines = [];
201
+ let showLineNumbers = false;
202
+ let metaRaw = "";
203
+ return {
204
+ name: "ardo:lines",
205
+ // preprocess runs BEFORE line() hooks, so state is ready for line()
206
+ preprocess(_code, shikiOptions) {
207
+ metaRaw = shikiOptions.meta?.__raw || "";
208
+ highlightLines = parseHighlightLines(metaRaw);
209
+ showLineNumbers = options.globalLineNumbers || metaRaw.includes("showLineNumbers");
210
+ },
211
+ // pre runs AFTER line() — used only for node property modifications
212
+ pre(node) {
213
+ node.properties = node.properties || {};
214
+ const title = parseTitle(metaRaw);
215
+ if (title) {
216
+ node.properties["data-title"] = title;
217
+ }
218
+ const labelMatch = metaRaw.match(/\[([^\]]+)\]/);
219
+ if (labelMatch) {
220
+ node.properties["data-label"] = labelMatch[1];
221
+ }
222
+ },
223
+ line(node, line) {
224
+ const currentClass = node.properties?.class || "";
225
+ const classes = currentClass ? currentClass.split(" ") : [];
226
+ classes.push("ardo-code-line");
227
+ if (highlightLines.includes(line)) {
228
+ classes.push("highlighted");
229
+ }
230
+ node.properties = node.properties || {};
231
+ node.properties.class = classes.join(" ");
232
+ if (showLineNumbers) {
233
+ node.children.unshift({
234
+ type: "element",
235
+ tagName: "span",
236
+ properties: { class: "ardo-line-number" },
237
+ children: [{ type: "text", value: String(line) }]
238
+ });
239
+ }
240
+ }
241
+ };
242
+ }
243
+
244
+ // src/markdown/containers.ts
245
+ import { visit as visit2 } from "unist-util-visit";
246
+ var containerTypes = [
247
+ "tip",
248
+ "warning",
249
+ "danger",
250
+ "info",
251
+ "note",
252
+ "details",
253
+ "code-group"
254
+ ];
255
+ var defaultTitles = {
256
+ tip: "TIP",
257
+ warning: "WARNING",
258
+ danger: "DANGER",
259
+ info: "INFO",
260
+ note: "NOTE",
261
+ details: "Details",
262
+ "code-group": ""
263
+ };
264
+ function remarkContainers() {
265
+ return function(tree) {
266
+ visit2(tree, "containerDirective", (node) => {
267
+ const type = node.name;
268
+ if (!containerTypes.includes(type)) {
269
+ return;
270
+ }
271
+ const data = node.data || (node.data = {});
272
+ const titleNode = node.children[0];
273
+ let customTitle;
274
+ if (titleNode && titleNode.type === "paragraph" && titleNode.children[0]?.type === "text" && titleNode.data?.directiveLabel) {
275
+ customTitle = titleNode.children[0].value;
276
+ node.children.shift();
277
+ }
278
+ const title = customTitle || defaultTitles[type];
279
+ if (type === "code-group") {
280
+ data.hName = "div";
281
+ data.hProperties = {
282
+ className: ["ardo-code-group"]
283
+ };
284
+ const tabs = [];
285
+ for (const child of node.children) {
286
+ if (child.type === "code") {
287
+ const codeNode = child;
288
+ const meta = codeNode.meta || "";
289
+ const labelMatch = meta.match(/\[([^\]]+)\]/);
290
+ const label = labelMatch ? labelMatch[1] : codeNode.lang || "Code";
291
+ tabs.push({ label, content: child });
292
+ }
293
+ }
294
+ const tabsHtml = tabs.map(
295
+ (tab, i) => `<button class="ardo-code-group-tab${i === 0 ? " active" : ""}" data-index="${i}">${escapeHtml2(tab.label)}</button>`
296
+ ).join("");
297
+ node.children = [
298
+ {
299
+ type: "html",
300
+ value: `<div class="ardo-code-group-tabs">${tabsHtml}</div>`
301
+ },
302
+ {
303
+ type: "html",
304
+ value: '<div class="ardo-code-group-panels">'
305
+ },
306
+ ...tabs.map(
307
+ (tab, i) => ({
308
+ type: "html",
309
+ value: `<div class="ardo-code-group-panel${i === 0 ? " active" : ""}" data-index="${i}">`
310
+ })
311
+ ),
312
+ ...node.children.flatMap((child, _i) => [
313
+ child,
314
+ {
315
+ type: "html",
316
+ value: "</div>"
317
+ }
318
+ ]),
319
+ {
320
+ type: "html",
321
+ value: "</div>"
322
+ }
323
+ ];
324
+ return;
325
+ }
326
+ if (type === "details") {
327
+ data.hName = "details";
328
+ data.hProperties = {
329
+ className: ["ardo-details"]
330
+ };
331
+ node.children.unshift({
332
+ type: "html",
333
+ value: `<summary class="ardo-details-summary">${escapeHtml2(title)}</summary>`
334
+ });
335
+ return;
336
+ }
337
+ data.hName = "div";
338
+ data.hProperties = {
339
+ className: ["ardo-container", `ardo-container-${type}`]
340
+ };
341
+ node.children.unshift({
342
+ type: "html",
343
+ value: `<p class="ardo-container-title">${escapeHtml2(title)}</p>`
344
+ });
345
+ });
346
+ };
347
+ }
348
+ function escapeHtml2(text) {
349
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
350
+ }
351
+ var containerToComponent = {
352
+ tip: "Tip",
353
+ warning: "Warning",
354
+ danger: "Danger",
355
+ info: "Info",
356
+ note: "Note"
357
+ };
358
+ function remarkContainersMdx() {
359
+ return function(tree) {
360
+ visit2(tree, "containerDirective", (node, index, parent) => {
361
+ if (!parent || typeof index !== "number") return;
362
+ if (node.name === "code-group") {
363
+ const labels = node.children.filter((child) => child.type === "code").map((child) => {
364
+ const meta = child.meta || "";
365
+ const match = meta.match(/\[([^\]]+)\]/);
366
+ return match ? match[1] : child.lang || "Code";
367
+ });
368
+ const jsxNode2 = {
369
+ type: "mdxJsxFlowElement",
370
+ name: "CodeGroup",
371
+ attributes: [
372
+ {
373
+ type: "mdxJsxAttribute",
374
+ name: "labels",
375
+ value: labels.join(",")
376
+ }
377
+ ],
378
+ children: node.children
379
+ };
380
+ parent.children[index] = jsxNode2;
381
+ return;
382
+ }
383
+ const componentName = containerToComponent[node.name];
384
+ if (!componentName) return;
385
+ const titleNode = node.children[0];
386
+ let customTitle;
387
+ if (titleNode && titleNode.type === "paragraph" && titleNode.children[0]?.type === "text" && titleNode.data?.directiveLabel) {
388
+ customTitle = titleNode.children[0].value;
389
+ node.children.shift();
390
+ }
391
+ const attributes = [];
392
+ if (customTitle) {
393
+ attributes.push({
394
+ type: "mdxJsxAttribute",
395
+ name: "title",
396
+ value: customTitle
397
+ });
398
+ }
399
+ const jsxNode = {
400
+ type: "mdxJsxFlowElement",
401
+ name: componentName,
402
+ attributes,
403
+ children: node.children
404
+ };
405
+ parent.children[index] = jsxNode;
406
+ });
407
+ };
408
+ }
409
+
410
+ // src/vite/plugin.ts
16
411
  import fs2 from "fs/promises";
17
412
  import fsSync2 from "fs";
18
413
  import path2 from "path";
19
414
  import { execSync } from "child_process";
415
+ import matter from "gray-matter";
20
416
 
21
417
  // src/vite/routes-plugin.ts
22
418
  import fs from "fs/promises";
@@ -182,6 +578,35 @@ function detectGitHubRepoName(cwd) {
182
578
  return void 0;
183
579
  }
184
580
  }
581
+ function readProjectMeta(root) {
582
+ const pkgPath = path2.join(root, "package.json");
583
+ try {
584
+ const raw = fsSync2.readFileSync(pkgPath, "utf-8");
585
+ const pkg = JSON.parse(raw);
586
+ let repository;
587
+ if (typeof pkg.repository === "string") {
588
+ repository = pkg.repository;
589
+ } else if (pkg.repository?.url) {
590
+ repository = pkg.repository.url.replace(/^git\+/, "").replace(/^git:\/\//, "https://").replace(/\.git$/, "");
591
+ }
592
+ let author;
593
+ if (typeof pkg.author === "string") {
594
+ author = pkg.author;
595
+ } else if (pkg.author?.name) {
596
+ author = pkg.author.name;
597
+ }
598
+ return {
599
+ name: pkg.name,
600
+ homepage: pkg.homepage,
601
+ repository,
602
+ version: pkg.version,
603
+ author,
604
+ license: pkg.license
605
+ };
606
+ } catch {
607
+ return {};
608
+ }
609
+ }
185
610
  var VIRTUAL_MODULE_ID = "virtual:ardo/config";
186
611
  var RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
187
612
  var VIRTUAL_SIDEBAR_ID = "virtual:ardo/sidebar";
@@ -225,6 +650,8 @@ function ardoPlugin(options = {}) {
225
650
  async configResolved(config) {
226
651
  const root = config.root;
227
652
  routesDir = routesDirOption || path2.join(root, "app", "routes");
653
+ const detectedProject = readProjectMeta(root);
654
+ const project = { ...detectedProject, ...pressConfig.project };
228
655
  const defaultConfig = {
229
656
  title: pressConfig.title ?? "Ardo",
230
657
  description: pressConfig.description ?? "Documentation powered by Ardo"
@@ -232,6 +659,7 @@ function ardoPlugin(options = {}) {
232
659
  const configWithRoutes = {
233
660
  ...defaultConfig,
234
661
  ...pressConfig,
662
+ project,
235
663
  srcDir: routesDir
236
664
  };
237
665
  resolvedConfig = resolveConfig(configWithRoutes, root);
@@ -254,7 +682,8 @@ function ardoPlugin(options = {}) {
254
682
  description: resolvedConfig.description,
255
683
  base: resolvedConfig.base,
256
684
  lang: resolvedConfig.lang,
257
- themeConfig: resolvedConfig.themeConfig
685
+ themeConfig: resolvedConfig.themeConfig,
686
+ project: resolvedConfig.project
258
687
  };
259
688
  return `export default ${JSON.stringify(clientConfig)}`;
260
689
  }
@@ -317,14 +746,17 @@ function ardoPlugin(options = {}) {
317
746
  }
318
747
  const themeConfig = pressConfig.markdown?.theme;
319
748
  const hasThemeObject = themeConfig && typeof themeConfig === "object" && "light" in themeConfig;
749
+ const lineNumbers = pressConfig.markdown?.lineNumbers || false;
320
750
  const shikiOptions = hasThemeObject ? {
321
751
  themes: {
322
752
  light: themeConfig.light || "github-light",
323
753
  dark: themeConfig.dark || "github-dark"
324
754
  },
325
- defaultColor: false
755
+ defaultColor: false,
756
+ transformers: [ardoLineTransformer({ globalLineNumbers: lineNumbers })]
326
757
  } : {
327
- theme: themeConfig || "github-dark"
758
+ theme: themeConfig || "github-dark",
759
+ transformers: [ardoLineTransformer({ globalLineNumbers: lineNumbers })]
328
760
  };
329
761
  const mdxPlugin = mdx({
330
762
  include: /\.(md|mdx)$/,
@@ -332,7 +764,9 @@ function ardoPlugin(options = {}) {
332
764
  remarkFrontmatter,
333
765
  [remarkMdxFrontmatter, { name: "frontmatter" }],
334
766
  remarkGfm,
335
- remarkDirective
767
+ remarkDirective,
768
+ remarkContainersMdx,
769
+ remarkCodeMeta
336
770
  ],
337
771
  rehypePlugins: [[rehypeShiki, shikiOptions]],
338
772
  providerImportSource: "ardo/mdx-provider"
@@ -352,251 +786,131 @@ async function generateSidebar(config, routesDir) {
352
786
  return themeConfig.sidebar;
353
787
  }
354
788
  try {
355
- const sidebar = await scanDirectory(routesDir, routesDir, config.base);
789
+ const sidebar = await scanDirectory(routesDir, routesDir);
356
790
  return sidebar;
357
791
  } catch {
358
792
  return [];
359
793
  }
360
794
  }
361
- async function scanDirectory(dir, rootDir, _basePath) {
795
+ async function scanDirectory(dir, rootDir) {
362
796
  const entries = await fs2.readdir(dir, { withFileTypes: true });
363
797
  const items = [];
364
798
  for (const entry of entries) {
365
799
  const fullPath = path2.join(dir, entry.name);
366
800
  const relativePath = path2.relative(rootDir, fullPath);
367
801
  if (entry.isDirectory()) {
368
- const children = await scanDirectory(fullPath, rootDir, _basePath);
802
+ const children = await scanDirectory(fullPath, rootDir);
369
803
  if (children.length > 0) {
370
804
  const indexPath = path2.join(fullPath, "index.mdx");
371
805
  let link;
372
806
  try {
373
- await fs2.access(indexPath);
374
- link = "/" + relativePath.replace(/\\/g, "/");
375
- } catch {
376
- }
377
- items.push({
378
- text: formatTitle(entry.name),
379
- link,
380
- items: children
381
- });
382
- }
383
- } else if ((entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) && entry.name !== "index.mdx" && entry.name !== "index.md") {
384
- const fileContent = await fs2.readFile(fullPath, "utf-8");
385
- const frontmatterMatch = fileContent.match(/^---\n([\s\S]*?)\n---/);
386
- const ext = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
387
- let title = formatTitle(entry.name.replace(ext, ""));
388
- let order;
389
- if (frontmatterMatch) {
390
- const frontmatterText = frontmatterMatch[1];
391
- const titleMatch = frontmatterText.match(/title:\s*["']?([^"'\n]+)["']?/);
392
- const orderMatch = frontmatterText.match(/order:\s*(\d+)/);
393
- if (titleMatch) {
394
- title = titleMatch[1].trim();
395
- }
396
- if (orderMatch) {
397
- order = parseInt(orderMatch[1], 10);
398
- }
399
- }
400
- const link = "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
401
- items.push({
402
- text: title,
403
- link,
404
- order
405
- });
406
- }
407
- }
408
- items.sort((a, b) => {
409
- if (a.order !== void 0 && b.order !== void 0) {
410
- return a.order - b.order;
411
- }
412
- if (a.order !== void 0) return -1;
413
- if (b.order !== void 0) return 1;
414
- return a.text.localeCompare(b.text);
415
- });
416
- return items.map(({ order: _order, ...item }) => item);
417
- }
418
- function formatTitle(name) {
419
- return name.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
420
- }
421
- async function generateSearchIndex(routesDir) {
422
- const docs = [];
423
- async function scanForSearch(dir, section) {
424
- try {
425
- const entries = await fs2.readdir(dir, { withFileTypes: true });
426
- for (const entry of entries) {
427
- const fullPath = path2.join(dir, entry.name);
428
- if (entry.isDirectory()) {
429
- const newSection = section ? `${section} > ${formatTitle(entry.name)}` : formatTitle(entry.name);
430
- await scanForSearch(fullPath, newSection);
431
- } else if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) {
432
- const relativePath = path2.relative(routesDir, fullPath);
433
- const fileContent = await fs2.readFile(fullPath, "utf-8");
434
- const frontmatterMatch = fileContent.match(/^---\n([\s\S]*?)\n---/);
435
- const ext = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
436
- let title = formatTitle(entry.name.replace(ext, ""));
437
- let content = fileContent;
438
- if (frontmatterMatch) {
439
- const frontmatterText = frontmatterMatch[1];
440
- const titleMatch = frontmatterText.match(/title:\s*["']?([^"'\n]+)["']?/);
441
- if (titleMatch) {
442
- title = titleMatch[1].trim();
443
- }
444
- content = fileContent.slice(frontmatterMatch[0].length);
445
- }
446
- content = content.replace(/```[\s\S]*?```/g, "").replace(/`[^`]+`/g, "").replace(/import\s+.*?from\s+['"].*?['"]/g, "").replace(/<[^>]+>/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[#*_~>]/g, "").replace(/\n+/g, " ").replace(/\s+/g, " ").trim().slice(0, 2e3);
447
- const routePath = entry.name === "index.mdx" || entry.name === "index.md" ? "/" + path2.dirname(relativePath).replace(/\\/g, "/") : "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
448
- const finalPath = routePath === "/." ? "/" : routePath;
449
- docs.push({
450
- id: relativePath,
451
- title,
452
- content,
453
- path: finalPath,
454
- section
455
- });
456
- }
457
- }
458
- } catch {
459
- }
460
- }
461
- await scanForSearch(routesDir);
462
- return docs;
463
- }
464
-
465
- // src/runtime/loader.ts
466
- import fs3 from "fs/promises";
467
- import path3 from "path";
468
-
469
- // src/markdown/pipeline.ts
470
- import { unified } from "unified";
471
- import remarkParse from "remark-parse";
472
- import remarkGfm2 from "remark-gfm";
473
- import remarkFrontmatter2 from "remark-frontmatter";
474
- import remarkDirective2 from "remark-directive";
475
- import remarkRehype from "remark-rehype";
476
- import rehypeStringify from "rehype-stringify";
477
- import matter from "gray-matter";
478
-
479
- // src/markdown/containers.ts
480
- import { visit } from "unist-util-visit";
481
- var containerTypes = [
482
- "tip",
483
- "warning",
484
- "danger",
485
- "info",
486
- "note",
487
- "details",
488
- "code-group"
489
- ];
490
- var defaultTitles = {
491
- tip: "TIP",
492
- warning: "WARNING",
493
- danger: "DANGER",
494
- info: "INFO",
495
- note: "NOTE",
496
- details: "Details",
497
- "code-group": ""
498
- };
499
- function remarkContainers() {
500
- return function(tree) {
501
- visit(tree, "containerDirective", (node) => {
502
- const type = node.name;
503
- if (!containerTypes.includes(type)) {
504
- return;
505
- }
506
- const data = node.data || (node.data = {});
507
- const titleNode = node.children[0];
508
- let customTitle;
509
- if (titleNode && titleNode.type === "paragraph" && titleNode.children[0]?.type === "text" && titleNode.data?.directiveLabel) {
510
- customTitle = titleNode.children[0].value;
511
- node.children.shift();
512
- }
513
- const title = customTitle || defaultTitles[type];
514
- if (type === "code-group") {
515
- data.hName = "div";
516
- data.hProperties = {
517
- className: ["ardo-code-group"]
518
- };
519
- const tabs = [];
520
- for (const child of node.children) {
521
- if (child.type === "code") {
522
- const codeNode = child;
523
- const meta = codeNode.meta || "";
524
- const labelMatch = meta.match(/\[([^\]]+)\]/);
525
- const label = labelMatch ? labelMatch[1] : codeNode.lang || "Code";
526
- tabs.push({ label, content: child });
527
- }
528
- }
529
- const tabsHtml = tabs.map(
530
- (tab, i) => `<button class="ardo-code-group-tab${i === 0 ? " active" : ""}" data-index="${i}">${escapeHtml(tab.label)}</button>`
531
- ).join("");
532
- node.children = [
533
- {
534
- type: "html",
535
- value: `<div class="ardo-code-group-tabs">${tabsHtml}</div>`
536
- },
537
- {
538
- type: "html",
539
- value: '<div class="ardo-code-group-panels">'
540
- },
541
- ...tabs.map(
542
- (tab, i) => ({
543
- type: "html",
544
- value: `<div class="ardo-code-group-panel${i === 0 ? " active" : ""}" data-index="${i}">`
545
- })
546
- ),
547
- ...node.children.flatMap((child, _i) => [
548
- child,
549
- {
550
- type: "html",
551
- value: "</div>"
552
- }
553
- ]),
554
- {
555
- type: "html",
556
- value: "</div>"
557
- }
558
- ];
559
- return;
560
- }
561
- if (type === "details") {
562
- data.hName = "details";
563
- data.hProperties = {
564
- className: ["ardo-details"]
565
- };
566
- node.children.unshift({
567
- type: "html",
568
- value: `<summary class="ardo-details-summary">${escapeHtml(title)}</summary>`
807
+ await fs2.access(indexPath);
808
+ link = "/" + relativePath.replace(/\\/g, "/");
809
+ } catch {
810
+ }
811
+ items.push({
812
+ text: formatTitle(entry.name),
813
+ link,
814
+ items: children
569
815
  });
570
- return;
571
816
  }
572
- data.hName = "div";
573
- data.hProperties = {
574
- className: ["ardo-container", `ardo-container-${type}`]
575
- };
576
- node.children.unshift({
577
- type: "html",
578
- value: `<p class="ardo-container-title">${escapeHtml(title)}</p>`
817
+ } else if ((entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) && entry.name !== "index.mdx" && entry.name !== "index.md") {
818
+ const fileContent = await fs2.readFile(fullPath, "utf-8");
819
+ const { data: frontmatter } = matter(fileContent);
820
+ const ext = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
821
+ const title = frontmatter.title || formatTitle(entry.name.replace(ext, ""));
822
+ const order = typeof frontmatter.order === "number" ? frontmatter.order : void 0;
823
+ const link = "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
824
+ items.push({
825
+ text: title,
826
+ link,
827
+ order
579
828
  });
580
- });
581
- };
829
+ }
830
+ }
831
+ items.sort((a, b) => {
832
+ if (a.order !== void 0 && b.order !== void 0) {
833
+ return a.order - b.order;
834
+ }
835
+ if (a.order !== void 0) return -1;
836
+ if (b.order !== void 0) return 1;
837
+ return a.text.localeCompare(b.text);
838
+ });
839
+ return items.map(({ order: _order, ...item }) => item);
582
840
  }
583
- function escapeHtml(text) {
584
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
841
+ function formatTitle(name) {
842
+ return name.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
843
+ }
844
+ async function generateSearchIndex(routesDir) {
845
+ const docs = [];
846
+ async function scanForSearch(dir, section) {
847
+ try {
848
+ const entries = await fs2.readdir(dir, { withFileTypes: true });
849
+ for (const entry of entries) {
850
+ const fullPath = path2.join(dir, entry.name);
851
+ if (entry.isDirectory()) {
852
+ const newSection = section ? `${section} > ${formatTitle(entry.name)}` : formatTitle(entry.name);
853
+ await scanForSearch(fullPath, newSection);
854
+ } else if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) {
855
+ const relativePath = path2.relative(routesDir, fullPath);
856
+ const fileContent = await fs2.readFile(fullPath, "utf-8");
857
+ const { data: frontmatter, content: rawContent } = matter(fileContent);
858
+ const ext = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
859
+ const title = frontmatter.title || formatTitle(entry.name.replace(ext, ""));
860
+ let content = rawContent;
861
+ content = content.replace(/```[\s\S]*?```/g, "").replace(/`[^`]+`/g, "").replace(/import\s+.*?from\s+['"].*?['"]/g, "").replace(/<[^>]+>/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[#*_~>]/g, "").replace(/\n+/g, " ").replace(/\s+/g, " ").trim().slice(0, 2e3);
862
+ const routePath = entry.name === "index.mdx" || entry.name === "index.md" ? "/" + path2.dirname(relativePath).replace(/\\/g, "/") : "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
863
+ const finalPath = routePath === "/." ? "/" : routePath;
864
+ docs.push({
865
+ id: relativePath,
866
+ title,
867
+ content,
868
+ path: finalPath,
869
+ section
870
+ });
871
+ }
872
+ }
873
+ } catch (error) {
874
+ console.warn(
875
+ "[ardo] Failed to scan for search index:",
876
+ error instanceof Error ? error.message : error
877
+ );
878
+ }
879
+ }
880
+ await scanForSearch(routesDir);
881
+ return docs;
585
882
  }
586
883
 
884
+ // src/runtime/loader.ts
885
+ import fs3 from "fs/promises";
886
+ import path3 from "path";
887
+
888
+ // src/markdown/pipeline.ts
889
+ import { unified } from "unified";
890
+ import remarkParse from "remark-parse";
891
+ import remarkGfm2 from "remark-gfm";
892
+ import remarkFrontmatter2 from "remark-frontmatter";
893
+ import remarkDirective2 from "remark-directive";
894
+ import remarkRehype from "remark-rehype";
895
+ import rehypeStringify from "rehype-stringify";
896
+ import matter2 from "gray-matter";
897
+
587
898
  // src/markdown/toc.ts
588
- import { visit as visit2 } from "unist-util-visit";
899
+ import { visit as visit3 } from "unist-util-visit";
589
900
  function remarkExtractToc(options) {
590
901
  const { tocExtraction, levels } = options;
591
902
  const [minLevel, maxLevel] = levels;
592
903
  return function(tree) {
593
904
  const headings = [];
594
- visit2(tree, "heading", (node) => {
905
+ let headingIndex = 0;
906
+ visit3(tree, "heading", (node) => {
595
907
  if (node.depth < minLevel || node.depth > maxLevel) {
596
908
  return;
597
909
  }
598
910
  const text = getHeadingText(node);
599
- const id = slugify(text);
911
+ const slug = slugify(text);
912
+ const id = slug || `heading-${headingIndex}`;
913
+ headingIndex++;
600
914
  headings.push({
601
915
  text,
602
916
  level: node.depth,
@@ -654,174 +968,6 @@ function buildTocTree(headings, _minLevel) {
654
968
  return result;
655
969
  }
656
970
 
657
- // src/markdown/shiki.ts
658
- import { createHighlighter } from "shiki";
659
- import { visit as visit3 } from "unist-util-visit";
660
- async function createShikiHighlighter(config) {
661
- const themeConfig = config.theme ?? {
662
- light: "github-light",
663
- dark: "github-dark"
664
- };
665
- const themes = typeof themeConfig === "string" ? [themeConfig] : [themeConfig.light, themeConfig.dark];
666
- const highlighter = await createHighlighter({
667
- themes,
668
- langs: [
669
- "javascript",
670
- "typescript",
671
- "jsx",
672
- "tsx",
673
- "json",
674
- "html",
675
- "css",
676
- "markdown",
677
- "bash",
678
- "shell",
679
- "yaml",
680
- "python",
681
- "rust",
682
- "go",
683
- "sql",
684
- "diff"
685
- ]
686
- });
687
- return highlighter;
688
- }
689
- function rehypeShikiFromHighlighter(options) {
690
- const { highlighter, config } = options;
691
- const themeConfig = config.theme ?? {
692
- light: "github-light",
693
- dark: "github-dark"
694
- };
695
- return function(tree) {
696
- visit3(tree, "element", (node, index, parent) => {
697
- if (node.tagName !== "pre" || !node.children[0] || node.children[0].tagName !== "code") {
698
- return;
699
- }
700
- const codeNode = node.children[0];
701
- const className = codeNode.properties?.className || [];
702
- const langClass = className.find((c) => c.startsWith("language-"));
703
- const lang = langClass ? langClass.replace("language-", "") : "text";
704
- const codeContent = getTextContent(codeNode);
705
- if (!codeContent.trim()) {
706
- return;
707
- }
708
- try {
709
- let html;
710
- if (typeof themeConfig === "string") {
711
- html = highlighter.codeToHtml(codeContent, {
712
- lang,
713
- theme: themeConfig
714
- });
715
- } else {
716
- html = highlighter.codeToHtml(codeContent, {
717
- lang,
718
- themes: {
719
- light: themeConfig.light,
720
- dark: themeConfig.dark
721
- },
722
- defaultColor: false
723
- });
724
- }
725
- const metaString = codeNode.properties?.metastring || "";
726
- const lineNumbers = config.lineNumbers || metaString.includes("showLineNumbers");
727
- const highlightLines = parseHighlightLines(metaString);
728
- const title = parseTitle(metaString);
729
- const wrapperHtml = buildCodeBlockHtml(html, {
730
- lang,
731
- lineNumbers,
732
- highlightLines,
733
- title
734
- });
735
- if (parent && typeof index === "number") {
736
- const newNode = {
737
- type: "element",
738
- tagName: "div",
739
- properties: {
740
- className: ["ardo-code-block"],
741
- "data-lang": lang
742
- },
743
- children: [
744
- {
745
- type: "raw",
746
- value: wrapperHtml
747
- }
748
- ]
749
- };
750
- parent.children[index] = newNode;
751
- }
752
- } catch {
753
- }
754
- });
755
- };
756
- }
757
- function getTextContent(node) {
758
- if (node.type === "text") {
759
- return node.value;
760
- }
761
- if ("children" in node) {
762
- return node.children.map((child) => getTextContent(child)).join("");
763
- }
764
- return "";
765
- }
766
- function parseHighlightLines(meta) {
767
- const match = meta.match(/\{([\d,-]+)\}/);
768
- if (!match) return [];
769
- const ranges = match[1].split(",");
770
- const lines = [];
771
- for (const range of ranges) {
772
- if (range.includes("-")) {
773
- const [start, end] = range.split("-").map(Number);
774
- for (let i = start; i <= end; i++) {
775
- lines.push(i);
776
- }
777
- } else {
778
- lines.push(Number(range));
779
- }
780
- }
781
- return lines;
782
- }
783
- function parseTitle(meta) {
784
- const match = meta.match(/title="([^"]+)"/);
785
- return match ? match[1] : void 0;
786
- }
787
- function buildCodeBlockHtml(shikiHtml, options) {
788
- const { lang, lineNumbers, highlightLines, title } = options;
789
- let html = "";
790
- if (title) {
791
- html += `<div class="ardo-code-title">${escapeHtml2(title)}</div>`;
792
- }
793
- html += `<div class="ardo-code-wrapper" data-lang="${lang}">`;
794
- if (lineNumbers || highlightLines.length > 0) {
795
- const lines = shikiHtml.split("\n");
796
- const processedHtml = lines.map((line, i) => {
797
- const lineNum = i + 1;
798
- const isHighlighted = highlightLines.includes(lineNum);
799
- const classes = ["ardo-code-line"];
800
- if (isHighlighted) classes.push("highlighted");
801
- let prefix = "";
802
- if (lineNumbers) {
803
- prefix = `<span class="ardo-line-number">${lineNum}</span>`;
804
- }
805
- return `<span class="${classes.join(" ")}">${prefix}${line}</span>`;
806
- }).join("\n");
807
- html += processedHtml;
808
- } else {
809
- html += shikiHtml;
810
- }
811
- html += `<button class="ardo-copy-button" data-code="${encodeURIComponent(extractCodeFromHtml(shikiHtml))}">
812
- <span class="ardo-copy-icon">Copy</span>
813
- <span class="ardo-copied-icon" style="display:none">Copied!</span>
814
- </button>`;
815
- html += "</div>";
816
- return html;
817
- }
818
- function extractCodeFromHtml(html) {
819
- return html.replace(/<[^>]+>/g, "").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
820
- }
821
- function escapeHtml2(text) {
822
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
823
- }
824
-
825
971
  // src/markdown/links.ts
826
972
  import { visit as visit4 } from "unist-util-visit";
827
973
  function rehypeLinks(options) {
@@ -847,7 +993,7 @@ function rehypeLinks(options) {
847
993
 
848
994
  // src/markdown/pipeline.ts
849
995
  async function transformMarkdown(content, config, options = {}) {
850
- const { data: frontmatter, content: markdownContent } = matter(content);
996
+ const { data: frontmatter, content: markdownContent } = matter2(content);
851
997
  const { basePath = "/", highlighter: providedHighlighter } = options;
852
998
  const tocExtraction = { toc: [] };
853
999
  const highlighter = providedHighlighter ?? await createShikiHighlighter(config);
@@ -961,7 +1107,7 @@ function getPageDataForRoute(docs, slug) {
961
1107
  // src/runtime/sidebar.ts
962
1108
  import fs4 from "fs/promises";
963
1109
  import path4 from "path";
964
- import matter2 from "gray-matter";
1110
+ import matter3 from "gray-matter";
965
1111
  async function generateSidebar2(options) {
966
1112
  const { contentDir, basePath, config } = options;
967
1113
  const configSidebar = config.themeConfig.sidebar;
@@ -998,7 +1144,7 @@ async function scanDirectoryForSidebar(dir, rootDir, _basePath) {
998
1144
  let order;
999
1145
  try {
1000
1146
  const indexContent = await fs4.readFile(indexPath, "utf-8");
1001
- const { data: frontmatter } = matter2(indexContent);
1147
+ const { data: frontmatter } = matter3(indexContent);
1002
1148
  if (frontmatter.title) {
1003
1149
  title = frontmatter.title;
1004
1150
  }
@@ -1018,7 +1164,7 @@ async function scanDirectoryForSidebar(dir, rootDir, _basePath) {
1018
1164
  }
1019
1165
  } else if (entry.name.endsWith(".md") && entry.name !== "index.md") {
1020
1166
  const fileContent = await fs4.readFile(fullPath, "utf-8");
1021
- const { data: frontmatter } = matter2(fileContent);
1167
+ const { data: frontmatter } = matter3(fileContent);
1022
1168
  if (frontmatter.sidebar === false) {
1023
1169
  continue;
1024
1170
  }
@@ -1050,9 +1196,9 @@ function normalizePath(p) {
1050
1196
  }
1051
1197
 
1052
1198
  export {
1199
+ createShikiHighlighter,
1053
1200
  ardoRoutesPlugin,
1054
1201
  ardoPlugin,
1055
- createShikiHighlighter,
1056
1202
  transformMarkdown,
1057
1203
  transformMarkdownToReact,
1058
1204
  loadDoc,
@@ -1061,4 +1207,4 @@ export {
1061
1207
  getPageDataForRoute,
1062
1208
  generateSidebar2 as generateSidebar
1063
1209
  };
1064
- //# sourceMappingURL=chunk-LUOUBO3L.js.map
1210
+ //# sourceMappingURL=chunk-SWER4H2H.js.map