ardo 3.1.0 → 3.2.1

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 (123) hide show
  1. package/README.md +9 -19
  2. package/dist/DocPage-CIBiCAxZ.js +1010 -0
  3. package/dist/DocPage-CIBiCAxZ.js.map +1 -0
  4. package/dist/assets/src/ui/Breadcrumb.css.ts.vanilla-Dpgq-C_p.css +20 -0
  5. package/dist/assets/src/ui/DocPage.css.ts.vanilla-CXKuz4U-.css +34 -0
  6. package/dist/assets/src/ui/Footer.css.ts.vanilla-BSzPIPt4.css +100 -0
  7. package/dist/assets/src/ui/Header.css.ts.vanilla-8QL0Jzgk.css +156 -0
  8. package/dist/assets/src/ui/Layout.css.ts.vanilla-Bpx_-gJt.css +67 -0
  9. package/dist/assets/src/ui/Nav.css.ts.vanilla-CsAQjogy.css +51 -0
  10. package/dist/assets/src/ui/Sidebar.css.ts.vanilla-D70qXTEr.css +115 -0
  11. package/dist/assets/src/ui/Toc.css.ts.vanilla-CYqcWgvD.css +52 -0
  12. package/dist/assets/src/ui/components/ApiItem.css.ts.vanilla-B_DW-1iJ.css +218 -0
  13. package/dist/assets/src/ui/components/CodeBlock.css.ts.vanilla-lNKqskjQ.css +182 -0
  14. package/dist/assets/src/ui/components/Container.css.ts.vanilla-CUhRUA9t.css +80 -0
  15. package/dist/assets/src/ui/components/CopyButton.css.ts.vanilla-DZZ5jgTM.css +24 -0
  16. package/dist/assets/src/ui/components/Features.css.ts.vanilla-D-pNXM9Q.css +129 -0
  17. package/dist/assets/src/ui/components/Hero.css.ts.vanilla-DHJVZ6GX.css +134 -0
  18. package/dist/assets/src/ui/components/Search.css.ts.vanilla-BYpWHzky.css +135 -0
  19. package/dist/assets/src/ui/components/Steps.css.ts.vanilla-CisaxeNj.css +59 -0
  20. package/dist/assets/src/ui/components/Tabs.css.ts.vanilla-C4-vJSnf.css +30 -0
  21. package/dist/assets/src/ui/components/ThemeToggle.css.ts.vanilla---sSUELC.css +22 -0
  22. package/dist/assets/src/ui/content.css.ts.vanilla-O_RaSPXm.css +106 -0
  23. package/dist/assets/src/ui/theme/animations.css.ts.vanilla-D6ImVUKy.css +10 -0
  24. package/dist/assets/src/ui/theme/dark.css.ts.vanilla-2iJgcpbU.css +87 -0
  25. package/dist/assets/src/ui/theme/light.css.ts.vanilla-CwinfWSf.css +87 -0
  26. package/dist/assets/src/ui/theme/reset.css.ts.vanilla-0Q3pLjfC.css +34 -0
  27. package/dist/brand-icons-DLJKqTun.js +59 -0
  28. package/dist/brand-icons-DLJKqTun.js.map +1 -0
  29. package/dist/config/index.d.ts +5 -5
  30. package/dist/config/index.d.ts.map +1 -0
  31. package/dist/config/index.js +54 -11
  32. package/dist/config/index.js.map +1 -1
  33. package/dist/contract.css-DYvFVCFE.d.ts +105 -0
  34. package/dist/contract.css-DYvFVCFE.d.ts.map +1 -0
  35. package/dist/generator-DPtRXxM_.js +1194 -0
  36. package/dist/generator-DPtRXxM_.js.map +1 -0
  37. package/dist/icons/index.d.ts +22 -1
  38. package/dist/icons/index.d.ts.map +1 -0
  39. package/dist/icons/index.js +2 -2
  40. package/dist/index-BTeHvysI.d.ts +807 -0
  41. package/dist/index-BTeHvysI.d.ts.map +1 -0
  42. package/dist/index-DySzkJlC.d.ts +78 -0
  43. package/dist/index-DySzkJlC.d.ts.map +1 -0
  44. package/dist/index.d.ts +5 -8
  45. package/dist/index.js +6 -100
  46. package/dist/mdx/provider.d.ts +61 -4
  47. package/dist/mdx/provider.d.ts.map +1 -0
  48. package/dist/mdx/provider.js +89 -117
  49. package/dist/mdx/provider.js.map +1 -1
  50. package/dist/runtime/index.d.ts +2 -41
  51. package/dist/runtime/index.js +2 -28
  52. package/dist/sidebar-utils-1Skqle1Q.js +109 -0
  53. package/dist/sidebar-utils-1Skqle1Q.js.map +1 -0
  54. package/dist/theme/index.d.ts +201 -182
  55. package/dist/theme/index.d.ts.map +1 -0
  56. package/dist/theme/index.js +288 -128
  57. package/dist/theme/index.js.map +1 -1
  58. package/dist/typedoc/components/index.d.ts +55 -0
  59. package/dist/typedoc/components/index.d.ts.map +1 -0
  60. package/dist/typedoc/components/index.js +339 -0
  61. package/dist/typedoc/components/index.js.map +1 -0
  62. package/dist/typedoc/index.d.ts +36 -305
  63. package/dist/typedoc/index.d.ts.map +1 -0
  64. package/dist/typedoc/index.js +97 -268
  65. package/dist/typedoc/index.js.map +1 -1
  66. package/dist/types-BCuJBsJu.d.ts +182 -0
  67. package/dist/types-BCuJBsJu.d.ts.map +1 -0
  68. package/dist/types-CTd_mkrv.d.ts +175 -0
  69. package/dist/types-CTd_mkrv.d.ts.map +1 -0
  70. package/dist/ui/index.d.ts +2 -178
  71. package/dist/ui/index.js +3 -95
  72. package/dist/ui/styles.css +1401 -1335
  73. package/dist/ui/styles.d.ts +1 -2
  74. package/dist/ui/styles.js +23 -4
  75. package/dist/ui-3grzJSsq.js +1314 -0
  76. package/dist/ui-3grzJSsq.js.map +1 -0
  77. package/dist/vite/index.d.ts +78 -86
  78. package/dist/vite/index.d.ts.map +1 -0
  79. package/dist/vite/index.js +2931 -1282
  80. package/dist/vite/index.js.map +1 -1
  81. package/package.json +30 -19
  82. package/dist/Features-D_Pt7zpA.d.ts +0 -615
  83. package/dist/Search-DOJMNI2T.css +0 -193
  84. package/dist/Search-DOJMNI2T.css.map +0 -1
  85. package/dist/Search-VYYG3D43.js +0 -10
  86. package/dist/Search-VYYG3D43.js.map +0 -1
  87. package/dist/chunk-4YQE3TNM.js +0 -1
  88. package/dist/chunk-4YQE3TNM.js.map +0 -1
  89. package/dist/chunk-AXLJDGQL.js +0 -1
  90. package/dist/chunk-AXLJDGQL.js.map +0 -1
  91. package/dist/chunk-CZM5NX27.js +0 -909
  92. package/dist/chunk-CZM5NX27.js.map +0 -1
  93. package/dist/chunk-FZP2AVJL.js +0 -43
  94. package/dist/chunk-FZP2AVJL.js.map +0 -1
  95. package/dist/chunk-IEPSORG5.js +0 -444
  96. package/dist/chunk-IEPSORG5.js.map +0 -1
  97. package/dist/chunk-KUWEUO37.js +0 -1
  98. package/dist/chunk-KUWEUO37.js.map +0 -1
  99. package/dist/chunk-NBRHGTR2.js +0 -79
  100. package/dist/chunk-NBRHGTR2.js.map +0 -1
  101. package/dist/chunk-PGHUPTGL.js +0 -1035
  102. package/dist/chunk-PGHUPTGL.js.map +0 -1
  103. package/dist/chunk-PMS3P4MA.js +0 -43
  104. package/dist/chunk-PMS3P4MA.js.map +0 -1
  105. package/dist/chunk-QELSOHIY.js +0 -46
  106. package/dist/chunk-QELSOHIY.js.map +0 -1
  107. package/dist/chunk-R2QKY6G3.js +0 -1
  108. package/dist/chunk-R2QKY6G3.js.map +0 -1
  109. package/dist/chunk-ZPYQQZ7J.js +0 -210
  110. package/dist/chunk-ZPYQQZ7J.js.map +0 -1
  111. package/dist/icons/index.js.map +0 -1
  112. package/dist/index.css +0 -1290
  113. package/dist/index.css.map +0 -1
  114. package/dist/index.js.map +0 -1
  115. package/dist/mdx/provider.css +0 -403
  116. package/dist/mdx/provider.css.map +0 -1
  117. package/dist/runtime/index.js.map +0 -1
  118. package/dist/types-CLkHwCch.d.ts +0 -248
  119. package/dist/ui/index.css +0 -1290
  120. package/dist/ui/index.css.map +0 -1
  121. package/dist/ui/index.js.map +0 -1
  122. package/dist/ui/styles.css.map +0 -1
  123. package/dist/ui/styles.js.map +0 -1
@@ -1,1335 +1,2984 @@
1
- import {
2
- defaultMarkdownConfig,
3
- resolveConfig
4
- } from "../chunk-NBRHGTR2.js";
5
- import {
6
- generateApiDocs
7
- } from "../chunk-PGHUPTGL.js";
8
-
9
- // src/vite/plugin.ts
10
- import { reactRouter } from "@react-router/dev/vite";
11
- import mdx from "@mdx-js/rollup";
1
+ import { defaultMarkdownConfig, resolveConfig } from "../config/index.js";
2
+ import { n as generateApiDocs } from "../generator-DPtRXxM_.js";
3
+ import path from "node:path";
4
+ import matter from "gray-matter";
5
+ import rehypeStringify from "rehype-stringify";
12
6
  import remarkFrontmatter from "remark-frontmatter";
13
- import remarkMdxFrontmatter from "remark-mdx-frontmatter";
14
7
  import remarkGfm from "remark-gfm";
15
- import rehypeShiki from "@shikijs/rehype";
16
-
17
- // src/markdown/shiki.ts
18
- import {
19
- createHighlighter
20
- } from "shiki";
8
+ import remarkParse from "remark-parse";
9
+ import remarkRehype from "remark-rehype";
10
+ import { unified } from "unified";
21
11
  import { visit } from "unist-util-visit";
22
- var DEFAULT_THEMES = {
23
- light: "github-light-default",
24
- dark: "github-dark-default"
12
+ import { createHighlighter } from "shiki";
13
+ import fs from "node:fs/promises";
14
+ import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
15
+ import fsSync from "node:fs";
16
+ import { execSync } from "node:child_process";
17
+ import mdx from "@mdx-js/rollup";
18
+ import { reactRouter } from "@react-router/dev/vite";
19
+ import rehypeShiki from "@shikijs/rehype";
20
+ import remarkMdxFrontmatter from "remark-mdx-frontmatter";
21
+ //#region src/markdown/links.ts
22
+ /**
23
+ * Rehype plugin that rewrites internal links to include the basePath.
24
+ * This is needed for static sites deployed to subpaths (e.g., GitHub Pages).
25
+ */
26
+ function rehypeLinks(options) {
27
+ const { basePath } = options;
28
+ const normalizedBase = basePath === "/" ? "" : basePath.replace(/\/$/, "");
29
+ return (tree) => {
30
+ if (!normalizedBase) return;
31
+ visit(tree, "element", (node) => {
32
+ if (node.tagName !== "a") return;
33
+ const href = node.properties.href;
34
+ if (typeof href === "string" && href.startsWith("/") && !href.startsWith("//") && !href.startsWith(normalizedBase)) node.properties.href = normalizedBase + href;
35
+ });
36
+ };
37
+ }
38
+ //#endregion
39
+ //#region src/markdown/shiki-theme.ts
40
+ /** Default Ardo themes used when no config is provided */
41
+ const DEFAULT_THEMES = {
42
+ light: "github-light-default",
43
+ dark: "github-dark-default"
25
44
  };
26
- var cachedHighlighter;
27
- async function highlightCode(code, language, options) {
28
- const themeConfig = options?.theme ?? DEFAULT_THEMES;
29
- if (!cachedHighlighter) {
30
- cachedHighlighter = await createShikiHighlighter({
31
- theme: themeConfig,
32
- lineNumbers: false,
33
- anchor: false,
34
- toc: { level: [2, 3] }
35
- });
36
- }
37
- if (typeof themeConfig === "string") {
38
- return cachedHighlighter.codeToHtml(code, { lang: language, theme: themeConfig });
39
- }
40
- return cachedHighlighter.codeToHtml(code, {
41
- lang: language,
42
- themes: { light: themeConfig.light, dark: themeConfig.dark },
43
- defaultColor: false
44
- });
45
+ function resolveThemeConfig(theme) {
46
+ return theme ?? DEFAULT_THEMES;
45
47
  }
46
- async function createShikiHighlighter(config) {
47
- const themeConfig = config.theme;
48
- const themes = typeof themeConfig === "string" ? [themeConfig] : [themeConfig.light, themeConfig.dark];
49
- const highlighter = await createHighlighter({
50
- themes,
51
- langs: [
52
- // Web fundamentals
53
- "javascript",
54
- "typescript",
55
- "jsx",
56
- "tsx",
57
- "html",
58
- "css",
59
- "scss",
60
- // Data & config formats
61
- "json",
62
- "jsonc",
63
- "yaml",
64
- "toml",
65
- "xml",
66
- "graphql",
67
- // Markdown & docs
68
- "markdown",
69
- "mdx",
70
- // Shell & DevOps
71
- "bash",
72
- "shell",
73
- "dockerfile",
74
- // General purpose
75
- "python",
76
- "rust",
77
- "go",
78
- "sql",
79
- "diff"
80
- ]
81
- });
82
- return highlighter;
48
+ function getBundledThemes(themeConfig) {
49
+ if (themeConfig == null) return [DEFAULT_THEMES.light, DEFAULT_THEMES.dark];
50
+ return typeof themeConfig === "string" ? [themeConfig] : [themeConfig.light, themeConfig.dark];
83
51
  }
84
- function rehypeShikiFromHighlighter(options) {
85
- const { highlighter, config } = options;
86
- const themeConfig = config.theme;
87
- return function(tree) {
88
- visit(tree, "element", (node, index, parent) => {
89
- if (node.tagName !== "pre" || !node.children[0] || node.children[0].tagName !== "code") {
90
- return;
91
- }
92
- const codeNode = node.children[0];
93
- const className = codeNode.properties?.className || [];
94
- const langClass = className.find((c) => c.startsWith("language-"));
95
- const lang = langClass ? langClass.replace("language-", "") : "text";
96
- const codeContent = getTextContent(codeNode);
97
- if (!codeContent.trim()) {
98
- return;
99
- }
100
- try {
101
- let html;
102
- if (typeof themeConfig === "string") {
103
- html = highlighter.codeToHtml(codeContent, {
104
- lang,
105
- theme: themeConfig
106
- });
107
- } else {
108
- html = highlighter.codeToHtml(codeContent, {
109
- lang,
110
- themes: {
111
- light: themeConfig.light,
112
- dark: themeConfig.dark
113
- },
114
- defaultColor: false
115
- });
116
- }
117
- const metaString = codeNode.properties?.metastring || "";
118
- const lineNumbers = config.lineNumbers || metaString.includes("showLineNumbers");
119
- const highlightLines = parseHighlightLines(metaString);
120
- const title = parseTitle(metaString);
121
- const wrapperHtml = buildCodeBlockHtml(html, {
122
- lang,
123
- lineNumbers,
124
- highlightLines,
125
- title
126
- });
127
- if (parent && typeof index === "number") {
128
- const newNode = {
129
- type: "element",
130
- tagName: "div",
131
- properties: {
132
- className: ["ardo-code-block"],
133
- "data-lang": lang
134
- },
135
- children: [
136
- {
137
- type: "raw",
138
- value: wrapperHtml
139
- }
140
- ]
141
- };
142
- parent.children[index] = newNode;
143
- }
144
- } catch {
145
- }
146
- });
147
- };
52
+ function highlightWithTheme(params) {
53
+ const { code, highlighter, language, themeConfig } = params;
54
+ const resolved = themeConfig ?? DEFAULT_THEMES;
55
+ if (typeof resolved === "string") return highlighter.codeToHtml(code, {
56
+ lang: language,
57
+ theme: resolved
58
+ });
59
+ return highlighter.codeToHtml(code, {
60
+ defaultColor: false,
61
+ lang: language,
62
+ themes: {
63
+ dark: resolved.dark,
64
+ light: resolved.light
65
+ }
66
+ });
148
67
  }
149
- function getTextContent(node) {
150
- if (node.type === "text") {
151
- return node.value;
152
- }
153
- if ("children" in node) {
154
- return node.children.map((child) => getTextContent(child)).join("");
155
- }
156
- return "";
68
+ //#endregion
69
+ //#region src/ui/components/code-block-classes.ts
70
+ const shikiContainerClassName = "ardo-shiki";
71
+ //#endregion
72
+ //#region src/markdown/shiki-html.ts
73
+ function buildCodeBlockHtml(shikiHtml, options) {
74
+ const titleHtml = renderTitle(options.title);
75
+ const codeHtml = renderCodeLines({
76
+ highlightLines: options.highlightLines,
77
+ lineNumbers: options.lineNumbers,
78
+ shikiHtml
79
+ });
80
+ const copyButton = renderCopyButton(shikiHtml);
81
+ return `${titleHtml}<div data-lang="${options.lang}">${codeHtml}${copyButton}</div>`;
157
82
  }
158
- function parseHighlightLines(meta) {
159
- const match = meta.match(/\{([\d,-]+)\}/);
160
- if (!match) return [];
161
- const ranges = match[1].split(",");
162
- const lines = [];
163
- for (const range of ranges) {
164
- if (range.includes("-")) {
165
- const [start, end] = range.split("-").map(Number);
166
- for (let i = start; i <= end; i++) {
167
- lines.push(i);
168
- }
169
- } else {
170
- lines.push(Number(range));
171
- }
172
- }
173
- return lines;
83
+ function renderTitle(title) {
84
+ if (title == null || title.length === 0) return "";
85
+ return `<div data-title>${escapeHtml(title)}</div>`;
174
86
  }
175
- function parseTitle(meta) {
176
- const match = meta.match(/title="([^"]+)"/);
177
- return match ? match[1] : void 0;
87
+ function renderCodeLines(params) {
88
+ const { highlightLines, lineNumbers, shikiHtml } = params;
89
+ if (!lineNumbers && highlightLines.length === 0) return shikiHtml;
90
+ return shikiHtml.split("\n").map((lineHtml, index) => renderSingleCodeLine({
91
+ highlightLines,
92
+ lineHtml,
93
+ lineNumber: index + 1,
94
+ lineNumbers
95
+ })).join("\n");
178
96
  }
179
- function buildCodeBlockHtml(shikiHtml, options) {
180
- const { lang, lineNumbers, highlightLines, title } = options;
181
- let html = "";
182
- if (title) {
183
- html += `<div class="ardo-code-title">${escapeHtml(title)}</div>`;
184
- }
185
- html += `<div class="ardo-code-wrapper" data-lang="${lang}">`;
186
- if (lineNumbers || highlightLines.length > 0) {
187
- const lines = shikiHtml.split("\n");
188
- const processedHtml = lines.map((line, i) => {
189
- const lineNum = i + 1;
190
- const isHighlighted = highlightLines.includes(lineNum);
191
- const classes = ["ardo-code-line"];
192
- if (isHighlighted) classes.push("highlighted");
193
- let prefix = "";
194
- if (lineNumbers) {
195
- prefix = `<span class="ardo-line-number">${lineNum}</span>`;
196
- }
197
- return `<span class="${classes.join(" ")}">${prefix}${line}</span>`;
198
- }).join("\n");
199
- html += processedHtml;
200
- } else {
201
- html += shikiHtml;
202
- }
203
- html += `<button class="ardo-copy-button" data-code="${encodeURIComponent(extractCodeFromHtml(shikiHtml))}">
204
- <span class="ardo-copy-icon">Copy</span>
205
- <span class="ardo-copied-icon" style="display:none">Copied!</span>
97
+ function renderSingleCodeLine(params) {
98
+ const { highlightLines, lineHtml, lineNumber, lineNumbers } = params;
99
+ return `<span class="${highlightLines.includes(lineNumber) ? "line highlighted" : "line"}"${lineNumbers ? ` data-ln="${lineNumber}"` : ""}>${lineHtml}</span>`;
100
+ }
101
+ function renderCopyButton(shikiHtml) {
102
+ return `<button data-code="${encodeURIComponent(extractCodeFromHtml(shikiHtml))}">
103
+ <span>Copy</span>
104
+ <span style="display:none">Copied!</span>
206
105
  </button>`;
207
- html += "</div>";
208
- return html;
209
106
  }
210
107
  function extractCodeFromHtml(html) {
211
- return html.replace(/<[^>]+>/g, "").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
108
+ return decodeCommonEntities(stripTags(html));
109
+ }
110
+ function stripTags(html) {
111
+ let result = "";
112
+ let inTag = false;
113
+ for (const char of html) {
114
+ if (char === "<") {
115
+ inTag = true;
116
+ continue;
117
+ }
118
+ if (inTag && char === ">") {
119
+ inTag = false;
120
+ continue;
121
+ }
122
+ if (!inTag) result += char;
123
+ }
124
+ return result;
125
+ }
126
+ function decodeCommonEntities(text) {
127
+ return text.replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&").replaceAll("&quot;", "\"").replaceAll("&#39;", "'");
212
128
  }
213
129
  function escapeHtml(text) {
214
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
130
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
131
+ }
132
+ //#endregion
133
+ //#region src/markdown/shiki-meta.ts
134
+ function parseHighlightLines(meta) {
135
+ const match = /\{([\d,-]+)\}/u.exec(meta);
136
+ if (match?.[1] == null) return [];
137
+ const lines = [];
138
+ for (const range of match[1].split(",")) appendRangeLines(lines, range);
139
+ return lines;
140
+ }
141
+ function appendRangeLines(lines, range) {
142
+ if (range.includes("-")) {
143
+ appendRangeSet(lines, range);
144
+ return;
145
+ }
146
+ const lineNumber = Number(range);
147
+ if (Number.isFinite(lineNumber)) lines.push(lineNumber);
148
+ }
149
+ function appendRangeSet(lines, range) {
150
+ const [start, end] = range.split("-").map(Number);
151
+ if (!Number.isFinite(start) || !Number.isFinite(end)) return;
152
+ for (let line = start; line <= end; line++) lines.push(line);
153
+ }
154
+ function parseTitle(meta) {
155
+ return /title="([^"]+)"/u.exec(meta)?.[1];
156
+ }
157
+ function parseLabel(meta) {
158
+ const start = meta.indexOf("[");
159
+ if (start === -1) return;
160
+ const end = meta.indexOf("]", start + 1);
161
+ if (end === -1) return;
162
+ const label = meta.slice(start + 1, end).trim();
163
+ return label.length > 0 ? label : void 0;
164
+ }
165
+ //#endregion
166
+ //#region src/markdown/shiki-rehype.ts
167
+ function rehypeShikiFromHighlighter(options) {
168
+ const themeConfig = resolveThemeConfig(options.config.theme);
169
+ return function(tree) {
170
+ visit(tree, "element", (node, index, parent) => {
171
+ transformCodeNode({
172
+ config: options.config,
173
+ highlighter: options.highlighter,
174
+ index,
175
+ node,
176
+ parent,
177
+ themeConfig
178
+ });
179
+ });
180
+ };
181
+ }
182
+ function transformCodeNode(context) {
183
+ const codeNode = getCodeNode(context.node);
184
+ if (codeNode == null) return;
185
+ const codeContent = getTextContent(codeNode);
186
+ if (codeContent.trim().length === 0) return;
187
+ const metaString = getMetaString(codeNode);
188
+ const innerHtml = tryRenderHighlightedHtml({
189
+ codeContent,
190
+ context,
191
+ language: getLanguage(codeNode),
192
+ metaString
193
+ });
194
+ if (innerHtml == null) return;
195
+ replaceNodeWithShikiContainer(context.parent, context.index, innerHtml);
196
+ }
197
+ function tryRenderHighlightedHtml(params) {
198
+ const { codeContent, context, language, metaString } = params;
199
+ try {
200
+ return buildCodeBlockHtml(highlightWithTheme({
201
+ code: codeContent,
202
+ highlighter: context.highlighter,
203
+ language,
204
+ themeConfig: context.themeConfig
205
+ }), {
206
+ highlightLines: parseHighlightLines(metaString),
207
+ lang: language,
208
+ lineNumbers: (context.config.lineNumbers ?? false) || metaString.includes("showLineNumbers"),
209
+ title: parseTitle(metaString)
210
+ });
211
+ } catch {
212
+ return null;
213
+ }
214
+ }
215
+ function getCodeNode(node) {
216
+ if (node.tagName !== "pre") return null;
217
+ const firstChild = node.children.at(0);
218
+ if (!isElementNode(firstChild) || firstChild.tagName !== "code") return null;
219
+ return firstChild;
220
+ }
221
+ function isElementNode(node) {
222
+ return isRecord$5(node) && node.type === "element";
223
+ }
224
+ function getMetaString(codeNode) {
225
+ const properties = toRecord(codeNode.properties);
226
+ return typeof properties.metastring === "string" ? properties.metastring : "";
227
+ }
228
+ function getLanguage(codeNode) {
229
+ const languageClass = toClassNameList(toRecord(codeNode.properties).className).find((className) => className.startsWith("language-"));
230
+ return languageClass == null ? "text" : languageClass.replace("language-", "");
231
+ }
232
+ function toClassNameList(className) {
233
+ if (Array.isArray(className)) return className.filter((entry) => typeof entry === "string");
234
+ if (typeof className === "string") return [className];
235
+ return [];
236
+ }
237
+ function getTextContent(node) {
238
+ if (node.type === "text") return node.value;
239
+ const parts = [];
240
+ for (const child of node.children) if (child.type === "text" || child.type === "element") parts.push(getTextContent(child));
241
+ return parts.join("");
242
+ }
243
+ function replaceNodeWithShikiContainer(parent, index, innerHtml) {
244
+ if (index == null || !hasChildrenArray(parent)) return;
245
+ parent.children[index] = {
246
+ type: "element",
247
+ tagName: "div",
248
+ properties: { className: [shikiContainerClassName] },
249
+ children: [{
250
+ type: "raw",
251
+ value: innerHtml
252
+ }]
253
+ };
254
+ }
255
+ function hasChildrenArray(value) {
256
+ return isRecord$5(value) && Array.isArray(value.children);
257
+ }
258
+ function toRecord(value) {
259
+ return isRecord$5(value) ? value : {};
260
+ }
261
+ function isRecord$5(value) {
262
+ return value != null && typeof value === "object";
215
263
  }
264
+ //#endregion
265
+ //#region src/markdown/shiki-transformer.ts
266
+ /**
267
+ * Remark plugin that extracts code fence meta info and stores it as HAST
268
+ * data attributes before MDX compilation can corrupt it.
269
+ */
216
270
  function remarkCodeMeta() {
217
- return function(tree) {
218
- visit(tree, "code", (node) => {
219
- if (!node.meta) return;
220
- const meta = node.meta;
221
- const data = node.data || (node.data = {});
222
- const hProperties = data.hProperties || {};
223
- hProperties.metastring = meta;
224
- data.hProperties = hProperties;
225
- node.meta = null;
226
- });
227
- };
271
+ return function(tree) {
272
+ visit(tree, "code", (node) => {
273
+ const meta = node.meta;
274
+ if (meta == null || meta.length === 0) return;
275
+ const data = ensureNodeData(node);
276
+ const hProperties = ensureRecord(data.hProperties);
277
+ hProperties.metastring = meta;
278
+ data.hProperties = hProperties;
279
+ node.meta = null;
280
+ });
281
+ };
228
282
  }
283
+ function ensureNodeData(node) {
284
+ node.data ??= {};
285
+ return node.data;
286
+ }
287
+ function ensureRecord(value) {
288
+ return isRecord$4(value) ? value : {};
289
+ }
290
+ function isRecord$4(value) {
291
+ return value != null && typeof value === "object";
292
+ }
293
+ /**
294
+ * Shiki transformer that adds line highlighting, line numbers, and title
295
+ * attributes to code blocks in the MDX pipeline.
296
+ */
229
297
  function ardoLineTransformer(options = {}) {
230
- let highlightLines = [];
231
- let showLineNumbers = false;
232
- let metaRaw = "";
233
- return {
234
- name: "ardo:lines",
235
- // preprocess runs BEFORE line() hooks, so state is ready for line()
236
- preprocess(_code, shikiOptions) {
237
- metaRaw = shikiOptions.meta?.__raw || "";
238
- highlightLines = parseHighlightLines(metaRaw);
239
- showLineNumbers = options.globalLineNumbers || metaRaw.includes("showLineNumbers");
240
- },
241
- // pre runs AFTER line() used only for node property modifications
242
- pre(node) {
243
- node.properties = node.properties || {};
244
- const title = parseTitle(metaRaw);
245
- if (title) {
246
- node.properties["data-title"] = title;
247
- }
248
- const labelMatch = metaRaw.match(/\[([^\]]+)\]/);
249
- if (labelMatch) {
250
- node.properties["data-label"] = labelMatch[1];
251
- }
252
- },
253
- line(node, line) {
254
- const currentClass = node.properties?.class || "";
255
- const classes = currentClass ? currentClass.split(" ") : [];
256
- classes.push("ardo-code-line");
257
- if (highlightLines.includes(line)) {
258
- classes.push("highlighted");
259
- }
260
- node.properties = node.properties || {};
261
- node.properties.class = classes.join(" ");
262
- if (showLineNumbers) {
263
- node.children.unshift({
264
- type: "element",
265
- tagName: "span",
266
- properties: { class: "ardo-line-number" },
267
- children: [{ type: "text", value: String(line) }]
268
- });
269
- }
270
- }
271
- };
298
+ const state = {
299
+ highlightLines: [],
300
+ metaRaw: "",
301
+ showLineNumbers: false
302
+ };
303
+ return {
304
+ name: "ardo:lines",
305
+ preprocess(_code, shikiOptions) {
306
+ const metaRaw = getMetaRaw(shikiOptions.meta);
307
+ state.metaRaw = metaRaw;
308
+ state.highlightLines = parseHighlightLines(metaRaw);
309
+ state.showLineNumbers = (options.globalLineNumbers ?? false) || metaRaw.includes("showLineNumbers");
310
+ },
311
+ pre(node) {
312
+ const properties = ensureNodeProperties(node);
313
+ applyTitleProperty(properties, state.metaRaw);
314
+ applyLabelProperty(properties, state.metaRaw);
315
+ },
316
+ line(node, line) {
317
+ const properties = ensureNodeProperties(node);
318
+ applyHighlightedLineClass(properties, state.highlightLines, line);
319
+ if (state.showLineNumbers) properties["data-ln"] = String(line);
320
+ }
321
+ };
272
322
  }
273
-
274
- // src/vite/plugin.ts
275
- import fs2 from "fs/promises";
276
- import fsSync2 from "fs";
277
- import path2 from "path";
278
- import { execSync } from "child_process";
279
- import matter from "gray-matter";
280
-
281
- // src/vite/routes-plugin.ts
282
- import fs from "fs/promises";
283
- import fsSync from "fs";
284
- import path from "path";
285
- function ardoRoutesPlugin(options = {}) {
286
- let routesDir;
287
- let appDir;
288
- let routesFilePath;
289
- function scanRoutesSync(dir, rootDir) {
290
- const routes = [];
291
- try {
292
- const entries = fsSync.readdirSync(dir, { withFileTypes: true });
293
- for (const entry of entries) {
294
- const fullPath = path.join(dir, entry.name);
295
- if (entry.isDirectory()) {
296
- const children = scanRoutesSync(fullPath, rootDir);
297
- routes.push(...children);
298
- } else if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md") || entry.name.endsWith(".tsx")) {
299
- if (entry.name === "root.tsx" || entry.name.startsWith("_")) {
300
- continue;
301
- }
302
- const relativePath = path.relative(rootDir, fullPath);
303
- const ext = entry.name.endsWith(".mdx") ? ".mdx" : entry.name.endsWith(".md") ? ".md" : ".tsx";
304
- const baseName = entry.name.replace(ext, "");
305
- let urlPath;
306
- if (baseName === "index" || baseName === "home") {
307
- const parentDir = path.dirname(relativePath);
308
- urlPath = parentDir === "." ? "/" : "/" + parentDir.replace(/\\/g, "/");
309
- } else {
310
- urlPath = "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
311
- }
312
- urlPath = urlPath.replace(/\$(\w+)/g, ":$1");
313
- routes.push({
314
- path: urlPath,
315
- file: "routes/" + relativePath.replace(/\\/g, "/"),
316
- isIndex: baseName === "index" || baseName === "home"
317
- });
318
- }
319
- }
320
- } catch {
321
- }
322
- return routes;
323
- }
324
- function generateRoutesFile(routes) {
325
- const sortedRoutes = [...routes].sort((a, b) => {
326
- if (a.path === "/" && b.path !== "/") return -1;
327
- if (b.path === "/" && a.path !== "/") return 1;
328
- if (a.isIndex && !b.isIndex) return -1;
329
- if (b.isIndex && !a.isIndex) return 1;
330
- return a.path.localeCompare(b.path);
331
- });
332
- const entries = sortedRoutes.map((r) => {
333
- if (r.path === "/") {
334
- return ` index("${r.file}"),`;
335
- }
336
- const routePath = r.path.substring(1);
337
- return ` route("${routePath}", "${r.file}"),`;
338
- });
339
- return `// AUTO-GENERATED by Ardo - Do not edit manually
340
-
341
- import { type RouteConfig, route, index } from "@react-router/dev/routes"
342
-
343
- export default [
344
- ${entries.join("\n")}
345
- ] satisfies RouteConfig
346
- `;
347
- }
348
- function writeRoutesFileSync() {
349
- const routes = scanRoutesSync(routesDir, routesDir);
350
- if (routes.length === 0) {
351
- return;
352
- }
353
- const content = generateRoutesFile(routes);
354
- try {
355
- const existing = fsSync.readFileSync(routesFilePath, "utf-8");
356
- if (existing === content) {
357
- return;
358
- }
359
- } catch {
360
- }
361
- fsSync.mkdirSync(appDir, { recursive: true });
362
- fsSync.writeFileSync(routesFilePath, content, "utf-8");
363
- console.log(`[ardo] Generated routes.ts with ${routes.length} routes`);
364
- }
365
- async function writeRoutesFile() {
366
- const routes = scanRoutesSync(routesDir, routesDir);
367
- if (routes.length === 0) {
368
- return;
369
- }
370
- const content = generateRoutesFile(routes);
371
- try {
372
- const existing = await fs.readFile(routesFilePath, "utf-8");
373
- if (existing === content) {
374
- return;
375
- }
376
- } catch {
377
- }
378
- await fs.mkdir(appDir, { recursive: true });
379
- await fs.writeFile(routesFilePath, content, "utf-8");
380
- }
381
- return {
382
- name: "ardo:routes",
383
- enforce: "pre",
384
- config(userConfig) {
385
- const root = userConfig.root || process.cwd();
386
- appDir = path.join(root, "app");
387
- routesDir = options.routesDir || path.join(appDir, "routes");
388
- routesFilePath = path.join(appDir, "routes.ts");
389
- try {
390
- writeRoutesFileSync();
391
- } catch (err) {
392
- console.warn("[ardo] Could not generate routes.ts in config phase:", err);
393
- }
394
- },
395
- configResolved(config) {
396
- if (!appDir) {
397
- appDir = path.join(config.root, "app");
398
- routesDir = options.routesDir || path.join(appDir, "routes");
399
- routesFilePath = path.join(appDir, "routes.ts");
400
- }
401
- },
402
- async buildStart() {
403
- await writeRoutesFile();
404
- },
405
- configureServer(server) {
406
- server.watcher.add(routesDir);
407
- const handleChange = async (changedPath) => {
408
- if (changedPath.startsWith(routesDir) && (changedPath.endsWith(".mdx") || changedPath.endsWith(".md") || changedPath.endsWith(".tsx"))) {
409
- await writeRoutesFile();
410
- }
411
- };
412
- server.watcher.on("add", handleChange);
413
- server.watcher.on("unlink", handleChange);
414
- }
415
- };
323
+ function getMetaRaw(meta) {
324
+ if (!isRecord$4(meta)) return "";
325
+ const raw = meta.__raw;
326
+ return typeof raw === "string" ? raw : "";
416
327
  }
417
-
418
- // src/vite/plugin.ts
419
- import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
420
-
421
- // src/vite/codeblock-plugin.ts
328
+ function ensureNodeProperties(node) {
329
+ node.properties ??= {};
330
+ return node.properties;
331
+ }
332
+ function applyTitleProperty(properties, metaRaw) {
333
+ const title = parseTitle(metaRaw);
334
+ if (title != null && title.length > 0) properties["data-title"] = title;
335
+ }
336
+ function applyLabelProperty(properties, metaRaw) {
337
+ const label = parseLabel(metaRaw);
338
+ if (label != null && label.length > 0) properties["data-label"] = label;
339
+ }
340
+ function applyHighlightedLineClass(properties, highlightLines, line) {
341
+ if (!highlightLines.includes(line)) return;
342
+ const currentClass = typeof properties.class === "string" ? properties.class : "";
343
+ properties.class = currentClass.length > 0 ? `${currentClass} highlighted` : "highlighted";
344
+ }
345
+ //#endregion
346
+ //#region src/markdown/shiki.ts
347
+ let cachedHighlighterPromise;
348
+ /**
349
+ * Highlights code using Shiki with Ardo's default themes.
350
+ * Creates and caches a highlighter instance for reuse.
351
+ */
352
+ async function highlightCode(code, language, options) {
353
+ const themeConfig = resolveThemeConfig(options?.theme);
354
+ return highlightWithTheme({
355
+ code,
356
+ highlighter: await getCachedHighlighter(themeConfig),
357
+ language,
358
+ themeConfig
359
+ });
360
+ }
361
+ async function getCachedHighlighter(themeConfig) {
362
+ cachedHighlighterPromise ??= createShikiHighlighter({
363
+ anchor: false,
364
+ lineNumbers: false,
365
+ theme: themeConfig,
366
+ toc: { level: [2, 3] }
367
+ });
368
+ return cachedHighlighterPromise;
369
+ }
370
+ async function createShikiHighlighter(config) {
371
+ return createHighlighter({
372
+ themes: getBundledThemes(resolveThemeConfig(config.theme)),
373
+ langs: [
374
+ "javascript",
375
+ "typescript",
376
+ "jsx",
377
+ "tsx",
378
+ "html",
379
+ "css",
380
+ "scss",
381
+ "json",
382
+ "jsonc",
383
+ "yaml",
384
+ "toml",
385
+ "xml",
386
+ "graphql",
387
+ "markdown",
388
+ "mdx",
389
+ "bash",
390
+ "shell",
391
+ "dockerfile",
392
+ "python",
393
+ "rust",
394
+ "go",
395
+ "sql",
396
+ "diff"
397
+ ]
398
+ });
399
+ }
400
+ //#endregion
401
+ //#region src/markdown/toc.ts
402
+ function remarkExtractToc(options) {
403
+ const { tocExtraction, levels } = options;
404
+ const [minLevel, maxLevel] = levels;
405
+ return function(tree) {
406
+ const headings = [];
407
+ let headingIndex = 0;
408
+ visit(tree, "heading", (node) => {
409
+ if (node.depth < minLevel || node.depth > maxLevel) return;
410
+ const text = getHeadingText$1(node);
411
+ const slug = slugify$1(text);
412
+ const id = slug === "" ? `heading-${headingIndex}` : slug;
413
+ headingIndex++;
414
+ headings.push({
415
+ text,
416
+ level: node.depth,
417
+ id
418
+ });
419
+ const hProperties = ensureHProperties$1(node);
420
+ hProperties.id = id;
421
+ });
422
+ tocExtraction.toc = buildTocTree(headings);
423
+ };
424
+ }
425
+ function getHeadingText$1(node) {
426
+ const textParts = [];
427
+ function extractText(child) {
428
+ if (!isRecord$3(child)) return;
429
+ if (child.type === "text") textParts.push(typeof child.value === "string" ? child.value : "");
430
+ else if (child.type === "inlineCode") textParts.push(typeof child.value === "string" ? child.value : "");
431
+ else if (Array.isArray(child.children)) child.children.forEach((nestedChild) => {
432
+ extractText(nestedChild);
433
+ });
434
+ }
435
+ node.children.forEach((child) => {
436
+ extractText(child);
437
+ });
438
+ return textParts.join("");
439
+ }
440
+ function slugify$1(text) {
441
+ let slug = text.toLowerCase().trim().replaceAll(/[^\s\w-]/g, "").replaceAll(/[\s_]/g, "-");
442
+ while (slug.includes("--")) slug = slug.replaceAll("--", "-");
443
+ if (slug.startsWith("-")) slug = slug.slice(1);
444
+ if (slug.endsWith("-")) slug = slug.slice(0, -1);
445
+ return slug;
446
+ }
447
+ function popStackUntilParent(stack, level) {
448
+ while (stack.length > 0) {
449
+ const last = stack.at(-1);
450
+ if (last === void 0 || last.level < level) break;
451
+ stack.pop();
452
+ }
453
+ }
454
+ function insertIntoTree(result, stack, item) {
455
+ const parent = stack.at(-1)?.item;
456
+ if (parent === void 0) result.push(item);
457
+ else {
458
+ parent.children ??= [];
459
+ parent.children.push(item);
460
+ }
461
+ }
462
+ function buildTocTree(headings) {
463
+ const result = [];
464
+ const stack = [];
465
+ for (const heading of headings) {
466
+ const item = {
467
+ id: heading.id,
468
+ text: heading.text,
469
+ level: heading.level
470
+ };
471
+ popStackUntilParent(stack, heading.level);
472
+ insertIntoTree(result, stack, item);
473
+ stack.push({
474
+ item,
475
+ level: heading.level
476
+ });
477
+ }
478
+ return result;
479
+ }
480
+ function ensureHProperties$1(node) {
481
+ const data = node.data ?? {};
482
+ node.data = data;
483
+ if (!isRecord$3(data.hProperties)) data.hProperties = {};
484
+ return data.hProperties;
485
+ }
486
+ function isRecord$3(value) {
487
+ return value != null && typeof value === "object";
488
+ }
489
+ //#endregion
490
+ //#region src/markdown/pipeline.ts
491
+ async function transformMarkdown(content, config, options = {}) {
492
+ const { data, content: markdownContent } = matter(content);
493
+ const frontmatterData = data;
494
+ const { basePath = "/", highlighter: providedHighlighter } = options;
495
+ const tocExtraction = { toc: [] };
496
+ const highlighter = providedHighlighter ?? await createShikiHighlighter(config);
497
+ const processor = unified().use(remarkParse).use(remarkFrontmatter, ["yaml"]).use(remarkGfm).use(remarkExtractToc, {
498
+ tocExtraction,
499
+ levels: config.toc?.level ?? [2, 3]
500
+ }).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeShikiFromHighlighter, {
501
+ highlighter,
502
+ config
503
+ }).use(rehypeLinks, { basePath }).use(rehypeStringify, { allowDangerousHtml: true });
504
+ if (config.remarkPlugins) processor.use(config.remarkPlugins);
505
+ if (config.rehypePlugins) processor.use(config.rehypePlugins);
506
+ const result = await processor.process(markdownContent);
507
+ return {
508
+ html: String(result),
509
+ frontmatter: readPageFrontmatter(frontmatterData),
510
+ toc: tocExtraction.toc
511
+ };
512
+ }
513
+ async function transformMarkdownToReact(content, config) {
514
+ return transformMarkdown(content, config);
515
+ }
516
+ function readPageFrontmatter(data) {
517
+ return isRecord$2(data) ? data : {};
518
+ }
519
+ function isRecord$2(value) {
520
+ return value != null && typeof value === "object";
521
+ }
522
+ //#endregion
523
+ //#region src/runtime/loader.ts
524
+ async function findFile(contentDir, slug) {
525
+ const possiblePaths = [path.join(contentDir, `${slug}.md`), path.join(contentDir, slug, "index.md")];
526
+ for (const tryPath of possiblePaths) try {
527
+ return {
528
+ filePath: tryPath,
529
+ fileContent: await fs.readFile(tryPath, "utf8")
530
+ };
531
+ } catch {
532
+ continue;
533
+ }
534
+ return null;
535
+ }
536
+ async function getLastUpdated(filePath) {
537
+ try {
538
+ return (await fs.stat(filePath)).mtimeMs;
539
+ } catch {
540
+ return;
541
+ }
542
+ }
543
+ async function loadDoc(options) {
544
+ const { slug, contentDir, config } = options;
545
+ const found = await findFile(contentDir, slug);
546
+ if (found === null) return null;
547
+ const result = await transformMarkdown(found.fileContent, config.markdown);
548
+ return {
549
+ content: result.html,
550
+ frontmatter: result.frontmatter,
551
+ toc: result.toc,
552
+ filePath: found.filePath,
553
+ relativePath: path.relative(contentDir, found.filePath),
554
+ lastUpdated: await getLastUpdated(found.filePath)
555
+ };
556
+ }
557
+ async function loadAllDocs(contentDir, config) {
558
+ const docs = [];
559
+ async function scanDir(dir) {
560
+ const entries = await fs.readdir(dir, { withFileTypes: true });
561
+ for (const entry of entries) {
562
+ const fullPath = path.join(dir, entry.name);
563
+ if (entry.isDirectory()) await scanDir(fullPath);
564
+ else if (entry.name.endsWith(".md")) {
565
+ const result = await transformMarkdown(await fs.readFile(fullPath, "utf8"), config.markdown);
566
+ const relativePath = path.relative(contentDir, fullPath);
567
+ let lastUpdated;
568
+ try {
569
+ lastUpdated = (await fs.stat(fullPath)).mtimeMs;
570
+ } catch {}
571
+ docs.push({
572
+ title: result.frontmatter.title ?? formatTitle$3(entry.name.replace(/\.md$/, "")),
573
+ description: result.frontmatter.description,
574
+ frontmatter: result.frontmatter,
575
+ content: result.html,
576
+ toc: result.toc,
577
+ filePath: fullPath,
578
+ relativePath,
579
+ lastUpdated
580
+ });
581
+ }
582
+ }
583
+ }
584
+ await scanDir(contentDir);
585
+ return docs;
586
+ }
587
+ function formatTitle$3(name) {
588
+ return name.replaceAll(/[_-]/g, " ").replaceAll(/\b\w/g, (c) => c.toUpperCase());
589
+ }
590
+ function getSlugFromPath(relativePath) {
591
+ return relativePath.replace(/\.md$/, "").replace(/\/index$/, "").replaceAll("\\", "/");
592
+ }
593
+ function getPageDataForRoute(docs, slug) {
594
+ return docs.find((doc) => {
595
+ const docSlug = getSlugFromPath(doc.relativePath);
596
+ return docSlug === slug || docSlug === `${slug}/index`;
597
+ });
598
+ }
599
+ //#endregion
600
+ //#region src/runtime/sidebar.ts
601
+ async function generateSidebar(options) {
602
+ const { contentDir } = options;
603
+ return scanDirectoryForSidebar(contentDir, contentDir);
604
+ }
605
+ async function scanDirectoryForSidebar(dir, rootDir) {
606
+ const entries = await readDirectoryEntries$1(dir);
607
+ const items = [];
608
+ for (const entry of entries) {
609
+ const sidebarItem = await createSidebarItemFromEntry({
610
+ dir,
611
+ entry,
612
+ rootDir
613
+ });
614
+ if (sidebarItem != null) items.push(sidebarItem);
615
+ }
616
+ sortSidebarItems(items);
617
+ return items.map(({ order: _order, ...item }) => item);
618
+ }
619
+ async function readDirectoryEntries$1(dir) {
620
+ try {
621
+ return await fs.readdir(dir, { withFileTypes: true });
622
+ } catch {
623
+ return [];
624
+ }
625
+ }
626
+ async function createSidebarItemFromEntry(params) {
627
+ const { dir, entry, rootDir } = params;
628
+ if (isIgnoredEntry(entry.name)) return null;
629
+ const fullPath = path.join(dir, entry.name);
630
+ const relativePath = path.relative(rootDir, fullPath);
631
+ if (entry.isDirectory()) return createDirectorySidebarItem(fullPath, relativePath, rootDir);
632
+ if (isMarkdownPage(entry.name)) return createMarkdownSidebarItem(fullPath, relativePath, entry.name);
633
+ return null;
634
+ }
635
+ function isIgnoredEntry(entryName) {
636
+ return entryName.startsWith(".") || entryName.startsWith("_");
637
+ }
638
+ function isMarkdownPage(entryName) {
639
+ return entryName.endsWith(".md") && entryName !== "index.md";
640
+ }
641
+ async function createDirectorySidebarItem(fullPath, relativePath, rootDir) {
642
+ const children = await scanDirectoryForSidebar(fullPath, rootDir);
643
+ if (children.length === 0) return null;
644
+ const metadata = await readDirectoryIndexMetadata(fullPath, relativePath);
645
+ const title = metadata.title ?? formatTitle$2(path.basename(fullPath));
646
+ return {
647
+ collapsed: false,
648
+ items: children,
649
+ link: metadata.link,
650
+ order: metadata.order,
651
+ text: title
652
+ };
653
+ }
654
+ async function readDirectoryIndexMetadata(fullPath, relativePath) {
655
+ const frontmatter = await readFrontmatter$1(path.join(fullPath, "index.md"));
656
+ return {
657
+ link: frontmatter == null ? void 0 : normalizePath(relativePath),
658
+ order: frontmatter?.order,
659
+ title: frontmatter?.title
660
+ };
661
+ }
662
+ async function createMarkdownSidebarItem(fullPath, relativePath, fileName) {
663
+ const frontmatter = await readFrontmatter$1(fullPath);
664
+ if (frontmatter?.sidebar === false) return null;
665
+ const fallbackTitle = formatTitle$2(fileName.replace(/\.md$/u, ""));
666
+ const title = frontmatter?.title ?? fallbackTitle;
667
+ return {
668
+ link: normalizePath(relativePath.replace(/\.md$/u, "")),
669
+ order: frontmatter?.order,
670
+ text: title
671
+ };
672
+ }
673
+ async function readFrontmatter$1(filePath) {
674
+ try {
675
+ return toSidebarFrontmatter(matter(await fs.readFile(filePath, "utf8")).data);
676
+ } catch {
677
+ return null;
678
+ }
679
+ }
680
+ function toSidebarFrontmatter(data) {
681
+ if (!isRecord$1(data)) return {};
682
+ return {
683
+ order: typeof data.order === "number" ? data.order : void 0,
684
+ sidebar: typeof data.sidebar === "boolean" ? data.sidebar : void 0,
685
+ title: typeof data.title === "string" ? data.title : void 0
686
+ };
687
+ }
688
+ function isRecord$1(value) {
689
+ return value != null && typeof value === "object";
690
+ }
691
+ function sortSidebarItems(items) {
692
+ items.sort((left, right) => {
693
+ if (left.order != null && right.order != null) return left.order - right.order;
694
+ if (left.order != null) return -1;
695
+ if (right.order != null) return 1;
696
+ return left.text.localeCompare(right.text);
697
+ });
698
+ }
699
+ function formatTitle$2(name) {
700
+ return name.replace(/^\d+-/u, "").replaceAll(/[_-]/gu, " ").replaceAll(/\b\w/gu, (char) => char.toUpperCase());
701
+ }
702
+ function normalizePath(p) {
703
+ return `/${p.replaceAll("\\", "/").replace(/^\/+/u, "")}`;
704
+ }
705
+ //#endregion
706
+ //#region src/vite/codeblock-scan.ts
707
+ const OPENING_TAG = "<ArdoCodeBlock";
708
+ const CLOSING_TAG = "</ArdoCodeBlock>";
709
+ /**
710
+ * Strips leading/trailing blank lines and removes common leading whitespace.
711
+ * Same logic as the runtime outdent in CodeBlock.tsx.
712
+ */
422
713
  function outdent(text) {
423
- const trimmed = text.replace(/^\n+/, "").replace(/\n\s*$/, "");
424
- const lines = trimmed.split("\n");
425
- const indent = lines.reduce((min, line) => {
426
- if (line.trim().length === 0) return min;
427
- const match = line.match(/^(\s*)/);
428
- return match ? Math.min(min, match[1].length) : min;
429
- }, Infinity);
430
- if (indent === 0 || indent === Infinity) return trimmed;
431
- return lines.map((line) => line.slice(indent)).join("\n");
432
- }
433
- function findSelfClosingCodeBlocks(source) {
434
- const results = [];
435
- const tag = "<CodeBlock";
436
- let searchFrom = 0;
437
- while (true) {
438
- const start = source.indexOf(tag, searchFrom);
439
- if (start === -1) break;
440
- const afterTag = start + tag.length;
441
- if (afterTag >= source.length || !/\s/.test(source[afterTag])) {
442
- searchFrom = afterTag;
443
- continue;
444
- }
445
- let i = afterTag;
446
- let depth = 0;
447
- let inSingle = false;
448
- let inDouble = false;
449
- let inTemplate = false;
450
- let found = false;
451
- while (i < source.length) {
452
- const ch = source[i];
453
- if ((inSingle || inDouble || inTemplate) && ch === "\\") {
454
- i += 2;
455
- continue;
456
- }
457
- if (inSingle) {
458
- if (ch === "'") inSingle = false;
459
- i++;
460
- continue;
461
- }
462
- if (inDouble) {
463
- if (ch === '"') inDouble = false;
464
- i++;
465
- continue;
466
- }
467
- if (inTemplate) {
468
- if (ch === "`") inTemplate = false;
469
- i++;
470
- continue;
471
- }
472
- if (ch === "'") {
473
- inSingle = true;
474
- i++;
475
- continue;
476
- }
477
- if (ch === '"') {
478
- inDouble = true;
479
- i++;
480
- continue;
481
- }
482
- if (ch === "`") {
483
- inTemplate = true;
484
- i++;
485
- continue;
486
- }
487
- if (ch === "{") {
488
- depth++;
489
- i++;
490
- continue;
491
- }
492
- if (ch === "}") {
493
- depth--;
494
- i++;
495
- continue;
496
- }
497
- if (depth === 0 && ch === "/" && i + 1 < source.length && source[i + 1] === ">") {
498
- const fullMatch = source.substring(start, i + 2);
499
- const propsStr = source.substring(afterTag, i).trim();
500
- results.push({ fullMatch, propsStr, index: start });
501
- found = true;
502
- searchFrom = i + 2;
503
- break;
504
- }
505
- if (depth === 0 && ch === ">") {
506
- searchFrom = i + 1;
507
- found = true;
508
- break;
509
- }
510
- i++;
511
- }
512
- if (!found) break;
513
- }
514
- return results;
714
+ const trimmedLines = trimBlankLines(text.split("\n"));
715
+ if (trimmedLines.length === 0) return "";
716
+ const commonIndent = getCommonIndent(trimmedLines);
717
+ if (commonIndent === 0) return trimmedLines.join("\n");
718
+ return trimmedLines.map((line) => stripIndent(line, commonIndent)).join("\n");
719
+ }
720
+ function trimBlankLines(lines) {
721
+ const result = [...lines];
722
+ while (result.length > 0 && result[0].trim().length === 0) result.shift();
723
+ while (result.length > 0 && result.at(-1)?.trim().length === 0) result.pop();
724
+ return result;
725
+ }
726
+ function getCommonIndent(lines) {
727
+ let minIndent = Number.POSITIVE_INFINITY;
728
+ for (const line of lines) {
729
+ if (line.trim().length === 0) continue;
730
+ const indent = getLeadingWhitespaceLength(line);
731
+ if (indent < minIndent) minIndent = indent;
732
+ }
733
+ return Number.isFinite(minIndent) ? minIndent : 0;
734
+ }
735
+ function stripIndent(line, commonIndent) {
736
+ const availableIndent = getLeadingWhitespaceLength(line);
737
+ const removeCount = Math.min(commonIndent, availableIndent);
738
+ return line.slice(removeCount);
739
+ }
740
+ function getLeadingWhitespaceLength(line) {
741
+ let index = 0;
742
+ while (index < line.length) {
743
+ const char = line[index];
744
+ if (char !== " " && char !== " ") break;
745
+ index++;
746
+ }
747
+ return index;
748
+ }
749
+ /**
750
+ * Finds all `<ArdoCodeBlock ... />` and `<ArdoCodeBlock ...>...</ArdoCodeBlock>` tags
751
+ * by scanning for balanced quotes and braces.
752
+ */
753
+ function scanArdoCodeBlocks(source) {
754
+ const blocks = [];
755
+ let cursor = 0;
756
+ while (cursor < source.length) {
757
+ const scanResult = scanNextCodeBlock(source, cursor);
758
+ if (scanResult == null) break;
759
+ const { block, nextCursor } = scanResult;
760
+ if (block != null) blocks.push(block);
761
+ cursor = nextCursor;
762
+ }
763
+ return blocks;
764
+ }
765
+ function scanNextCodeBlock(source, cursor) {
766
+ const start = source.indexOf(OPENING_TAG, cursor);
767
+ if (start === -1) return null;
768
+ const opening = scanOpeningTag(source, start);
769
+ if (opening == null) return {
770
+ block: null,
771
+ nextCursor: start + 14
772
+ };
773
+ const block = createScannedBlock(source, start, opening);
774
+ if (block == null) return null;
775
+ return {
776
+ block,
777
+ nextCursor: block.end
778
+ };
779
+ }
780
+ function scanOpeningTag(source, start) {
781
+ const afterTag = start + 14;
782
+ if (!isWhitespaceChar(source[afterTag])) return null;
783
+ let index = afterTag;
784
+ let braceDepth = 0;
785
+ let quote = null;
786
+ while (index < source.length) {
787
+ const step = advanceOpeningTagScan(source, {
788
+ braceDepth,
789
+ index,
790
+ quote
791
+ });
792
+ if (step.tagEndIndex != null) return finalizeOpeningTag(source, afterTag, step.tagEndIndex);
793
+ index = step.nextIndex;
794
+ braceDepth = step.nextBraceDepth;
795
+ quote = step.nextQuote;
796
+ }
797
+ return null;
798
+ }
799
+ function finalizeOpeningTag(source, afterTag, endBracketIndex) {
800
+ const isSelfClosing = source[endBracketIndex - 1] === "/";
801
+ const propsEnd = isSelfClosing ? endBracketIndex - 1 : endBracketIndex;
802
+ const props = source.slice(afterTag, propsEnd).trim();
803
+ return {
804
+ end: endBracketIndex + 1,
805
+ isSelfClosing,
806
+ props
807
+ };
808
+ }
809
+ function advanceOpeningTagScan(source, state) {
810
+ const { braceDepth, index, quote } = state;
811
+ if (quote != null) return scanQuotedStep(source, {
812
+ braceDepth,
813
+ index,
814
+ quote
815
+ });
816
+ return scanUnquotedStep(source, index, braceDepth);
817
+ }
818
+ function scanQuotedStep(source, state) {
819
+ const { braceDepth, index, quote } = state;
820
+ if (source[index] === "\\") return {
821
+ nextBraceDepth: braceDepth,
822
+ nextIndex: Math.min(source.length, index + 2),
823
+ nextQuote: quote,
824
+ tagEndIndex: null
825
+ };
826
+ const closesQuote = source[index] === quote;
827
+ return {
828
+ nextBraceDepth: braceDepth,
829
+ nextIndex: index + 1,
830
+ nextQuote: closesQuote ? null : quote,
831
+ tagEndIndex: null
832
+ };
833
+ }
834
+ function scanUnquotedStep(source, index, braceDepth) {
835
+ const char = source[index];
836
+ if (isQuote(char)) return makeScanStep(index + 1, braceDepth, char);
837
+ if (char === "{") return makeScanStep(index + 1, braceDepth + 1, null);
838
+ if (char === "}") return makeScanStep(index + 1, Math.max(0, braceDepth - 1), null);
839
+ if (braceDepth === 0 && char === ">") return {
840
+ nextBraceDepth: braceDepth,
841
+ nextIndex: index + 1,
842
+ nextQuote: null,
843
+ tagEndIndex: index
844
+ };
845
+ return makeScanStep(index + 1, braceDepth, null);
846
+ }
847
+ function makeScanStep(nextIndex, nextBraceDepth, nextQuote) {
848
+ return {
849
+ nextBraceDepth,
850
+ nextIndex,
851
+ nextQuote,
852
+ tagEndIndex: null
853
+ };
854
+ }
855
+ function createScannedBlock(source, start, opening) {
856
+ if (opening.isSelfClosing) return {
857
+ children: null,
858
+ end: opening.end,
859
+ fullMatch: source.slice(start, opening.end),
860
+ props: opening.props,
861
+ start
862
+ };
863
+ const closeStart = source.indexOf(CLOSING_TAG, opening.end);
864
+ if (closeStart === -1) return null;
865
+ const end = closeStart + 16;
866
+ return {
867
+ children: source.slice(opening.end, closeStart),
868
+ end,
869
+ fullMatch: source.slice(start, end),
870
+ props: opening.props,
871
+ start
872
+ };
873
+ }
874
+ function isQuote(char) {
875
+ return char === "'" || char === "\"" || char === "`";
876
+ }
877
+ function isWhitespaceChar(char) {
878
+ return char === " " || char === "\n" || char === "\r" || char === " ";
879
+ }
880
+ //#endregion
881
+ //#region src/vite/codeblock-transform.ts
882
+ async function transformArdoCodeBlocks(source, markdownConfig) {
883
+ let result = source;
884
+ let offset = 0;
885
+ const blocks = scanArdoCodeBlocks(source);
886
+ for (const block of blocks) {
887
+ const replacement = await createReplacement(block, markdownConfig);
888
+ if (replacement == null) continue;
889
+ const adjustedStart = block.start + offset;
890
+ const adjustedEnd = block.end + offset;
891
+ result = result.slice(0, adjustedStart) + replacement + result.slice(adjustedEnd);
892
+ offset += replacement.length - block.fullMatch.length;
893
+ }
894
+ return result;
895
+ }
896
+ async function createReplacement(block, markdownConfig) {
897
+ if (block.props.includes("__html")) return null;
898
+ if (block.children == null) return createSelfClosingReplacement(block, markdownConfig);
899
+ return createChildrenReplacement(block, markdownConfig);
900
+ }
901
+ async function createSelfClosingReplacement(block, markdownConfig) {
902
+ const codeValue = extractCodeValue(block.props);
903
+ const language = extractPropValue(block.props, "language");
904
+ if (codeValue == null || language == null) return null;
905
+ const html = await safeHighlightCode(codeValue, language, markdownConfig);
906
+ if (html == null) return null;
907
+ const newProps = `__html={${JSON.stringify(html)}} ${block.props}`;
908
+ return block.fullMatch.replace(block.props, newProps);
515
909
  }
910
+ async function createChildrenReplacement(block, markdownConfig) {
911
+ const language = extractPropValue(block.props, "language");
912
+ if (language == null || block.children == null) return null;
913
+ const codeContent = outdent(unwrapTemplateChildren(block.children));
914
+ const html = await safeHighlightCode(codeContent, language, markdownConfig);
915
+ if (html == null) return null;
916
+ return `<ArdoCodeBlock __html={${JSON.stringify(html)}} code={${JSON.stringify(codeContent)}} ${block.props} />`;
917
+ }
918
+ function unwrapTemplateChildren(rawChildren) {
919
+ const trimmed = rawChildren.trim();
920
+ if (!trimmed.startsWith("{`") || !trimmed.endsWith("`}")) return rawChildren;
921
+ return trimmed.slice(2, -2);
922
+ }
923
+ function extractCodeValue(props) {
924
+ const code = extractPropValue(props, "code");
925
+ if (code == null) return null;
926
+ return decodeEscapedString(code);
927
+ }
928
+ function decodeEscapedString(value) {
929
+ return value.replaceAll("\\n", "\n").replaceAll("\\\"", "\"").replaceAll("\\\\", "\\");
930
+ }
931
+ function extractPropValue(props, propName) {
932
+ const patterns = getPropPatterns(propName);
933
+ for (const pattern of patterns) {
934
+ const match = pattern.exec(props);
935
+ if (match?.[1] != null) return match[1];
936
+ }
937
+ return null;
938
+ }
939
+ function getPropPatterns(propName) {
940
+ return [
941
+ new RegExp(`\\b${propName}="((?:[^"\\\\]|\\\\.)*)"`, "su"),
942
+ new RegExp(`\\b${propName}=\\{\\s*"((?:[^"\\\\]|\\\\.)*)"\\s*\\}`, "su"),
943
+ new RegExp(`\\b${propName}=\\{\\s*'((?:[^'\\\\]|\\\\.)*)'\\s*\\}`, "su")
944
+ ];
945
+ }
946
+ async function safeHighlightCode(codeContent, language, markdownConfig) {
947
+ try {
948
+ return await highlightCode(codeContent, language, { theme: markdownConfig?.theme });
949
+ } catch {
950
+ return null;
951
+ }
952
+ }
953
+ //#endregion
954
+ //#region src/vite/codeblock-plugin.ts
955
+ /**
956
+ * Vite plugin that pre-highlights ArdoCodeBlock components at build time.
957
+ *
958
+ * Runs before the JSX parser, so children can contain arbitrary code
959
+ * (including `<`, `{`, etc.) without causing syntax errors.
960
+ */
516
961
  function ardoCodeBlockPlugin(markdownConfig) {
517
- return {
518
- name: "ardo:codeblock-highlight",
519
- enforce: "pre",
520
- async transform(code, id) {
521
- if (!/\.[jt]sx$/.test(id)) return;
522
- if (!code.includes("CodeBlock")) return;
523
- if (id.includes("node_modules")) return;
524
- let result = code;
525
- let offset = 0;
526
- const propMatches = findSelfClosingCodeBlocks(code);
527
- for (const match of propMatches) {
528
- const { fullMatch, propsStr } = match;
529
- const codeMatch = propsStr.match(/\bcode="((?:[^"\\]|\\.)*)"/s) || propsStr.match(/\bcode=\{\s*"((?:[^"\\]|\\.)*)"\s*\}/s) || propsStr.match(/\bcode=\{\s*'((?:[^'\\]|\\.)*)'\s*\}/s);
530
- if (!codeMatch) continue;
531
- const langMatch = propsStr.match(/\blanguage="([^"]*)"/) || propsStr.match(/\blanguage=\{"([^"]*)"\}/) || propsStr.match(/\blanguage=\{'([^']*)'\}/);
532
- if (!langMatch) continue;
533
- if (propsStr.includes("__html")) continue;
534
- const codeContent = codeMatch[1].replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
535
- const language = langMatch[1];
536
- try {
537
- const html = await highlightCode(codeContent, language, {
538
- theme: markdownConfig?.theme
539
- });
540
- const escapedHtml = JSON.stringify(html);
541
- const newPropsStr = `__html={${escapedHtml}} ` + propsStr;
542
- const newFullMatch = fullMatch.replace(propsStr, newPropsStr);
543
- result = result.slice(0, match.index + offset) + newFullMatch + result.slice(match.index + offset + fullMatch.length);
544
- offset += newFullMatch.length - fullMatch.length;
545
- } catch {
546
- }
547
- }
548
- const childrenRegex = /<CodeBlock\s+([^>]*?)>([\s\S]*?)<\/CodeBlock>/g;
549
- offset = result.length - code.length;
550
- let regexMatch;
551
- while ((regexMatch = childrenRegex.exec(code)) !== null) {
552
- const fullMatch = regexMatch[0];
553
- const propsStr = regexMatch[1];
554
- let rawChildren = regexMatch[2];
555
- const langMatch = propsStr.match(/\blanguage="([^"]*)"/) || propsStr.match(/\blanguage=\{"([^"]*)"\}/) || propsStr.match(/\blanguage=\{'([^']*)'\}/);
556
- if (!langMatch) continue;
557
- if (propsStr.includes("__html")) continue;
558
- const templateMatch = rawChildren.match(/^\s*\{`([\s\S]*)`\}\s*$/);
559
- if (templateMatch) {
560
- rawChildren = templateMatch[1];
561
- }
562
- const codeContent = outdent(rawChildren);
563
- const language = langMatch[1];
564
- try {
565
- const html = await highlightCode(codeContent, language, {
566
- theme: markdownConfig?.theme
567
- });
568
- const escapedHtml = JSON.stringify(html);
569
- const escapedCode = JSON.stringify(codeContent);
570
- const newTag = `<CodeBlock __html={${escapedHtml}} code={${escapedCode}} ${propsStr} />`;
571
- result = result.slice(0, regexMatch.index + offset) + newTag + result.slice(regexMatch.index + offset + fullMatch.length);
572
- offset += newTag.length - fullMatch.length;
573
- } catch {
574
- }
575
- }
576
- if (result !== code) {
577
- return { code: result, map: null };
578
- }
579
- }
580
- };
962
+ return {
963
+ enforce: "pre",
964
+ name: "ardo:codeblock-highlight",
965
+ async transform(code, id) {
966
+ if (!shouldProcessFile(code, id)) return;
967
+ const transformed = await transformArdoCodeBlocks(code, markdownConfig);
968
+ if (transformed === code) return;
969
+ return {
970
+ code: transformed,
971
+ map: null
972
+ };
973
+ }
974
+ };
581
975
  }
582
-
583
- // src/vite/plugin.ts
976
+ function shouldProcessFile(code, id) {
977
+ if (!/\.[jt]sx$/u.test(id)) return false;
978
+ if (id.includes("node_modules")) return false;
979
+ return code.includes("ArdoCodeBlock");
980
+ }
981
+ //#endregion
982
+ //#region src/vite/git-utils.ts
983
+ /**
984
+ * Finds the package root by looking for package.json in parent directories.
985
+ * Returns the path relative to cwd, or undefined if not found.
986
+ */
584
987
  function findPackageRoot(cwd) {
585
- let dir = path2.resolve(cwd);
586
- const root = path2.parse(dir).root;
587
- while (dir !== root) {
588
- const parentDir = path2.dirname(dir);
589
- const packageJsonPath = path2.join(parentDir, "package.json");
590
- if (fsSync2.existsSync(packageJsonPath)) {
591
- return path2.relative(cwd, parentDir) || ".";
592
- }
593
- dir = parentDir;
594
- }
595
- return void 0;
988
+ let currentDir = path.resolve(cwd);
989
+ const filesystemRoot = path.parse(currentDir).root;
990
+ while (currentDir !== filesystemRoot) {
991
+ const parentDir = path.dirname(currentDir);
992
+ const packageJsonPath = path.join(parentDir, "package.json");
993
+ if (fsSync.existsSync(packageJsonPath)) {
994
+ const relativePath = path.relative(cwd, parentDir);
995
+ return relativePath === "" ? "." : relativePath;
996
+ }
997
+ currentDir = parentDir;
998
+ }
596
999
  }
1000
+ /**
1001
+ * Detects the GitHub repository name from git remote URL.
1002
+ * Returns the repo name (e.g., "ardo" from "github.com/sebastian-software/ardo")
1003
+ * or undefined if not a GitHub repo.
1004
+ */
597
1005
  function detectGitHubRepoName(cwd) {
598
- try {
599
- const remoteUrl = execSync("git remote get-url origin", {
600
- cwd,
601
- encoding: "utf-8",
602
- stdio: ["pipe", "pipe", "pipe"]
603
- }).trim();
604
- const match = remoteUrl.match(/github\.com[/:][\w-]+\/([\w.-]+?)(?:\.git)?$/);
605
- return match?.[1];
606
- } catch {
607
- return void 0;
608
- }
1006
+ const remoteUrl = runGitCommand(cwd, "git remote get-url origin");
1007
+ if (remoteUrl == null) return;
1008
+ return parseGitHubRepoName(remoteUrl);
609
1009
  }
1010
+ /**
1011
+ * Detects the current short git commit hash.
1012
+ */
610
1013
  function detectGitHash(cwd) {
611
- try {
612
- return execSync("git rev-parse --short HEAD", {
613
- cwd,
614
- encoding: "utf-8",
615
- stdio: ["pipe", "pipe", "pipe"]
616
- }).trim();
617
- } catch {
618
- return void 0;
619
- }
1014
+ return runGitCommand(cwd, "git rev-parse --short HEAD");
620
1015
  }
621
- function readProjectMeta(root) {
622
- const pkgPath = path2.join(root, "package.json");
623
- try {
624
- const raw = fsSync2.readFileSync(pkgPath, "utf-8");
625
- const pkg = JSON.parse(raw);
626
- let repository;
627
- if (typeof pkg.repository === "string") {
628
- repository = pkg.repository;
629
- } else if (pkg.repository?.url) {
630
- repository = pkg.repository.url.replace(/^git\+/, "").replace(/^git:\/\//, "https://").replace(/\.git$/, "");
631
- }
632
- let author;
633
- if (typeof pkg.author === "string") {
634
- author = pkg.author;
635
- } else if (pkg.author?.name) {
636
- author = pkg.author.name;
637
- }
638
- return {
639
- name: pkg.name,
640
- homepage: pkg.homepage,
641
- repository,
642
- version: pkg.version,
643
- author,
644
- license: pkg.license
645
- };
646
- } catch {
647
- return {};
648
- }
1016
+ /**
1017
+ * Detects the GitHub Pages basename from the git remote URL.
1018
+ * Returns "/" in dev mode or when no GitHub repo is detected.
1019
+ */
1020
+ function detectGitHubBasename(cwd) {
1021
+ if (process.env.NODE_ENV !== "production") return "/";
1022
+ const repoName = detectGitHubRepoName(cwd ?? process.cwd());
1023
+ return repoName != null ? `/${repoName}/` : "/";
649
1024
  }
1025
+ /**
1026
+ * Recursively copies files from src to dest, overwriting existing files.
1027
+ */
650
1028
  function copyRecursive(src, dest) {
651
- const stat = fsSync2.statSync(src);
652
- if (stat.isDirectory()) {
653
- if (!fsSync2.existsSync(dest)) {
654
- fsSync2.mkdirSync(dest, { recursive: true });
655
- }
656
- for (const item of fsSync2.readdirSync(src)) {
657
- copyRecursive(path2.join(src, item), path2.join(dest, item));
658
- }
659
- } else {
660
- fsSync2.copyFileSync(src, dest);
661
- }
1029
+ if (!fsSync.statSync(src).isDirectory()) {
1030
+ fsSync.copyFileSync(src, dest);
1031
+ return;
1032
+ }
1033
+ if (!fsSync.existsSync(dest)) fsSync.mkdirSync(dest, { recursive: true });
1034
+ for (const item of fsSync.readdirSync(src)) copyRecursive(path.join(src, item), path.join(dest, item));
662
1035
  }
663
- function detectGitHubBasename(cwd) {
664
- if (process.env.NODE_ENV !== "production") {
665
- return "/";
666
- }
667
- const repoName = detectGitHubRepoName(cwd || process.cwd());
668
- return repoName ? `/${repoName}/` : "/";
669
- }
670
- var VIRTUAL_MODULE_ID = "virtual:ardo/config";
671
- var RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
672
- var VIRTUAL_SIDEBAR_ID = "virtual:ardo/sidebar";
673
- var RESOLVED_VIRTUAL_SIDEBAR_ID = "\0" + VIRTUAL_SIDEBAR_ID;
674
- var VIRTUAL_SEARCH_ID = "virtual:ardo/search-index";
675
- var RESOLVED_VIRTUAL_SEARCH_ID = "\0" + VIRTUAL_SEARCH_ID;
676
- var typedocGenerated = false;
677
- var flattenExecuted = false;
678
- function ardoPlugin(options = {}) {
679
- let resolvedConfig;
680
- let routesDir;
681
- const {
682
- routes,
683
- typedoc,
684
- githubPages = true,
685
- routesDir: routesDirOption,
686
- ...pressConfig
687
- } = options;
688
- const mainPlugin = {
689
- name: "ardo",
690
- enforce: "pre",
691
- config(userConfig, env) {
692
- const root = userConfig.root || process.cwd();
693
- routesDir = routesDirOption || path2.join(root, "app", "routes");
694
- const result = {
695
- define: {
696
- __BUILD_TIME__: JSON.stringify((/* @__PURE__ */ new Date()).toISOString())
697
- },
698
- optimizeDeps: {
699
- exclude: ["ardo/ui/styles.css"]
700
- },
701
- ssr: {
702
- noExternal: ["ardo"]
703
- }
704
- };
705
- if (githubPages && env.command === "build" && !userConfig.base) {
706
- const repoName = detectGitHubRepoName(root);
707
- if (repoName) {
708
- result.base = `/${repoName}/`;
709
- console.log(`[ardo] GitHub Pages detected, using base: ${result.base}`);
710
- }
711
- }
712
- return result;
713
- },
714
- async configResolved(config) {
715
- const root = config.root;
716
- routesDir = routesDirOption || path2.join(root, "app", "routes");
717
- const detectedProject = readProjectMeta(root);
718
- const project = { ...detectedProject, ...pressConfig.project };
719
- const defaultConfig = {
720
- title: pressConfig.title ?? "Ardo",
721
- description: pressConfig.description ?? "Documentation powered by Ardo"
722
- };
723
- const configWithRoutes = {
724
- ...defaultConfig,
725
- ...pressConfig,
726
- project,
727
- srcDir: routesDir
728
- };
729
- resolvedConfig = resolveConfig(configWithRoutes, root);
730
- },
731
- resolveId(id) {
732
- if (id === VIRTUAL_MODULE_ID) {
733
- return RESOLVED_VIRTUAL_MODULE_ID;
734
- }
735
- if (id === VIRTUAL_SIDEBAR_ID) {
736
- return RESOLVED_VIRTUAL_SIDEBAR_ID;
737
- }
738
- if (id === VIRTUAL_SEARCH_ID) {
739
- return RESOLVED_VIRTUAL_SEARCH_ID;
740
- }
741
- },
742
- async load(id) {
743
- if (id === RESOLVED_VIRTUAL_MODULE_ID) {
744
- const clientConfig = {
745
- title: resolvedConfig.title,
746
- description: resolvedConfig.description,
747
- base: resolvedConfig.base,
748
- lang: resolvedConfig.lang,
749
- themeConfig: resolvedConfig.themeConfig,
750
- project: resolvedConfig.project,
751
- buildTime: (/* @__PURE__ */ new Date()).toISOString(),
752
- buildHash: detectGitHash(resolvedConfig.root)
753
- };
754
- return `export default ${JSON.stringify(clientConfig)}`;
755
- }
756
- if (id === RESOLVED_VIRTUAL_SIDEBAR_ID) {
757
- const sidebar = await generateSidebar(resolvedConfig, routesDir);
758
- return `export default ${JSON.stringify(sidebar)}`;
759
- }
760
- if (id === RESOLVED_VIRTUAL_SEARCH_ID) {
761
- const searchIndex = await generateSearchIndex(routesDir);
762
- return `export default ${JSON.stringify(searchIndex)}`;
763
- }
764
- },
765
- transform(code, id) {
766
- if (!/\.(mdx|md)$/.test(id)) return;
767
- if (!id.startsWith(routesDir)) return;
768
- if (/export\s+(const|function)\s+meta\b/.test(code)) return;
769
- const titleMatch = code.match(
770
- /export\s+const\s+frontmatter\s*=\s*\{[^}]*title\s*:\s*"([^"]*)"/
771
- );
772
- const descMatch = code.match(
773
- /export\s+const\s+frontmatter\s*=\s*\{[^}]*description\s*:\s*"([^"]*)"/
774
- );
775
- const pageTitle = titleMatch?.[1];
776
- if (!pageTitle) return;
777
- const siteTitle = resolvedConfig.title;
778
- const separator = resolvedConfig.titleSeparator;
779
- const fullTitle = `${pageTitle}${separator}${siteTitle}`;
780
- const description = descMatch?.[1];
781
- const metaEntries = [`{ title: ${JSON.stringify(fullTitle)} }`];
782
- if (description) {
783
- metaEntries.push(`{ name: "description", content: ${JSON.stringify(description)} }`);
784
- }
785
- return {
786
- code: `${code}
787
- export const meta = () => [${metaEntries.join(", ")}];
788
- `,
789
- map: null
790
- };
791
- }
792
- };
793
- const plugins = [mainPlugin];
794
- if (routes !== false) {
795
- plugins.push(
796
- ardoRoutesPlugin({
797
- routesDir: routesDirOption,
798
- ...routes
799
- })
800
- );
801
- }
802
- if (typedoc) {
803
- const packageRoot = findPackageRoot(process.cwd());
804
- const defaultEntryPoint = packageRoot ? `${packageRoot}/src/index.ts` : "./src/index.ts";
805
- const defaultTsconfig = packageRoot ? `${packageRoot}/tsconfig.json` : "./tsconfig.json";
806
- const defaultTypedocConfig = {
807
- enabled: true,
808
- entryPoints: [defaultEntryPoint],
809
- tsconfig: defaultTsconfig,
810
- out: "api-reference",
811
- excludePrivate: true,
812
- excludeInternal: true
813
- };
814
- const typedocConfig = typedoc === true ? defaultTypedocConfig : { ...defaultTypedocConfig, ...typedoc };
815
- const typedocPlugin = {
816
- name: "ardo:typedoc",
817
- async buildStart() {
818
- if (typedocGenerated || !typedocConfig.enabled) {
819
- return;
820
- }
821
- console.log("[ardo] Generating API documentation with TypeDoc...");
822
- const startTime = Date.now();
823
- try {
824
- const outputDir = routesDirOption || "./app/routes";
825
- const docs = await generateApiDocs(typedocConfig, outputDir);
826
- const duration = Date.now() - startTime;
827
- console.log(`[ardo] Generated ${docs.length} API documentation pages in ${duration}ms`);
828
- } catch (error) {
829
- console.warn("[ardo] TypeDoc generation failed. API documentation will not be available.");
830
- console.warn("[ardo] Check your typedoc.entryPoints configuration.");
831
- if (error instanceof Error) {
832
- console.warn(`[ardo] Error: ${error.message}`);
833
- }
834
- }
835
- typedocGenerated = true;
836
- }
837
- };
838
- plugins.unshift(typedocPlugin);
839
- }
840
- plugins.push(ardoCodeBlockPlugin(pressConfig.markdown));
841
- const themeConfig = pressConfig.markdown?.theme ?? defaultMarkdownConfig.theme;
842
- const hasThemeObject = themeConfig && typeof themeConfig === "object" && "light" in themeConfig;
843
- const lineNumbers = pressConfig.markdown?.lineNumbers || false;
844
- const shikiOptions = hasThemeObject ? {
845
- themes: {
846
- light: themeConfig.light,
847
- dark: themeConfig.dark
848
- },
849
- defaultColor: false,
850
- transformers: [ardoLineTransformer({ globalLineNumbers: lineNumbers })]
851
- } : {
852
- theme: themeConfig,
853
- transformers: [ardoLineTransformer({ globalLineNumbers: lineNumbers })]
854
- };
855
- const mdxPlugin = mdx({
856
- include: /\.(md|mdx)$/,
857
- remarkPlugins: [
858
- remarkFrontmatter,
859
- [remarkMdxFrontmatter, { name: "frontmatter" }],
860
- remarkGfm,
861
- remarkCodeMeta
862
- ],
863
- rehypePlugins: [[rehypeShiki, shikiOptions]],
864
- providerImportSource: "ardo/mdx-provider"
865
- });
866
- plugins.push(mdxPlugin);
867
- plugins.push(...vanillaExtractPlugin());
868
- const reactRouterPlugin = reactRouter();
869
- const reactRouterPlugins = (Array.isArray(reactRouterPlugin) ? reactRouterPlugin : [reactRouterPlugin]).filter((p) => p != null);
870
- plugins.push(...reactRouterPlugins);
871
- if (githubPages) {
872
- let detectedBase;
873
- const flattenPlugin = {
874
- name: "ardo:flatten-github-pages",
875
- enforce: "post",
876
- configResolved(config) {
877
- if (config.base && config.base !== "/") {
878
- detectedBase = config.base;
879
- }
880
- },
881
- closeBundle() {
882
- if (flattenExecuted || !detectedBase) {
883
- return;
884
- }
885
- const baseName = detectedBase.replace(/^\/|\/$/g, "");
886
- if (!baseName) return;
887
- const buildDir = path2.join(process.cwd(), "build", "client");
888
- const nestedDir = path2.join(buildDir, baseName);
889
- if (!fsSync2.existsSync(nestedDir)) {
890
- return;
891
- }
892
- console.log(`[ardo] Flattening build/client/${baseName}/ to build/client/ for GitHub Pages`);
893
- copyRecursive(nestedDir, buildDir);
894
- fsSync2.rmSync(nestedDir, { recursive: true, force: true });
895
- console.log("[ardo] Build output flattened successfully.");
896
- flattenExecuted = true;
897
- }
898
- };
899
- plugins.push(flattenPlugin);
900
- }
901
- return plugins;
902
- }
903
- async function generateSidebar(config, routesDir) {
904
- const { themeConfig } = config;
905
- if (themeConfig.sidebar && !Array.isArray(themeConfig.sidebar)) {
906
- return themeConfig.sidebar;
907
- }
908
- if (themeConfig.sidebar && Array.isArray(themeConfig.sidebar) && themeConfig.sidebar.length > 0) {
909
- return themeConfig.sidebar;
910
- }
911
- try {
912
- const sidebar = await scanDirectory(routesDir, routesDir);
913
- return sidebar;
914
- } catch {
915
- return [];
916
- }
917
- }
918
- async function scanDirectory(dir, rootDir) {
919
- const entries = await fs2.readdir(dir, { withFileTypes: true });
920
- const items = [];
921
- for (const entry of entries) {
922
- const fullPath = path2.join(dir, entry.name);
923
- const relativePath = path2.relative(rootDir, fullPath);
924
- if (entry.isDirectory()) {
925
- const children = await scanDirectory(fullPath, rootDir);
926
- if (children.length > 0) {
927
- const indexPath = path2.join(fullPath, "index.mdx");
928
- let link;
929
- try {
930
- await fs2.access(indexPath);
931
- link = "/" + relativePath.replace(/\\/g, "/");
932
- } catch {
933
- }
934
- items.push({
935
- text: formatTitle(entry.name),
936
- link,
937
- items: children
938
- });
939
- }
940
- } else if ((entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) && entry.name !== "index.mdx" && entry.name !== "index.md") {
941
- const fileContent = await fs2.readFile(fullPath, "utf-8");
942
- const { data: frontmatter } = matter(fileContent);
943
- const ext = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
944
- const title = frontmatter.title || formatTitle(entry.name.replace(ext, ""));
945
- const order = typeof frontmatter.order === "number" ? frontmatter.order : void 0;
946
- const link = "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
947
- items.push({
948
- text: title,
949
- link,
950
- order
951
- });
952
- }
953
- }
954
- items.sort((a, b) => {
955
- if (a.order !== void 0 && b.order !== void 0) {
956
- return a.order - b.order;
957
- }
958
- if (a.order !== void 0) return -1;
959
- if (b.order !== void 0) return 1;
960
- return a.text.localeCompare(b.text);
961
- });
962
- return items.map(({ order: _order, ...item }) => item);
1036
+ function runGitCommand(cwd, command) {
1037
+ try {
1038
+ const commandResult = execSync(command, {
1039
+ cwd,
1040
+ encoding: "utf8",
1041
+ stdio: [
1042
+ "pipe",
1043
+ "pipe",
1044
+ "pipe"
1045
+ ]
1046
+ }).trim();
1047
+ return commandResult === "" ? void 0 : commandResult;
1048
+ } catch {
1049
+ return;
1050
+ }
963
1051
  }
964
- function formatTitle(name) {
965
- return name.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
1052
+ function parseGitHubRepoName(remoteUrl) {
1053
+ const normalizedUrl = remoteUrl.trim();
1054
+ if (!normalizedUrl.includes("github.com")) return;
1055
+ const urlSegments = (normalizedUrl.endsWith(".git") ? normalizedUrl.slice(0, -4) : normalizedUrl).replace(":", "/").split("/");
1056
+ const repoName = urlSegments.at(-1);
1057
+ const ownerName = urlSegments.at(-2);
1058
+ if (repoName == null || repoName === "" || ownerName == null || ownerName === "") return;
1059
+ return repoName;
966
1060
  }
967
- async function generateSearchIndex(routesDir) {
968
- const docs = [];
969
- async function scanForSearch(dir, section) {
970
- try {
971
- const entries = await fs2.readdir(dir, { withFileTypes: true });
972
- for (const entry of entries) {
973
- const fullPath = path2.join(dir, entry.name);
974
- if (entry.isDirectory()) {
975
- const newSection = section ? `${section} > ${formatTitle(entry.name)}` : formatTitle(entry.name);
976
- await scanForSearch(fullPath, newSection);
977
- } else if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) {
978
- const relativePath = path2.relative(routesDir, fullPath);
979
- const fileContent = await fs2.readFile(fullPath, "utf-8");
980
- const { data: frontmatter, content: rawContent } = matter(fileContent);
981
- const ext = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
982
- const title = frontmatter.title || formatTitle(entry.name.replace(ext, ""));
983
- let content = rawContent;
984
- 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);
985
- const routePath = entry.name === "index.mdx" || entry.name === "index.md" ? "/" + path2.dirname(relativePath).replace(/\\/g, "/") : "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
986
- const finalPath = routePath === "/." ? "/" : routePath;
987
- docs.push({
988
- id: relativePath,
989
- title,
990
- content,
991
- path: finalPath,
992
- section
993
- });
994
- }
995
- }
996
- } catch (error) {
997
- console.warn(
998
- "[ardo] Failed to scan for search index:",
999
- error instanceof Error ? error.message : error
1000
- );
1001
- }
1002
- }
1003
- await scanForSearch(routesDir);
1004
- return docs;
1061
+ //#endregion
1062
+ //#region src/vite/flatten-plugin.ts
1063
+ let flattenExecuted = false;
1064
+ function createFlattenPlugin() {
1065
+ let detectedBase;
1066
+ return {
1067
+ name: "ardo:flatten-github-pages",
1068
+ enforce: "post",
1069
+ configResolved(config) {
1070
+ detectedBase = config.base === "/" ? void 0 : config.base;
1071
+ },
1072
+ closeBundle() {
1073
+ if (flattenExecuted || detectedBase == null) return;
1074
+ const baseName = trimSlashes(detectedBase);
1075
+ if (baseName === "") return;
1076
+ const buildDir = path.join(process.cwd(), "build", "client");
1077
+ const nestedDir = path.join(buildDir, baseName);
1078
+ if (!fsSync.existsSync(nestedDir)) return;
1079
+ console.log(`[ardo] Flattening build/client/${baseName}/ to build/client/ for GitHub Pages`);
1080
+ copyRecursive(nestedDir, buildDir);
1081
+ fsSync.rmSync(nestedDir, {
1082
+ recursive: true,
1083
+ force: true
1084
+ });
1085
+ console.log("[ardo] Build output flattened successfully.");
1086
+ flattenExecuted = true;
1087
+ }
1088
+ };
1005
1089
  }
1006
-
1007
- // src/runtime/loader.ts
1008
- import fs3 from "fs/promises";
1009
- import path3 from "path";
1010
-
1011
- // src/markdown/pipeline.ts
1012
- import { unified } from "unified";
1013
- import remarkParse from "remark-parse";
1014
- import remarkGfm2 from "remark-gfm";
1015
- import remarkFrontmatter2 from "remark-frontmatter";
1016
- import remarkRehype from "remark-rehype";
1017
- import rehypeStringify from "rehype-stringify";
1018
- import matter2 from "gray-matter";
1019
-
1020
- // src/markdown/toc.ts
1021
- import { visit as visit2 } from "unist-util-visit";
1022
- function remarkExtractToc(options) {
1023
- const { tocExtraction, levels } = options;
1024
- const [minLevel, maxLevel] = levels;
1025
- return function(tree) {
1026
- const headings = [];
1027
- let headingIndex = 0;
1028
- visit2(tree, "heading", (node) => {
1029
- if (node.depth < minLevel || node.depth > maxLevel) {
1030
- return;
1031
- }
1032
- const text = getHeadingText(node);
1033
- const slug = slugify(text);
1034
- const id = slug || `heading-${headingIndex}`;
1035
- headingIndex++;
1036
- headings.push({
1037
- text,
1038
- level: node.depth,
1039
- id
1040
- });
1041
- const data = node.data || (node.data = {});
1042
- const hProperties = data.hProperties || (data.hProperties = {});
1043
- hProperties.id = id;
1044
- });
1045
- tocExtraction.toc = buildTocTree(headings, minLevel);
1046
- };
1090
+ function trimSlashes(value) {
1091
+ let trimmed = value;
1092
+ while (trimmed.startsWith("/")) trimmed = trimmed.slice(1);
1093
+ while (trimmed.endsWith("/")) trimmed = trimmed.slice(0, -1);
1094
+ return trimmed;
1095
+ }
1096
+ //#endregion
1097
+ //#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
+ /**
1099
+ * Create an ESTree identifier node for a given name.
1100
+ *
1101
+ * @param name
1102
+ * The name of the identifier.
1103
+ * @returns
1104
+ * The identifier node.
1105
+ */
1106
+ function identifier(name) {
1107
+ return {
1108
+ type: "Identifier",
1109
+ name
1110
+ };
1111
+ }
1112
+ /**
1113
+ * Create an ESTree literal node for a given value.
1114
+ *
1115
+ * @param value
1116
+ * The value for which to create a literal.
1117
+ * @returns
1118
+ * The literal node.
1119
+ */
1120
+ function literal(value) {
1121
+ return {
1122
+ type: "Literal",
1123
+ value
1124
+ };
1125
+ }
1126
+ /**
1127
+ * Create an ESTree call expression on an object member.
1128
+ *
1129
+ * @param object
1130
+ * The object to call the method on.
1131
+ * @param name
1132
+ * The name of the method to call.
1133
+ * @param args
1134
+ * Arguments to pass to the function call
1135
+ * @returns
1136
+ * The call expression node.
1137
+ */
1138
+ function methodCall(object, name, args) {
1139
+ return {
1140
+ type: "CallExpression",
1141
+ optional: false,
1142
+ callee: {
1143
+ type: "MemberExpression",
1144
+ computed: false,
1145
+ optional: false,
1146
+ object,
1147
+ property: identifier(name)
1148
+ },
1149
+ arguments: args
1150
+ };
1151
+ }
1152
+ /**
1153
+ * Turn a number or bigint into an ESTree expression. This handles positive and negative numbers and
1154
+ * bigints as well as special numbers.
1155
+ *
1156
+ * @param number
1157
+ * The value to turn into an ESTree expression.
1158
+ * @returns
1159
+ * An expression that represents the given value.
1160
+ */
1161
+ function processNumber(number) {
1162
+ if (number < 0 || Object.is(number, -0)) return {
1163
+ type: "UnaryExpression",
1164
+ operator: "-",
1165
+ prefix: true,
1166
+ argument: processNumber(-number)
1167
+ };
1168
+ if (typeof number === "bigint") return {
1169
+ type: "Literal",
1170
+ bigint: String(number)
1171
+ };
1172
+ if (number === Number.POSITIVE_INFINITY || Number.isNaN(number)) return identifier(String(number));
1173
+ return literal(number);
1174
+ }
1175
+ /**
1176
+ * Process an array of numbers. This is a shortcut for iterables whose constructor takes an array of
1177
+ * numbers as input.
1178
+ *
1179
+ * @param numbers
1180
+ * The numbers to add to the array expression.
1181
+ * @returns
1182
+ * An ESTree array expression whose elements match the input numbers.
1183
+ */
1184
+ function processNumberArray(numbers) {
1185
+ return {
1186
+ type: "ArrayExpression",
1187
+ elements: Array.from(numbers, processNumber)
1188
+ };
1189
+ }
1190
+ /**
1191
+ * Check whether a value can be constructed from its string representation.
1192
+ *
1193
+ * @param value
1194
+ * The value to check
1195
+ * @returns
1196
+ * Whether or not the value can be constructed from its string representation.
1197
+ */
1198
+ function isStringReconstructable(value) {
1199
+ return value instanceof URL || value instanceof URLSearchParams;
1200
+ }
1201
+ /**
1202
+ * Check whether a value can be constructed from its `valueOf()` result.
1203
+ *
1204
+ * @param value
1205
+ * The value to check
1206
+ * @returns
1207
+ * Whether or not the value can be constructed from its `valueOf()` result.
1208
+ */
1209
+ function isValueReconstructable(value) {
1210
+ return value instanceof Boolean || value instanceof Date || value instanceof Number || value instanceof String;
1211
+ }
1212
+ const wellKnownSymbols = /* @__PURE__ */ new Map();
1213
+ for (const name of Reflect.ownKeys(Symbol)) {
1214
+ const value = Symbol[name];
1215
+ if (typeof value === "symbol") wellKnownSymbols.set(value, name);
1216
+ }
1217
+ /**
1218
+ * Check whether a value is a Temporal value.
1219
+ *
1220
+ * @param value
1221
+ * The value to check
1222
+ * @returns
1223
+ * Whether or not the value is a Temporal value.
1224
+ */
1225
+ function isTemporal(value) {
1226
+ return typeof Temporal !== "undefined" && (value instanceof Temporal.Duration || value instanceof Temporal.Instant || value instanceof Temporal.PlainDate || value instanceof Temporal.PlainDateTime || value instanceof Temporal.PlainYearMonth || value instanceof Temporal.PlainMonthDay || value instanceof Temporal.PlainTime || value instanceof Temporal.ZonedDateTime);
1227
+ }
1228
+ /**
1229
+ * Check whether a value is a typed array.
1230
+ *
1231
+ * @param value
1232
+ * The value to check
1233
+ * @returns
1234
+ * Whether or not the value is a typed array.
1235
+ */
1236
+ function isTypedArray(value) {
1237
+ return value instanceof BigInt64Array || value instanceof BigUint64Array || typeof Float16Array !== "undefined" && value instanceof Float16Array || value instanceof Float32Array || value instanceof Float64Array || value instanceof Int8Array || value instanceof Int16Array || value instanceof Int32Array || value instanceof Uint8Array || value instanceof Uint8ClampedArray || value instanceof Uint16Array || value instanceof Uint32Array;
1238
+ }
1239
+ /**
1240
+ * Compare two value contexts for sorting them based on reference count.
1241
+ *
1242
+ * @param a
1243
+ * The first context to compare.
1244
+ * @param b
1245
+ * The second context to compare.
1246
+ * @returns
1247
+ * The count of context a minus the count of context b.
1248
+ */
1249
+ function compareContexts(a, b) {
1250
+ const aReferencedByB = a.referencedBy.has(b.value);
1251
+ const bReferencedByA = b.referencedBy.has(a.value);
1252
+ if (aReferencedByB) {
1253
+ if (bReferencedByA) return a.count - b.count;
1254
+ return -1;
1255
+ }
1256
+ if (bReferencedByA) return 1;
1257
+ return a.count - b.count;
1258
+ }
1259
+ /**
1260
+ * Replace the assigned right hand expression with the new expression.
1261
+ *
1262
+ * If there is no assignment expression, the original expression is returned. Otherwise the
1263
+ * assignment is modified and returned.
1264
+ *
1265
+ * @param expression
1266
+ * The expression to use for the assignment.
1267
+ * @param assignment
1268
+ * The existing assignmentexpression
1269
+ * @returns
1270
+ * The new expression.
1271
+ */
1272
+ function replaceAssignment(expression, assignment) {
1273
+ if (!assignment || assignment.type !== "AssignmentExpression") return expression;
1274
+ let node = assignment;
1275
+ while (node.right.type === "AssignmentExpression") node = node.right;
1276
+ node.right = expression;
1277
+ return assignment;
1278
+ }
1279
+ /**
1280
+ * Create an ESTree epxression to represent a symbol. Global and well-known symbols are supported.
1281
+ *
1282
+ * @param symbol
1283
+ * The symbol to represent.
1284
+ * @returns
1285
+ * An ESTree expression to represent the symbol.
1286
+ */
1287
+ function symbolToEstree(symbol) {
1288
+ const name = wellKnownSymbols.get(symbol);
1289
+ if (name) return {
1290
+ type: "MemberExpression",
1291
+ computed: false,
1292
+ optional: false,
1293
+ object: identifier("Symbol"),
1294
+ property: identifier(name)
1295
+ };
1296
+ if (symbol.description && symbol === Symbol.for(symbol.description)) return methodCall(identifier("Symbol"), "for", [literal(symbol.description)]);
1297
+ throw new TypeError(`Only global symbols are supported, got: ${String(symbol)}`, { cause: symbol });
1298
+ }
1299
+ /**
1300
+ * Create an ESTree property from a key and a value expression.
1301
+ *
1302
+ * @param key
1303
+ * The property key value
1304
+ * @param value
1305
+ * The property value as an ESTree expression.
1306
+ * @returns
1307
+ * The ESTree properry node.
1308
+ */
1309
+ function property(key, value) {
1310
+ const isString = typeof key === "string";
1311
+ return {
1312
+ type: "Property",
1313
+ method: false,
1314
+ shorthand: false,
1315
+ computed: key === "__proto__" || !isString,
1316
+ kind: "init",
1317
+ key: isString ? literal(key) : symbolToEstree(key),
1318
+ value
1319
+ };
1320
+ }
1321
+ /**
1322
+ * Convert a value to an ESTree node.
1323
+ *
1324
+ * @param value
1325
+ * The value to convert.
1326
+ * @param options
1327
+ * Additional options to configure the output.
1328
+ * @returns
1329
+ * The ESTree node.
1330
+ */
1331
+ function valueToEstree(value, options = {}) {
1332
+ const stack = [];
1333
+ const collectedContexts = /* @__PURE__ */ new Map();
1334
+ const namedContexts = [];
1335
+ const customTrees = /* @__PURE__ */ new Map();
1336
+ /**
1337
+ * Analyze a value and collect all reference contexts.
1338
+ *
1339
+ * @param val
1340
+ * The value to analyze.
1341
+ */
1342
+ function analyze(val) {
1343
+ if (typeof val !== "object" && typeof val !== "function") return;
1344
+ if (val == null) return;
1345
+ const context = collectedContexts.get(val);
1346
+ if (context) {
1347
+ if (options.preserveReferences) context.count += 1;
1348
+ for (const ancestor of stack) context.referencedBy.add(ancestor);
1349
+ if (stack.includes(val)) {
1350
+ if (!options.preserveReferences) throw new Error(`Found circular reference: ${val}`, { cause: val });
1351
+ const parent = stack.at(-1);
1352
+ const parentContext = collectedContexts.get(parent);
1353
+ parentContext.recursive = true;
1354
+ context.recursive = true;
1355
+ }
1356
+ return;
1357
+ }
1358
+ collectedContexts.set(val, {
1359
+ count: 1,
1360
+ recursive: false,
1361
+ referencedBy: new Set(stack),
1362
+ value: val
1363
+ });
1364
+ const estree = options?.replacer?.(val);
1365
+ if (estree) {
1366
+ customTrees.set(val, estree);
1367
+ return;
1368
+ }
1369
+ if (typeof val === "function") throw new TypeError(`Unsupported value: ${val}`, { cause: val });
1370
+ if (isTypedArray(val)) return;
1371
+ if (isStringReconstructable(val)) return;
1372
+ if (isValueReconstructable(val)) return;
1373
+ if (value instanceof RegExp) return;
1374
+ if (isTemporal(value)) return;
1375
+ stack.push(val);
1376
+ if (val instanceof Map) for (const pair of val) {
1377
+ analyze(pair[0]);
1378
+ analyze(pair[1]);
1379
+ }
1380
+ else if (Array.isArray(val) || val instanceof Set) for (const entry of val) analyze(entry);
1381
+ else {
1382
+ const proto = Object.getPrototypeOf(val);
1383
+ if (proto != null && proto !== Object.prototype && !options.instanceAsObject) throw new TypeError(`Unsupported value: ${val}`, { cause: val });
1384
+ for (const key of Reflect.ownKeys(val)) analyze(val[key]);
1385
+ }
1386
+ stack.pop();
1387
+ }
1388
+ /**
1389
+ * Recursively generate the ESTree expression needed to reconstruct the value.
1390
+ *
1391
+ * @param val
1392
+ * The value to process.
1393
+ * @param isDeclaration
1394
+ * Whether or not this is for a variable declaration.
1395
+ * @returns
1396
+ * The ESTree expression to reconstruct the value.
1397
+ */
1398
+ function generate(val, isDeclaration) {
1399
+ if (val === void 0) return identifier(String(val));
1400
+ if (val == null || typeof val === "string" || typeof val === "boolean") return literal(val);
1401
+ if (typeof val === "bigint" || typeof val === "number") return processNumber(val);
1402
+ if (typeof val === "symbol") return symbolToEstree(val);
1403
+ const context = collectedContexts.get(val);
1404
+ if (!isDeclaration && context?.name) return identifier(context.name);
1405
+ const tree = customTrees.get(val);
1406
+ if (tree) return tree;
1407
+ if (isValueReconstructable(val)) return {
1408
+ type: "NewExpression",
1409
+ callee: identifier(val.constructor.name),
1410
+ arguments: [generate(val.valueOf())]
1411
+ };
1412
+ if (val instanceof RegExp) return {
1413
+ type: "Literal",
1414
+ regex: {
1415
+ pattern: val.source,
1416
+ flags: val.flags
1417
+ }
1418
+ };
1419
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(val)) return methodCall(identifier("Buffer"), "from", [processNumberArray(val)]);
1420
+ if (isTypedArray(val)) return {
1421
+ type: "NewExpression",
1422
+ callee: identifier(val.constructor.name),
1423
+ arguments: [processNumberArray(val)]
1424
+ };
1425
+ if (isStringReconstructable(val)) return {
1426
+ type: "NewExpression",
1427
+ callee: identifier(val.constructor.name),
1428
+ arguments: [literal(String(val))]
1429
+ };
1430
+ if (isTemporal(val)) return methodCall({
1431
+ type: "MemberExpression",
1432
+ computed: false,
1433
+ optional: false,
1434
+ object: identifier("Temporal"),
1435
+ property: identifier(val.constructor.name)
1436
+ }, "from", [literal(String(val))]);
1437
+ if (Array.isArray(val)) {
1438
+ const elements = Array.from({ length: val.length });
1439
+ let trimmable;
1440
+ for (let index = 0; index < val.length; index += 1) {
1441
+ if (!(index in val)) {
1442
+ elements[index] = null;
1443
+ trimmable = void 0;
1444
+ continue;
1445
+ }
1446
+ const child = val[index];
1447
+ const childContext = collectedContexts.get(child);
1448
+ if (context && childContext && namedContexts.indexOf(childContext) >= namedContexts.indexOf(context)) {
1449
+ elements[index] = null;
1450
+ trimmable ||= index;
1451
+ childContext.assignment = {
1452
+ type: "AssignmentExpression",
1453
+ operator: "=",
1454
+ left: {
1455
+ type: "MemberExpression",
1456
+ computed: true,
1457
+ optional: false,
1458
+ object: identifier(context.name),
1459
+ property: literal(index)
1460
+ },
1461
+ right: childContext.assignment || identifier(childContext.name)
1462
+ };
1463
+ } else {
1464
+ elements[index] = generate(child);
1465
+ trimmable = void 0;
1466
+ }
1467
+ }
1468
+ if (trimmable != null) elements.splice(trimmable);
1469
+ return {
1470
+ type: "ArrayExpression",
1471
+ elements
1472
+ };
1473
+ }
1474
+ if (val instanceof Set) {
1475
+ const elements = [];
1476
+ let finalizer;
1477
+ for (const child of val) if (finalizer) finalizer = methodCall(finalizer, "add", [generate(child)]);
1478
+ else {
1479
+ const childContext = collectedContexts.get(child);
1480
+ if (context && childContext && namedContexts.indexOf(childContext) >= namedContexts.indexOf(context)) finalizer = methodCall(identifier(context.name), "add", [generate(child)]);
1481
+ else elements.push(generate(child));
1482
+ }
1483
+ if (context && finalizer) context.assignment = replaceAssignment(finalizer, context.assignment);
1484
+ return {
1485
+ type: "NewExpression",
1486
+ callee: identifier("Set"),
1487
+ arguments: elements.length ? [{
1488
+ type: "ArrayExpression",
1489
+ elements
1490
+ }] : []
1491
+ };
1492
+ }
1493
+ if (val instanceof Map) {
1494
+ const elements = [];
1495
+ let finalizer;
1496
+ for (const [key, item] of val) if (finalizer) finalizer = methodCall(finalizer, "set", [generate(key), generate(item)]);
1497
+ else {
1498
+ const keyContext = collectedContexts.get(key);
1499
+ const itemContext = collectedContexts.get(item);
1500
+ if (context && (keyContext && namedContexts.indexOf(keyContext) >= namedContexts.indexOf(context) || itemContext && namedContexts.indexOf(itemContext) >= namedContexts.indexOf(context))) finalizer = methodCall(identifier(context.name), "set", [generate(key), generate(item)]);
1501
+ else elements.push({
1502
+ type: "ArrayExpression",
1503
+ elements: [generate(key), generate(item)]
1504
+ });
1505
+ }
1506
+ if (context && finalizer) context.assignment = replaceAssignment(finalizer, context.assignment);
1507
+ return {
1508
+ type: "NewExpression",
1509
+ callee: identifier("Map"),
1510
+ arguments: elements.length ? [{
1511
+ type: "ArrayExpression",
1512
+ elements
1513
+ }] : []
1514
+ };
1515
+ }
1516
+ const properties = [];
1517
+ if (Object.getPrototypeOf(val) == null) properties.push({
1518
+ type: "Property",
1519
+ method: false,
1520
+ shorthand: false,
1521
+ computed: false,
1522
+ kind: "init",
1523
+ key: identifier("__proto__"),
1524
+ value: literal(null)
1525
+ });
1526
+ const object = val;
1527
+ const propertyDescriptors = [];
1528
+ for (const key of Reflect.ownKeys(val)) {
1529
+ const child = object[key];
1530
+ const { configurable, enumerable, writable } = Object.getOwnPropertyDescriptor(val, key);
1531
+ const childContext = collectedContexts.get(child);
1532
+ if (!configurable || !enumerable || !writable) {
1533
+ const propertyDescriptor = [property("value", generate(child))];
1534
+ if (configurable) propertyDescriptor.push(property("configurable", literal(true)));
1535
+ if (enumerable) propertyDescriptor.push(property("enumerable", literal(true)));
1536
+ if (writable) propertyDescriptor.push(property("writable", literal(true)));
1537
+ propertyDescriptors.push([key, {
1538
+ type: "ObjectExpression",
1539
+ properties: propertyDescriptor
1540
+ }]);
1541
+ } else if (context && childContext && namedContexts.indexOf(childContext) >= namedContexts.indexOf(context)) if (key === "__proto__") propertyDescriptors.push([key, {
1542
+ type: "ObjectExpression",
1543
+ properties: [
1544
+ property("value", generate(child)),
1545
+ property("configurable", literal(true)),
1546
+ property("enumerable", literal(true)),
1547
+ property("writable", literal(true))
1548
+ ]
1549
+ }]);
1550
+ else childContext.assignment = {
1551
+ type: "AssignmentExpression",
1552
+ operator: "=",
1553
+ left: {
1554
+ type: "MemberExpression",
1555
+ computed: true,
1556
+ optional: false,
1557
+ object: identifier(context.name),
1558
+ property: generate(key)
1559
+ },
1560
+ right: childContext.assignment || generate(child)
1561
+ };
1562
+ else properties.push(property(key, generate(child)));
1563
+ }
1564
+ const objectExpression = {
1565
+ type: "ObjectExpression",
1566
+ properties
1567
+ };
1568
+ if (propertyDescriptors.length) {
1569
+ let name;
1570
+ let args;
1571
+ if (propertyDescriptors.length === 1) {
1572
+ const [[key, expression]] = propertyDescriptors;
1573
+ name = "defineProperty";
1574
+ args = [typeof key === "string" ? literal(key) : symbolToEstree(key), expression];
1575
+ } else {
1576
+ name = "defineProperties";
1577
+ args = [{
1578
+ type: "ObjectExpression",
1579
+ properties: propertyDescriptors.map(([key, expression]) => property(key, expression))
1580
+ }];
1581
+ }
1582
+ if (!context) return methodCall(identifier("Object"), name, [objectExpression, ...args]);
1583
+ context.assignment = replaceAssignment(methodCall(identifier("Object"), name, [identifier(context.name), ...args]), context.assignment);
1584
+ }
1585
+ return objectExpression;
1586
+ }
1587
+ analyze(value);
1588
+ for (const [val, context] of collectedContexts) if (context.recursive || context.count > 1) {
1589
+ context.name = `$${namedContexts.length}`;
1590
+ namedContexts.push(context);
1591
+ } else collectedContexts.delete(val);
1592
+ if (!namedContexts.length) return generate(value);
1593
+ const params = namedContexts.sort(compareContexts).map((context) => ({
1594
+ type: "AssignmentPattern",
1595
+ left: identifier(context.name),
1596
+ right: generate(context.value, true)
1597
+ }));
1598
+ const rootContext = collectedContexts.get(value);
1599
+ const finalizers = [];
1600
+ for (const context of collectedContexts.values()) if (context !== rootContext && context.assignment) finalizers.push(context.assignment);
1601
+ finalizers.push(rootContext ? rootContext.assignment || identifier(rootContext.name) : generate(value));
1602
+ return {
1603
+ type: "CallExpression",
1604
+ optional: false,
1605
+ arguments: [],
1606
+ callee: {
1607
+ type: "ArrowFunctionExpression",
1608
+ expression: false,
1609
+ params,
1610
+ body: {
1611
+ type: "SequenceExpression",
1612
+ expressions: finalizers
1613
+ }
1614
+ }
1615
+ };
1616
+ }
1617
+ //#endregion
1618
+ //#region ../../node_modules/.pnpm/estree-util-is-identifier-name@3.0.0/node_modules/estree-util-is-identifier-name/lib/index.js
1619
+ const nameRe = /^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u;
1620
+ const nameReJsx = /^[$_\p{ID_Start}][-$_\u{200C}\u{200D}\p{ID_Continue}]*$/u;
1621
+ /** @type {Options} */
1622
+ const emptyOptions = {};
1623
+ /**
1624
+ * Checks if the given value is a valid identifier name.
1625
+ *
1626
+ * @param {string} name
1627
+ * Identifier to check.
1628
+ * @param {Options | null | undefined} [options]
1629
+ * Configuration (optional).
1630
+ * @returns {boolean}
1631
+ * Whether `name` can be an identifier.
1632
+ */
1633
+ function name(name, options) {
1634
+ return ((options || emptyOptions).jsx ? nameReJsx : nameRe).test(name);
1635
+ }
1636
+ //#endregion
1637
+ //#region ../../node_modules/.pnpm/estree-util-scope@1.0.0/node_modules/estree-util-scope/lib/index.js
1638
+ /**
1639
+ * @import {Node, Pattern} from 'estree'
1640
+ * @import {Scope, Visitors} from './types.js'
1641
+ */
1642
+ /**
1643
+ * Create state to track what’s defined.
1644
+ *
1645
+ * @returns {Visitors}
1646
+ * State.
1647
+ */
1648
+ function createVisitors() {
1649
+ /** @type {[topLevel: Scope, ...rest: Array<Scope>]} */
1650
+ const scopes = [{
1651
+ block: false,
1652
+ defined: []
1653
+ }];
1654
+ return {
1655
+ enter,
1656
+ exit,
1657
+ scopes
1658
+ };
1659
+ /**
1660
+ * @param {Node} node
1661
+ * Node.
1662
+ * @returns {undefined}
1663
+ * Nothing.
1664
+ */
1665
+ function enter(node) {
1666
+ if (node.type === "ArrowFunctionExpression") {
1667
+ scopes.push({
1668
+ block: false,
1669
+ defined: []
1670
+ });
1671
+ for (const parameter of node.params) definePattern(parameter, false);
1672
+ } else if (node.type === "BlockStatement" || node.type === "DoWhileStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement" || node.type === "ForStatement" || node.type === "WhileStatement") scopes.push({
1673
+ block: true,
1674
+ defined: []
1675
+ });
1676
+ else if (node.type === "CatchClause") {
1677
+ scopes.push({
1678
+ block: true,
1679
+ defined: []
1680
+ });
1681
+ if (node.param) definePattern(node.param, true);
1682
+ } else if (node.type === "ClassDeclaration") defineIdentifier(node.id.name, false);
1683
+ else if (node.type === "FunctionDeclaration") {
1684
+ defineIdentifier(node.id.name, false);
1685
+ scopes.push({
1686
+ block: false,
1687
+ defined: []
1688
+ });
1689
+ for (const parameter of node.params) definePattern(parameter, false);
1690
+ } else if (node.type === "FunctionExpression") {
1691
+ if (node.id) defineIdentifier(node.id.name, false);
1692
+ scopes.push({
1693
+ block: false,
1694
+ defined: []
1695
+ });
1696
+ for (const parameter of node.params) definePattern(parameter, false);
1697
+ } else if (node.type === "ImportDeclaration") for (const specifier of node.specifiers) defineIdentifier(specifier.local.name, false);
1698
+ else if (node.type === "VariableDeclaration") for (const declaration of node.declarations) definePattern(declaration.id, node.kind !== "var");
1699
+ }
1700
+ /**
1701
+ * @param {Node} node
1702
+ * Node.
1703
+ * @returns {undefined}
1704
+ * Nothing.
1705
+ */
1706
+ function exit(node) {
1707
+ if (node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression") scopes.pop().block;
1708
+ else if (node.type === "BlockStatement" || node.type === "CatchClause" || node.type === "DoWhileStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement" || node.type === "ForStatement" || node.type === "WhileStatement") scopes.pop().block;
1709
+ }
1710
+ /**
1711
+ * Define an identifier in a scope.
1712
+ *
1713
+ * @param {string} id
1714
+ * @param {boolean} block
1715
+ * @returns {undefined}
1716
+ */
1717
+ function defineIdentifier(id, block) {
1718
+ let index = scopes.length;
1719
+ /** @type {Scope | undefined} */
1720
+ let scope;
1721
+ while (index--) {
1722
+ scope = scopes[index];
1723
+ if (block || !scope.block) break;
1724
+ }
1725
+ scope.defined.push(id);
1726
+ }
1727
+ /**
1728
+ * Define a pattern in a scope.
1729
+ *
1730
+ * @param {Pattern} pattern
1731
+ * @param {boolean} block
1732
+ */
1733
+ function definePattern(pattern, block) {
1734
+ if (pattern.type === "ArrayPattern") {
1735
+ for (const element of pattern.elements) if (element) definePattern(element, block);
1736
+ } else if (pattern.type === "AssignmentPattern") definePattern(pattern.left, block);
1737
+ else if (pattern.type === "Identifier") defineIdentifier(pattern.name, block);
1738
+ else if (pattern.type === "ObjectPattern") for (const property of pattern.properties) if (property.type === "Property") definePattern(property.value, block);
1739
+ else {
1740
+ property.type;
1741
+ definePattern(property, block);
1742
+ }
1743
+ else {
1744
+ pattern.type;
1745
+ definePattern(pattern.argument, block);
1746
+ }
1747
+ }
1748
+ }
1749
+ //#endregion
1750
+ //#region ../../node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/walker.js
1751
+ /**
1752
+ * @typedef { import('estree').Node} Node
1753
+ * @typedef {{
1754
+ * skip: () => void;
1755
+ * remove: () => void;
1756
+ * replace: (node: Node) => void;
1757
+ * }} WalkerContext
1758
+ */
1759
+ var WalkerBase = class {
1760
+ constructor() {
1761
+ /** @type {boolean} */
1762
+ this.should_skip = false;
1763
+ /** @type {boolean} */
1764
+ this.should_remove = false;
1765
+ /** @type {Node | null} */
1766
+ this.replacement = null;
1767
+ /** @type {WalkerContext} */
1768
+ this.context = {
1769
+ skip: () => this.should_skip = true,
1770
+ remove: () => this.should_remove = true,
1771
+ replace: (node) => this.replacement = node
1772
+ };
1773
+ }
1774
+ /**
1775
+ * @template {Node} Parent
1776
+ * @param {Parent | null | undefined} parent
1777
+ * @param {keyof Parent | null | undefined} prop
1778
+ * @param {number | null | undefined} index
1779
+ * @param {Node} node
1780
+ */
1781
+ replace(parent, prop, index, node) {
1782
+ if (parent && prop) if (index != null)
1783
+ /** @type {Array<Node>} */ parent[prop][index] = node;
1784
+ else
1785
+ /** @type {Node} */ parent[prop] = node;
1786
+ }
1787
+ /**
1788
+ * @template {Node} Parent
1789
+ * @param {Parent | null | undefined} parent
1790
+ * @param {keyof Parent | null | undefined} prop
1791
+ * @param {number | null | undefined} index
1792
+ */
1793
+ remove(parent, prop, index) {
1794
+ if (parent && prop) if (index !== null && index !== void 0)
1795
+ /** @type {Array<Node>} */ parent[prop].splice(index, 1);
1796
+ else delete parent[prop];
1797
+ }
1798
+ };
1799
+ //#endregion
1800
+ //#region ../../node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/sync.js
1801
+ /**
1802
+ * @typedef { import('estree').Node} Node
1803
+ * @typedef { import('./walker.js').WalkerContext} WalkerContext
1804
+ * @typedef {(
1805
+ * this: WalkerContext,
1806
+ * node: Node,
1807
+ * parent: Node | null,
1808
+ * key: string | number | symbol | null | undefined,
1809
+ * index: number | null | undefined
1810
+ * ) => void} SyncHandler
1811
+ */
1812
+ var SyncWalker = class extends WalkerBase {
1813
+ /**
1814
+ *
1815
+ * @param {SyncHandler} [enter]
1816
+ * @param {SyncHandler} [leave]
1817
+ */
1818
+ constructor(enter, leave) {
1819
+ super();
1820
+ /** @type {boolean} */
1821
+ this.should_skip = false;
1822
+ /** @type {boolean} */
1823
+ this.should_remove = false;
1824
+ /** @type {Node | null} */
1825
+ this.replacement = null;
1826
+ /** @type {WalkerContext} */
1827
+ this.context = {
1828
+ skip: () => this.should_skip = true,
1829
+ remove: () => this.should_remove = true,
1830
+ replace: (node) => this.replacement = node
1831
+ };
1832
+ /** @type {SyncHandler | undefined} */
1833
+ this.enter = enter;
1834
+ /** @type {SyncHandler | undefined} */
1835
+ this.leave = leave;
1836
+ }
1837
+ /**
1838
+ * @template {Node} Parent
1839
+ * @param {Node} node
1840
+ * @param {Parent | null} parent
1841
+ * @param {keyof Parent} [prop]
1842
+ * @param {number | null} [index]
1843
+ * @returns {Node | null}
1844
+ */
1845
+ visit(node, parent, prop, index) {
1846
+ if (node) {
1847
+ if (this.enter) {
1848
+ const _should_skip = this.should_skip;
1849
+ const _should_remove = this.should_remove;
1850
+ const _replacement = this.replacement;
1851
+ this.should_skip = false;
1852
+ this.should_remove = false;
1853
+ this.replacement = null;
1854
+ this.enter.call(this.context, node, parent, prop, index);
1855
+ if (this.replacement) {
1856
+ node = this.replacement;
1857
+ this.replace(parent, prop, index, node);
1858
+ }
1859
+ if (this.should_remove) this.remove(parent, prop, index);
1860
+ const skipped = this.should_skip;
1861
+ const removed = this.should_remove;
1862
+ this.should_skip = _should_skip;
1863
+ this.should_remove = _should_remove;
1864
+ this.replacement = _replacement;
1865
+ if (skipped) return node;
1866
+ if (removed) return null;
1867
+ }
1868
+ /** @type {keyof Node} */
1869
+ let key;
1870
+ for (key in node) {
1871
+ /** @type {unknown} */
1872
+ const value = node[key];
1873
+ if (value && typeof value === "object") {
1874
+ if (Array.isArray(value)) {
1875
+ const nodes = value;
1876
+ for (let i = 0; i < nodes.length; i += 1) {
1877
+ const item = nodes[i];
1878
+ if (isNode(item)) {
1879
+ if (!this.visit(item, node, key, i)) i--;
1880
+ }
1881
+ }
1882
+ } else if (isNode(value)) this.visit(value, node, key, null);
1883
+ }
1884
+ }
1885
+ if (this.leave) {
1886
+ const _replacement = this.replacement;
1887
+ const _should_remove = this.should_remove;
1888
+ this.replacement = null;
1889
+ this.should_remove = false;
1890
+ this.leave.call(this.context, node, parent, prop, index);
1891
+ if (this.replacement) {
1892
+ node = this.replacement;
1893
+ this.replace(parent, prop, index, node);
1894
+ }
1895
+ if (this.should_remove) this.remove(parent, prop, index);
1896
+ const removed = this.should_remove;
1897
+ this.replacement = _replacement;
1898
+ this.should_remove = _should_remove;
1899
+ if (removed) return null;
1900
+ }
1901
+ }
1902
+ return node;
1903
+ }
1904
+ };
1905
+ /**
1906
+ * Ducktype a node.
1907
+ *
1908
+ * @param {unknown} value
1909
+ * @returns {value is Node}
1910
+ */
1911
+ function isNode(value) {
1912
+ return value !== null && typeof value === "object" && "type" in value && typeof value.type === "string";
1913
+ }
1914
+ //#endregion
1915
+ //#region ../../node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/index.js
1916
+ /**
1917
+ * @typedef {import('estree').Node} Node
1918
+ * @typedef {import('./sync.js').SyncHandler} SyncHandler
1919
+ * @typedef {import('./async.js').AsyncHandler} AsyncHandler
1920
+ */
1921
+ /**
1922
+ * @param {Node} ast
1923
+ * @param {{
1924
+ * enter?: SyncHandler
1925
+ * leave?: SyncHandler
1926
+ * }} walker
1927
+ * @returns {Node | null}
1928
+ */
1929
+ function walk(ast, { enter, leave }) {
1930
+ return new SyncWalker(enter, leave).visit(ast, null);
1931
+ }
1932
+ //#endregion
1933
+ //#region ../../node_modules/.pnpm/unist-util-mdx-define@1.1.2/node_modules/unist-util-mdx-define/dist/unist-util-mdx-define.js
1934
+ /**
1935
+ * @param program
1936
+ * The ESTree program to scan.
1937
+ * @param file
1938
+ * The {@link VFile} to emit warnings to.
1939
+ * @param variables
1940
+ * The variables that should be injected.
1941
+ * @param options
1942
+ * {@link define}.options
1943
+ * @returns
1944
+ * The position in the body where the export may be injected.
1945
+ */
1946
+ function scan(program, file, variables, options) {
1947
+ const visitors = createVisitors();
1948
+ const [scope] = visitors.scopes;
1949
+ const identifiers = /* @__PURE__ */ new Map();
1950
+ let injectIndex = 0;
1951
+ walk(program, {
1952
+ enter(node, parent) {
1953
+ visitors.enter(node);
1954
+ switch (node.type) {
1955
+ case "Identifier":
1956
+ if (scope.defined.includes(node.name) && !identifiers.has(node.name)) identifiers.set(node.name, node);
1957
+ break;
1958
+ case "ArrowFunctionExpression":
1959
+ case "ClassDeclaration":
1960
+ case "ClassExpression":
1961
+ case "FunctionExpression":
1962
+ case "FunctionDeclaration":
1963
+ this.skip();
1964
+ break;
1965
+ case "ExpressionStatement":
1966
+ if (parent === program && node.expression.type === "Literal" && typeof node.expression.value === "string") injectIndex = program.body.indexOf(node) + 1;
1967
+ break;
1968
+ default:
1969
+ }
1970
+ },
1971
+ leave: visitors.exit
1972
+ });
1973
+ for (const name of scope.defined) if (variables.has(name)) {
1974
+ if (options?.conflict !== "skip") {
1975
+ const identifier = identifiers.get(name);
1976
+ const message = file.message(`Variable name conflict: ${name}`, {
1977
+ place: identifier?.loc,
1978
+ ruleId: "conflict",
1979
+ source: "unist-util-mdx-define"
1980
+ });
1981
+ message.url = "https://github.com/remcohaszing/unist-util-mdx-define";
1982
+ if (options?.conflict !== "warn") {
1983
+ message.fatal = true;
1984
+ throw message;
1985
+ }
1986
+ }
1987
+ variables.delete(name);
1988
+ }
1989
+ return injectIndex;
1990
+ }
1991
+ /**
1992
+ * Generate an export named declaration.
1993
+ *
1994
+ * @param variables
1995
+ * The variables for which to generate an declaration.
1996
+ * @param options
1997
+ * {@link define} options
1998
+ * @param returnStatement
1999
+ * The return statement of the program to inject into.
2000
+ * @returns
2001
+ * The export named declaration.
2002
+ */
2003
+ function generate(variables, options, returnStatement) {
2004
+ if (options?.export === "namespace") {
2005
+ const statements = [];
2006
+ for (const [name$1, right] of variables) {
2007
+ const isIdentifier = name(name$1);
2008
+ statements.push({
2009
+ type: "ExpressionStatement",
2010
+ expression: {
2011
+ type: "AssignmentExpression",
2012
+ left: {
2013
+ type: "MemberExpression",
2014
+ computed: !isIdentifier,
2015
+ object: {
2016
+ type: "Identifier",
2017
+ name: "MDXContent"
2018
+ },
2019
+ optional: false,
2020
+ property: isIdentifier ? {
2021
+ type: "Identifier",
2022
+ name: name$1
2023
+ } : {
2024
+ type: "Literal",
2025
+ value: name$1
2026
+ }
2027
+ },
2028
+ operator: "=",
2029
+ right
2030
+ }
2031
+ });
2032
+ }
2033
+ return statements;
2034
+ }
2035
+ const declarations = [];
2036
+ for (const [name, init] of variables) declarations.push({
2037
+ type: "VariableDeclaration",
2038
+ kind: "const",
2039
+ declarations: [{
2040
+ type: "VariableDeclarator",
2041
+ id: {
2042
+ type: "Identifier",
2043
+ name
2044
+ },
2045
+ init
2046
+ }]
2047
+ });
2048
+ if (options?.export === false) return declarations;
2049
+ if (!returnStatement) return declarations.map((declaration) => ({
2050
+ type: "ExportNamedDeclaration",
2051
+ declaration,
2052
+ specifiers: []
2053
+ }));
2054
+ if (returnStatement.argument?.type === "ObjectExpression") returnStatement.argument.properties.splice(-1, 0, ...Array.from(variables.keys(), (name) => ({
2055
+ type: "Property",
2056
+ computed: false,
2057
+ kind: "init",
2058
+ method: false,
2059
+ shorthand: true,
2060
+ key: {
2061
+ type: "Identifier",
2062
+ name
2063
+ },
2064
+ value: {
2065
+ type: "Identifier",
2066
+ name
2067
+ }
2068
+ })));
2069
+ return declarations;
2070
+ }
2071
+ /**
2072
+ * Define variables in an MDX related AST.
2073
+ *
2074
+ * @param ast
2075
+ * The AST in which to define an export
2076
+ * @param file
2077
+ * The {@link VFile} to emit warnings to.
2078
+ * @param variables
2079
+ * A mapping of variables to define. They keys are the names. The values are the ESTree expression
2080
+ * to represent them.
2081
+ * @param options
2082
+ * Additional options to configure behaviour.
2083
+ */
2084
+ function define(ast, file, variables, options) {
2085
+ const map = new Map(Object.entries(variables));
2086
+ if (options?.export !== "namespace") for (const name$2 of map.keys()) {
2087
+ if (name$2 === "_createMdxContent" || name$2 === "_Fragment" || name$2 === "_jsx" || name$2 === "_jsxs" || name$2 === "_missingMdxReference" || name$2 === "MDXContent") {
2088
+ const message = file.message(`MDX internal name conflict: ${name$2}`, {
2089
+ ruleId: "internal",
2090
+ source: "unist-util-mdx-define"
2091
+ });
2092
+ message.url = "https://github.com/remcohaszing/unist-util-mdx-define";
2093
+ message.fatal = true;
2094
+ throw message;
2095
+ }
2096
+ if (!name(name$2)) {
2097
+ const message = file.message(`Invalid identifier name: ${name$2}`, {
2098
+ ruleId: "invalid-identifier",
2099
+ source: "unist-util-mdx-define"
2100
+ });
2101
+ message.url = "https://github.com/remcohaszing/unist-util-mdx-define";
2102
+ message.fatal = true;
2103
+ throw message;
2104
+ }
2105
+ }
2106
+ if (ast.type === "root") {
2107
+ for (const child of ast.children) {
2108
+ if (child.type !== "mdxjsEsm") continue;
2109
+ const program = child.data?.estree;
2110
+ /* c8 ignore start */
2111
+ if (!program) continue;
2112
+ /* c8 ignore stop */
2113
+ scan(program, file, map, options);
2114
+ }
2115
+ if (map.size) ast.children.unshift({
2116
+ type: "mdxjsEsm",
2117
+ value: "",
2118
+ data: { estree: {
2119
+ type: "Program",
2120
+ sourceType: "module",
2121
+ body: generate(map, options)
2122
+ } }
2123
+ });
2124
+ } else {
2125
+ const returnStatement = ast.body.find((node) => node.type === "ReturnStatement");
2126
+ const injectIndex = scan(ast, file, map, options);
2127
+ if (map.size) ast.body.splice(injectIndex, 0, ...generate(map, options, returnStatement));
2128
+ }
2129
+ }
2130
+ //#endregion
2131
+ //#region src/markdown/remark-mdx-toc.ts
2132
+ function remarkMdxToc(options = {}) {
2133
+ const { name = "toc", levels = [2, 3] } = options;
2134
+ const [minLevel, maxLevel] = levels;
2135
+ return function(tree, file) {
2136
+ const items = [];
2137
+ let headingIndex = 0;
2138
+ visit(tree, "heading", (node) => {
2139
+ if (node.depth < minLevel || node.depth > maxLevel) return;
2140
+ const text = getHeadingText(node);
2141
+ const slug = slugify(text);
2142
+ const id = slug === "" ? `heading-${String(headingIndex)}` : slug;
2143
+ headingIndex++;
2144
+ items.push({
2145
+ id,
2146
+ text,
2147
+ level: node.depth
2148
+ });
2149
+ const hProperties = ensureHProperties(node);
2150
+ hProperties.id = id;
2151
+ });
2152
+ define(tree, file, { [name]: valueToEstree(items) });
2153
+ };
1047
2154
  }
1048
2155
  function getHeadingText(node) {
1049
- const textParts = [];
1050
- function extractText(child) {
1051
- if (!child || typeof child !== "object") return;
1052
- const typedChild = child;
1053
- if (typedChild.type === "text") {
1054
- textParts.push(typedChild.value || "");
1055
- } else if (typedChild.type === "inlineCode") {
1056
- textParts.push(typedChild.value || "");
1057
- } else if (Array.isArray(typedChild.children)) {
1058
- typedChild.children.forEach(extractText);
1059
- }
1060
- }
1061
- node.children.forEach(extractText);
1062
- return textParts.join("");
2156
+ const parts = [];
2157
+ function extract(child) {
2158
+ if (!isRecord(child)) return;
2159
+ if (child.type === "text" || child.type === "inlineCode") parts.push(typeof child.value === "string" ? child.value : "");
2160
+ else if (Array.isArray(child.children)) for (const nested of child.children) extract(nested);
2161
+ }
2162
+ for (const child of node.children) extract(child);
2163
+ return parts.join("");
1063
2164
  }
1064
2165
  function slugify(text) {
1065
- return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
1066
- }
1067
- function buildTocTree(headings, _minLevel) {
1068
- const result = [];
1069
- const stack = [];
1070
- for (const heading of headings) {
1071
- const item = {
1072
- id: heading.id,
1073
- text: heading.text,
1074
- level: heading.level
1075
- };
1076
- while (stack.length > 0 && stack[stack.length - 1].level >= heading.level) {
1077
- stack.pop();
1078
- }
1079
- if (stack.length === 0) {
1080
- result.push(item);
1081
- } else {
1082
- const parent = stack[stack.length - 1].item;
1083
- if (!parent.children) {
1084
- parent.children = [];
1085
- }
1086
- parent.children.push(item);
1087
- }
1088
- stack.push({ item, level: heading.level });
1089
- }
1090
- return result;
2166
+ let slug = text.toLowerCase().trim().replaceAll(/[^\s\w-]/g, "").replaceAll(/[\s_]/g, "-");
2167
+ while (slug.includes("--")) slug = slug.replaceAll("--", "-");
2168
+ return slug.replaceAll(/^-|-$/g, "");
1091
2169
  }
1092
-
1093
- // src/markdown/links.ts
1094
- import { visit as visit3 } from "unist-util-visit";
1095
- function rehypeLinks(options) {
1096
- const { basePath } = options;
1097
- const normalizedBase = basePath === "/" ? "" : basePath.replace(/\/$/, "");
1098
- return (tree) => {
1099
- if (!normalizedBase) {
1100
- return;
1101
- }
1102
- visit3(tree, "element", (node) => {
1103
- if (node.tagName === "a") {
1104
- const href = node.properties?.href;
1105
- if (typeof href === "string") {
1106
- if (href.startsWith("/") && !href.startsWith("//") && !href.startsWith(normalizedBase)) {
1107
- node.properties = node.properties || {};
1108
- node.properties.href = normalizedBase + href;
1109
- }
1110
- }
1111
- }
1112
- });
1113
- };
2170
+ function ensureHProperties(node) {
2171
+ const data = node.data ?? {};
2172
+ node.data = data;
2173
+ if (!isRecord(data.hProperties)) data.hProperties = {};
2174
+ return data.hProperties;
1114
2175
  }
1115
-
1116
- // src/markdown/pipeline.ts
1117
- async function transformMarkdown(content, config, options = {}) {
1118
- const { data: frontmatter, content: markdownContent } = matter2(content);
1119
- const { basePath = "/", highlighter: providedHighlighter } = options;
1120
- const tocExtraction = { toc: [] };
1121
- const highlighter = providedHighlighter ?? await createShikiHighlighter(config);
1122
- const processor = unified().use(remarkParse).use(remarkFrontmatter2, ["yaml"]).use(remarkGfm2).use(remarkExtractToc, { tocExtraction, levels: config.toc?.level ?? [2, 3] }).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeShikiFromHighlighter, { highlighter, config }).use(rehypeLinks, { basePath }).use(rehypeStringify, { allowDangerousHtml: true });
1123
- if (config.remarkPlugins) {
1124
- for (const plugin of config.remarkPlugins) {
1125
- processor.use(plugin);
1126
- }
1127
- }
1128
- if (config.rehypePlugins) {
1129
- for (const plugin of config.rehypePlugins) {
1130
- processor.use(plugin);
1131
- }
1132
- }
1133
- const result = await processor.process(markdownContent);
1134
- return {
1135
- html: String(result),
1136
- frontmatter,
1137
- toc: tocExtraction.toc
1138
- };
2176
+ function isRecord(value) {
2177
+ return value != null && typeof value === "object";
1139
2178
  }
1140
- async function transformMarkdownToReact(content, config) {
1141
- return transformMarkdown(content, config);
2179
+ //#endregion
2180
+ //#region src/vite/recma-wrap-export.ts
2181
+ function findDefaultExport(body) {
2182
+ for (const [i, node] of body.entries()) if (node.type === "ExportDefaultDeclaration" && node.declaration?.type === "FunctionDeclaration" && node.declaration.id?.name !== void 0 && node.declaration.id.name !== "") return {
2183
+ index: i,
2184
+ name: node.declaration.id.name
2185
+ };
2186
+ return {
2187
+ index: -1,
2188
+ name: ""
2189
+ };
1142
2190
  }
1143
-
1144
- // src/runtime/loader.ts
1145
- async function loadDoc(options) {
1146
- const { slug, contentDir, config } = options;
1147
- const possiblePaths = [
1148
- path3.join(contentDir, `${slug}.md`),
1149
- path3.join(contentDir, slug, "index.md")
1150
- ];
1151
- let filePath = null;
1152
- let fileContent = null;
1153
- for (const tryPath of possiblePaths) {
1154
- try {
1155
- fileContent = await fs3.readFile(tryPath, "utf-8");
1156
- filePath = tryPath;
1157
- break;
1158
- } catch {
1159
- continue;
1160
- }
1161
- }
1162
- if (!filePath || !fileContent) {
1163
- return null;
1164
- }
1165
- const result = await transformMarkdown(fileContent, config.markdown);
1166
- const relativePath = path3.relative(contentDir, filePath);
1167
- let lastUpdated;
1168
- try {
1169
- const stat = await fs3.stat(filePath);
1170
- lastUpdated = stat.mtimeMs;
1171
- } catch {
1172
- }
1173
- return {
1174
- content: result.html,
1175
- frontmatter: result.frontmatter,
1176
- toc: result.toc,
1177
- filePath,
1178
- relativePath,
1179
- lastUpdated
1180
- };
2191
+ function createImport(imported, local, source) {
2192
+ return {
2193
+ type: "ImportDeclaration",
2194
+ specifiers: [{
2195
+ type: "ImportSpecifier",
2196
+ imported: {
2197
+ type: "Identifier",
2198
+ name: imported
2199
+ },
2200
+ local: {
2201
+ type: "Identifier",
2202
+ name: local
2203
+ }
2204
+ }],
2205
+ source: {
2206
+ type: "Literal",
2207
+ value: source
2208
+ }
2209
+ };
1181
2210
  }
1182
- async function loadAllDocs(contentDir, config) {
1183
- const docs = [];
1184
- async function scanDir(dir) {
1185
- const entries = await fs3.readdir(dir, { withFileTypes: true });
1186
- for (const entry of entries) {
1187
- const fullPath = path3.join(dir, entry.name);
1188
- if (entry.isDirectory()) {
1189
- await scanDir(fullPath);
1190
- } else if (entry.name.endsWith(".md")) {
1191
- const fileContent = await fs3.readFile(fullPath, "utf-8");
1192
- const result = await transformMarkdown(fileContent, config.markdown);
1193
- const relativePath = path3.relative(contentDir, fullPath);
1194
- let lastUpdated;
1195
- try {
1196
- const stat = await fs3.stat(fullPath);
1197
- lastUpdated = stat.mtimeMs;
1198
- } catch {
1199
- }
1200
- docs.push({
1201
- title: result.frontmatter.title || formatTitle2(entry.name.replace(/\.md$/, "")),
1202
- description: result.frontmatter.description,
1203
- frontmatter: result.frontmatter,
1204
- content: result.html,
1205
- toc: result.toc,
1206
- filePath: fullPath,
1207
- relativePath,
1208
- lastUpdated
1209
- });
1210
- }
1211
- }
1212
- }
1213
- await scanDir(contentDir);
1214
- return docs;
1215
- }
1216
- function formatTitle2(name) {
1217
- return name.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
2211
+ function createShorthandProp(name) {
2212
+ return {
2213
+ type: "Property",
2214
+ key: {
2215
+ type: "Identifier",
2216
+ name
2217
+ },
2218
+ value: {
2219
+ type: "Identifier",
2220
+ name
2221
+ },
2222
+ kind: "init",
2223
+ shorthand: true,
2224
+ computed: false,
2225
+ method: false
2226
+ };
1218
2227
  }
1219
- function getSlugFromPath(relativePath) {
1220
- return relativePath.replace(/\.md$/, "").replace(/\/index$/, "").replace(/\\/g, "/");
2228
+ function createWrapperFunction(fnName) {
2229
+ return {
2230
+ type: "FunctionDeclaration",
2231
+ id: {
2232
+ type: "Identifier",
2233
+ name: "_ArdoWrapped"
2234
+ },
2235
+ params: [{
2236
+ type: "Identifier",
2237
+ name: "props"
2238
+ }],
2239
+ body: {
2240
+ type: "BlockStatement",
2241
+ body: [{
2242
+ type: "ReturnStatement",
2243
+ argument: {
2244
+ type: "CallExpression",
2245
+ callee: {
2246
+ type: "Identifier",
2247
+ name: "_ardoJsx"
2248
+ },
2249
+ arguments: [{
2250
+ type: "Identifier",
2251
+ name: "_ArdoPageDP"
2252
+ }, {
2253
+ type: "ObjectExpression",
2254
+ properties: [
2255
+ createShorthandProp("frontmatter"),
2256
+ createShorthandProp("toc"),
2257
+ {
2258
+ type: "Property",
2259
+ key: {
2260
+ type: "Identifier",
2261
+ name: "children"
2262
+ },
2263
+ value: {
2264
+ type: "CallExpression",
2265
+ callee: {
2266
+ type: "Identifier",
2267
+ name: "_ardoJsx"
2268
+ },
2269
+ arguments: [{
2270
+ type: "Identifier",
2271
+ name: fnName
2272
+ }, {
2273
+ type: "Identifier",
2274
+ name: "props"
2275
+ }],
2276
+ optional: false
2277
+ },
2278
+ kind: "init",
2279
+ shorthand: false,
2280
+ computed: false,
2281
+ method: false
2282
+ }
2283
+ ]
2284
+ }],
2285
+ optional: false
2286
+ }
2287
+ }]
2288
+ },
2289
+ generator: false,
2290
+ async: false
2291
+ };
1221
2292
  }
1222
- function getPageDataForRoute(docs, slug) {
1223
- return docs.find((doc) => {
1224
- const docSlug = getSlugFromPath(doc.relativePath);
1225
- return docSlug === slug || docSlug === `${slug}/index`;
1226
- });
2293
+ function recmaWrapExport() {
2294
+ return (tree) => {
2295
+ const { index, name } = findDefaultExport(tree.body);
2296
+ if (index === -1) return;
2297
+ const decl = tree.body[index].declaration;
2298
+ if (decl == null) return;
2299
+ tree.body[index] = decl;
2300
+ tree.body.unshift(createImport("ArdoPageDataProvider", "_ArdoPageDP", "ardo/runtime"), createImport("jsx", "_ardoJsx", "react/jsx-runtime"));
2301
+ tree.body.push(createWrapperFunction(name), {
2302
+ type: "ExportDefaultDeclaration",
2303
+ declaration: {
2304
+ type: "Identifier",
2305
+ name: "_ArdoWrapped"
2306
+ }
2307
+ });
2308
+ };
2309
+ }
2310
+ //#endregion
2311
+ //#region src/vite/mdx-plugin.ts
2312
+ function createMdxPlugin(markdownConfig) {
2313
+ const themeConfig = markdownConfig?.theme ?? defaultMarkdownConfig.theme;
2314
+ const lineNumbers = markdownConfig?.lineNumbers ?? false;
2315
+ const shikiOptions = isShikiThemeObject(themeConfig) ? {
2316
+ themes: {
2317
+ light: themeConfig.light,
2318
+ dark: themeConfig.dark
2319
+ },
2320
+ defaultColor: false,
2321
+ transformers: [ardoLineTransformer({ globalLineNumbers: lineNumbers })]
2322
+ } : {
2323
+ theme: themeConfig,
2324
+ transformers: [ardoLineTransformer({ globalLineNumbers: lineNumbers })]
2325
+ };
2326
+ return mdx({
2327
+ include: /\.(md|mdx)$/,
2328
+ remarkPlugins: [
2329
+ remarkFrontmatter,
2330
+ [remarkMdxFrontmatter, { name: "frontmatter" }],
2331
+ remarkGfm,
2332
+ remarkCodeMeta,
2333
+ [remarkMdxToc, { levels: markdownConfig?.toc?.level ?? [2, 3] }]
2334
+ ],
2335
+ rehypePlugins: [[rehypeShiki, shikiOptions]],
2336
+ recmaPlugins: [recmaWrapExport],
2337
+ providerImportSource: "ardo/mdx-provider"
2338
+ });
2339
+ }
2340
+ function getReactRouterPlugins() {
2341
+ const routerPlugin = reactRouter();
2342
+ return Array.isArray(routerPlugin) ? routerPlugin : [routerPlugin];
2343
+ }
2344
+ function isShikiThemeObject(themeConfig) {
2345
+ return typeof themeConfig === "object" && themeConfig != null && "light" in themeConfig && "dark" in themeConfig;
2346
+ }
2347
+ //#endregion
2348
+ //#region src/vite/project-meta.ts
2349
+ /**
2350
+ * Reads project metadata from package.json.
2351
+ */
2352
+ function readProjectMeta(root) {
2353
+ const packageJsonPath = path.join(root, "package.json");
2354
+ try {
2355
+ const rawPackageJson = fsSync.readFileSync(packageJsonPath, "utf8");
2356
+ const parsedPackageJson = JSON.parse(rawPackageJson);
2357
+ if (!isPackageJsonShape(parsedPackageJson)) return {};
2358
+ const repository = extractRepository(parsedPackageJson.repository);
2359
+ const author = extractAuthor(parsedPackageJson.author);
2360
+ return {
2361
+ name: parsedPackageJson.name,
2362
+ homepage: parsedPackageJson.homepage,
2363
+ repository,
2364
+ version: parsedPackageJson.version,
2365
+ author,
2366
+ license: parsedPackageJson.license
2367
+ };
2368
+ } catch {
2369
+ return {};
2370
+ }
2371
+ }
2372
+ function extractRepository(repository) {
2373
+ if (typeof repository === "string") return normalizeRepository(repository);
2374
+ if (repository == null || typeof repository.url !== "string") return;
2375
+ return normalizeRepository(repository.url);
2376
+ }
2377
+ function extractAuthor(author) {
2378
+ if (typeof author === "string") return author;
2379
+ if (author == null || typeof author.name !== "string") return;
2380
+ return author.name;
2381
+ }
2382
+ function normalizeRepository(repository) {
2383
+ return repository.replace(/^git\+/, "").replace(/^git:\/\//, "https://").replace(/\.git$/, "");
2384
+ }
2385
+ function isPackageJsonShape(value) {
2386
+ return typeof value === "object" && value != null;
2387
+ }
2388
+ //#endregion
2389
+ //#region src/vite/routes-core.ts
2390
+ function scanRoutesSync(params) {
2391
+ const { dir, rootDir } = params;
2392
+ const routes = [];
2393
+ const entries = readDirectoryEntries(dir);
2394
+ for (const entry of entries) {
2395
+ const fullPath = path.join(dir, entry.name);
2396
+ if (entry.isDirectory()) {
2397
+ routes.push(...scanRoutesSync({
2398
+ dir: fullPath,
2399
+ rootDir
2400
+ }));
2401
+ continue;
2402
+ }
2403
+ const route = createRouteInfo({
2404
+ entryName: entry.name,
2405
+ fullPath,
2406
+ rootDir
2407
+ });
2408
+ if (route != null) routes.push(route);
2409
+ }
2410
+ return routes;
1227
2411
  }
2412
+ function readDirectoryEntries(dir) {
2413
+ try {
2414
+ return fsSync.readdirSync(dir, { withFileTypes: true });
2415
+ } catch {
2416
+ return [];
2417
+ }
2418
+ }
2419
+ function createRouteInfo(params) {
2420
+ const { entryName, fullPath, rootDir } = params;
2421
+ if (!isRouteFile(entryName) || isIgnoredRouteFile(entryName)) return null;
2422
+ const extension = getRouteExtension(entryName);
2423
+ if (extension == null) return null;
2424
+ const relativePath = path.relative(rootDir, fullPath);
2425
+ const baseName = entryName.replace(extension, "");
2426
+ const urlPath = toRoutePath({
2427
+ baseName,
2428
+ extension,
2429
+ relativePath
2430
+ });
2431
+ return {
2432
+ file: `routes/${relativePath.replaceAll("\\", "/")}`,
2433
+ isIndex: isIndexRoute(baseName),
2434
+ path: urlPath
2435
+ };
2436
+ }
2437
+ function isRouteFile(entryName) {
2438
+ return entryName.endsWith(".md") || entryName.endsWith(".mdx") || entryName.endsWith(".tsx");
2439
+ }
2440
+ function isIgnoredRouteFile(entryName) {
2441
+ return entryName === "root.tsx" || entryName.startsWith("_");
2442
+ }
2443
+ function getRouteExtension(entryName) {
2444
+ if (entryName.endsWith(".mdx")) return ".mdx";
2445
+ if (entryName.endsWith(".md")) return ".md";
2446
+ if (entryName.endsWith(".tsx")) return ".tsx";
2447
+ return null;
2448
+ }
2449
+ function toRoutePath(params) {
2450
+ const { baseName, extension, relativePath } = params;
2451
+ if (isIndexRoute(baseName)) {
2452
+ const parentDir = path.dirname(relativePath);
2453
+ return applyDynamicSegments(`/${parentDir === "." ? "" : parentDir.replaceAll("\\", "/")}`);
2454
+ }
2455
+ return applyDynamicSegments(`/${relativePath.replace(extension, "").replaceAll("\\", "/")}`);
2456
+ }
2457
+ function isIndexRoute(baseName) {
2458
+ return baseName === "index" || baseName === "home";
2459
+ }
2460
+ function applyDynamicSegments(urlPath) {
2461
+ return urlPath.replaceAll(/\$(\w+)/gu, ":$1");
2462
+ }
2463
+ function generateRoutesFile(routes) {
2464
+ return `// AUTO-GENERATED by Ardo - Do not edit manually
1228
2465
 
1229
- // src/runtime/sidebar.ts
1230
- import fs4 from "fs/promises";
1231
- import path4 from "path";
1232
- import matter3 from "gray-matter";
1233
- async function generateSidebar2(options) {
1234
- const { contentDir, basePath, config } = options;
1235
- const configSidebar = config.themeConfig.sidebar;
1236
- if (configSidebar) {
1237
- if (Array.isArray(configSidebar) && configSidebar.length > 0) {
1238
- return configSidebar;
1239
- }
1240
- if (!Array.isArray(configSidebar)) {
1241
- return [];
1242
- }
1243
- }
1244
- return await scanDirectoryForSidebar(contentDir, contentDir, basePath);
1245
- }
1246
- async function scanDirectoryForSidebar(dir, rootDir, _basePath) {
1247
- let entries;
1248
- try {
1249
- entries = await fs4.readdir(dir, { withFileTypes: true });
1250
- } catch {
1251
- return [];
1252
- }
1253
- const items = [];
1254
- for (const entry of entries) {
1255
- const fullPath = path4.join(dir, entry.name);
1256
- const relativePath = path4.relative(rootDir, fullPath);
1257
- if (entry.name.startsWith(".") || entry.name.startsWith("_")) {
1258
- continue;
1259
- }
1260
- if (entry.isDirectory()) {
1261
- const children = await scanDirectoryForSidebar(fullPath, rootDir, _basePath);
1262
- if (children.length > 0) {
1263
- const indexPath = path4.join(fullPath, "index.md");
1264
- let link;
1265
- let title = formatTitle3(entry.name);
1266
- let order;
1267
- try {
1268
- const indexContent = await fs4.readFile(indexPath, "utf-8");
1269
- const { data: frontmatter } = matter3(indexContent);
1270
- if (frontmatter.title) {
1271
- title = frontmatter.title;
1272
- }
1273
- if (typeof frontmatter.order === "number") {
1274
- order = frontmatter.order;
1275
- }
1276
- link = normalizePath(relativePath);
1277
- } catch {
1278
- }
1279
- items.push({
1280
- text: title,
1281
- link,
1282
- collapsed: false,
1283
- items: children,
1284
- order
1285
- });
1286
- }
1287
- } else if (entry.name.endsWith(".md") && entry.name !== "index.md") {
1288
- const fileContent = await fs4.readFile(fullPath, "utf-8");
1289
- const { data: frontmatter } = matter3(fileContent);
1290
- if (frontmatter.sidebar === false) {
1291
- continue;
1292
- }
1293
- const title = frontmatter.title || formatTitle3(entry.name.replace(/\.md$/, ""));
1294
- const order = typeof frontmatter.order === "number" ? frontmatter.order : void 0;
1295
- const link = normalizePath(relativePath.replace(/\.md$/, ""));
1296
- items.push({
1297
- text: title,
1298
- link,
1299
- order
1300
- });
1301
- }
1302
- }
1303
- items.sort((a, b) => {
1304
- if (a.order !== void 0 && b.order !== void 0) {
1305
- return a.order - b.order;
1306
- }
1307
- if (a.order !== void 0) return -1;
1308
- if (b.order !== void 0) return 1;
1309
- return a.text.localeCompare(b.text);
1310
- });
1311
- return items.map(({ order: _order, ...item }) => item);
1312
- }
1313
- function formatTitle3(name) {
1314
- return name.replace(/^\d+-/, "").replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
2466
+ import { type RouteConfig, route, index } from "@react-router/dev/routes"
2467
+
2468
+ export default [
2469
+ ${sortRoutes(routes).map((routeInfo) => renderRouteEntry(routeInfo)).join("\n")}
2470
+ ] satisfies RouteConfig
2471
+ `;
1315
2472
  }
1316
- function normalizePath(p) {
1317
- return "/" + p.replace(/\\/g, "/").replace(/^\/+/, "");
1318
- }
1319
- export {
1320
- ardoPlugin as ardo,
1321
- ardoPlugin,
1322
- ardoRoutesPlugin,
1323
- createShikiHighlighter,
1324
- ardoPlugin as default,
1325
- detectGitHubBasename,
1326
- generateSidebar2 as generateSidebar,
1327
- getPageDataForRoute,
1328
- getSlugFromPath,
1329
- highlightCode,
1330
- loadAllDocs,
1331
- loadDoc,
1332
- transformMarkdown,
1333
- transformMarkdownToReact
2473
+ function sortRoutes(routes) {
2474
+ return [...routes].sort((left, right) => {
2475
+ if (left.path === "/" && right.path !== "/") return -1;
2476
+ if (right.path === "/" && left.path !== "/") return 1;
2477
+ if (left.isIndex && !right.isIndex) return -1;
2478
+ if (right.isIndex && !left.isIndex) return 1;
2479
+ return left.path.localeCompare(right.path);
2480
+ });
2481
+ }
2482
+ function renderRouteEntry(routeInfo) {
2483
+ if (routeInfo.path === "/") return ` index("${routeInfo.file}"),`;
2484
+ return ` route("${routeInfo.path.slice(1)}", "${routeInfo.file}"),`;
2485
+ }
2486
+ function writeRoutesFileSync(params) {
2487
+ const { appDir, routesDir, routesFilePath } = params;
2488
+ const routes = scanRoutesSync({
2489
+ dir: routesDir,
2490
+ rootDir: routesDir
2491
+ });
2492
+ if (routes.length === 0) return;
2493
+ const content = generateRoutesFile(routes);
2494
+ if (!hasRoutesContentChangedSync(routesFilePath, content)) return;
2495
+ fsSync.mkdirSync(appDir, { recursive: true });
2496
+ fsSync.writeFileSync(routesFilePath, content, "utf8");
2497
+ console.log(`[ardo] Generated routes.ts with ${routes.length} routes`);
2498
+ }
2499
+ function hasRoutesContentChangedSync(routesFilePath, nextContent) {
2500
+ try {
2501
+ return fsSync.readFileSync(routesFilePath, "utf8") !== nextContent;
2502
+ } catch {
2503
+ return true;
2504
+ }
2505
+ }
2506
+ async function writeRoutesFile(params) {
2507
+ const { appDir, routesDir, routesFilePath } = params;
2508
+ const routes = scanRoutesSync({
2509
+ dir: routesDir,
2510
+ rootDir: routesDir
2511
+ });
2512
+ if (routes.length === 0) return;
2513
+ const content = generateRoutesFile(routes);
2514
+ if (!await hasRoutesContentChanged(routesFilePath, content)) return;
2515
+ await fs.mkdir(appDir, { recursive: true });
2516
+ await fs.writeFile(routesFilePath, content, "utf8");
2517
+ }
2518
+ async function hasRoutesContentChanged(routesFilePath, nextContent) {
2519
+ try {
2520
+ return await fs.readFile(routesFilePath, "utf8") !== nextContent;
2521
+ } catch {
2522
+ return true;
2523
+ }
2524
+ }
2525
+ //#endregion
2526
+ //#region src/vite/routes-plugin.ts
2527
+ /**
2528
+ * Vite plugin that generates routes.ts for React Router Framework Mode.
2529
+ */
2530
+ function ardoRoutesPlugin(options = {}) {
2531
+ let paths = createDefaultPaths(process.cwd(), options);
2532
+ return {
2533
+ name: "ardo:routes",
2534
+ enforce: "pre",
2535
+ config(userConfig) {
2536
+ paths = createDefaultPaths(userConfig.root ?? process.cwd(), options);
2537
+ try {
2538
+ writeRoutesFileSync(paths);
2539
+ } catch (error) {
2540
+ console.warn("[ardo] Could not generate routes.ts in config phase:", error);
2541
+ }
2542
+ },
2543
+ configResolved(resolvedConfig) {
2544
+ paths = createDefaultPaths(resolvedConfig.root, options);
2545
+ },
2546
+ async buildStart() {
2547
+ await writeRoutesFile(paths);
2548
+ },
2549
+ configureServer(server) {
2550
+ server.watcher.add(paths.routesDir);
2551
+ const handleChange = (changedPath) => {
2552
+ if (shouldHandleRouteChange(changedPath, paths.routesDir)) writeRoutesFile(paths);
2553
+ };
2554
+ server.watcher.on("add", handleChange);
2555
+ server.watcher.on("unlink", handleChange);
2556
+ }
2557
+ };
2558
+ }
2559
+ function createDefaultPaths(root, options) {
2560
+ const appDir = path.join(root, "app");
2561
+ return {
2562
+ appDir,
2563
+ routesDir: options.routesDir ?? path.join(appDir, "routes"),
2564
+ routesFilePath: path.join(appDir, "routes.ts")
2565
+ };
2566
+ }
2567
+ function shouldHandleRouteChange(changedPath, routesDir) {
2568
+ if (!changedPath.startsWith(routesDir)) return false;
2569
+ return changedPath.endsWith(".md") || changedPath.endsWith(".mdx") || changedPath.endsWith(".tsx");
2570
+ }
2571
+ //#endregion
2572
+ //#region src/vite/search-index.ts
2573
+ async function generateSearchIndex(routesDir) {
2574
+ const context = {
2575
+ docs: [],
2576
+ routesDir
2577
+ };
2578
+ await scanDirectoryForSearch(routesDir, void 0, context);
2579
+ return context.docs;
2580
+ }
2581
+ async function scanDirectoryForSearch(dir, section, context) {
2582
+ let entries;
2583
+ try {
2584
+ entries = await fs.readdir(dir, { withFileTypes: true });
2585
+ } catch (error) {
2586
+ const errorMessage = error instanceof Error ? error.message : String(error);
2587
+ console.warn("[ardo] Failed to scan for search index:", errorMessage);
2588
+ return;
2589
+ }
2590
+ for (const entry of entries) await processSearchEntry(entry, {
2591
+ dir,
2592
+ section,
2593
+ scanContext: context
2594
+ });
2595
+ }
2596
+ async function processSearchEntry(entry, context) {
2597
+ const fullPath = path.join(context.dir, entry.name);
2598
+ if (entry.isDirectory()) {
2599
+ await scanDirectoryForSearch(fullPath, createNestedSection(context.section, entry.name), context.scanContext);
2600
+ return;
2601
+ }
2602
+ if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) {
2603
+ const doc = await createSearchDocFromFile(entry.name, fullPath, {
2604
+ routesDir: context.scanContext.routesDir,
2605
+ section: context.section
2606
+ });
2607
+ if (doc != null) context.scanContext.docs.push(doc);
2608
+ }
2609
+ }
2610
+ async function createSearchDocFromFile(fileName, filePath, context) {
2611
+ const parsed = matter(await fs.readFile(filePath, "utf8"));
2612
+ const extension = fileName.endsWith(".mdx") ? ".mdx" : ".md";
2613
+ const title = typeof parsed.data.title === "string" ? parsed.data.title : formatTitle$1(fileName.replace(extension, ""));
2614
+ const relativePath = path.relative(context.routesDir, filePath);
2615
+ const routePath = buildRoutePath(relativePath, fileName, extension);
2616
+ return {
2617
+ id: relativePath,
2618
+ title,
2619
+ content: sanitizeSearchContent(parsed.content),
2620
+ path: routePath,
2621
+ section: context.section
2622
+ };
2623
+ }
2624
+ function sanitizeSearchContent(content) {
2625
+ return collapseWhitespace(replacePunctuationWithSpaces(removeImportLines(removeCodeFences(content)))).slice(0, 2e3);
2626
+ }
2627
+ function removeCodeFences(content) {
2628
+ const lines = content.split("\n");
2629
+ const keptLines = [];
2630
+ let isInsideFence = false;
2631
+ for (const line of lines) {
2632
+ if (line.trimStart().startsWith("```")) {
2633
+ isInsideFence = !isInsideFence;
2634
+ continue;
2635
+ }
2636
+ if (!isInsideFence) keptLines.push(line);
2637
+ }
2638
+ return keptLines.join("\n");
2639
+ }
2640
+ function removeImportLines(content) {
2641
+ const lines = content.split("\n");
2642
+ const keptLines = [];
2643
+ for (const line of lines) if (!line.trimStart().startsWith("import ")) keptLines.push(line);
2644
+ return keptLines.join("\n");
2645
+ }
2646
+ function replacePunctuationWithSpaces(content) {
2647
+ let normalized = content;
2648
+ for (const token of [
2649
+ "`",
2650
+ "#",
2651
+ "*",
2652
+ "_",
2653
+ "~",
2654
+ "[",
2655
+ "]",
2656
+ "(",
2657
+ ")",
2658
+ "<",
2659
+ ">",
2660
+ "|",
2661
+ "!"
2662
+ ]) normalized = normalized.replaceAll(token, " ");
2663
+ return normalized;
2664
+ }
2665
+ function collapseWhitespace(content) {
2666
+ let result = "";
2667
+ let previousWasSpace = false;
2668
+ for (const character of content) {
2669
+ if (character === " " || character === "\n" || character === " " || character === "\r") {
2670
+ if (!previousWasSpace) result += " ";
2671
+ previousWasSpace = true;
2672
+ continue;
2673
+ }
2674
+ result += character;
2675
+ previousWasSpace = false;
2676
+ }
2677
+ return result.trim();
2678
+ }
2679
+ function buildRoutePath(relativePath, fileName, extension) {
2680
+ if (fileName === "index.mdx" || fileName === "index.md") {
2681
+ const directoryPath = path.dirname(relativePath).replaceAll("\\", "/");
2682
+ return directoryPath === "." ? "/" : `/${directoryPath}`;
2683
+ }
2684
+ return `/${relativePath.replace(extension, "").replaceAll("\\", "/")}`;
2685
+ }
2686
+ function createNestedSection(section, directoryName) {
2687
+ const currentTitle = formatTitle$1(directoryName);
2688
+ return section != null ? `${section} > ${currentTitle}` : currentTitle;
2689
+ }
2690
+ function formatTitle$1(name) {
2691
+ return name.replaceAll(/[_-]/g, " ").replaceAll(/\b\w/g, (char) => char.toUpperCase());
2692
+ }
2693
+ //#endregion
2694
+ //#region src/vite/sidebar-index.ts
2695
+ async function generateSidebar$1(routesDir) {
2696
+ try {
2697
+ return (await scanSidebarDirectory(routesDir, routesDir)).map((node) => stripOrderFromNode(node));
2698
+ } catch {
2699
+ return [];
2700
+ }
2701
+ }
2702
+ async function scanSidebarDirectory(dir, rootDir) {
2703
+ const entries = await fs.readdir(dir, { withFileTypes: true });
2704
+ const nodes = [];
2705
+ for (const entry of entries) {
2706
+ const fullPath = path.join(dir, entry.name);
2707
+ const relativePath = path.relative(rootDir, fullPath);
2708
+ if (entry.isDirectory()) {
2709
+ const directoryNode = await createDirectoryNode(entry, fullPath, rootDir);
2710
+ if (directoryNode != null) nodes.push(directoryNode);
2711
+ continue;
2712
+ }
2713
+ const fileNode = await createMarkdownNode(entry, fullPath, relativePath);
2714
+ if (fileNode != null) nodes.push(fileNode);
2715
+ }
2716
+ sortNodes(nodes);
2717
+ return nodes;
2718
+ }
2719
+ async function createDirectoryNode(entry, fullPath, rootDir) {
2720
+ const relativePath = path.relative(rootDir, fullPath);
2721
+ const children = await scanSidebarDirectory(fullPath, rootDir);
2722
+ if (children.length === 0) return null;
2723
+ const link = await fileExists(path.join(fullPath, "index.mdx")) ? `/${relativePath.replaceAll("\\", "/")}` : void 0;
2724
+ return {
2725
+ text: formatTitle(entry.name),
2726
+ link,
2727
+ items: children
2728
+ };
2729
+ }
2730
+ async function createMarkdownNode(entry, fullPath, relativePath) {
2731
+ if (!isSidebarMarkdownFile(entry.name)) return null;
2732
+ const extension = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
2733
+ const frontmatter = readFrontmatter(await fs.readFile(fullPath, "utf8"));
2734
+ return {
2735
+ text: frontmatter.title ?? formatTitle(entry.name.replace(extension, "")),
2736
+ link: `/${relativePath.replace(extension, "").replaceAll("\\", "/")}`,
2737
+ order: frontmatter.order
2738
+ };
2739
+ }
2740
+ function readFrontmatter(fileContent) {
2741
+ const parsed = matter(fileContent);
2742
+ return {
2743
+ title: typeof parsed.data.title === "string" ? parsed.data.title : void 0,
2744
+ order: typeof parsed.data.order === "number" ? parsed.data.order : void 0
2745
+ };
2746
+ }
2747
+ function sortNodes(nodes) {
2748
+ nodes.sort((leftNode, rightNode) => {
2749
+ if (leftNode.order != null && rightNode.order != null) return leftNode.order - rightNode.order;
2750
+ if (leftNode.order != null) return -1;
2751
+ if (rightNode.order != null) return 1;
2752
+ return leftNode.text.localeCompare(rightNode.text);
2753
+ });
2754
+ }
2755
+ function isSidebarMarkdownFile(fileName) {
2756
+ return (fileName.endsWith(".mdx") || fileName.endsWith(".md")) && !(fileName === "index.mdx" || fileName === "index.md");
2757
+ }
2758
+ function formatTitle(name) {
2759
+ return name.replaceAll(/[_-]/g, " ").replaceAll(/\b\w/g, (char) => char.toUpperCase());
2760
+ }
2761
+ function stripOrderFromNode(node) {
2762
+ return {
2763
+ text: node.text,
2764
+ link: node.link,
2765
+ items: node.items?.map((item) => stripOrderFromNode(item))
2766
+ };
2767
+ }
2768
+ async function fileExists(filePath) {
2769
+ try {
2770
+ await fs.access(filePath);
2771
+ return true;
2772
+ } catch {
2773
+ return false;
2774
+ }
2775
+ }
2776
+ //#endregion
2777
+ //#region src/vite/plugin.ts
2778
+ const VIRTUAL_MODULE_ID = "virtual:ardo/config";
2779
+ const VIRTUAL_SIDEBAR_ID = "virtual:ardo/sidebar";
2780
+ const VIRTUAL_SEARCH_ID = "virtual:ardo/search-index";
2781
+ const RESOLVED_IDS = {
2782
+ [VIRTUAL_MODULE_ID]: `\0${VIRTUAL_MODULE_ID}`,
2783
+ [VIRTUAL_SIDEBAR_ID]: `\0${VIRTUAL_SIDEBAR_ID}`,
2784
+ [VIRTUAL_SEARCH_ID]: `\0${VIRTUAL_SEARCH_ID}`
1334
2785
  };
2786
+ let typedocGenerated = false;
2787
+ function ardoPlugin(options = {}) {
2788
+ const { routes, typedoc, githubPages = true, routesDir: routesDirOption, ...pressConfig } = options;
2789
+ const state = { routesDir: resolveRoutesDir(process.cwd(), routesDirOption) };
2790
+ const plugins = [createMainPlugin(state, {
2791
+ githubPages,
2792
+ pressConfig,
2793
+ routesDirOption
2794
+ })];
2795
+ addRoutesPlugin(plugins, routes, routesDirOption);
2796
+ addTypeDocPlugin(plugins, typedoc, state);
2797
+ plugins.push(ardoCodeBlockPlugin(pressConfig.markdown));
2798
+ plugins.push(createMdxPlugin(pressConfig.markdown));
2799
+ plugins.push(...vanillaExtractPlugin({ identifiers: "short" }));
2800
+ plugins.push(...getReactRouterPlugins());
2801
+ if (githubPages) plugins.push(createFlattenPlugin());
2802
+ return plugins;
2803
+ }
2804
+ function addRoutesPlugin(plugins, routes, routesDirOption) {
2805
+ if (routes === false) return;
2806
+ const routePluginOptions = routes ?? {};
2807
+ plugins.push(ardoRoutesPlugin({
2808
+ routesDir: routesDirOption,
2809
+ ...routePluginOptions
2810
+ }));
2811
+ }
2812
+ function addTypeDocPlugin(plugins, typedoc, state) {
2813
+ const typedocConfig = resolveTypedocConfig(typedoc);
2814
+ if (typedocConfig != null) plugins.unshift(createTypeDocPlugin(typedocConfig, state));
2815
+ }
2816
+ function createMainPlugin(state, options) {
2817
+ return {
2818
+ name: "ardo",
2819
+ enforce: "pre",
2820
+ config(userConfig, env) {
2821
+ return createMainConfig(state, {
2822
+ userConfig,
2823
+ command: env.command,
2824
+ githubPages: options.githubPages,
2825
+ routesDirOption: options.routesDirOption
2826
+ });
2827
+ },
2828
+ configResolved(config) {
2829
+ state.routesDir = resolveRoutesDir(config.root, options.routesDirOption);
2830
+ state.resolvedConfig = resolveArdoConfig(config.root, state.routesDir, options.pressConfig);
2831
+ },
2832
+ resolveId(id) {
2833
+ return resolveVirtualModuleId(id);
2834
+ },
2835
+ async load(id) {
2836
+ return loadVirtualModule(id, state);
2837
+ },
2838
+ transform(code, id) {
2839
+ return transformMarkdownMeta(code, id, state);
2840
+ }
2841
+ };
2842
+ }
2843
+ function createMainConfig(state, input) {
2844
+ const { command, githubPages, routesDirOption, userConfig } = input;
2845
+ const root = userConfig.root ?? process.cwd();
2846
+ state.routesDir = resolveRoutesDir(root, routesDirOption);
2847
+ const config = {
2848
+ define: { __BUILD_TIME__: JSON.stringify((/* @__PURE__ */ new Date()).toISOString()) },
2849
+ optimizeDeps: { exclude: ["ardo/ui/styles.css"] },
2850
+ ssr: { noExternal: ["ardo"] }
2851
+ };
2852
+ if (githubPages && command === "build" && userConfig.base == null) {
2853
+ const repoName = detectGitHubRepoName(root);
2854
+ if (repoName != null) {
2855
+ config.base = `/${repoName}/`;
2856
+ console.log(`[ardo] GitHub Pages detected, using base: ${config.base}`);
2857
+ }
2858
+ }
2859
+ return config;
2860
+ }
2861
+ function resolveArdoConfig(root, routesDir, pressConfig) {
2862
+ const project = {
2863
+ ...readProjectMeta(root),
2864
+ ...pressConfig.project
2865
+ };
2866
+ return resolveConfig({
2867
+ title: pressConfig.title ?? "Ardo",
2868
+ description: pressConfig.description ?? "Documentation powered by Ardo",
2869
+ ...pressConfig,
2870
+ project,
2871
+ srcDir: routesDir
2872
+ }, root);
2873
+ }
2874
+ function resolveVirtualModuleId(id) {
2875
+ return RESOLVED_IDS[id];
2876
+ }
2877
+ async function loadVirtualModule(id, state) {
2878
+ if (state.resolvedConfig == null) return;
2879
+ if (id === RESOLVED_IDS[VIRTUAL_MODULE_ID]) {
2880
+ const clientConfig = {
2881
+ title: state.resolvedConfig.title,
2882
+ description: state.resolvedConfig.description,
2883
+ base: state.resolvedConfig.base,
2884
+ lang: state.resolvedConfig.lang,
2885
+ project: state.resolvedConfig.project,
2886
+ buildTime: (/* @__PURE__ */ new Date()).toISOString(),
2887
+ buildHash: detectGitHash(state.resolvedConfig.root)
2888
+ };
2889
+ return `export default ${JSON.stringify(clientConfig)}`;
2890
+ }
2891
+ if (id === RESOLVED_IDS[VIRTUAL_SIDEBAR_ID]) {
2892
+ const sidebar = await generateSidebar$1(state.routesDir);
2893
+ return `export default ${JSON.stringify(sidebar)}`;
2894
+ }
2895
+ if (id === RESOLVED_IDS[VIRTUAL_SEARCH_ID]) {
2896
+ const searchIndex = await generateSearchIndex(state.routesDir);
2897
+ return `export default ${JSON.stringify(searchIndex)}`;
2898
+ }
2899
+ }
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
+ function resolveRoutesDir(root, routesDirOption) {
2979
+ return routesDirOption ?? path.join(root, "app", "routes");
2980
+ }
2981
+ //#endregion
2982
+ export { ardoPlugin as ardo, ardoPlugin, ardoPlugin as default, ardoRoutesPlugin, createShikiHighlighter, detectGitHubBasename, generateSidebar, getPageDataForRoute, getSlugFromPath, highlightCode, loadAllDocs, loadDoc, transformMarkdown, transformMarkdownToReact };
2983
+
1335
2984
  //# sourceMappingURL=index.js.map