@marlinjai/clearify 1.5.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/LICENSE +21 -0
- package/README.md +81 -0
- package/bin/clearify.js +2 -0
- package/dist/node/chunk-5TD7NQIW.js +25 -0
- package/dist/node/chunk-B2Q23JW3.js +55 -0
- package/dist/node/chunk-CQ4MNGBE.js +301 -0
- package/dist/node/chunk-GFD54GNO.js +223 -0
- package/dist/node/chunk-IBK35HZR.js +194 -0
- package/dist/node/chunk-L24ILRSX.js +125 -0
- package/dist/node/chunk-NXQNNLGC.js +395 -0
- package/dist/node/chunk-PRTER35L.js +48 -0
- package/dist/node/chunk-SCZZB7OE.js +9 -0
- package/dist/node/chunk-V7LLYIRO.js +8 -0
- package/dist/node/chunk-WT5W333R.js +136 -0
- package/dist/node/cli/index.d.ts +2 -0
- package/dist/node/cli/index.js +41 -0
- package/dist/node/core/config.d.ts +9 -0
- package/dist/node/core/config.js +16 -0
- package/dist/node/core/mermaid-renderer.d.ts +22 -0
- package/dist/node/core/mermaid-renderer.js +125 -0
- package/dist/node/core/mermaid-utils.d.ts +3 -0
- package/dist/node/core/mermaid-utils.js +6 -0
- package/dist/node/core/navigation.d.ts +2 -0
- package/dist/node/core/navigation.js +13 -0
- package/dist/node/core/openapi-parser.d.ts +14 -0
- package/dist/node/core/openapi-parser.js +6 -0
- package/dist/node/core/remark-mermaid.d.ts +10 -0
- package/dist/node/core/remark-mermaid.js +11 -0
- package/dist/node/core/search.d.ts +31 -0
- package/dist/node/core/search.js +6 -0
- package/dist/node/node/build.d.ts +3 -0
- package/dist/node/node/build.js +14 -0
- package/dist/node/node/check.d.ts +3 -0
- package/dist/node/node/check.js +10 -0
- package/dist/node/node/index.d.ts +11 -0
- package/dist/node/node/index.js +108 -0
- package/dist/node/node/init.d.ts +6 -0
- package/dist/node/node/init.js +6 -0
- package/dist/node/presets/nestjs.d.ts +15 -0
- package/dist/node/presets/nestjs.js +98 -0
- package/dist/node/types/index.d.ts +79 -0
- package/dist/node/types/index.js +6 -0
- package/dist/node/vite-plugin/index.d.ts +13 -0
- package/dist/node/vite-plugin/index.js +11 -0
- package/package.json +94 -0
- package/src/client/App.tsx +101 -0
- package/src/client/Page.tsx +15 -0
- package/src/client/entry-server.tsx +79 -0
- package/src/client/index.html +18 -0
- package/src/client/main.tsx +11 -0
- package/src/theme/CodeBlock.tsx +103 -0
- package/src/theme/Content.tsx +32 -0
- package/src/theme/Footer.tsx +53 -0
- package/src/theme/Head.tsx +80 -0
- package/src/theme/HeadContext.tsx +32 -0
- package/src/theme/Header.tsx +177 -0
- package/src/theme/Layout.tsx +44 -0
- package/src/theme/MDXComponents.tsx +40 -0
- package/src/theme/NotFound.tsx +246 -0
- package/src/theme/Search.tsx +359 -0
- package/src/theme/Sidebar.tsx +325 -0
- package/src/theme/TableOfContents.tsx +153 -0
- package/src/theme/ThemeProvider.tsx +77 -0
- package/src/theme/components/Accordion.tsx +109 -0
- package/src/theme/components/Badge.tsx +72 -0
- package/src/theme/components/Breadcrumbs.tsx +88 -0
- package/src/theme/components/Callout.tsx +115 -0
- package/src/theme/components/Card.tsx +103 -0
- package/src/theme/components/CodeGroup.tsx +79 -0
- package/src/theme/components/Columns.tsx +42 -0
- package/src/theme/components/Frame.tsx +55 -0
- package/src/theme/components/Mermaid.tsx +99 -0
- package/src/theme/components/MermaidStatic.tsx +32 -0
- package/src/theme/components/OpenAPI.tsx +160 -0
- package/src/theme/components/OpenAPIPage.tsx +16 -0
- package/src/theme/components/Steps.tsx +76 -0
- package/src/theme/components/Tabs.tsx +75 -0
- package/src/theme/components/Tooltip.tsx +108 -0
- package/src/theme/components/index.ts +14 -0
- package/src/theme/styles/globals.css +363 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// src/node/init.ts
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
4
|
+
async function init(options = {}) {
|
|
5
|
+
const cwd = process.cwd();
|
|
6
|
+
const publicDir = resolve(cwd, "docs/public");
|
|
7
|
+
const internalDir = resolve(cwd, "docs/internal");
|
|
8
|
+
const configPath = resolve(cwd, "clearify.config.ts");
|
|
9
|
+
console.log("\n Initializing Clearify...\n");
|
|
10
|
+
if (!existsSync(publicDir)) {
|
|
11
|
+
mkdirSync(publicDir, { recursive: true });
|
|
12
|
+
console.log(" Created docs/public/");
|
|
13
|
+
} else {
|
|
14
|
+
console.log(" docs/public/ already exists, skipping");
|
|
15
|
+
}
|
|
16
|
+
const indexPath = resolve(publicDir, "index.md");
|
|
17
|
+
if (!existsSync(indexPath)) {
|
|
18
|
+
writeFileSync(
|
|
19
|
+
indexPath,
|
|
20
|
+
`---
|
|
21
|
+
title: Welcome
|
|
22
|
+
description: Welcome to your documentation
|
|
23
|
+
order: 0
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Welcome
|
|
27
|
+
|
|
28
|
+
Welcome to your documentation site, powered by **Clearify**.
|
|
29
|
+
|
|
30
|
+
## Getting Started
|
|
31
|
+
|
|
32
|
+
Edit this file at \`docs/public/index.md\` to update your home page.
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- Write docs in **Markdown** or **MDX**
|
|
37
|
+
- Built-in **search** across all pages
|
|
38
|
+
- **Dark mode** support out of the box
|
|
39
|
+
- **Syntax highlighting** with Shiki
|
|
40
|
+
- **Zero config** \u2014 just add markdown files
|
|
41
|
+
|
|
42
|
+
## Next Steps
|
|
43
|
+
|
|
44
|
+
- Add more pages to the \`docs/public/\` folder
|
|
45
|
+
- Create subfolders for organized navigation
|
|
46
|
+
- Customize with \`clearify.config.ts\`
|
|
47
|
+
`
|
|
48
|
+
);
|
|
49
|
+
console.log(" Created docs/public/index.md");
|
|
50
|
+
}
|
|
51
|
+
const gettingStartedPath = resolve(publicDir, "getting-started.md");
|
|
52
|
+
if (!existsSync(gettingStartedPath)) {
|
|
53
|
+
writeFileSync(
|
|
54
|
+
gettingStartedPath,
|
|
55
|
+
`---
|
|
56
|
+
title: Getting Started
|
|
57
|
+
description: How to get started with your documentation
|
|
58
|
+
order: 1
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
# Getting Started
|
|
62
|
+
|
|
63
|
+
## Adding Pages
|
|
64
|
+
|
|
65
|
+
Create \`.md\` or \`.mdx\` files in the \`docs/public/\` folder. Each file becomes a page.
|
|
66
|
+
|
|
67
|
+
## Organizing with Folders
|
|
68
|
+
|
|
69
|
+
Create subfolders to group related pages:
|
|
70
|
+
|
|
71
|
+
\`\`\`
|
|
72
|
+
docs/
|
|
73
|
+
\u251C\u2500\u2500 public/
|
|
74
|
+
\u2502 \u251C\u2500\u2500 index.md # Home page
|
|
75
|
+
\u2502 \u251C\u2500\u2500 getting-started.md # This page
|
|
76
|
+
\u2502 \u2514\u2500\u2500 guides/
|
|
77
|
+
\u2502 \u251C\u2500\u2500 installation.md
|
|
78
|
+
\u2502 \u2514\u2500\u2500 configuration.md
|
|
79
|
+
\u2514\u2500\u2500 internal/ # Design docs, roadmaps
|
|
80
|
+
\u2514\u2500\u2500 index.md
|
|
81
|
+
\`\`\`
|
|
82
|
+
|
|
83
|
+
Folders automatically become navigation groups.
|
|
84
|
+
|
|
85
|
+
## Frontmatter
|
|
86
|
+
|
|
87
|
+
Control page metadata with frontmatter:
|
|
88
|
+
|
|
89
|
+
\`\`\`yaml
|
|
90
|
+
---
|
|
91
|
+
title: My Page Title
|
|
92
|
+
description: A brief description
|
|
93
|
+
order: 1
|
|
94
|
+
---
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
## Running the Dev Server
|
|
98
|
+
|
|
99
|
+
\`\`\`bash
|
|
100
|
+
npx clearify dev
|
|
101
|
+
\`\`\`
|
|
102
|
+
|
|
103
|
+
## Building for Production
|
|
104
|
+
|
|
105
|
+
\`\`\`bash
|
|
106
|
+
npx clearify build
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
This outputs a static site to \`docs-dist/\`.
|
|
110
|
+
`
|
|
111
|
+
);
|
|
112
|
+
console.log(" Created docs/public/getting-started.md");
|
|
113
|
+
}
|
|
114
|
+
if (!options.noInternal) {
|
|
115
|
+
if (!existsSync(internalDir)) {
|
|
116
|
+
mkdirSync(internalDir, { recursive: true });
|
|
117
|
+
console.log(" Created docs/internal/");
|
|
118
|
+
} else {
|
|
119
|
+
console.log(" docs/internal/ already exists, skipping");
|
|
120
|
+
}
|
|
121
|
+
const internalIndexPath = resolve(internalDir, "index.md");
|
|
122
|
+
if (!existsSync(internalIndexPath)) {
|
|
123
|
+
writeFileSync(
|
|
124
|
+
internalIndexPath,
|
|
125
|
+
`---
|
|
126
|
+
title: Internal Docs
|
|
127
|
+
description: Internal documentation \u2014 design docs, roadmaps, and architecture
|
|
128
|
+
order: 0
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
# Internal Docs
|
|
132
|
+
|
|
133
|
+
This section is for internal documentation that isn't published to your public site.
|
|
134
|
+
|
|
135
|
+
## What goes here?
|
|
136
|
+
|
|
137
|
+
- **Design documents** \u2014 technical designs and proposals
|
|
138
|
+
- **Roadmaps** \u2014 planned features and milestones
|
|
139
|
+
- **Architecture decisions** \u2014 ADRs and context for past choices
|
|
140
|
+
- **Meeting notes** \u2014 decisions and action items
|
|
141
|
+
|
|
142
|
+
> This section is marked as \`draft\` and won't be included in production builds.
|
|
143
|
+
`
|
|
144
|
+
);
|
|
145
|
+
console.log(" Created docs/internal/index.md");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const changelogPath = resolve(cwd, "CHANGELOG.md");
|
|
149
|
+
if (!existsSync(changelogPath)) {
|
|
150
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
151
|
+
writeFileSync(
|
|
152
|
+
changelogPath,
|
|
153
|
+
`# Changelog
|
|
154
|
+
|
|
155
|
+
All notable changes to this project will be documented in this file.
|
|
156
|
+
|
|
157
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
158
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
159
|
+
|
|
160
|
+
## [Unreleased]
|
|
161
|
+
|
|
162
|
+
## [0.1.0] - ${today}
|
|
163
|
+
|
|
164
|
+
### Added
|
|
165
|
+
|
|
166
|
+
- Initial project setup
|
|
167
|
+
- Documentation site powered by Clearify
|
|
168
|
+
`
|
|
169
|
+
);
|
|
170
|
+
console.log(" Created CHANGELOG.md");
|
|
171
|
+
} else {
|
|
172
|
+
console.log(" CHANGELOG.md already exists, skipping");
|
|
173
|
+
}
|
|
174
|
+
if (!existsSync(configPath)) {
|
|
175
|
+
if (options.noInternal) {
|
|
176
|
+
writeFileSync(
|
|
177
|
+
configPath,
|
|
178
|
+
`import { defineConfig } from 'clearify';
|
|
179
|
+
|
|
180
|
+
export default defineConfig({
|
|
181
|
+
name: 'My Documentation',
|
|
182
|
+
docsDir: './docs/public',
|
|
183
|
+
theme: {
|
|
184
|
+
primaryColor: '#3B82F6',
|
|
185
|
+
mode: 'auto',
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
`
|
|
189
|
+
);
|
|
190
|
+
} else {
|
|
191
|
+
writeFileSync(
|
|
192
|
+
configPath,
|
|
193
|
+
`import { defineConfig } from 'clearify';
|
|
194
|
+
|
|
195
|
+
export default defineConfig({
|
|
196
|
+
name: 'My Documentation',
|
|
197
|
+
sections: [
|
|
198
|
+
{ label: 'Docs', docsDir: './docs/public' },
|
|
199
|
+
{ label: 'Internal', docsDir: './docs/internal', basePath: '/internal', draft: true },
|
|
200
|
+
],
|
|
201
|
+
theme: {
|
|
202
|
+
primaryColor: '#3B82F6',
|
|
203
|
+
mode: 'auto',
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
console.log(" Created clearify.config.ts");
|
|
210
|
+
} else {
|
|
211
|
+
console.log(" clearify.config.ts already exists, skipping");
|
|
212
|
+
}
|
|
213
|
+
console.log(`
|
|
214
|
+
Done! Next steps:
|
|
215
|
+
|
|
216
|
+
npx clearify dev Start the dev server
|
|
217
|
+
npx clearify build Build for production
|
|
218
|
+
`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export {
|
|
222
|
+
init
|
|
223
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// src/core/config.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { resolve, basename } from "path";
|
|
4
|
+
import { pathToFileURL } from "url";
|
|
5
|
+
import { existsSync, readFileSync, rmSync } from "fs";
|
|
6
|
+
import { tmpdir } from "os";
|
|
7
|
+
import { randomBytes } from "crypto";
|
|
8
|
+
var NavigationItemSchema = z.lazy(
|
|
9
|
+
() => z.object({
|
|
10
|
+
label: z.string(),
|
|
11
|
+
path: z.string().optional(),
|
|
12
|
+
icon: z.string().optional(),
|
|
13
|
+
badge: z.string().optional(),
|
|
14
|
+
badgeColor: z.string().optional(),
|
|
15
|
+
children: z.array(NavigationItemSchema).optional()
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
var SectionConfigSchema = z.object({
|
|
19
|
+
label: z.string(),
|
|
20
|
+
docsDir: z.string(),
|
|
21
|
+
basePath: z.string().optional(),
|
|
22
|
+
draft: z.boolean().optional(),
|
|
23
|
+
sitemap: z.boolean().optional(),
|
|
24
|
+
exclude: z.array(z.string()).optional()
|
|
25
|
+
});
|
|
26
|
+
var MermaidConfigSchema = z.object({
|
|
27
|
+
strategy: z.enum(["client", "build"]).default("client")
|
|
28
|
+
}).default({ strategy: "client" });
|
|
29
|
+
var OpenAPIConfigSchema = z.object({
|
|
30
|
+
spec: z.string(),
|
|
31
|
+
basePath: z.string().default("/api"),
|
|
32
|
+
generatePages: z.boolean().default(true)
|
|
33
|
+
}).optional();
|
|
34
|
+
var ClearifyConfigSchema = z.object({
|
|
35
|
+
name: z.string().default("Documentation"),
|
|
36
|
+
docsDir: z.string().default("./docs"),
|
|
37
|
+
outDir: z.string().default("./docs-dist"),
|
|
38
|
+
port: z.number().default(4747),
|
|
39
|
+
siteUrl: z.string().optional(),
|
|
40
|
+
theme: z.object({
|
|
41
|
+
primaryColor: z.string().default("#3B82F6"),
|
|
42
|
+
mode: z.enum(["light", "dark", "auto"]).default("auto")
|
|
43
|
+
}).default({ primaryColor: "#3B82F6", mode: "auto" }),
|
|
44
|
+
logo: z.object({
|
|
45
|
+
light: z.string().optional(),
|
|
46
|
+
dark: z.string().optional()
|
|
47
|
+
}).optional(),
|
|
48
|
+
sections: z.array(SectionConfigSchema).optional(),
|
|
49
|
+
navigation: z.array(NavigationItemSchema).nullable().default(null),
|
|
50
|
+
exclude: z.array(z.string()).default([]),
|
|
51
|
+
mermaid: MermaidConfigSchema,
|
|
52
|
+
openapi: OpenAPIConfigSchema,
|
|
53
|
+
links: z.record(z.string(), z.string()).optional(),
|
|
54
|
+
customCss: z.string().optional(),
|
|
55
|
+
headTags: z.array(z.string()).optional()
|
|
56
|
+
});
|
|
57
|
+
var defaultConfig = {
|
|
58
|
+
name: "Documentation",
|
|
59
|
+
docsDir: "./docs",
|
|
60
|
+
outDir: "./docs-dist",
|
|
61
|
+
port: 4747,
|
|
62
|
+
theme: {
|
|
63
|
+
primaryColor: "#3B82F6",
|
|
64
|
+
mode: "auto"
|
|
65
|
+
},
|
|
66
|
+
navigation: null
|
|
67
|
+
};
|
|
68
|
+
function detectProjectName(root) {
|
|
69
|
+
const pkgPath = resolve(root, "package.json");
|
|
70
|
+
if (existsSync(pkgPath)) {
|
|
71
|
+
try {
|
|
72
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
73
|
+
if (pkg.name) {
|
|
74
|
+
const raw = pkg.name.replace(/^@[^/]+\//, "");
|
|
75
|
+
return raw.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const dirName = basename(root);
|
|
81
|
+
return dirName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
82
|
+
}
|
|
83
|
+
async function loadTsConfig(configPath) {
|
|
84
|
+
const { build } = await import("esbuild");
|
|
85
|
+
const tmpId = randomBytes(4).toString("hex");
|
|
86
|
+
const outFile = resolve(tmpdir(), `clearify-config-${tmpId}.mjs`);
|
|
87
|
+
try {
|
|
88
|
+
await build({
|
|
89
|
+
entryPoints: [configPath],
|
|
90
|
+
outfile: outFile,
|
|
91
|
+
bundle: true,
|
|
92
|
+
format: "esm",
|
|
93
|
+
platform: "node",
|
|
94
|
+
write: true,
|
|
95
|
+
// Bundle the clearify import (defineConfig is a trivial identity fn),
|
|
96
|
+
// but keep everything else external so we don't pull in user deps.
|
|
97
|
+
plugins: [{
|
|
98
|
+
name: "externalize-except-clearify",
|
|
99
|
+
setup(build2) {
|
|
100
|
+
build2.onResolve({ filter: /.*/ }, (args) => {
|
|
101
|
+
if (args.path.startsWith(".") || args.path.startsWith("/") || args.path === "clearify") {
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
return { path: args.path, external: true };
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}]
|
|
108
|
+
});
|
|
109
|
+
const mod = await import(pathToFileURL(outFile).href);
|
|
110
|
+
return mod.default ?? mod;
|
|
111
|
+
} finally {
|
|
112
|
+
try {
|
|
113
|
+
rmSync(outFile, { force: true });
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function loadUserConfig(root) {
|
|
119
|
+
const configFiles = [
|
|
120
|
+
"clearify.config.ts",
|
|
121
|
+
"clearify.config.js",
|
|
122
|
+
"clearify.config.mjs"
|
|
123
|
+
];
|
|
124
|
+
for (const file of configFiles) {
|
|
125
|
+
const configPath = resolve(root, file);
|
|
126
|
+
if (existsSync(configPath)) {
|
|
127
|
+
try {
|
|
128
|
+
if (file.endsWith(".ts")) {
|
|
129
|
+
return await loadTsConfig(configPath);
|
|
130
|
+
}
|
|
131
|
+
const mod = await import(pathToFileURL(configPath).href);
|
|
132
|
+
return mod.default ?? mod;
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return {};
|
|
138
|
+
}
|
|
139
|
+
function resolveConfig(userConfig = {}, root) {
|
|
140
|
+
const parsed = ClearifyConfigSchema.parse(userConfig);
|
|
141
|
+
if (!userConfig.name && root) {
|
|
142
|
+
parsed.name = detectProjectName(root);
|
|
143
|
+
}
|
|
144
|
+
return parsed;
|
|
145
|
+
}
|
|
146
|
+
function slugify(str) {
|
|
147
|
+
return str.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
148
|
+
}
|
|
149
|
+
function resolveSections(config, root) {
|
|
150
|
+
if (config.sections && config.sections.length > 0) {
|
|
151
|
+
const sections = config.sections.map((s, i) => {
|
|
152
|
+
const id = slugify(s.label);
|
|
153
|
+
const draft = s.draft ?? false;
|
|
154
|
+
const basePath = s.basePath ?? (i === 0 ? "/" : "/" + id);
|
|
155
|
+
const sitemap = s.sitemap ?? !draft;
|
|
156
|
+
const exclude = [...config.exclude ?? [], ...s.exclude ?? []];
|
|
157
|
+
return {
|
|
158
|
+
id,
|
|
159
|
+
label: s.label,
|
|
160
|
+
docsDir: resolve(root, s.docsDir),
|
|
161
|
+
basePath,
|
|
162
|
+
draft,
|
|
163
|
+
sitemap,
|
|
164
|
+
exclude
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
const seen = /* @__PURE__ */ new Set();
|
|
168
|
+
for (const s of sections) {
|
|
169
|
+
if (seen.has(s.basePath)) {
|
|
170
|
+
throw new Error(`Duplicate section basePath: "${s.basePath}"`);
|
|
171
|
+
}
|
|
172
|
+
seen.add(s.basePath);
|
|
173
|
+
}
|
|
174
|
+
return sections;
|
|
175
|
+
}
|
|
176
|
+
return [
|
|
177
|
+
{
|
|
178
|
+
id: "default",
|
|
179
|
+
label: config.name,
|
|
180
|
+
docsDir: resolve(root, config.docsDir),
|
|
181
|
+
basePath: "/",
|
|
182
|
+
draft: false,
|
|
183
|
+
sitemap: true,
|
|
184
|
+
exclude: config.exclude ?? []
|
|
185
|
+
}
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export {
|
|
190
|
+
defaultConfig,
|
|
191
|
+
loadUserConfig,
|
|
192
|
+
resolveConfig,
|
|
193
|
+
resolveSections
|
|
194
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildSearchIndex
|
|
3
|
+
} from "./chunk-5TD7NQIW.js";
|
|
4
|
+
|
|
5
|
+
// src/core/navigation.ts
|
|
6
|
+
import { resolve, relative, basename, dirname, extname } from "path";
|
|
7
|
+
import { readFileSync, existsSync } from "fs";
|
|
8
|
+
import matter from "gray-matter";
|
|
9
|
+
import { globbySync } from "globby";
|
|
10
|
+
function toTitleCase(str) {
|
|
11
|
+
return str.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
12
|
+
}
|
|
13
|
+
function fileToRoutePath(filePath, docsDir, basePath = "/") {
|
|
14
|
+
const rel = relative(docsDir, filePath);
|
|
15
|
+
const ext = extname(rel);
|
|
16
|
+
const withoutExt = rel.slice(0, -ext.length);
|
|
17
|
+
let routePath;
|
|
18
|
+
if (withoutExt === "index") routePath = "/";
|
|
19
|
+
else if (withoutExt.endsWith("/index")) routePath = "/" + withoutExt.slice(0, -"/index".length);
|
|
20
|
+
else routePath = "/" + withoutExt;
|
|
21
|
+
if (basePath === "/") return routePath;
|
|
22
|
+
const prefix = basePath.replace(/\/$/, "");
|
|
23
|
+
return routePath === "/" ? prefix : prefix + routePath;
|
|
24
|
+
}
|
|
25
|
+
function scanDocs(docsDir, exclude = [], basePath = "/") {
|
|
26
|
+
const absDocsDir = resolve(docsDir);
|
|
27
|
+
const files = globbySync("**/*.{md,mdx}", {
|
|
28
|
+
cwd: absDocsDir,
|
|
29
|
+
ignore: ["**/node_modules/**", "**/_*", "**/_*/**", ...exclude],
|
|
30
|
+
absolute: true
|
|
31
|
+
});
|
|
32
|
+
return files.map((filePath) => {
|
|
33
|
+
const content = readFileSync(filePath, "utf-8");
|
|
34
|
+
const { data } = matter(content);
|
|
35
|
+
const routePath = fileToRoutePath(filePath, absDocsDir, basePath);
|
|
36
|
+
return {
|
|
37
|
+
filePath,
|
|
38
|
+
routePath,
|
|
39
|
+
frontmatter: {
|
|
40
|
+
title: data.title ?? toTitleCase(basename(filePath, extname(filePath))),
|
|
41
|
+
description: data.description,
|
|
42
|
+
icon: data.icon,
|
|
43
|
+
order: data.order
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function buildNavigation(docs, basePath = "/") {
|
|
49
|
+
const rootDir = basePath === "/" ? "/" : basePath.replace(/\/$/, "");
|
|
50
|
+
const groups = /* @__PURE__ */ new Map();
|
|
51
|
+
for (const doc of docs) {
|
|
52
|
+
const dir = dirname(doc.routePath);
|
|
53
|
+
if (!groups.has(dir)) groups.set(dir, []);
|
|
54
|
+
groups.get(dir).push(doc);
|
|
55
|
+
}
|
|
56
|
+
for (const files of groups.values()) {
|
|
57
|
+
files.sort((a, b) => {
|
|
58
|
+
const orderA = a.frontmatter.order ?? 999;
|
|
59
|
+
const orderB = b.frontmatter.order ?? 999;
|
|
60
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
61
|
+
return a.routePath.localeCompare(b.routePath);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const nav = [];
|
|
65
|
+
const rootFiles = groups.get(rootDir) ?? [];
|
|
66
|
+
for (const doc of rootFiles) {
|
|
67
|
+
if (doc.routePath === rootDir || doc.routePath === "/") continue;
|
|
68
|
+
const item = { label: doc.frontmatter.title, path: doc.routePath };
|
|
69
|
+
if (doc.frontmatter.icon) item.icon = doc.frontmatter.icon;
|
|
70
|
+
nav.push(item);
|
|
71
|
+
}
|
|
72
|
+
const dirs = [...groups.keys()].filter((d) => d !== rootDir).sort();
|
|
73
|
+
for (const dir of dirs) {
|
|
74
|
+
const files = groups.get(dir);
|
|
75
|
+
const label = toTitleCase(basename(dir));
|
|
76
|
+
const children = files.map((doc) => {
|
|
77
|
+
const child = { label: doc.frontmatter.title, path: doc.routePath };
|
|
78
|
+
if (doc.frontmatter.icon) child.icon = doc.frontmatter.icon;
|
|
79
|
+
return child;
|
|
80
|
+
});
|
|
81
|
+
nav.push({ label, children });
|
|
82
|
+
}
|
|
83
|
+
return nav;
|
|
84
|
+
}
|
|
85
|
+
function buildRoutes(docs, sectionId) {
|
|
86
|
+
return docs.map((doc) => ({
|
|
87
|
+
path: doc.routePath,
|
|
88
|
+
filePath: doc.filePath,
|
|
89
|
+
frontmatter: doc.frontmatter,
|
|
90
|
+
...sectionId ? { sectionId } : {}
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
function buildSectionData(section, changelogPath, customNavigation) {
|
|
94
|
+
const docs = scanDocs(section.docsDir, section.exclude, section.basePath);
|
|
95
|
+
if (section.basePath === "/" && changelogPath && existsSync(changelogPath)) {
|
|
96
|
+
const content = readFileSync(changelogPath, "utf-8");
|
|
97
|
+
const { data } = matter(content);
|
|
98
|
+
docs.push({
|
|
99
|
+
filePath: changelogPath,
|
|
100
|
+
routePath: "/changelog",
|
|
101
|
+
frontmatter: {
|
|
102
|
+
title: data.title ?? "Changelog",
|
|
103
|
+
description: data.description ?? "Release history",
|
|
104
|
+
order: 9999
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const nav = section.basePath === "/" && customNavigation ? customNavigation : buildNavigation(docs, section.basePath);
|
|
109
|
+
const navigation = {
|
|
110
|
+
id: section.id,
|
|
111
|
+
label: section.label,
|
|
112
|
+
basePath: section.basePath,
|
|
113
|
+
navigation: nav
|
|
114
|
+
};
|
|
115
|
+
const routes = buildRoutes(docs, section.id);
|
|
116
|
+
const searchEntries = buildSearchIndex(docs, section.id, section.label);
|
|
117
|
+
return { section, docs, navigation, routes, searchEntries };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export {
|
|
121
|
+
scanDocs,
|
|
122
|
+
buildNavigation,
|
|
123
|
+
buildRoutes,
|
|
124
|
+
buildSectionData
|
|
125
|
+
};
|