@@ -4,12 +4,15 @@ import { join } from "node:path";
44import yaml from "js-yaml" ;
55import { isCloudflare } from "./detectCloudflare" ;
66import { notFound } from "next/navigation" ;
7+ import crypto from "node:crypto" ;
78
89export interface MarkdownSection {
10+ file : string ; // ファイル名
911 id : string ;
1012 level : number ;
1113 title : string ;
1214 rawContent : string ; // 見出しも含めたもとのmarkdownの内容
15+ md5 : string ; // mdファイル全体のmd5
1316}
1417
1518export interface PageEntry {
@@ -36,6 +39,17 @@ interface IndexYml {
3639 } [ ] ;
3740}
3841
42+ export interface RevisionYmlEntry {
43+ lang ?: string ;
44+ page ?: string ;
45+ rev : SectionRevision [ ] ;
46+ }
47+ export interface SectionRevision {
48+ md5 : string ; // mdファイル全体のmd5
49+ commit : string ;
50+ path : string ;
51+ }
52+
3953async function readPublicFile ( path : string ) : Promise < string > {
4054 try {
4155 if ( isCloudflare ( ) ) {
@@ -119,6 +133,16 @@ export async function getSectionsList(
119133 . sort ( naturalSortMdFiles ) ;
120134 }
121135}
136+
137+ export async function getRevisions (
138+ sectionId : string
139+ ) : Promise < RevisionYmlEntry | undefined > {
140+ const revisionsYml = await readPublicFile ( `docs/revisions.yml` ) ;
141+ return ( yaml . load ( revisionsYml ) as Record < string , RevisionYmlEntry > ) [
142+ sectionId
143+ ] ;
144+ }
145+
122146/**
123147 * public/docs/{lang}/{pageId}/ 以下のmdファイルを結合して MarkdownSection[] を返す。
124148 */
@@ -134,10 +158,12 @@ export async function getMarkdownSections(
134158 if ( file === "-intro.md" ) {
135159 // イントロセクションはフロントマターなし・見出しなし
136160 sections . push ( {
161+ file,
137162 id : `${ lang } -${ pageId } -intro` ,
138163 level : 1 ,
139164 title : "" ,
140165 rawContent : raw ,
166+ md5 : "" ,
141167 } ) ;
142168 } else {
143169 sections . push ( parseFrontmatter ( raw , file ) ) ;
@@ -166,9 +192,39 @@ function parseFrontmatter(content: string, file: string): MarkdownSection {
166192 // TODO: validation of frontmatter using zod
167193 const rawContent = content . slice ( endIdx + 5 ) ;
168194 return {
195+ file,
169196 id : fm ?. id ?? "" ,
170197 title : fm ?. title ?? "" ,
171198 level : fm ?. level ?? 2 ,
172199 rawContent,
200+ md5 : crypto . createHash ( "md5" ) . update ( content ) . digest ( "base64" ) ,
173201 } ;
174202}
203+
204+ export async function getRevisionOfMarkdownSection (
205+ sectionId : string ,
206+ md5 : string
207+ ) : Promise < MarkdownSection > {
208+ const revisions = await getRevisions ( sectionId ) ;
209+ const targetRevision = revisions ?. rev . find ( ( r ) => r . md5 === md5 ) ;
210+ if ( targetRevision ) {
211+ const rawRes = await fetch (
212+ `https://raw.githubusercontent.com/ut-code/my-code/${ targetRevision . commit } /${ targetRevision . path } `
213+ ) ;
214+ if ( rawRes . ok ) {
215+ const raw = await rawRes . text ( ) ;
216+ return parseFrontmatter (
217+ raw ,
218+ `${ targetRevision . commit } /${ targetRevision . path } `
219+ ) ;
220+ } else {
221+ throw new Error (
222+ `Failed to fetch ${ targetRevision . commit } /${ targetRevision . path } . ${ rawRes . status } : ${ await rawRes . text ( ) } `
223+ ) ;
224+ }
225+ } else {
226+ throw new Error (
227+ `Revision for sectionId=${ sectionId } , md5=${ md5 } not found`
228+ ) ;
229+ }
230+ }
0 commit comments