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.
- package/README.md +29 -47
- package/dist/{CopyButton-BkACwxQM.d.ts → Features-C_06EvGb.d.ts} +212 -10
- package/dist/{chunk-SZVJKB3V.js → chunk-G5L4ZUTS.js} +40 -40
- package/dist/chunk-G5L4ZUTS.js.map +1 -0
- package/dist/{chunk-VMA2OXSY.js → chunk-LUOUBO3L.js} +587 -669
- package/dist/chunk-LUOUBO3L.js.map +1 -0
- package/dist/{chunk-3U63LMDZ.js → chunk-N5CEHG2F.js} +3 -3
- package/dist/{chunk-3U63LMDZ.js.map → chunk-N5CEHG2F.js.map} +1 -1
- package/dist/chunk-OTUACKCQ.js +896 -0
- package/dist/chunk-OTUACKCQ.js.map +1 -0
- package/dist/chunk-UWAVET45.js +311 -0
- package/dist/chunk-UWAVET45.js.map +1 -0
- package/dist/chunk-ZXPAEM3M.js +854 -0
- package/dist/chunk-ZXPAEM3M.js.map +1 -0
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +1 -1
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +3 -0
- package/dist/icons/index.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +34 -26
- package/dist/mdx/provider.d.ts +9 -0
- package/dist/mdx/provider.js +114 -0
- package/dist/mdx/provider.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/typedoc/index.d.ts +25 -0
- package/dist/typedoc/index.js +2 -2
- package/dist/{types-C22M-Kor.d.ts → types-DchPWkJl.d.ts} +1 -1
- package/dist/ui/index.d.ts +116 -0
- package/dist/{theme → ui}/index.js +26 -12
- package/dist/ui/styles.css +2198 -0
- package/dist/vite/index.d.ts +13 -23
- package/dist/vite/index.js +5 -5
- package/package.json +23 -11
- package/dist/chunk-2JBVPO6S.js +0 -1144
- package/dist/chunk-2JBVPO6S.js.map +0 -1
- package/dist/chunk-SZVJKB3V.js.map +0 -1
- package/dist/chunk-VMA2OXSY.js.map +0 -1
- package/dist/chunk-YN6PP526.js +0 -441
- package/dist/chunk-YN6PP526.js.map +0 -1
- package/dist/theme/index.d.ts +0 -70
- package/dist/theme/styles.css +0 -1454
- /package/dist/{theme → ui}/index.js.map +0 -0
|
@@ -1,16 +1,477 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveConfig
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-N5CEHG2F.js";
|
|
4
4
|
import {
|
|
5
5
|
generateApiDocs
|
|
6
|
-
} from "./chunk-
|
|
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
|
|
12
|
-
import
|
|
13
|
-
import
|
|
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: ["
|
|
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="
|
|
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="
|
|
535
|
+
value: `<div class="ardo-code-group-tabs">${tabsHtml}</div>`
|
|
75
536
|
},
|
|
76
537
|
{
|
|
77
538
|
type: "html",
|
|
78
|
-
value: '<div class="
|
|
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="
|
|
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: ["
|
|
564
|
+
className: ["ardo-details"]
|
|
104
565
|
};
|
|
105
566
|
node.children.unshift({
|
|
106
567
|
type: "html",
|
|
107
|
-
value: `<summary class="
|
|
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: ["
|
|
574
|
+
className: ["ardo-container", `ardo-container-${type}`]
|
|
114
575
|
};
|
|
115
576
|
node.children.unshift({
|
|
116
577
|
type: "html",
|
|
117
|
-
value: `<p class="
|
|
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: ["
|
|
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(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
359
|
-
}
|
|
360
|
-
function escapeHtml2(text) {
|
|
361
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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
|
-
|
|
710
|
-
|
|
711
|
-
|
|
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 (
|
|
722
|
-
|
|
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
|
|
764
|
+
return "";
|
|
729
765
|
}
|
|
730
|
-
function
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
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
|
-
|
|
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
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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
|
-
|
|
828
|
-
|
|
829
|
-
const
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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
|
-
|
|
805
|
+
return `<span class="${classes.join(" ")}">${prefix}${line}</span>`;
|
|
806
|
+
}).join("\n");
|
|
807
|
+
html += processedHtml;
|
|
808
|
+
} else {
|
|
809
|
+
html += shikiHtml;
|
|
865
810
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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
|
-
|
|
882
|
-
|
|
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(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
895
820
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
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
|
-
|
|
943
|
-
|
|
944
|
-
|
|
860
|
+
if (config.rehypePlugins) {
|
|
861
|
+
for (const plugin of config.rehypePlugins) {
|
|
862
|
+
processor.use(plugin);
|
|
945
863
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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
|
|
953
|
-
return
|
|
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-
|
|
1064
|
+
//# sourceMappingURL=chunk-LUOUBO3L.js.map
|