@@ -50,10 +50,24 @@ function convertDetailsToMarkdown(content) {
5050 } ) ;
5151}
5252
53+ // Flatten nested Docusaurus route tree into a flat array
54+ function flattenRoutes ( routes ) {
55+ return routes . flatMap ( route => [
56+ route ,
57+ ...( route . routes ? flattenRoutes ( route . routes ) : [ ] ) ,
58+ ] ) ;
59+ }
60+
61+ // Strip baseUrl prefix from a URL path to get build-relative path
62+ function stripBaseUrl ( urlPath , baseUrl ) {
63+ if ( baseUrl !== '/' && urlPath . startsWith ( baseUrl ) ) {
64+ return urlPath . slice ( baseUrl . length ) ;
65+ }
66+ return urlPath . startsWith ( '/' ) ? urlPath . slice ( 1 ) : urlPath ;
67+ }
68+
5369// Clean markdown content for raw display - remove MDX/Docusaurus-specific syntax
54- function cleanMarkdownForDisplay ( content , filepath ) {
55- // Get the directory path for this file (relative to docs root)
56- const fileDir = filepath . replace ( / [ ^ / ] * $ / , '' ) ; // Remove filename, keep directory
70+ function cleanMarkdownForDisplay ( content , routeDir ) {
5771
5872 // 1. Strip YAML front matter (--- at start, content, then ---)
5973 content = content . replace ( / ^ - - - \r ? \n [ \s \S ] * ?\r ? \n - - - \r ? \n / , '' ) ;
@@ -98,13 +112,12 @@ function cleanMarkdownForDisplay(content, filepath) {
98112 // This runs AFTER Tabs/details conversion to preserve their content
99113 content = content . replace ( / < [ A - Z ] [ a - z A - Z ] * [ \s \S ] * ?(?: \/ > | < \/ [ A - Z ] [ a - z A - Z ] * > ) / g, '' ) ;
100114
101- // 10. Convert relative image paths to absolute paths from /docs/ root (Claude style)
115+ // 10. Convert relative image paths to absolute paths using route URL directory
102116 // Matches:  or 
103117 content = content . replace (
104118 / ! \[ ( [ ^ \] ] * ) \] \( ( \. \/ ) ? i m g \/ ( [ ^ ) ] + ) \) / g,
105119 ( match , alt , relPrefix , filename ) => {
106- // Convert to absolute path: /docs/path/to/file/img/filename
107- return `` ;
120+ return `` ;
108121 }
109122 ) ;
110123
@@ -114,72 +127,6 @@ function cleanMarkdownForDisplay(content, filepath) {
114127 return content ;
115128}
116129
117- // Recursively find all markdown files in a directory
118- function findMarkdownFiles ( dir , fileList = [ ] , baseDir = dir ) {
119- const files = fs . readdirSync ( dir ) ;
120-
121- files . forEach ( ( file ) => {
122- const filePath = path . join ( dir , file ) ;
123- const stat = fs . statSync ( filePath ) ;
124-
125- if ( stat . isDirectory ( ) ) {
126- findMarkdownFiles ( filePath , fileList , baseDir ) ;
127- } else if ( file . endsWith ( '.md' ) ) {
128- // Store relative path from base directory
129- const relativePath = path . relative ( baseDir , filePath ) ;
130- fileList . push ( relativePath ) ;
131- }
132- } ) ;
133-
134- return fileList ;
135- }
136-
137- // Copy image directories from docs to build
138- async function copyImageDirectories ( docsDir , buildDir ) {
139- const imageDirs = [ ] ;
140-
141- // Recursively find all 'img' directories in docs
142- function findImgDirs ( dir , baseDir = dir ) {
143- const files = fs . readdirSync ( dir ) ;
144-
145- files . forEach ( ( file ) => {
146- const filePath = path . join ( dir , file ) ;
147- const stat = fs . statSync ( filePath ) ;
148-
149- if ( stat . isDirectory ( ) ) {
150- if ( file === 'img' ) {
151- // Found an img directory, store its relative path
152- const relativePath = path . relative ( baseDir , dir ) ;
153- imageDirs . push ( { source : filePath , relativePath } ) ;
154- } else {
155- // Continue searching in subdirectories
156- findImgDirs ( filePath , baseDir ) ;
157- }
158- }
159- } ) ;
160- }
161-
162- // Find all img directories
163- findImgDirs ( docsDir ) ;
164-
165- // Copy each img directory to build
166- let copiedCount = 0 ;
167- for ( const { source, relativePath } of imageDirs ) {
168- const destination = path . join ( buildDir , relativePath , 'img' ) ;
169-
170- try {
171- await fs . copy ( source , destination ) ;
172- const imageCount = fs . readdirSync ( source ) . length ;
173- console . log ( ` ✓ Copied: ${ relativePath } /img/ (${ imageCount } images)` ) ;
174- copiedCount ++ ;
175- } catch ( error ) {
176- console . error ( ` ✗ Failed to copy ${ relativePath } /img/:` , error . message ) ;
177- }
178- }
179-
180- return copiedCount ;
181- }
182-
183130module . exports = function markdownSourcePlugin ( context , options ) {
184131 return {
185132 name : 'markdown-source-plugin' ,
@@ -189,47 +136,76 @@ module.exports = function markdownSourcePlugin(context, options) {
189136 return path . resolve ( __dirname , './theme' ) ;
190137 } ,
191138
192- async postBuild ( { outDir } ) {
193- const docsDir = path . join ( context . siteDir , 'docs' ) ;
194- const buildDir = outDir ;
139+ async postBuild ( { outDir, routes, baseUrl } ) {
140+ console . log ( '[markdown-source-plugin] Processing markdown source files...' ) ;
195141
196- console . log ( '[markdown-source-plugin] Copying markdown source files...' ) ;
142+ // Flatten nested routes and filter to markdown sources
143+ const allRoutes = flattenRoutes ( routes ) ;
144+ const mdRoutes = allRoutes . filter ( route => {
145+ const src = route . metadata ?. sourceFilePath ;
146+ return src && ( src . endsWith ( '.md' ) || src . endsWith ( '.mdx' ) ) ;
147+ } ) ;
197148
198- // Find all markdown files in docs directory
199- const mdFiles = findMarkdownFiles ( docsDir ) ;
149+ console . log ( `[markdown-source-plugin] Found ${ mdRoutes . length } markdown routes` ) ;
200150
201151 let copiedCount = 0 ;
152+ const imgDirsToCopy = new Map ( ) ; // sourceImgDir -> destImgDir
202153
203- // Process each markdown file to build directory
204- for ( const mdFile of mdFiles ) {
205- const sourcePath = path . join ( docsDir , mdFile ) ;
206- const destPath = path . join ( buildDir , mdFile ) ;
154+ for ( const route of mdRoutes ) {
155+ const sourceRelPath = route . metadata . sourceFilePath ;
156+ const sourcePath = path . join ( context . siteDir , sourceRelPath ) ;
207157
208- try {
209- // Ensure destination directory exists
210- await fs . ensureDir ( path . dirname ( destPath ) ) ;
158+ // Get route URL directory for image path rewriting
159+ const routeDir = route . path . endsWith ( '/' )
160+ ? route . path
161+ : route . path . replace ( / [ ^ / ] + $ / , '' ) ;
211162
212- // Read the markdown file
213- const content = await fs . readFile ( sourcePath , 'utf8' ) ;
163+ // Construct the fetch URL the client dropdown will request
164+ const fetchUrl = route . path . endsWith ( '/' )
165+ ? route . path + 'intro.md'
166+ : route . path + '.md' ;
214167
215- // Clean markdown for raw display
216- const cleanedContent = cleanMarkdownForDisplay ( content , mdFile ) ;
168+ // Strip baseUrl to get build-relative path
169+ const buildRelPath = stripBaseUrl ( fetchUrl , baseUrl ) ;
170+ const destPath = path . join ( outDir , buildRelPath ) ;
217171
218- // Write the cleaned content
172+ try {
173+ await fs . ensureDir ( path . dirname ( destPath ) ) ;
174+ const content = await fs . readFile ( sourcePath , 'utf8' ) ;
175+ const cleanedContent = cleanMarkdownForDisplay ( content , routeDir ) ;
219176 await fs . writeFile ( destPath , cleanedContent , 'utf8' ) ;
220177 copiedCount ++ ;
221-
222- console . log ( ` ✓ Processed: ${ mdFile } ` ) ;
178+ console . log ( ` ✓ Processed: ${ sourceRelPath } → ${ buildRelPath } ` ) ;
223179 } catch ( error ) {
224- console . error ( ` ✗ Failed to process ${ mdFile } :` , error . message ) ;
180+ console . error ( ` ✗ Failed to process ${ sourceRelPath } :` , error . message ) ;
181+ }
182+
183+ // Track img directories near this source file for copying
184+ const sourceDir = path . dirname ( sourcePath ) ;
185+ const imgDir = path . join ( sourceDir , 'img' ) ;
186+ if ( ! imgDirsToCopy . has ( imgDir ) ) {
187+ const imgOutRelDir = stripBaseUrl ( routeDir , baseUrl ) ;
188+ imgDirsToCopy . set ( imgDir , path . join ( outDir , imgOutRelDir , 'img' ) ) ;
225189 }
226190 }
227191
228- console . log ( `[markdown-source-plugin] Successfully copied ${ copiedCount } markdown files` ) ;
192+ console . log ( `[markdown-source-plugin] Successfully processed ${ copiedCount } markdown files` ) ;
229193
230194 // Copy image directories
231195 console . log ( '[markdown-source-plugin] Copying image directories...' ) ;
232- const imgDirCount = await copyImageDirectories ( docsDir , buildDir ) ;
196+ let imgDirCount = 0 ;
197+ for ( const [ source , dest ] of imgDirsToCopy ) {
198+ if ( await fs . pathExists ( source ) ) {
199+ try {
200+ await fs . copy ( source , dest ) ;
201+ const imageCount = fs . readdirSync ( source ) . length ;
202+ console . log ( ` ✓ Copied: ${ path . relative ( context . siteDir , source ) } (${ imageCount } files)` ) ;
203+ imgDirCount ++ ;
204+ } catch ( error ) {
205+ console . error ( ` ✗ Failed to copy ${ path . relative ( context . siteDir , source ) } :` , error . message ) ;
206+ }
207+ }
208+ }
233209 console . log ( `[markdown-source-plugin] Successfully copied ${ imgDirCount } image directories` ) ;
234210 } ,
235211 } ;
0 commit comments