@docubook/flame 1.0.0-beta.70 → 1.0.0-beta.80
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/.docu/components/base/pagination/index.ts +21 -0
- package/.docu/components/base/pagination/pagination-docs.tsx +39 -0
- package/.docu/components/base/{pagination.tsx → pagination/pagination-numbers.tsx} +11 -180
- package/.docu/components/base/pagination/types.ts +129 -0
- package/.docu/node/build.ts +23 -5
- package/.docu/node/mdx.ts +13 -3
- package/.docu/node/search-indexer.ts +2 -20
- package/.docu/node/utils.ts +29 -0
- package/package.json +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { PaginationDocs } from "./pagination-docs";
|
|
2
|
+
export {
|
|
3
|
+
Pagination,
|
|
4
|
+
PaginationItem,
|
|
5
|
+
PaginationButtons,
|
|
6
|
+
PaginationRange,
|
|
7
|
+
PaginationInfo,
|
|
8
|
+
PaginationFull,
|
|
9
|
+
} from "./pagination-numbers";
|
|
10
|
+
export { getPaginationRange } from "./types";
|
|
11
|
+
export type {
|
|
12
|
+
PaginationRootProps as PaginationProps,
|
|
13
|
+
PaginationItemProps,
|
|
14
|
+
PaginationRangeProps,
|
|
15
|
+
PaginationButtonsProps,
|
|
16
|
+
PaginationInfoProps,
|
|
17
|
+
PaginationFullProps,
|
|
18
|
+
PaginationDocsProps,
|
|
19
|
+
PaginationSize,
|
|
20
|
+
PaginationVariant,
|
|
21
|
+
} from "./types";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { cn } from "../../../node/utils";
|
|
2
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
3
|
+
import type { PaginationDocsProps } from "./types";
|
|
4
|
+
|
|
5
|
+
export function PaginationDocs({ prev, next, className }: PaginationDocsProps) {
|
|
6
|
+
return (
|
|
7
|
+
<div className={cn("grid grow grid-cols-1 gap-4 py-8 sm:grid-cols-2", className)}>
|
|
8
|
+
<div>
|
|
9
|
+
{prev && (
|
|
10
|
+
<a
|
|
11
|
+
href={prev.href}
|
|
12
|
+
className="btn btn-outline border-base-300 items-start! py-2! h-auto w-full flex-col pl-4 no-underline"
|
|
13
|
+
>
|
|
14
|
+
<span className="text-muted-foreground flex items-center text-xs">
|
|
15
|
+
<ChevronLeft className="mr-1 h-4 w-4" />
|
|
16
|
+
Previous
|
|
17
|
+
</span>
|
|
18
|
+
<span className="text-base-content mt-1 text-sm font-medium">{prev.title}</span>
|
|
19
|
+
</a>
|
|
20
|
+
)}
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div>
|
|
24
|
+
{next && (
|
|
25
|
+
<a
|
|
26
|
+
href={next.href}
|
|
27
|
+
className="btn btn-outline border-base-300 items-end! py-2! h-auto w-full flex-col pr-4 no-underline"
|
|
28
|
+
>
|
|
29
|
+
<span className="text-muted-foreground flex items-center text-xs">
|
|
30
|
+
Next
|
|
31
|
+
<ChevronRight className="ml-1 h-4 w-4" />
|
|
32
|
+
</span>
|
|
33
|
+
<span className="text-base-content mt-1 text-sm font-medium">{next.title}</span>
|
|
34
|
+
</a>
|
|
35
|
+
)}
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -1,136 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { cn } from "
|
|
3
|
+
import { cn } from "../../../node/utils";
|
|
4
4
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
5
|
-
import { useMemo
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
type
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface PaginationItemProps {
|
|
18
|
-
children: ReactNode;
|
|
19
|
-
active?: boolean;
|
|
20
|
-
disabled?: boolean;
|
|
21
|
-
onClick?: () => void;
|
|
22
|
-
className?: string;
|
|
23
|
-
size?: PaginationSize;
|
|
24
|
-
variant?: PaginationVariant;
|
|
25
|
-
"aria-label"?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface PaginationRangeProps {
|
|
29
|
-
start: number;
|
|
30
|
-
end: number;
|
|
31
|
-
total: number;
|
|
32
|
-
current: number;
|
|
33
|
-
onPageChange: (page: number) => void;
|
|
34
|
-
siblingCount?: number;
|
|
35
|
-
className?: string;
|
|
36
|
-
size?: PaginationSize;
|
|
37
|
-
variant?: PaginationVariant;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface PaginationButtonsProps {
|
|
41
|
-
page: number;
|
|
42
|
-
totalPages: number;
|
|
43
|
-
onPageChange: (page: number) => void;
|
|
44
|
-
className?: string;
|
|
45
|
-
size?: PaginationSize;
|
|
46
|
-
showFirstLast?: boolean;
|
|
47
|
-
showPrevNext?: boolean;
|
|
48
|
-
prevLabel?: string;
|
|
49
|
-
nextLabel?: string;
|
|
50
|
-
firstLabel?: string;
|
|
51
|
-
lastLabel?: string;
|
|
52
|
-
disabled?: boolean;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
interface PaginationInfoProps {
|
|
56
|
-
current: number;
|
|
57
|
-
total: number;
|
|
58
|
-
pageSize?: number;
|
|
59
|
-
className?: string;
|
|
60
|
-
label?: string;
|
|
61
|
-
showTotal?: boolean;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
interface PaginationFullProps {
|
|
65
|
-
current: number;
|
|
66
|
-
total: number;
|
|
67
|
-
pageSize?: number;
|
|
68
|
-
onPageChange: (page: number) => void;
|
|
69
|
-
siblingCount?: number;
|
|
70
|
-
className?: string;
|
|
71
|
-
size?: PaginationSize;
|
|
72
|
-
variant?: PaginationVariant;
|
|
73
|
-
showFirstLast?: boolean;
|
|
74
|
-
showPrevNext?: boolean;
|
|
75
|
-
infoClassName?: string;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
interface PaginationDocsProps {
|
|
79
|
-
prev?: { href: string; title: string };
|
|
80
|
-
next?: { href: string; title: string };
|
|
81
|
-
className?: string;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function getPaginationRange({
|
|
85
|
-
totalCount,
|
|
86
|
-
pageSize,
|
|
87
|
-
siblingCount = 1,
|
|
88
|
-
currentPage,
|
|
89
|
-
}: {
|
|
90
|
-
totalCount: number;
|
|
91
|
-
pageSize: number;
|
|
92
|
-
siblingCount?: number;
|
|
93
|
-
currentPage: number;
|
|
94
|
-
}): (number | "ellipsis")[] {
|
|
95
|
-
const totalPages = Math.ceil(totalCount / pageSize);
|
|
96
|
-
const DOTS = "ellipsis" as const;
|
|
97
|
-
|
|
98
|
-
if (totalPages === 1) {
|
|
99
|
-
return [1];
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const totalPageNumbers = siblingCount + 5;
|
|
103
|
-
|
|
104
|
-
if (totalPages < totalPageNumbers) {
|
|
105
|
-
return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
|
|
109
|
-
const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);
|
|
110
|
-
|
|
111
|
-
const showLeftDots = leftSiblingIndex > 2;
|
|
112
|
-
const showRightDots = rightSiblingIndex < totalPages - 1;
|
|
113
|
-
|
|
114
|
-
if (!showLeftDots && showRightDots) {
|
|
115
|
-
const leftRange = Array.from({ length: 3 }, (_, i) => i + 1);
|
|
116
|
-
return [...leftRange, DOTS, totalPages - 1, totalPages];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (showLeftDots && !showRightDots) {
|
|
120
|
-
const rightRange = Array.from({ length: 3 }, (_, i) => totalPages - 2 + i);
|
|
121
|
-
return [1, 2, DOTS, ...rightRange];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (showLeftDots && showRightDots) {
|
|
125
|
-
const middleRange = Array.from(
|
|
126
|
-
{ length: rightSiblingIndex - leftSiblingIndex + 1 },
|
|
127
|
-
(_, i) => leftSiblingIndex + i
|
|
128
|
-
);
|
|
129
|
-
return [1, 2, DOTS, ...middleRange, DOTS, totalPages - 1, totalPages];
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return [1];
|
|
133
|
-
}
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
import {
|
|
7
|
+
getPaginationRange,
|
|
8
|
+
type PaginationRootProps,
|
|
9
|
+
type PaginationItemProps,
|
|
10
|
+
type PaginationButtonsProps,
|
|
11
|
+
type PaginationRangeProps,
|
|
12
|
+
type PaginationInfoProps,
|
|
13
|
+
type PaginationFullProps,
|
|
14
|
+
} from "./types";
|
|
134
15
|
|
|
135
16
|
export function Pagination({
|
|
136
17
|
children,
|
|
@@ -496,53 +377,3 @@ export function PaginationFull({
|
|
|
496
377
|
</div>
|
|
497
378
|
);
|
|
498
379
|
}
|
|
499
|
-
|
|
500
|
-
export function PaginationDocs({ prev, next, className }: PaginationDocsProps) {
|
|
501
|
-
return (
|
|
502
|
-
<div className={cn("grid grow grid-cols-1 gap-4 py-8 sm:grid-cols-2", className)}>
|
|
503
|
-
<div>
|
|
504
|
-
{prev && (
|
|
505
|
-
<a
|
|
506
|
-
href={prev.href}
|
|
507
|
-
className="btn btn-outline border-base-300 items-start! py-2! h-auto w-full flex-col pl-4 no-underline"
|
|
508
|
-
>
|
|
509
|
-
<span className="text-muted-foreground flex items-center text-xs">
|
|
510
|
-
<ChevronLeft className="mr-1 h-4 w-4" />
|
|
511
|
-
Previous
|
|
512
|
-
</span>
|
|
513
|
-
<span className="text-base-content mt-1 text-sm font-medium">{prev.title}</span>
|
|
514
|
-
</a>
|
|
515
|
-
)}
|
|
516
|
-
</div>
|
|
517
|
-
|
|
518
|
-
<div>
|
|
519
|
-
{next && (
|
|
520
|
-
<a
|
|
521
|
-
href={next.href}
|
|
522
|
-
className="btn btn-outline border-base-300 items-end! py-2! h-auto w-full flex-col pr-4 no-underline"
|
|
523
|
-
>
|
|
524
|
-
<span className="text-muted-foreground flex items-center text-xs">
|
|
525
|
-
Next
|
|
526
|
-
<ChevronRight className="ml-1 h-4 w-4" />
|
|
527
|
-
</span>
|
|
528
|
-
<span className="text-base-content mt-1 text-sm font-medium">{next.title}</span>
|
|
529
|
-
</a>
|
|
530
|
-
)}
|
|
531
|
-
</div>
|
|
532
|
-
</div>
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
export type {
|
|
537
|
-
PaginationRootProps as PaginationProps,
|
|
538
|
-
PaginationItemProps,
|
|
539
|
-
PaginationRangeProps,
|
|
540
|
-
PaginationButtonsProps,
|
|
541
|
-
PaginationInfoProps,
|
|
542
|
-
PaginationFullProps,
|
|
543
|
-
PaginationDocsProps,
|
|
544
|
-
PaginationSize,
|
|
545
|
-
PaginationVariant,
|
|
546
|
-
};
|
|
547
|
-
|
|
548
|
-
export { getPaginationRange };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export type PaginationSize = "lg" | "md" | "sm" | "xs";
|
|
4
|
+
export type PaginationVariant = "default" | "square" | "rounded";
|
|
5
|
+
|
|
6
|
+
export interface PaginationRootProps {
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
size?: PaginationSize;
|
|
10
|
+
variant?: PaginationVariant;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface PaginationItemProps {
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
active?: boolean;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
onClick?: () => void;
|
|
18
|
+
className?: string;
|
|
19
|
+
size?: PaginationSize;
|
|
20
|
+
variant?: PaginationVariant;
|
|
21
|
+
"aria-label"?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PaginationRangeProps {
|
|
25
|
+
start: number;
|
|
26
|
+
end: number;
|
|
27
|
+
total: number;
|
|
28
|
+
current: number;
|
|
29
|
+
onPageChange: (page: number) => void;
|
|
30
|
+
siblingCount?: number;
|
|
31
|
+
className?: string;
|
|
32
|
+
size?: PaginationSize;
|
|
33
|
+
variant?: PaginationVariant;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface PaginationButtonsProps {
|
|
37
|
+
page: number;
|
|
38
|
+
totalPages: number;
|
|
39
|
+
onPageChange: (page: number) => void;
|
|
40
|
+
className?: string;
|
|
41
|
+
size?: PaginationSize;
|
|
42
|
+
showFirstLast?: boolean;
|
|
43
|
+
showPrevNext?: boolean;
|
|
44
|
+
prevLabel?: string;
|
|
45
|
+
nextLabel?: string;
|
|
46
|
+
firstLabel?: string;
|
|
47
|
+
lastLabel?: string;
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface PaginationInfoProps {
|
|
52
|
+
current: number;
|
|
53
|
+
total: number;
|
|
54
|
+
pageSize?: number;
|
|
55
|
+
className?: string;
|
|
56
|
+
label?: string;
|
|
57
|
+
showTotal?: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface PaginationFullProps {
|
|
61
|
+
current: number;
|
|
62
|
+
total: number;
|
|
63
|
+
pageSize?: number;
|
|
64
|
+
onPageChange: (page: number) => void;
|
|
65
|
+
siblingCount?: number;
|
|
66
|
+
className?: string;
|
|
67
|
+
size?: PaginationSize;
|
|
68
|
+
variant?: PaginationVariant;
|
|
69
|
+
showFirstLast?: boolean;
|
|
70
|
+
showPrevNext?: boolean;
|
|
71
|
+
infoClassName?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface PaginationDocsProps {
|
|
75
|
+
prev?: { href: string; title: string };
|
|
76
|
+
next?: { href: string; title: string };
|
|
77
|
+
className?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function getPaginationRange({
|
|
81
|
+
totalCount,
|
|
82
|
+
pageSize,
|
|
83
|
+
siblingCount = 1,
|
|
84
|
+
currentPage,
|
|
85
|
+
}: {
|
|
86
|
+
totalCount: number;
|
|
87
|
+
pageSize: number;
|
|
88
|
+
siblingCount?: number;
|
|
89
|
+
currentPage: number;
|
|
90
|
+
}): (number | "ellipsis")[] {
|
|
91
|
+
const totalPages = Math.ceil(totalCount / pageSize);
|
|
92
|
+
const DOTS = "ellipsis" as const;
|
|
93
|
+
|
|
94
|
+
if (totalPages === 1) {
|
|
95
|
+
return [1];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const totalPageNumbers = siblingCount + 5;
|
|
99
|
+
|
|
100
|
+
if (totalPages < totalPageNumbers) {
|
|
101
|
+
return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
|
|
105
|
+
const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);
|
|
106
|
+
|
|
107
|
+
const showLeftDots = leftSiblingIndex > 2;
|
|
108
|
+
const showRightDots = rightSiblingIndex < totalPages - 1;
|
|
109
|
+
|
|
110
|
+
if (!showLeftDots && showRightDots) {
|
|
111
|
+
const leftRange = Array.from({ length: 3 }, (_, i) => i + 1);
|
|
112
|
+
return [...leftRange, DOTS, totalPages - 1, totalPages];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (showLeftDots && !showRightDots) {
|
|
116
|
+
const rightRange = Array.from({ length: 3 }, (_, i) => totalPages - 2 + i);
|
|
117
|
+
return [1, 2, DOTS, ...rightRange];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (showLeftDots && showRightDots) {
|
|
121
|
+
const middleRange = Array.from(
|
|
122
|
+
{ length: rightSiblingIndex - leftSiblingIndex + 1 },
|
|
123
|
+
(_, i) => leftSiblingIndex + i
|
|
124
|
+
);
|
|
125
|
+
return [1, 2, DOTS, ...middleRange, DOTS, totalPages - 1, totalPages];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return [1];
|
|
129
|
+
}
|
package/.docu/node/build.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { renderToString } from "react-dom/server";
|
|
7
|
-
import { compileMdx } from "./mdx";
|
|
7
|
+
import { compileMdx, getGitLastModifiedBatch } from "./mdx";
|
|
8
8
|
import {
|
|
9
9
|
DOCS_DIR,
|
|
10
10
|
DIST_DIR,
|
|
@@ -103,10 +103,15 @@ function htmlShell(title: string, description: string, body: string): string {
|
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
async function renderDocsPage(
|
|
106
|
+
async function renderDocsPage(
|
|
107
|
+
slug: string,
|
|
108
|
+
rawMdx: string,
|
|
109
|
+
filePath: string,
|
|
110
|
+
gitDates?: Map<string, string>
|
|
111
|
+
): Promise<string> {
|
|
107
112
|
let result;
|
|
108
113
|
try {
|
|
109
|
-
result = await compileMdx(rawMdx, filePath);
|
|
114
|
+
result = await compileMdx(rawMdx, filePath, gitDates);
|
|
110
115
|
} catch (err) {
|
|
111
116
|
const msg = err instanceof Error ? err.message : "Unknown MDX error";
|
|
112
117
|
throw new Error(`MDX Error in: docs/${slug}.mdx\n${msg}`, { cause: err });
|
|
@@ -194,6 +199,19 @@ async function build() {
|
|
|
194
199
|
logger.spinner.start("Building pages...");
|
|
195
200
|
t = performance.now();
|
|
196
201
|
|
|
202
|
+
const allRelPaths = mdxFiles
|
|
203
|
+
.map((f) => {
|
|
204
|
+
const mdxPath1 = join(DOCS_DIR, f.path, "index.mdx");
|
|
205
|
+
const mdxPath2 = join(DOCS_DIR, `${f.path}.mdx`);
|
|
206
|
+
const mdxPath3 = join(DOCS_DIR, `${f.path}.md`);
|
|
207
|
+
for (const p of [mdxPath1, mdxPath2, mdxPath3]) {
|
|
208
|
+
if (existsSync(p)) return p.replace(PROJECT_ROOT + "/", "");
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
})
|
|
212
|
+
.filter((p): p is string => p !== null);
|
|
213
|
+
const gitDates = await getGitLastModifiedBatch(allRelPaths);
|
|
214
|
+
|
|
197
215
|
const CONCURRENCY = Math.max(1, parseInt(process.env.BUILD_CONCURRENCY || "10", 10) || 10);
|
|
198
216
|
const buildTasks = [];
|
|
199
217
|
const errors: string[] = [];
|
|
@@ -232,7 +250,7 @@ async function build() {
|
|
|
232
250
|
|
|
233
251
|
buildTasks.push(async () => {
|
|
234
252
|
try {
|
|
235
|
-
const html = await renderDocsPage(capturedFile.path, capturedRawMdx, relPath);
|
|
253
|
+
const html = await renderDocsPage(capturedFile.path, capturedRawMdx, relPath, gitDates);
|
|
236
254
|
const outputPath = join(DIST_DIR, "docs", `${capturedFile.path}.html`);
|
|
237
255
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
238
256
|
await writeFile(outputPath, html);
|
|
@@ -258,7 +276,7 @@ async function build() {
|
|
|
258
276
|
const indexMdxPath = join(DOCS_DIR, "index.mdx");
|
|
259
277
|
const indexRaw = await readFile(indexMdxPath, "utf-8");
|
|
260
278
|
const indexRelPath = indexMdxPath.replace(PROJECT_ROOT + "/", "");
|
|
261
|
-
const indexHtml = await renderDocsPage("", indexRaw, indexRelPath);
|
|
279
|
+
const indexHtml = await renderDocsPage("", indexRaw, indexRelPath, gitDates);
|
|
262
280
|
await mkdir(join(DIST_DIR, "docs"), { recursive: true });
|
|
263
281
|
await writeFile(join(DIST_DIR, "docs", "index.html"), indexHtml);
|
|
264
282
|
} catch (err) {
|
package/.docu/node/mdx.ts
CHANGED
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
MDXRemote,
|
|
9
9
|
} from "@docubook/core";
|
|
10
10
|
import { createMdxComponents } from "@docubook/mdx-content";
|
|
11
|
-
import { getGitLastModified } from "./utils";
|
|
11
|
+
import { getGitLastModified, getGitLastModifiedBatch } from "./utils";
|
|
12
|
+
|
|
13
|
+
export { getGitLastModifiedBatch };
|
|
12
14
|
|
|
13
15
|
export interface MdxResult {
|
|
14
16
|
content: React.ReactElement;
|
|
@@ -17,7 +19,11 @@ export interface MdxResult {
|
|
|
17
19
|
tocs: ReturnType<typeof extractTocsFromRawMdx>;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export async function compileMdx(
|
|
22
|
+
export async function compileMdx(
|
|
23
|
+
rawMdx: string,
|
|
24
|
+
filePath: string,
|
|
25
|
+
gitDates?: Map<string, string>
|
|
26
|
+
): Promise<MdxResult> {
|
|
21
27
|
const tocs = extractTocsFromRawMdx(rawMdx);
|
|
22
28
|
const { frontmatter, strippedContent } = extractFrontmatterWithContent<{
|
|
23
29
|
title?: string;
|
|
@@ -40,7 +46,11 @@ export async function compileMdx(rawMdx: string, filePath: string): Promise<MdxR
|
|
|
40
46
|
components,
|
|
41
47
|
});
|
|
42
48
|
|
|
43
|
-
const date =
|
|
49
|
+
const date =
|
|
50
|
+
frontmatter.date ||
|
|
51
|
+
gitDates?.get(filePath) ||
|
|
52
|
+
(await getGitLastModified(filePath)) ||
|
|
53
|
+
undefined;
|
|
44
54
|
|
|
45
55
|
return {
|
|
46
56
|
content,
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { readFile, writeFile, readdir, mkdir } from "node:fs/promises";
|
|
15
15
|
import { resolve, join } from "node:path";
|
|
16
|
+
import { extractFrontmatterWithContent } from "@docubook/core";
|
|
16
17
|
import { DOCS_DIR, ASSETS_DIR, loadDocuConfig } from "./paths";
|
|
17
18
|
|
|
18
19
|
const docuConfig = loadDocuConfig();
|
|
@@ -37,25 +38,6 @@ interface Frontmatter {
|
|
|
37
38
|
description?: string;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
function parseFrontmatter(raw: string): { frontmatter: Frontmatter; content: string } {
|
|
41
|
-
if (!raw.startsWith("---")) return { frontmatter: {}, content: raw };
|
|
42
|
-
const end = raw.indexOf("---", 3);
|
|
43
|
-
if (end < 0) return { frontmatter: {}, content: raw };
|
|
44
|
-
|
|
45
|
-
const fm: Frontmatter = {};
|
|
46
|
-
const lines = raw.slice(3, end).trim().split("\n");
|
|
47
|
-
for (const line of lines) {
|
|
48
|
-
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
49
|
-
if (match) {
|
|
50
|
-
const [, key, value] = match;
|
|
51
|
-
if (key === "title" || key === "description") {
|
|
52
|
-
fm[key] = value.trim();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return { frontmatter: fm, content: raw.slice(end + 3) };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
41
|
function getSectionTitle(filePath: string): string {
|
|
60
42
|
const parts = filePath.split("/");
|
|
61
43
|
if (parts.length > 1) {
|
|
@@ -75,7 +57,7 @@ function slugify(text: string): string {
|
|
|
75
57
|
}
|
|
76
58
|
|
|
77
59
|
function extractRecords(filePath: string, raw: string): SearchRecord[] {
|
|
78
|
-
const { frontmatter, content } =
|
|
60
|
+
const { frontmatter, strippedContent: content } = extractFrontmatterWithContent<Frontmatter>(raw);
|
|
79
61
|
const records: SearchRecord[] = [];
|
|
80
62
|
const url = `/docs/${filePath}`;
|
|
81
63
|
const lvl0 = getSectionTitle(filePath);
|
package/.docu/node/utils.ts
CHANGED
|
@@ -33,6 +33,35 @@ export async function getGitLastModified(filePath: string): Promise<string | nul
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/** Batch git last modified dates for multiple files in a single spawn */
|
|
37
|
+
export async function getGitLastModifiedBatch(filePaths: string[]): Promise<Map<string, string>> {
|
|
38
|
+
const result = new Map<string, string>();
|
|
39
|
+
if (filePaths.length === 0) return result;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const proc = Bun.spawn(
|
|
43
|
+
["git", "log", "--format=%cI", "--name-only", "--diff-filter=ACMR", ...filePaths],
|
|
44
|
+
{ stderr: "ignore" }
|
|
45
|
+
);
|
|
46
|
+
const text = await new Response(proc.stdout).text();
|
|
47
|
+
let currentDate = "";
|
|
48
|
+
|
|
49
|
+
for (const line of text.split("\n")) {
|
|
50
|
+
const trimmed = line.trim();
|
|
51
|
+
if (!trimmed) continue;
|
|
52
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(trimmed)) {
|
|
53
|
+
currentDate = trimmed;
|
|
54
|
+
} else if (currentDate && !result.has(trimmed)) {
|
|
55
|
+
result.set(trimmed, currentDate);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
// fallback: return empty map, callers use frontmatter date
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
36
65
|
const MIME_TYPES: Record<string, string> = {
|
|
37
66
|
html: "text/html",
|
|
38
67
|
css: "text/css",
|