prev-cli 0.4.0 → 0.6.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/dist/cli.js +78 -18
- package/dist/vite/pages.d.ts +14 -0
- package/package.json +1 -1
- package/src/theme/entry.tsx +1 -1
package/dist/cli.js
CHANGED
|
@@ -71,45 +71,103 @@ async function ensureCacheDir(rootDir) {
|
|
|
71
71
|
import fg from "fast-glob";
|
|
72
72
|
import { readFile } from "fs/promises";
|
|
73
73
|
import path2 from "path";
|
|
74
|
+
function parseFrontmatter(content) {
|
|
75
|
+
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
76
|
+
const match = content.match(frontmatterRegex);
|
|
77
|
+
if (!match) {
|
|
78
|
+
return { frontmatter: {}, content };
|
|
79
|
+
}
|
|
80
|
+
const frontmatterStr = match[1];
|
|
81
|
+
const restContent = content.slice(match[0].length);
|
|
82
|
+
const frontmatter = {};
|
|
83
|
+
for (const line of frontmatterStr.split(`
|
|
84
|
+
`)) {
|
|
85
|
+
const colonIndex = line.indexOf(":");
|
|
86
|
+
if (colonIndex === -1)
|
|
87
|
+
continue;
|
|
88
|
+
const key = line.slice(0, colonIndex).trim();
|
|
89
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
90
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
91
|
+
value = value.slice(1, -1);
|
|
92
|
+
}
|
|
93
|
+
if (value === "true")
|
|
94
|
+
value = true;
|
|
95
|
+
else if (value === "false")
|
|
96
|
+
value = false;
|
|
97
|
+
else if (!isNaN(Number(value)) && value !== "")
|
|
98
|
+
value = Number(value);
|
|
99
|
+
if (key) {
|
|
100
|
+
frontmatter[key] = value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { frontmatter, content: restContent };
|
|
104
|
+
}
|
|
105
|
+
function isIndexFile(basename) {
|
|
106
|
+
const lower = basename.toLowerCase();
|
|
107
|
+
return lower === "index" || lower === "readme";
|
|
108
|
+
}
|
|
74
109
|
function fileToRoute(file) {
|
|
75
110
|
const withoutExt = file.replace(/\.mdx?$/, "");
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
111
|
+
const basename = path2.basename(withoutExt).toLowerCase();
|
|
112
|
+
if (basename === "index" || basename === "readme") {
|
|
113
|
+
const dir = path2.dirname(withoutExt);
|
|
114
|
+
if (dir === ".") {
|
|
115
|
+
return "/";
|
|
116
|
+
}
|
|
117
|
+
return "/" + dir;
|
|
81
118
|
}
|
|
82
119
|
return "/" + withoutExt;
|
|
83
120
|
}
|
|
84
121
|
async function scanPages(rootDir) {
|
|
85
|
-
const files = await fg.glob("**/*.mdx", {
|
|
122
|
+
const files = await fg.glob("**/*.{md,mdx}", {
|
|
86
123
|
cwd: rootDir,
|
|
87
124
|
ignore: ["node_modules/**", "dist/**", ".cache/**"]
|
|
88
125
|
});
|
|
89
|
-
const
|
|
126
|
+
const routeMap = new Map;
|
|
90
127
|
for (const file of files) {
|
|
128
|
+
const route = fileToRoute(file);
|
|
129
|
+
const basename = path2.basename(file, path2.extname(file)).toLowerCase();
|
|
130
|
+
const priority = basename === "index" ? 1 : basename === "readme" ? 2 : 0;
|
|
131
|
+
const existing = routeMap.get(route);
|
|
132
|
+
if (!existing || priority > 0 && priority < existing.priority) {
|
|
133
|
+
routeMap.set(route, { file, priority });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const pages = [];
|
|
137
|
+
for (const { file } of routeMap.values()) {
|
|
91
138
|
const fullPath = path2.join(rootDir, file);
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
139
|
+
const rawContent = await readFile(fullPath, "utf-8");
|
|
140
|
+
const { frontmatter, content } = parseFrontmatter(rawContent);
|
|
141
|
+
const title = extractTitle(content, file, frontmatter);
|
|
142
|
+
const page = {
|
|
95
143
|
route: fileToRoute(file),
|
|
96
144
|
title,
|
|
97
145
|
file
|
|
98
|
-
}
|
|
146
|
+
};
|
|
147
|
+
if (frontmatter.description) {
|
|
148
|
+
page.description = frontmatter.description;
|
|
149
|
+
}
|
|
150
|
+
if (Object.keys(frontmatter).length > 0) {
|
|
151
|
+
page.frontmatter = frontmatter;
|
|
152
|
+
}
|
|
153
|
+
pages.push(page);
|
|
99
154
|
}
|
|
100
155
|
return pages.sort((a, b) => a.route.localeCompare(b.route));
|
|
101
156
|
}
|
|
102
|
-
function extractTitle(content, file) {
|
|
157
|
+
function extractTitle(content, file, frontmatter) {
|
|
158
|
+
if (frontmatter?.title && typeof frontmatter.title === "string") {
|
|
159
|
+
return frontmatter.title;
|
|
160
|
+
}
|
|
103
161
|
const match = content.match(/^#\s+(.+)$/m);
|
|
104
162
|
if (match) {
|
|
105
163
|
return match[1].trim();
|
|
106
164
|
}
|
|
107
|
-
const basename = path2.basename(file, path2.extname(file));
|
|
108
|
-
if (basename
|
|
165
|
+
const basename = path2.basename(file, path2.extname(file)).toLowerCase();
|
|
166
|
+
if (isIndexFile(basename)) {
|
|
109
167
|
const dirname = path2.dirname(file);
|
|
110
168
|
return dirname === "." ? "Home" : capitalize(path2.basename(dirname));
|
|
111
169
|
}
|
|
112
|
-
return capitalize(basename);
|
|
170
|
+
return capitalize(path2.basename(file, path2.extname(file)));
|
|
113
171
|
}
|
|
114
172
|
function capitalize(str) {
|
|
115
173
|
return str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
|
|
@@ -164,7 +222,7 @@ function pagesPlugin(rootDir) {
|
|
|
164
222
|
}
|
|
165
223
|
},
|
|
166
224
|
handleHotUpdate({ file, server }) {
|
|
167
|
-
if (file.endsWith(".mdx")) {
|
|
225
|
+
if (file.endsWith(".mdx") || file.endsWith(".md")) {
|
|
168
226
|
const mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_MODULE_ID);
|
|
169
227
|
if (mod) {
|
|
170
228
|
server.moduleGraph.invalidateModule(mod);
|
|
@@ -482,7 +540,7 @@ function printWelcome(type) {
|
|
|
482
540
|
}
|
|
483
541
|
function printReady() {
|
|
484
542
|
console.log();
|
|
485
|
-
console.log(" Edit your .mdx files and see changes instantly.");
|
|
543
|
+
console.log(" Edit your .md/.mdx files and see changes instantly.");
|
|
486
544
|
console.log(" Press Ctrl+C to stop.");
|
|
487
545
|
console.log();
|
|
488
546
|
}
|
|
@@ -537,12 +595,13 @@ var { values, positionals } = parseArgs({
|
|
|
537
595
|
options: {
|
|
538
596
|
port: { type: "string", short: "p" },
|
|
539
597
|
days: { type: "string", short: "d" },
|
|
598
|
+
cwd: { type: "string", short: "c" },
|
|
540
599
|
help: { type: "boolean", short: "h" }
|
|
541
600
|
},
|
|
542
601
|
allowPositionals: true
|
|
543
602
|
});
|
|
544
603
|
var command = positionals[0] || "dev";
|
|
545
|
-
var rootDir = positionals[1] || process.cwd();
|
|
604
|
+
var rootDir = values.cwd || positionals[1] || process.cwd();
|
|
546
605
|
function printHelp() {
|
|
547
606
|
console.log(`
|
|
548
607
|
prev - Zero-config documentation site generator
|
|
@@ -557,6 +616,7 @@ Commands:
|
|
|
557
616
|
clean Remove old cache directories
|
|
558
617
|
|
|
559
618
|
Options:
|
|
619
|
+
-c, --cwd <path> Set working directory
|
|
560
620
|
-p, --port <port> Specify port (dev/preview)
|
|
561
621
|
-d, --days <days> Cache age threshold for clean (default: 30)
|
|
562
622
|
-h, --help Show this help message
|
package/dist/vite/pages.d.ts
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
+
export interface Frontmatter {
|
|
2
|
+
title?: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
1
6
|
export interface Page {
|
|
2
7
|
route: string;
|
|
3
8
|
title: string;
|
|
4
9
|
file: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
frontmatter?: Frontmatter;
|
|
5
12
|
}
|
|
6
13
|
export interface SidebarItem {
|
|
7
14
|
title: string;
|
|
8
15
|
route?: string;
|
|
9
16
|
children?: SidebarItem[];
|
|
10
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Parse YAML frontmatter from markdown content
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseFrontmatter(content: string): {
|
|
22
|
+
frontmatter: Frontmatter;
|
|
23
|
+
content: string;
|
|
24
|
+
};
|
|
11
25
|
export declare function fileToRoute(file: string): string;
|
|
12
26
|
export declare function scanPages(rootDir: string): Promise<Page[]>;
|
|
13
27
|
export declare function buildSidebarTree(pages: Page[]): SidebarItem[];
|
package/package.json
CHANGED
package/src/theme/entry.tsx
CHANGED
|
@@ -54,7 +54,7 @@ function convertToPageTree(items: any[]): PageTree.Root {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// Dynamic imports for MDX pages
|
|
57
|
-
const pageModules = import.meta.glob('/**/*.mdx', { eager: true })
|
|
57
|
+
const pageModules = import.meta.glob('/**/*.{md,mdx}', { eager: true })
|
|
58
58
|
|
|
59
59
|
function getPageComponent(file: string): React.ComponentType | null {
|
|
60
60
|
const mod = pageModules[`/${file}`] as { default: React.ComponentType } | undefined
|