ardo 1.2.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +29 -47
  2. package/dist/{CopyButton-BkACwxQM.d.ts → Features-C_06EvGb.d.ts} +212 -10
  3. package/dist/{chunk-SZVJKB3V.js → chunk-G5L4ZUTS.js} +40 -40
  4. package/dist/chunk-G5L4ZUTS.js.map +1 -0
  5. package/dist/{chunk-VMA2OXSY.js → chunk-LUOUBO3L.js} +587 -669
  6. package/dist/chunk-LUOUBO3L.js.map +1 -0
  7. package/dist/{chunk-3U63LMDZ.js → chunk-N5CEHG2F.js} +3 -3
  8. package/dist/{chunk-3U63LMDZ.js.map → chunk-N5CEHG2F.js.map} +1 -1
  9. package/dist/chunk-OTUACKCQ.js +896 -0
  10. package/dist/chunk-OTUACKCQ.js.map +1 -0
  11. package/dist/chunk-UWAVET45.js +311 -0
  12. package/dist/chunk-UWAVET45.js.map +1 -0
  13. package/dist/chunk-ZXPAEM3M.js +854 -0
  14. package/dist/chunk-ZXPAEM3M.js.map +1 -0
  15. package/dist/config/index.d.ts +2 -2
  16. package/dist/config/index.js +1 -1
  17. package/dist/icons/index.d.ts +1 -0
  18. package/dist/icons/index.js +3 -0
  19. package/dist/icons/index.js.map +1 -0
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.js +34 -26
  22. package/dist/mdx/provider.d.ts +9 -0
  23. package/dist/mdx/provider.js +114 -0
  24. package/dist/mdx/provider.js.map +1 -0
  25. package/dist/runtime/index.d.ts +1 -1
  26. package/dist/typedoc/index.d.ts +25 -0
  27. package/dist/typedoc/index.js +2 -2
  28. package/dist/{types-C22M-Kor.d.ts → types-DchPWkJl.d.ts} +1 -1
  29. package/dist/ui/index.d.ts +116 -0
  30. package/dist/{theme → ui}/index.js +26 -12
  31. package/dist/ui/styles.css +2198 -0
  32. package/dist/vite/index.d.ts +13 -23
  33. package/dist/vite/index.js +5 -5
  34. package/package.json +23 -11
  35. package/dist/chunk-2JBVPO6S.js +0 -1144
  36. package/dist/chunk-2JBVPO6S.js.map +0 -1
  37. package/dist/chunk-SZVJKB3V.js.map +0 -1
  38. package/dist/chunk-VMA2OXSY.js.map +0 -1
  39. package/dist/chunk-YN6PP526.js +0 -441
  40. package/dist/chunk-YN6PP526.js.map +0 -1
  41. package/dist/theme/index.d.ts +0 -70
  42. package/dist/theme/styles.css +0 -1454
  43. /package/dist/{theme → ui}/index.js.map +0 -0
@@ -1,16 +1,477 @@
1
1
  import {
2
2
  resolveConfig
3
- } from "./chunk-3U63LMDZ.js";
3
+ } from "./chunk-N5CEHG2F.js";
4
4
  import {
5
5
  generateApiDocs
6
- } from "./chunk-YN6PP526.js";
6
+ } from "./chunk-OTUACKCQ.js";
7
+
8
+ // src/vite/plugin.ts
9
+ import { reactRouter } from "@react-router/dev/vite";
10
+ import mdx from "@mdx-js/rollup";
11
+ import remarkFrontmatter from "remark-frontmatter";
12
+ import remarkMdxFrontmatter from "remark-mdx-frontmatter";
13
+ import remarkGfm from "remark-gfm";
14
+ import remarkDirective from "remark-directive";
15
+ import rehypeShiki from "@shikijs/rehype";
16
+ import fs2 from "fs/promises";
17
+ import fsSync2 from "fs";
18
+ import path2 from "path";
19
+ import { execSync } from "child_process";
20
+
21
+ // src/vite/routes-plugin.ts
22
+ import fs from "fs/promises";
23
+ import fsSync from "fs";
24
+ import path from "path";
25
+ function ardoRoutesPlugin(options = {}) {
26
+ let routesDir;
27
+ let appDir;
28
+ let routesFilePath;
29
+ function scanRoutesSync(dir, rootDir) {
30
+ const routes = [];
31
+ try {
32
+ const entries = fsSync.readdirSync(dir, { withFileTypes: true });
33
+ for (const entry of entries) {
34
+ const fullPath = path.join(dir, entry.name);
35
+ if (entry.isDirectory()) {
36
+ const children = scanRoutesSync(fullPath, rootDir);
37
+ routes.push(...children);
38
+ } else if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md") || entry.name.endsWith(".tsx")) {
39
+ if (entry.name === "root.tsx" || entry.name.startsWith("_")) {
40
+ continue;
41
+ }
42
+ const relativePath = path.relative(rootDir, fullPath);
43
+ const ext = entry.name.endsWith(".mdx") ? ".mdx" : entry.name.endsWith(".md") ? ".md" : ".tsx";
44
+ const baseName = entry.name.replace(ext, "");
45
+ let urlPath;
46
+ if (baseName === "index" || baseName === "home") {
47
+ const parentDir = path.dirname(relativePath);
48
+ urlPath = parentDir === "." ? "/" : "/" + parentDir.replace(/\\/g, "/");
49
+ } else {
50
+ urlPath = "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
51
+ }
52
+ urlPath = urlPath.replace(/\$(\w+)/g, ":$1");
53
+ routes.push({
54
+ path: urlPath,
55
+ file: "routes/" + relativePath.replace(/\\/g, "/"),
56
+ isIndex: baseName === "index" || baseName === "home"
57
+ });
58
+ }
59
+ }
60
+ } catch {
61
+ }
62
+ return routes;
63
+ }
64
+ function generateRoutesFile(routes) {
65
+ const sortedRoutes = [...routes].sort((a, b) => {
66
+ if (a.path === "/" && b.path !== "/") return -1;
67
+ if (b.path === "/" && a.path !== "/") return 1;
68
+ if (a.isIndex && !b.isIndex) return -1;
69
+ if (b.isIndex && !a.isIndex) return 1;
70
+ return a.path.localeCompare(b.path);
71
+ });
72
+ const entries = sortedRoutes.map((r) => {
73
+ if (r.path === "/") {
74
+ return ` index("${r.file}"),`;
75
+ }
76
+ const routePath = r.path.substring(1);
77
+ return ` route("${routePath}", "${r.file}"),`;
78
+ });
79
+ return `// AUTO-GENERATED by Ardo - Do not edit manually
80
+
81
+ import { type RouteConfig, route, index } from "@react-router/dev/routes"
82
+
83
+ export default [
84
+ ${entries.join("\n")}
85
+ ] satisfies RouteConfig
86
+ `;
87
+ }
88
+ function writeRoutesFileSync() {
89
+ const routes = scanRoutesSync(routesDir, routesDir);
90
+ if (routes.length === 0) {
91
+ return;
92
+ }
93
+ const content = generateRoutesFile(routes);
94
+ try {
95
+ const existing = fsSync.readFileSync(routesFilePath, "utf-8");
96
+ if (existing === content) {
97
+ return;
98
+ }
99
+ } catch {
100
+ }
101
+ fsSync.mkdirSync(appDir, { recursive: true });
102
+ fsSync.writeFileSync(routesFilePath, content, "utf-8");
103
+ console.log(`[ardo] Generated routes.ts with ${routes.length} routes`);
104
+ }
105
+ async function writeRoutesFile() {
106
+ const routes = scanRoutesSync(routesDir, routesDir);
107
+ if (routes.length === 0) {
108
+ return;
109
+ }
110
+ const content = generateRoutesFile(routes);
111
+ try {
112
+ const existing = await fs.readFile(routesFilePath, "utf-8");
113
+ if (existing === content) {
114
+ return;
115
+ }
116
+ } catch {
117
+ }
118
+ await fs.mkdir(appDir, { recursive: true });
119
+ await fs.writeFile(routesFilePath, content, "utf-8");
120
+ }
121
+ return {
122
+ name: "ardo:routes",
123
+ enforce: "pre",
124
+ config(userConfig) {
125
+ const root = userConfig.root || process.cwd();
126
+ appDir = path.join(root, "app");
127
+ routesDir = options.routesDir || path.join(appDir, "routes");
128
+ routesFilePath = path.join(appDir, "routes.ts");
129
+ try {
130
+ writeRoutesFileSync();
131
+ } catch (err) {
132
+ console.warn("[ardo] Could not generate routes.ts in config phase:", err);
133
+ }
134
+ },
135
+ configResolved(config) {
136
+ if (!appDir) {
137
+ appDir = path.join(config.root, "app");
138
+ routesDir = options.routesDir || path.join(appDir, "routes");
139
+ routesFilePath = path.join(appDir, "routes.ts");
140
+ }
141
+ },
142
+ async buildStart() {
143
+ await writeRoutesFile();
144
+ },
145
+ configureServer(server) {
146
+ server.watcher.add(routesDir);
147
+ const handleChange = async (changedPath) => {
148
+ if (changedPath.startsWith(routesDir) && (changedPath.endsWith(".mdx") || changedPath.endsWith(".md") || changedPath.endsWith(".tsx"))) {
149
+ await writeRoutesFile();
150
+ }
151
+ };
152
+ server.watcher.on("add", handleChange);
153
+ server.watcher.on("unlink", handleChange);
154
+ }
155
+ };
156
+ }
157
+
158
+ // src/vite/plugin.ts
159
+ function findPackageRoot(cwd) {
160
+ let dir = path2.resolve(cwd);
161
+ const root = path2.parse(dir).root;
162
+ while (dir !== root) {
163
+ const parentDir = path2.dirname(dir);
164
+ const packageJsonPath = path2.join(parentDir, "package.json");
165
+ if (fsSync2.existsSync(packageJsonPath)) {
166
+ return path2.relative(cwd, parentDir) || ".";
167
+ }
168
+ dir = parentDir;
169
+ }
170
+ return void 0;
171
+ }
172
+ function detectGitHubRepoName(cwd) {
173
+ try {
174
+ const remoteUrl = execSync("git remote get-url origin", {
175
+ cwd,
176
+ encoding: "utf-8",
177
+ stdio: ["pipe", "pipe", "pipe"]
178
+ }).trim();
179
+ const match = remoteUrl.match(/github\.com[/:][\w-]+\/([\w.-]+?)(?:\.git)?$/);
180
+ return match?.[1];
181
+ } catch {
182
+ return void 0;
183
+ }
184
+ }
185
+ var VIRTUAL_MODULE_ID = "virtual:ardo/config";
186
+ var RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
187
+ var VIRTUAL_SIDEBAR_ID = "virtual:ardo/sidebar";
188
+ var RESOLVED_VIRTUAL_SIDEBAR_ID = "\0" + VIRTUAL_SIDEBAR_ID;
189
+ var VIRTUAL_SEARCH_ID = "virtual:ardo/search-index";
190
+ var RESOLVED_VIRTUAL_SEARCH_ID = "\0" + VIRTUAL_SEARCH_ID;
191
+ var typedocGenerated = false;
192
+ function ardoPlugin(options = {}) {
193
+ let resolvedConfig;
194
+ let routesDir;
195
+ const {
196
+ routes,
197
+ typedoc,
198
+ githubPages = true,
199
+ routesDir: routesDirOption,
200
+ ...pressConfig
201
+ } = options;
202
+ const mainPlugin = {
203
+ name: "ardo",
204
+ enforce: "pre",
205
+ config(userConfig, env) {
206
+ const root = userConfig.root || process.cwd();
207
+ routesDir = routesDirOption || path2.join(root, "app", "routes");
208
+ const result = {
209
+ optimizeDeps: {
210
+ exclude: ["ardo/ui/styles.css"]
211
+ },
212
+ ssr: {
213
+ noExternal: ["ardo"]
214
+ }
215
+ };
216
+ if (githubPages && env.command === "build" && !userConfig.base) {
217
+ const repoName = detectGitHubRepoName(root);
218
+ if (repoName) {
219
+ result.base = `/${repoName}/`;
220
+ console.log(`[ardo] GitHub Pages detected, using base: ${result.base}`);
221
+ }
222
+ }
223
+ return result;
224
+ },
225
+ async configResolved(config) {
226
+ const root = config.root;
227
+ routesDir = routesDirOption || path2.join(root, "app", "routes");
228
+ const defaultConfig = {
229
+ title: pressConfig.title ?? "Ardo",
230
+ description: pressConfig.description ?? "Documentation powered by Ardo"
231
+ };
232
+ const configWithRoutes = {
233
+ ...defaultConfig,
234
+ ...pressConfig,
235
+ srcDir: routesDir
236
+ };
237
+ resolvedConfig = resolveConfig(configWithRoutes, root);
238
+ },
239
+ resolveId(id) {
240
+ if (id === VIRTUAL_MODULE_ID) {
241
+ return RESOLVED_VIRTUAL_MODULE_ID;
242
+ }
243
+ if (id === VIRTUAL_SIDEBAR_ID) {
244
+ return RESOLVED_VIRTUAL_SIDEBAR_ID;
245
+ }
246
+ if (id === VIRTUAL_SEARCH_ID) {
247
+ return RESOLVED_VIRTUAL_SEARCH_ID;
248
+ }
249
+ },
250
+ async load(id) {
251
+ if (id === RESOLVED_VIRTUAL_MODULE_ID) {
252
+ const clientConfig = {
253
+ title: resolvedConfig.title,
254
+ description: resolvedConfig.description,
255
+ base: resolvedConfig.base,
256
+ lang: resolvedConfig.lang,
257
+ themeConfig: resolvedConfig.themeConfig
258
+ };
259
+ return `export default ${JSON.stringify(clientConfig)}`;
260
+ }
261
+ if (id === RESOLVED_VIRTUAL_SIDEBAR_ID) {
262
+ const sidebar = await generateSidebar(resolvedConfig, routesDir);
263
+ return `export default ${JSON.stringify(sidebar)}`;
264
+ }
265
+ if (id === RESOLVED_VIRTUAL_SEARCH_ID) {
266
+ const searchIndex = await generateSearchIndex(routesDir);
267
+ return `export default ${JSON.stringify(searchIndex)}`;
268
+ }
269
+ }
270
+ };
271
+ const plugins = [mainPlugin];
272
+ if (routes !== false) {
273
+ plugins.push(
274
+ ardoRoutesPlugin({
275
+ routesDir: routesDirOption,
276
+ ...routes
277
+ })
278
+ );
279
+ }
280
+ if (typedoc) {
281
+ const packageRoot = findPackageRoot(process.cwd());
282
+ const defaultEntryPoint = packageRoot ? `${packageRoot}/src/index.ts` : "./src/index.ts";
283
+ const defaultTsconfig = packageRoot ? `${packageRoot}/tsconfig.json` : "./tsconfig.json";
284
+ const defaultTypedocConfig = {
285
+ enabled: true,
286
+ entryPoints: [defaultEntryPoint],
287
+ tsconfig: defaultTsconfig,
288
+ out: "api-reference",
289
+ excludePrivate: true,
290
+ excludeInternal: true
291
+ };
292
+ const typedocConfig = typedoc === true ? defaultTypedocConfig : { ...defaultTypedocConfig, ...typedoc };
293
+ const typedocPlugin = {
294
+ name: "ardo:typedoc",
295
+ async buildStart() {
296
+ if (typedocGenerated || !typedocConfig.enabled) {
297
+ return;
298
+ }
299
+ console.log("[ardo] Generating API documentation with TypeDoc...");
300
+ const startTime = Date.now();
301
+ try {
302
+ const outputDir = routesDirOption || "./app/routes";
303
+ const docs = await generateApiDocs(typedocConfig, outputDir);
304
+ const duration = Date.now() - startTime;
305
+ console.log(`[ardo] Generated ${docs.length} API documentation pages in ${duration}ms`);
306
+ } catch (error) {
307
+ console.warn("[ardo] TypeDoc generation failed. API documentation will not be available.");
308
+ console.warn("[ardo] Check your typedoc.entryPoints configuration.");
309
+ if (error instanceof Error) {
310
+ console.warn(`[ardo] Error: ${error.message}`);
311
+ }
312
+ }
313
+ typedocGenerated = true;
314
+ }
315
+ };
316
+ plugins.unshift(typedocPlugin);
317
+ }
318
+ const themeConfig = pressConfig.markdown?.theme;
319
+ const hasThemeObject = themeConfig && typeof themeConfig === "object" && "light" in themeConfig;
320
+ const shikiOptions = hasThemeObject ? {
321
+ themes: {
322
+ light: themeConfig.light || "github-light",
323
+ dark: themeConfig.dark || "github-dark"
324
+ },
325
+ defaultColor: false
326
+ } : {
327
+ theme: themeConfig || "github-dark"
328
+ };
329
+ const mdxPlugin = mdx({
330
+ include: /\.(md|mdx)$/,
331
+ remarkPlugins: [
332
+ remarkFrontmatter,
333
+ [remarkMdxFrontmatter, { name: "frontmatter" }],
334
+ remarkGfm,
335
+ remarkDirective
336
+ ],
337
+ rehypePlugins: [[rehypeShiki, shikiOptions]],
338
+ providerImportSource: "ardo/mdx-provider"
339
+ });
340
+ plugins.push(mdxPlugin);
341
+ const reactRouterPlugin = reactRouter();
342
+ const reactRouterPlugins = (Array.isArray(reactRouterPlugin) ? reactRouterPlugin : [reactRouterPlugin]).filter((p) => p != null);
343
+ plugins.push(...reactRouterPlugins);
344
+ return plugins;
345
+ }
346
+ async function generateSidebar(config, routesDir) {
347
+ const { themeConfig } = config;
348
+ if (themeConfig.sidebar && !Array.isArray(themeConfig.sidebar)) {
349
+ return themeConfig.sidebar;
350
+ }
351
+ if (themeConfig.sidebar && Array.isArray(themeConfig.sidebar) && themeConfig.sidebar.length > 0) {
352
+ return themeConfig.sidebar;
353
+ }
354
+ try {
355
+ const sidebar = await scanDirectory(routesDir, routesDir, config.base);
356
+ return sidebar;
357
+ } catch {
358
+ return [];
359
+ }
360
+ }
361
+ async function scanDirectory(dir, rootDir, _basePath) {
362
+ const entries = await fs2.readdir(dir, { withFileTypes: true });
363
+ const items = [];
364
+ for (const entry of entries) {
365
+ const fullPath = path2.join(dir, entry.name);
366
+ const relativePath = path2.relative(rootDir, fullPath);
367
+ if (entry.isDirectory()) {
368
+ const children = await scanDirectory(fullPath, rootDir, _basePath);
369
+ if (children.length > 0) {
370
+ const indexPath = path2.join(fullPath, "index.mdx");
371
+ let link;
372
+ try {
373
+ await fs2.access(indexPath);
374
+ link = "/" + relativePath.replace(/\\/g, "/");
375
+ } catch {
376
+ }
377
+ items.push({
378
+ text: formatTitle(entry.name),
379
+ link,
380
+ items: children
381
+ });
382
+ }
383
+ } else if ((entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) && entry.name !== "index.mdx" && entry.name !== "index.md") {
384
+ const fileContent = await fs2.readFile(fullPath, "utf-8");
385
+ const frontmatterMatch = fileContent.match(/^---\n([\s\S]*?)\n---/);
386
+ const ext = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
387
+ let title = formatTitle(entry.name.replace(ext, ""));
388
+ let order;
389
+ if (frontmatterMatch) {
390
+ const frontmatterText = frontmatterMatch[1];
391
+ const titleMatch = frontmatterText.match(/title:\s*["']?([^"'\n]+)["']?/);
392
+ const orderMatch = frontmatterText.match(/order:\s*(\d+)/);
393
+ if (titleMatch) {
394
+ title = titleMatch[1].trim();
395
+ }
396
+ if (orderMatch) {
397
+ order = parseInt(orderMatch[1], 10);
398
+ }
399
+ }
400
+ const link = "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
401
+ items.push({
402
+ text: title,
403
+ link,
404
+ order
405
+ });
406
+ }
407
+ }
408
+ items.sort((a, b) => {
409
+ if (a.order !== void 0 && b.order !== void 0) {
410
+ return a.order - b.order;
411
+ }
412
+ if (a.order !== void 0) return -1;
413
+ if (b.order !== void 0) return 1;
414
+ return a.text.localeCompare(b.text);
415
+ });
416
+ return items.map(({ order: _order, ...item }) => item);
417
+ }
418
+ function formatTitle(name) {
419
+ return name.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
420
+ }
421
+ async function generateSearchIndex(routesDir) {
422
+ const docs = [];
423
+ async function scanForSearch(dir, section) {
424
+ try {
425
+ const entries = await fs2.readdir(dir, { withFileTypes: true });
426
+ for (const entry of entries) {
427
+ const fullPath = path2.join(dir, entry.name);
428
+ if (entry.isDirectory()) {
429
+ const newSection = section ? `${section} > ${formatTitle(entry.name)}` : formatTitle(entry.name);
430
+ await scanForSearch(fullPath, newSection);
431
+ } else if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) {
432
+ const relativePath = path2.relative(routesDir, fullPath);
433
+ const fileContent = await fs2.readFile(fullPath, "utf-8");
434
+ const frontmatterMatch = fileContent.match(/^---\n([\s\S]*?)\n---/);
435
+ const ext = entry.name.endsWith(".mdx") ? ".mdx" : ".md";
436
+ let title = formatTitle(entry.name.replace(ext, ""));
437
+ let content = fileContent;
438
+ if (frontmatterMatch) {
439
+ const frontmatterText = frontmatterMatch[1];
440
+ const titleMatch = frontmatterText.match(/title:\s*["']?([^"'\n]+)["']?/);
441
+ if (titleMatch) {
442
+ title = titleMatch[1].trim();
443
+ }
444
+ content = fileContent.slice(frontmatterMatch[0].length);
445
+ }
446
+ content = content.replace(/```[\s\S]*?```/g, "").replace(/`[^`]+`/g, "").replace(/import\s+.*?from\s+['"].*?['"]/g, "").replace(/<[^>]+>/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[#*_~>]/g, "").replace(/\n+/g, " ").replace(/\s+/g, " ").trim().slice(0, 2e3);
447
+ const routePath = entry.name === "index.mdx" || entry.name === "index.md" ? "/" + path2.dirname(relativePath).replace(/\\/g, "/") : "/" + relativePath.replace(ext, "").replace(/\\/g, "/");
448
+ const finalPath = routePath === "/." ? "/" : routePath;
449
+ docs.push({
450
+ id: relativePath,
451
+ title,
452
+ content,
453
+ path: finalPath,
454
+ section
455
+ });
456
+ }
457
+ }
458
+ } catch {
459
+ }
460
+ }
461
+ await scanForSearch(routesDir);
462
+ return docs;
463
+ }
464
+
465
+ // src/runtime/loader.ts
466
+ import fs3 from "fs/promises";
467
+ import path3 from "path";
7
468
 
8
469
  // src/markdown/pipeline.ts
9
470
  import { unified } from "unified";
10
471
  import remarkParse from "remark-parse";
11
- import remarkGfm from "remark-gfm";
12
- import remarkFrontmatter from "remark-frontmatter";
13
- import remarkDirective from "remark-directive";
472
+ import remarkGfm2 from "remark-gfm";
473
+ import remarkFrontmatter2 from "remark-frontmatter";
474
+ import remarkDirective2 from "remark-directive";
14
475
  import remarkRehype from "remark-rehype";
15
476
  import rehypeStringify from "rehype-stringify";
16
477
  import matter from "gray-matter";
@@ -53,7 +514,7 @@ function remarkContainers() {
53
514
  if (type === "code-group") {
54
515
  data.hName = "div";
55
516
  data.hProperties = {
56
- className: ["press-code-group"]
517
+ className: ["ardo-code-group"]
57
518
  };
58
519
  const tabs = [];
59
520
  for (const child of node.children) {
@@ -66,21 +527,21 @@ function remarkContainers() {
66
527
  }
67
528
  }
68
529
  const tabsHtml = tabs.map(
69
- (tab, i) => `<button class="press-code-group-tab${i === 0 ? " active" : ""}" data-index="${i}">${escapeHtml(tab.label)}</button>`
530
+ (tab, i) => `<button class="ardo-code-group-tab${i === 0 ? " active" : ""}" data-index="${i}">${escapeHtml(tab.label)}</button>`
70
531
  ).join("");
71
532
  node.children = [
72
533
  {
73
534
  type: "html",
74
- value: `<div class="press-code-group-tabs">${tabsHtml}</div>`
535
+ value: `<div class="ardo-code-group-tabs">${tabsHtml}</div>`
75
536
  },
76
537
  {
77
538
  type: "html",
78
- value: '<div class="press-code-group-panels">'
539
+ value: '<div class="ardo-code-group-panels">'
79
540
  },
80
541
  ...tabs.map(
81
542
  (tab, i) => ({
82
543
  type: "html",
83
- value: `<div class="press-code-group-panel${i === 0 ? " active" : ""}" data-index="${i}">`
544
+ value: `<div class="ardo-code-group-panel${i === 0 ? " active" : ""}" data-index="${i}">`
84
545
  })
85
546
  ),
86
547
  ...node.children.flatMap((child, _i) => [
@@ -100,21 +561,21 @@ function remarkContainers() {
100
561
  if (type === "details") {
101
562
  data.hName = "details";
102
563
  data.hProperties = {
103
- className: ["press-details"]
564
+ className: ["ardo-details"]
104
565
  };
105
566
  node.children.unshift({
106
567
  type: "html",
107
- value: `<summary class="press-details-summary">${escapeHtml(title)}</summary>`
568
+ value: `<summary class="ardo-details-summary">${escapeHtml(title)}</summary>`
108
569
  });
109
570
  return;
110
571
  }
111
572
  data.hName = "div";
112
573
  data.hProperties = {
113
- className: ["press-container", `press-container-${type}`]
574
+ className: ["ardo-container", `ardo-container-${type}`]
114
575
  };
115
576
  node.children.unshift({
116
577
  type: "html",
117
- value: `<p class="press-container-title">${escapeHtml(title)}</p>`
578
+ value: `<p class="ardo-container-title">${escapeHtml(title)}</p>`
118
579
  });
119
580
  });
120
581
  };
@@ -276,7 +737,7 @@ function rehypeShikiFromHighlighter(options) {
276
737
  type: "element",
277
738
  tagName: "div",
278
739
  properties: {
279
- className: ["press-code-block"],
740
+ className: ["ardo-code-block"],
280
741
  "data-lang": lang
281
742
  },
282
743
  children: [
@@ -284,678 +745,135 @@ function rehypeShikiFromHighlighter(options) {
284
745
  type: "raw",
285
746
  value: wrapperHtml
286
747
  }
287
- ]
288
- };
289
- parent.children[index] = newNode;
290
- }
291
- } catch {
292
- }
293
- });
294
- };
295
- }
296
- function getTextContent(node) {
297
- if (node.type === "text") {
298
- return node.value;
299
- }
300
- if ("children" in node) {
301
- return node.children.map((child) => getTextContent(child)).join("");
302
- }
303
- return "";
304
- }
305
- function parseHighlightLines(meta) {
306
- const match = meta.match(/\{([\d,-]+)\}/);
307
- if (!match) return [];
308
- const ranges = match[1].split(",");
309
- const lines = [];
310
- for (const range of ranges) {
311
- if (range.includes("-")) {
312
- const [start, end] = range.split("-").map(Number);
313
- for (let i = start; i <= end; i++) {
314
- lines.push(i);
315
- }
316
- } else {
317
- lines.push(Number(range));
318
- }
319
- }
320
- return lines;
321
- }
322
- function parseTitle(meta) {
323
- const match = meta.match(/title="([^"]+)"/);
324
- return match ? match[1] : void 0;
325
- }
326
- function buildCodeBlockHtml(shikiHtml, options) {
327
- const { lang, lineNumbers, highlightLines, title } = options;
328
- let html = "";
329
- if (title) {
330
- html += `<div class="press-code-title">${escapeHtml2(title)}</div>`;
331
- }
332
- html += `<div class="press-code-wrapper" data-lang="${lang}">`;
333
- if (lineNumbers || highlightLines.length > 0) {
334
- const lines = shikiHtml.split("\n");
335
- const processedHtml = lines.map((line, i) => {
336
- const lineNum = i + 1;
337
- const isHighlighted = highlightLines.includes(lineNum);
338
- const classes = ["press-code-line"];
339
- if (isHighlighted) classes.push("highlighted");
340
- let prefix = "";
341
- if (lineNumbers) {
342
- prefix = `<span class="press-line-number">${lineNum}</span>`;
343
- }
344
- return `<span class="${classes.join(" ")}">${prefix}${line}</span>`;
345
- }).join("\n");
346
- html += processedHtml;
347
- } else {
348
- html += shikiHtml;
349
- }
350
- html += `<button class="press-copy-button" data-code="${encodeURIComponent(extractCodeFromHtml(shikiHtml))}">
351
- <span class="press-copy-icon">Copy</span>
352
- <span class="press-copied-icon" style="display:none">Copied!</span>
353
- </button>`;
354
- html += "</div>";
355
- return html;
356
- }
357
- function extractCodeFromHtml(html) {
358
- return html.replace(/<[^>]+>/g, "").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
359
- }
360
- function escapeHtml2(text) {
361
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
362
- }
363
-
364
- // src/markdown/links.ts
365
- import { visit as visit4 } from "unist-util-visit";
366
- function rehypeLinks(options) {
367
- const { basePath } = options;
368
- const normalizedBase = basePath === "/" ? "" : basePath.replace(/\/$/, "");
369
- return (tree) => {
370
- if (!normalizedBase) {
371
- return;
372
- }
373
- visit4(tree, "element", (node) => {
374
- if (node.tagName === "a") {
375
- const href = node.properties?.href;
376
- if (typeof href === "string") {
377
- if (href.startsWith("/") && !href.startsWith("//") && !href.startsWith(normalizedBase)) {
378
- node.properties = node.properties || {};
379
- node.properties.href = normalizedBase + href;
380
- }
381
- }
382
- }
383
- });
384
- };
385
- }
386
-
387
- // src/markdown/pipeline.ts
388
- async function transformMarkdown(content, config, options = {}) {
389
- const { data: frontmatter, content: markdownContent } = matter(content);
390
- const { basePath = "/", highlighter: providedHighlighter } = options;
391
- const tocExtraction = { toc: [] };
392
- const highlighter = providedHighlighter ?? await createShikiHighlighter(config);
393
- const processor = unified().use(remarkParse).use(remarkFrontmatter, ["yaml"]).use(remarkGfm).use(remarkDirective).use(remarkContainers).use(remarkExtractToc, { tocExtraction, levels: config.toc?.level ?? [2, 3] }).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeShikiFromHighlighter, { highlighter, config }).use(rehypeLinks, { basePath }).use(rehypeStringify, { allowDangerousHtml: true });
394
- if (config.remarkPlugins) {
395
- for (const plugin of config.remarkPlugins) {
396
- processor.use(plugin);
397
- }
398
- }
399
- if (config.rehypePlugins) {
400
- for (const plugin of config.rehypePlugins) {
401
- processor.use(plugin);
402
- }
403
- }
404
- const result = await processor.process(markdownContent);
405
- return {
406
- html: String(result),
407
- frontmatter,
408
- toc: tocExtraction.toc
409
- };
410
- }
411
- async function transformMarkdownToReact(content, config) {
412
- return transformMarkdown(content, config);
413
- }
414
-
415
- // src/vite/routes-plugin.ts
416
- import fs from "fs/promises";
417
- import fsSync from "fs";
418
- import path from "path";
419
- function pressRoutesPlugin(getConfig, options = {}) {
420
- const { layoutMode = "layoutRoute" } = options;
421
- let routesDir;
422
- let contentDir;
423
- let isDevMode = false;
424
- let hasCleanedRoutes = false;
425
- function scanContentDirSync(dir, rootDir) {
426
- const routes = [];
427
- try {
428
- const entries = fsSync.readdirSync(dir, { withFileTypes: true });
429
- for (const entry of entries) {
430
- const fullPath = path.join(dir, entry.name);
431
- if (entry.isDirectory()) {
432
- const children = scanContentDirSync(fullPath, rootDir);
433
- routes.push(...children);
434
- } else if (entry.name.endsWith(".md") && entry.name !== "index.md") {
435
- const relativePath = path.relative(rootDir, fullPath);
436
- const slug = relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
437
- routes.push({
438
- slug,
439
- mdPath: fullPath,
440
- relativePath: relativePath.replace(/\\/g, "/")
441
- });
442
- } else if (entry.name === "index.md") {
443
- const parentDir = path.dirname(fullPath);
444
- const relativePath = path.relative(rootDir, fullPath);
445
- if (parentDir !== rootDir) {
446
- const slug = path.relative(rootDir, parentDir).replace(/\\/g, "/");
447
- routes.push({
448
- slug,
449
- mdPath: fullPath,
450
- relativePath: relativePath.replace(/\\/g, "/")
451
- });
452
- }
453
- }
454
- }
455
- } catch {
456
- }
457
- return routes;
458
- }
459
- function generateRouteCode(route) {
460
- const { slug, relativePath } = route;
461
- const depthToProjectRoot = slug.split("/").length + 1;
462
- const toProjectRoot = "../".repeat(depthToProjectRoot);
463
- const contentImportPath = `${toProjectRoot}content/${relativePath}`;
464
- const componentName = slug.split("/").map((part) => part.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())).join("").replace(/\s/g, "") + "Page";
465
- const routePath = `/${slug}`;
466
- const defaultTitle = slug.split("/").pop().replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
467
- const contentComponent = layoutMode === "docPage" ? "DocPage" : "DocContent";
468
- return `// This file is auto-generated by ardo. Do not edit manually.
469
- import { createFileRoute } from '@tanstack/react-router'
470
- import { ${contentComponent} } from 'ardo/theme'
471
- import { PressProvider } from 'ardo/runtime'
472
- import config from 'virtual:ardo/config'
473
- import sidebar from 'virtual:ardo/sidebar'
474
- import Content, { frontmatter, toc } from '${contentImportPath}'
475
-
476
- export const Route = createFileRoute('${routePath}')({
477
- head: () => ({
478
- meta: [
479
- { title: (frontmatter.title as string) ? \`\${frontmatter.title} | \${config.title}\` : config.title },
480
- ...(frontmatter.description ? [{ name: 'description', content: frontmatter.description as string }] : []),
481
- // OpenGraph
482
- { property: 'og:title', content: (frontmatter.title as string) || '${defaultTitle}' },
483
- ...(frontmatter.description ? [{ property: 'og:description', content: frontmatter.description as string }] : []),
484
- { property: 'og:type', content: 'article' },
485
- // Twitter
486
- { name: 'twitter:card', content: 'summary' },
487
- { name: 'twitter:title', content: (frontmatter.title as string) || '${defaultTitle}' },
488
- ...(frontmatter.description ? [{ name: 'twitter:description', content: frontmatter.description as string }] : []),
489
- ],
490
- }),
491
- component: ${componentName},
492
- })
493
-
494
- function ${componentName}() {
495
- const pageData = {
496
- title: (frontmatter.title as string) || '${defaultTitle}',
497
- description: frontmatter.description as string | undefined,
498
- frontmatter,
499
- content: '',
500
- toc,
501
- filePath: '${relativePath}',
502
- relativePath: '${relativePath}',
503
- }
504
-
505
- return (
506
- <PressProvider config={config} sidebar={sidebar} currentPage={pageData}>
507
- <${contentComponent}>
508
- <Content />
509
- </${contentComponent}>
510
- </PressProvider>
511
- )
512
- }
513
- `;
514
- }
515
- function writeRouteFileSync(route) {
516
- const routeFilePath = path.join(routesDir, `${route.slug}.tsx`);
517
- const code = generateRouteCode(route);
518
- try {
519
- const existingContent = fsSync.readFileSync(routeFilePath, "utf-8");
520
- if (existingContent === code) {
521
- return false;
522
- }
523
- } catch {
524
- }
525
- fsSync.mkdirSync(path.dirname(routeFilePath), { recursive: true });
526
- fsSync.writeFileSync(routeFilePath, code, "utf-8");
527
- return true;
528
- }
529
- function generateAllRoutesSync() {
530
- const routes = scanContentDirSync(contentDir, contentDir);
531
- let writtenCount = 0;
532
- for (const route of routes) {
533
- if (writeRouteFileSync(route)) {
534
- writtenCount++;
535
- }
536
- }
537
- if (writtenCount > 0) {
538
- console.log(`[ardo] Generated ${writtenCount} content route files`);
539
- }
540
- }
541
- async function ensureDirectoryExists(dir) {
542
- try {
543
- await fs.mkdir(dir, { recursive: true });
544
- } catch {
545
- }
546
- }
547
- async function writeRouteFile(route) {
548
- const routeFilePath = path.join(routesDir, `${route.slug}.tsx`);
549
- const routeFileDir = path.dirname(routeFilePath);
550
- await ensureDirectoryExists(routeFileDir);
551
- const code = generateRouteCode(route);
552
- try {
553
- const existingContent = await fs.readFile(routeFilePath, "utf-8");
554
- if (existingContent === code) {
555
- return false;
556
- }
557
- } catch {
558
- }
559
- await fs.writeFile(routeFilePath, code, "utf-8");
560
- return true;
561
- }
562
- async function cleanGeneratedRoutes() {
563
- const pressRoutesDir = routesDir;
564
- try {
565
- const entries = await fs.readdir(pressRoutesDir, { withFileTypes: true, recursive: true });
566
- for (const entry of entries) {
567
- if (entry.isFile() && entry.name.endsWith(".tsx") && entry.name !== "_layout.tsx") {
568
- const fullPath = path.join(entry.parentPath ?? pressRoutesDir, entry.name);
569
- try {
570
- const content = await fs.readFile(fullPath, "utf-8");
571
- if (content.startsWith("// This file is auto-generated by ardo")) {
572
- await fs.unlink(fullPath);
573
- }
574
- } catch {
575
- }
576
- }
577
- }
578
- } catch {
579
- }
580
- }
581
- async function scanContentDir(dir, rootDir) {
582
- const routes = [];
583
- const entries = await fs.readdir(dir, { withFileTypes: true });
584
- for (const entry of entries) {
585
- const fullPath = path.join(dir, entry.name);
586
- if (entry.isDirectory()) {
587
- const children = await scanContentDir(fullPath, rootDir);
588
- routes.push(...children);
589
- } else if (entry.name.endsWith(".md") && entry.name !== "index.md") {
590
- const relativePath = path.relative(rootDir, fullPath);
591
- const slug = relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
592
- routes.push({
593
- slug,
594
- mdPath: fullPath,
595
- relativePath: relativePath.replace(/\\/g, "/")
596
- });
597
- } else if (entry.name === "index.md") {
598
- const parentDir = path.dirname(fullPath);
599
- const relativePath = path.relative(rootDir, fullPath);
600
- if (parentDir !== rootDir) {
601
- const slug = path.relative(rootDir, parentDir).replace(/\\/g, "/");
602
- routes.push({
603
- slug,
604
- mdPath: fullPath,
605
- relativePath: relativePath.replace(/\\/g, "/")
606
- });
607
- }
608
- }
609
- }
610
- return routes;
611
- }
612
- async function generateAllRoutes() {
613
- if (isDevMode && !hasCleanedRoutes) {
614
- await cleanGeneratedRoutes();
615
- hasCleanedRoutes = true;
616
- }
617
- const routes = await scanContentDir(contentDir, contentDir);
618
- let writtenCount = 0;
619
- for (const route of routes) {
620
- if (await writeRouteFile(route)) {
621
- writtenCount++;
622
- }
623
- }
624
- if (writtenCount > 0) {
625
- console.log(`[ardo] Generated ${writtenCount} content route files`);
626
- }
627
- }
628
- return {
629
- name: "ardo:routes",
630
- enforce: "pre",
631
- config(userConfig, env) {
632
- const root = userConfig.root || process.cwd();
633
- const srcDir = path.join(root, "src");
634
- routesDir = options.routesDir || path.join(srcDir, "routes");
635
- isDevMode = env.command === "serve";
636
- contentDir = path.resolve(root, options.srcDir || "content");
637
- try {
638
- generateAllRoutesSync();
639
- } catch (err) {
640
- console.warn("[ardo] Could not generate routes in config phase:", err);
641
- }
642
- },
643
- configResolved(viteConfig) {
644
- if (!routesDir) {
645
- routesDir = options.routesDir || path.join(viteConfig.root, "src", "routes");
646
- isDevMode = viteConfig.command === "serve";
647
- }
648
- },
649
- async buildStart() {
650
- const config = getConfig();
651
- contentDir = config.contentDir;
652
- await generateAllRoutes();
653
- },
654
- configureServer(server) {
655
- server.watcher.add(contentDir);
656
- server.watcher.on("change", async (changedPath) => {
657
- if (changedPath.startsWith(contentDir) && changedPath.endsWith(".md")) {
658
- await generateAllRoutes();
659
- }
660
- });
661
- server.watcher.on("add", async (addedPath) => {
662
- if (addedPath.startsWith(contentDir) && addedPath.endsWith(".md")) {
663
- await generateAllRoutes();
664
- }
665
- });
666
- server.watcher.on("unlink", async (removedPath) => {
667
- if (removedPath.startsWith(contentDir) && removedPath.endsWith(".md")) {
668
- hasCleanedRoutes = false;
669
- await generateAllRoutes();
670
- }
671
- });
672
- }
673
- };
674
- }
675
-
676
- // src/vite/plugin.ts
677
- import { tanstackStart } from "@tanstack/react-start/plugin/vite";
678
- import react from "@vitejs/plugin-react";
679
- import fs2 from "fs/promises";
680
- import fsSync2 from "fs";
681
- import path2 from "path";
682
- import { execSync } from "child_process";
683
- function findPackageRoot(cwd) {
684
- let dir = path2.resolve(cwd);
685
- const root = path2.parse(dir).root;
686
- while (dir !== root) {
687
- const parentDir = path2.dirname(dir);
688
- const packageJsonPath = path2.join(parentDir, "package.json");
689
- if (fsSync2.existsSync(packageJsonPath)) {
690
- return path2.relative(cwd, parentDir) || ".";
691
- }
692
- dir = parentDir;
693
- }
694
- return void 0;
695
- }
696
- function detectGitHubRepoName(cwd) {
697
- try {
698
- const remoteUrl = execSync("git remote get-url origin", {
699
- cwd,
700
- encoding: "utf-8",
701
- stdio: ["pipe", "pipe", "pipe"]
702
- }).trim();
703
- const match = remoteUrl.match(/github\.com[/:][\w-]+\/([\w.-]+?)(?:\.git)?$/);
704
- return match?.[1];
705
- } catch {
706
- return void 0;
707
- }
748
+ ]
749
+ };
750
+ parent.children[index] = newNode;
751
+ }
752
+ } catch {
753
+ }
754
+ });
755
+ };
708
756
  }
709
- var VIRTUAL_MODULE_ID = "virtual:ardo/config";
710
- var RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
711
- var VIRTUAL_SIDEBAR_ID = "virtual:ardo/sidebar";
712
- var RESOLVED_VIRTUAL_SIDEBAR_ID = "\0" + VIRTUAL_SIDEBAR_ID;
713
- var SHIKI_CACHE_KEY = "__ardo_shiki_highlighter__";
714
- var shikiHighlighterPromise = null;
715
- function getShikiHighlighter(config) {
716
- if (globalThis[SHIKI_CACHE_KEY]) {
717
- return Promise.resolve(
718
- globalThis[SHIKI_CACHE_KEY]
719
- );
757
+ function getTextContent(node) {
758
+ if (node.type === "text") {
759
+ return node.value;
720
760
  }
721
- if (!shikiHighlighterPromise) {
722
- shikiHighlighterPromise = createShikiHighlighter(config.markdown).then((highlighter) => {
723
- ;
724
- globalThis[SHIKI_CACHE_KEY] = highlighter;
725
- return highlighter;
726
- });
761
+ if ("children" in node) {
762
+ return node.children.map((child) => getTextContent(child)).join("");
727
763
  }
728
- return shikiHighlighterPromise;
764
+ return "";
729
765
  }
730
- function ardoPlugin(options = {}) {
731
- let resolvedConfig;
732
- const { routes, prerender, typedoc, githubPages = true, ...pressConfig } = options;
733
- const mainPlugin = {
734
- name: "ardo",
735
- enforce: "pre",
736
- config(userConfig, env) {
737
- const result = {
738
- optimizeDeps: {
739
- exclude: ["ardo/theme/styles.css"]
740
- },
741
- ssr: {
742
- noExternal: ["ardo"]
743
- }
744
- };
745
- if (githubPages && env.command === "build" && !userConfig.base) {
746
- const repoName = detectGitHubRepoName(userConfig.root || process.cwd());
747
- if (repoName) {
748
- result.base = `/${repoName}/`;
749
- console.log(`[ardo] GitHub Pages detected, using base: ${result.base}`);
750
- }
751
- }
752
- return result;
753
- },
754
- async configResolved(config) {
755
- const root = config.root;
756
- const defaultConfig = {
757
- title: pressConfig.title ?? "Ardo",
758
- description: pressConfig.description ?? "Documentation powered by Ardo"
759
- };
760
- resolvedConfig = resolveConfig({ ...defaultConfig, ...pressConfig }, root);
761
- },
762
- resolveId(id) {
763
- if (id === VIRTUAL_MODULE_ID) {
764
- return RESOLVED_VIRTUAL_MODULE_ID;
765
- }
766
- if (id === VIRTUAL_SIDEBAR_ID) {
767
- return RESOLVED_VIRTUAL_SIDEBAR_ID;
768
- }
769
- },
770
- async load(id) {
771
- if (id === RESOLVED_VIRTUAL_MODULE_ID) {
772
- const clientConfig = {
773
- title: resolvedConfig.title,
774
- description: resolvedConfig.description,
775
- base: resolvedConfig.base,
776
- lang: resolvedConfig.lang,
777
- themeConfig: resolvedConfig.themeConfig
778
- };
779
- return `export default ${JSON.stringify(clientConfig)}`;
780
- }
781
- if (id === RESOLVED_VIRTUAL_SIDEBAR_ID) {
782
- const sidebar = await generateSidebar(resolvedConfig);
783
- return `export default ${JSON.stringify(sidebar)}`;
766
+ function parseHighlightLines(meta) {
767
+ const match = meta.match(/\{([\d,-]+)\}/);
768
+ if (!match) return [];
769
+ const ranges = match[1].split(",");
770
+ const lines = [];
771
+ for (const range of ranges) {
772
+ if (range.includes("-")) {
773
+ const [start, end] = range.split("-").map(Number);
774
+ for (let i = start; i <= end; i++) {
775
+ lines.push(i);
784
776
  }
777
+ } else {
778
+ lines.push(Number(range));
785
779
  }
786
- };
787
- const markdownPlugin = {
788
- name: "ardo:markdown",
789
- enforce: "pre",
790
- async transform(code, id) {
791
- if (!id.endsWith(".md")) {
792
- return;
793
- }
794
- const highlighter = await getShikiHighlighter(resolvedConfig);
795
- const result = await transformMarkdown(code, resolvedConfig.markdown, {
796
- basePath: resolvedConfig.base,
797
- highlighter
798
- });
799
- const componentCode = `
800
- import { createElement } from 'react'
801
-
802
- export const frontmatter = ${JSON.stringify(result.frontmatter)}
803
- export const toc = ${JSON.stringify(result.toc)}
804
-
805
- export default function MarkdownContent() {
806
- return createElement('div', {
807
- className: 'press-content',
808
- dangerouslySetInnerHTML: { __html: ${JSON.stringify(result.html)} }
809
- })
780
+ }
781
+ return lines;
810
782
  }
811
- `;
812
- return {
813
- code: componentCode,
814
- map: null
815
- };
816
- }
817
- };
818
- const plugins = [mainPlugin, markdownPlugin];
819
- if (routes !== false) {
820
- plugins.unshift(
821
- pressRoutesPlugin(() => resolvedConfig, {
822
- srcDir: pressConfig.srcDir,
823
- ...routes
824
- })
825
- );
783
+ function parseTitle(meta) {
784
+ const match = meta.match(/title="([^"]+)"/);
785
+ return match ? match[1] : void 0;
786
+ }
787
+ function buildCodeBlockHtml(shikiHtml, options) {
788
+ const { lang, lineNumbers, highlightLines, title } = options;
789
+ let html = "";
790
+ if (title) {
791
+ html += `<div class="ardo-code-title">${escapeHtml2(title)}</div>`;
826
792
  }
827
- if (typedoc) {
828
- const packageRoot = findPackageRoot(process.cwd());
829
- const defaultEntryPoint = packageRoot ? `${packageRoot}/src/index.ts` : "./src/index.ts";
830
- const defaultTypedocConfig = {
831
- enabled: true,
832
- entryPoints: [defaultEntryPoint],
833
- out: "api-reference",
834
- excludePrivate: true,
835
- excludeInternal: true
836
- };
837
- const typedocConfig = typedoc === true ? defaultTypedocConfig : { ...defaultTypedocConfig, ...typedoc };
838
- let hasGenerated = false;
839
- const typedocPlugin = {
840
- name: "ardo:typedoc",
841
- async buildStart() {
842
- if (!hasGenerated && typedocConfig.enabled) {
843
- console.log("[ardo] Generating API documentation with TypeDoc...");
844
- const startTime = Date.now();
845
- try {
846
- const contentDir = pressConfig.srcDir ?? "./content";
847
- const docs = await generateApiDocs(typedocConfig, contentDir);
848
- const duration = Date.now() - startTime;
849
- console.log(`[ardo] Generated ${docs.length} API documentation pages in ${duration}ms`);
850
- hasGenerated = true;
851
- } catch (error) {
852
- console.warn(
853
- "[ardo] TypeDoc generation failed. API documentation will not be available."
854
- );
855
- console.warn("[ardo] Check your typedoc.entryPoints configuration.");
856
- if (error instanceof Error) {
857
- console.warn(`[ardo] Error: ${error.message}`);
858
- }
859
- hasGenerated = true;
860
- }
861
- }
793
+ html += `<div class="ardo-code-wrapper" data-lang="${lang}">`;
794
+ if (lineNumbers || highlightLines.length > 0) {
795
+ const lines = shikiHtml.split("\n");
796
+ const processedHtml = lines.map((line, i) => {
797
+ const lineNum = i + 1;
798
+ const isHighlighted = highlightLines.includes(lineNum);
799
+ const classes = ["ardo-code-line"];
800
+ if (isHighlighted) classes.push("highlighted");
801
+ let prefix = "";
802
+ if (lineNumbers) {
803
+ prefix = `<span class="ardo-line-number">${lineNum}</span>`;
862
804
  }
863
- };
864
- plugins.unshift(typedocPlugin);
805
+ return `<span class="${classes.join(" ")}">${prefix}${line}</span>`;
806
+ }).join("\n");
807
+ html += processedHtml;
808
+ } else {
809
+ html += shikiHtml;
865
810
  }
866
- const tanstackPlugin = tanstackStart({
867
- prerender: {
868
- enabled: prerender?.enabled ?? true,
869
- crawlLinks: prerender?.crawlLinks ?? false
870
- }
871
- });
872
- const tanstackPlugins = (Array.isArray(tanstackPlugin) ? tanstackPlugin : [tanstackPlugin]).filter((p) => p != null);
873
- plugins.push(...tanstackPlugins);
874
- const reactPlugin = react();
875
- const reactPlugins = (Array.isArray(reactPlugin) ? reactPlugin : [reactPlugin]).filter(
876
- (p) => p != null
877
- );
878
- plugins.push(...reactPlugins);
879
- return plugins;
811
+ html += `<button class="ardo-copy-button" data-code="${encodeURIComponent(extractCodeFromHtml(shikiHtml))}">
812
+ <span class="ardo-copy-icon">Copy</span>
813
+ <span class="ardo-copied-icon" style="display:none">Copied!</span>
814
+ </button>`;
815
+ html += "</div>";
816
+ return html;
880
817
  }
881
- async function generateSidebar(config) {
882
- const { contentDir, themeConfig } = config;
883
- if (themeConfig.sidebar && !Array.isArray(themeConfig.sidebar)) {
884
- return themeConfig.sidebar;
885
- }
886
- if (themeConfig.sidebar && Array.isArray(themeConfig.sidebar) && themeConfig.sidebar.length > 0) {
887
- return themeConfig.sidebar;
888
- }
889
- try {
890
- const sidebar = await scanDirectory(contentDir, contentDir, config.base);
891
- return sidebar;
892
- } catch {
893
- return [];
894
- }
818
+ function extractCodeFromHtml(html) {
819
+ return html.replace(/<[^>]+>/g, "").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
895
820
  }
896
- async function scanDirectory(dir, rootDir, _basePath) {
897
- const entries = await fs2.readdir(dir, { withFileTypes: true });
898
- const items = [];
899
- for (const entry of entries) {
900
- const fullPath = path2.join(dir, entry.name);
901
- const relativePath = path2.relative(rootDir, fullPath);
902
- if (entry.isDirectory()) {
903
- const children = await scanDirectory(fullPath, rootDir, _basePath);
904
- if (children.length > 0) {
905
- const indexPath = path2.join(fullPath, "index.md");
906
- let link;
907
- try {
908
- await fs2.access(indexPath);
909
- link = "/" + relativePath.replace(/\\/g, "/");
910
- } catch {
911
- }
912
- items.push({
913
- text: formatTitle(entry.name),
914
- link,
915
- items: children
916
- });
917
- }
918
- } else if (entry.name.endsWith(".md") && entry.name !== "index.md") {
919
- const fileContent = await fs2.readFile(fullPath, "utf-8");
920
- const frontmatterMatch = fileContent.match(/^---\n([\s\S]*?)\n---/);
921
- let title = formatTitle(entry.name.replace(/\.md$/, ""));
922
- let order;
923
- if (frontmatterMatch) {
924
- const frontmatterText = frontmatterMatch[1];
925
- const titleMatch = frontmatterText.match(/title:\s*["']?([^"'\n]+)["']?/);
926
- const orderMatch = frontmatterText.match(/order:\s*(\d+)/);
927
- if (titleMatch) {
928
- title = titleMatch[1].trim();
929
- }
930
- if (orderMatch) {
931
- order = parseInt(orderMatch[1], 10);
821
+ function escapeHtml2(text) {
822
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
823
+ }
824
+
825
+ // src/markdown/links.ts
826
+ import { visit as visit4 } from "unist-util-visit";
827
+ function rehypeLinks(options) {
828
+ const { basePath } = options;
829
+ const normalizedBase = basePath === "/" ? "" : basePath.replace(/\/$/, "");
830
+ return (tree) => {
831
+ if (!normalizedBase) {
832
+ return;
833
+ }
834
+ visit4(tree, "element", (node) => {
835
+ if (node.tagName === "a") {
836
+ const href = node.properties?.href;
837
+ if (typeof href === "string") {
838
+ if (href.startsWith("/") && !href.startsWith("//") && !href.startsWith(normalizedBase)) {
839
+ node.properties = node.properties || {};
840
+ node.properties.href = normalizedBase + href;
841
+ }
932
842
  }
933
843
  }
934
- const link = "/" + relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
935
- items.push({
936
- text: title,
937
- link,
938
- order
939
- });
844
+ });
845
+ };
846
+ }
847
+
848
+ // src/markdown/pipeline.ts
849
+ async function transformMarkdown(content, config, options = {}) {
850
+ const { data: frontmatter, content: markdownContent } = matter(content);
851
+ const { basePath = "/", highlighter: providedHighlighter } = options;
852
+ const tocExtraction = { toc: [] };
853
+ const highlighter = providedHighlighter ?? await createShikiHighlighter(config);
854
+ const processor = unified().use(remarkParse).use(remarkFrontmatter2, ["yaml"]).use(remarkGfm2).use(remarkDirective2).use(remarkContainers).use(remarkExtractToc, { tocExtraction, levels: config.toc?.level ?? [2, 3] }).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeShikiFromHighlighter, { highlighter, config }).use(rehypeLinks, { basePath }).use(rehypeStringify, { allowDangerousHtml: true });
855
+ if (config.remarkPlugins) {
856
+ for (const plugin of config.remarkPlugins) {
857
+ processor.use(plugin);
940
858
  }
941
859
  }
942
- items.sort((a, b) => {
943
- if (a.order !== void 0 && b.order !== void 0) {
944
- return a.order - b.order;
860
+ if (config.rehypePlugins) {
861
+ for (const plugin of config.rehypePlugins) {
862
+ processor.use(plugin);
945
863
  }
946
- if (a.order !== void 0) return -1;
947
- if (b.order !== void 0) return 1;
948
- return a.text.localeCompare(b.text);
949
- });
950
- return items.map(({ order: _order, ...item }) => item);
864
+ }
865
+ const result = await processor.process(markdownContent);
866
+ return {
867
+ html: String(result),
868
+ frontmatter,
869
+ toc: tocExtraction.toc
870
+ };
951
871
  }
952
- function formatTitle(name) {
953
- return name.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
872
+ async function transformMarkdownToReact(content, config) {
873
+ return transformMarkdown(content, config);
954
874
  }
955
875
 
956
876
  // src/runtime/loader.ts
957
- import fs3 from "fs/promises";
958
- import path3 from "path";
959
877
  async function loadDoc(options) {
960
878
  const { slug, contentDir, config } = options;
961
879
  const possiblePaths = [
@@ -1132,15 +1050,15 @@ function normalizePath(p) {
1132
1050
  }
1133
1051
 
1134
1052
  export {
1053
+ ardoRoutesPlugin,
1054
+ ardoPlugin,
1135
1055
  createShikiHighlighter,
1136
1056
  transformMarkdown,
1137
1057
  transformMarkdownToReact,
1138
- pressRoutesPlugin,
1139
- ardoPlugin,
1140
1058
  loadDoc,
1141
1059
  loadAllDocs,
1142
1060
  getSlugFromPath,
1143
1061
  getPageDataForRoute,
1144
1062
  generateSidebar2 as generateSidebar
1145
1063
  };
1146
- //# sourceMappingURL=chunk-VMA2OXSY.js.map
1064
+ //# sourceMappingURL=chunk-LUOUBO3L.js.map