-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathindex.js
More file actions
125 lines (106 loc) · 4.88 KB
/
index.js
File metadata and controls
125 lines (106 loc) · 4.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
const fs = require('fs-extra');
const path = require('path');
const { getMarkdownUrl } = require('./lib/markdown-path');
const { cleanMarkdownForDisplay } = require('./lib/clean-markdown');
const { recordImgMapping } = require('./lib/img-mapping');
/**
* Docusaurus plugin to copy raw markdown files to build output
* This allows users to view markdown source by appending .md to URLs
*/
// Flatten nested Docusaurus route tree into a flat array
function flattenRoutes(routes) {
return routes.flatMap(route => [
route,
...(route.routes ? flattenRoutes(route.routes) : []),
]);
}
// Strip baseUrl prefix from a URL path to get build-relative path
function stripBaseUrl(urlPath, baseUrl) {
if (baseUrl !== '/' && urlPath.startsWith(baseUrl)) {
return urlPath.slice(baseUrl.length);
}
return urlPath.startsWith('/') ? urlPath.slice(1) : urlPath;
}
// Normalize docsPath option to a consistent format for pathname matching
// Accepts: '/docs/', '/docs', 'docs', '/' → returns '/docs/' or '/'
function normalizeDocsPath(input) {
if (!input || typeof input !== 'string') return '/docs/';
if (input === '/') return '/';
let p = input;
if (!p.startsWith('/')) p = '/' + p;
if (!p.endsWith('/')) p += '/';
return p;
}
module.exports = function markdownSourcePlugin(context, options = {}) {
return {
name: 'markdown-source-plugin',
// Provide theme components from the plugin (eliminates need for manual copying)
getThemePath() {
return path.resolve(__dirname, './theme');
},
async contentLoaded({ actions }) {
const docsPath = normalizeDocsPath(options.docsPath || '/docs/');
actions.setGlobalData({ docsPath });
},
async postBuild({ outDir, routes, baseUrl }) {
console.log('[markdown-source-plugin] Processing markdown source files...');
// Flatten nested routes and filter to markdown sources
const allRoutes = flattenRoutes(routes);
const mdRoutes = allRoutes.filter(route => {
const src = route.metadata?.sourceFilePath;
return src && (src.endsWith('.md') || src.endsWith('.mdx'));
});
console.log(`[markdown-source-plugin] Found ${mdRoutes.length} markdown routes`);
let copiedCount = 0;
const imgDirsToCopy = new Map(); // sourceImgDir -> Set<destImgDir>
for (const route of mdRoutes) {
const sourceRelPath = route.metadata.sourceFilePath;
const sourcePath = path.join(context.siteDir, sourceRelPath);
// Get route URL directory for image path rewriting
const routeDir = route.path.endsWith('/')
? route.path
: route.path.replace(/[^/]+$/, '');
// Construct the fetch URL the client dropdown will request
const fetchUrl = getMarkdownUrl(route.path);
// Strip baseUrl to get build-relative path
const buildRelPath = stripBaseUrl(fetchUrl, baseUrl);
const destPath = path.join(outDir, buildRelPath);
try {
await fs.ensureDir(path.dirname(destPath));
const content = await fs.readFile(sourcePath, 'utf8');
const cleanedContent = cleanMarkdownForDisplay(content, routeDir);
await fs.writeFile(destPath, cleanedContent, 'utf8');
copiedCount++;
console.log(` ✓ Processed: ${sourceRelPath} → ${buildRelPath}`);
} catch (error) {
console.error(` ✗ Failed to process ${sourceRelPath}:`, error.message);
}
// Track img directories near this source file for copying. A single source
// dir may need to be copied to multiple destinations when sibling docs in the
// same dir are routed to different URL spaces (e.g. via slug:).
const sourceDir = path.dirname(sourcePath);
const imgDir = path.join(sourceDir, 'img');
const imgOutRelDir = stripBaseUrl(routeDir, baseUrl);
recordImgMapping(imgDirsToCopy, imgDir, path.join(outDir, imgOutRelDir, 'img'));
}
console.log(`[markdown-source-plugin] Successfully processed ${copiedCount} markdown files`);
// Copy image directories. Each source dir may have multiple destinations.
console.log('[markdown-source-plugin] Copying image directories...');
let imgDirCount = 0;
for (const [source, dests] of imgDirsToCopy) {
if (!(await fs.pathExists(source))) continue;
const imageCount = fs.readdirSync(source).length;
for (const dest of dests) {
try {
await fs.copy(source, dest);
console.log(` ✓ Copied: ${path.relative(context.siteDir, source)} → ${path.relative(outDir, dest)} (${imageCount} files)`);
imgDirCount++;
} catch (error) {
console.error(` ✗ Failed to copy ${path.relative(context.siteDir, source)} → ${path.relative(outDir, dest)}:`, error.message);
}
}
}
console.log(`[markdown-source-plugin] Successfully copied ${imgDirCount} image directories`);
},
};
};