Skip to content

Commit 2a1cbf7

Browse files
committed
Add rehype plugin to prepend basePath for local <img> paths in MDX
1 parent 5e39a3b commit 2a1cbf7

File tree

2 files changed

+40
-63
lines changed

2 files changed

+40
-63
lines changed

mdx-components.tsx

Lines changed: 9 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -144,55 +144,6 @@ const mdxComponents = {
144144
const src = typeof props.src === 'object' && props.src !== null && 'src' in props.src ? props.src.src : props.src;
145145

146146
// Skip using ImageZoom for SVG, data URLs, img.shields.io, and if no src
147-
if (
148-
!src ||
149-
(typeof src === 'string' && (
150-
src.endsWith(".svg") ||
151-
src.startsWith("data:") ||
152-
src.includes("img.shields.io")
153-
))
154-
) {
155-
// If props.src is an object (static import), pass it to next/image, otherwise standard img
156-
if (typeof props.src === 'object') {
157-
// We can't use standard <img> for object src,
158-
// but if we are skipping ImageZoom, we likely want raw <img> behavior?
159-
// Actually, if it's an object, it's likely a standard image format (png/jpg)
160-
// that we might want to Zoom, unless explicit opt-out?
161-
// But if we are here, strict check failed.
162-
// Wait, if it's an object, src.endsWith('.svg') is false (since src is contents).
163-
// So we proceed to ImageZoom below.
164-
} else {
165-
return <img {...props} />;
166-
}
167-
}
168-
169-
// If it's a static import (object), we proceed to use ImageZoom.
170-
// ImageZoom from fumadocs-ui handles static imports?
171-
// Let's assume yes. Or fallback to Next.js Image.
172-
173-
// ...
174-
// Wait, the original code had:
175-
/*
176-
if (
177-
!props.src ||
178-
props.src.endsWith(".svg") || ...
179-
) { return <img ... /> }
180-
*/
181-
182-
// With my new check:
183-
/*
184-
const src = ...
185-
if (!src || (typeof src === 'string' && (...))) {
186-
return <img {...props} />;
187-
}
188-
*/
189-
190-
// If props.src is object: src is string (url).
191-
// If url ends with .svg, we enter block.
192-
// <img src={object} /> is INVALID in HTML.
193-
// <img src={object.src} /> is valid.
194-
195-
// So distinct handling is needed.
196147

197148
if (
198149
!src ||
@@ -202,11 +153,11 @@ const mdxComponents = {
202153
src.includes("img.shields.io")
203154
))
204155
) {
205-
if (typeof props.src === 'object') {
206-
return <img {...props} src={src} />;
207-
}
208-
return <img {...props} />;
209-
}
156+
if (typeof props.src === 'object') {
157+
return <img {...props} src={src} />;
158+
}
159+
return <img {...props} />;
160+
}
210161

211162
const defaultHeight = 300;
212163
const defaultWidth = 700;
@@ -222,15 +173,10 @@ const mdxComponents = {
222173
: parseInt(props.height) || defaultHeight
223174
: defaultHeight;
224175

225-
// Prepend NEXT_PUBLIC_BASE_PATH for local public images (src starts with /).
226-
// Next.js does NOT automatically prepend basePath to plain <img> or ImageZoom src strings —
227-
// only its own <Image> component gets that treatment. Since fetch-docs writes
228-
// <img src="/filaletter/..." /> with absolute paths, we must add the prefix here.
229-
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
230-
let url: string =
231-
typeof src === "string" && src.startsWith("/") && !src.startsWith("//")
232-
? basePath + src
233-
: (src as string);
176+
// Next.js <Image> (used by ImageZoom) automatically prepends basePath to local paths,
177+
// so we pass src directly — no manual basePath prefix needed here.
178+
// (assetPrefix only applies to webpack JS/CSS bundles, not public/ static files.)
179+
const url: string = src as string;
234180

235181
return (
236182
<ImageZoom

source.config.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,42 @@ export const docs = defineDocs({
1919
},
2020
});
2121

22+
// Rehype plugin that prepends basePath to local <img src="/..."> paths.
23+
// Raw HTML <img> tags in MDX bypass the mdx-components.tsx `img` override and are
24+
// emitted as literal HTML, so Next.js never gets a chance to apply basePath automatically.
25+
// We rewrite them here at MDX compile time instead.
26+
function rehypePrependBasePath() {
27+
const basePath =
28+
process.env.NEXT_PUBLIC_BASE_PATH ||
29+
(process.env.NODE_ENV === 'production' ? '/plugins-doc-site' : '');
30+
31+
if (!basePath) return (tree: any) => tree;
32+
33+
function walk(node: any) {
34+
if (
35+
node.type === 'element' &&
36+
node.tagName === 'img' &&
37+
typeof node.properties?.src === 'string'
38+
) {
39+
const src: string = node.properties.src;
40+
if (src.startsWith('/') && !src.startsWith('//') && !src.startsWith(basePath)) {
41+
node.properties.src = basePath + src;
42+
}
43+
}
44+
if (Array.isArray(node.children)) {
45+
for (const child of node.children) walk(child);
46+
}
47+
}
48+
49+
return (tree: any) => walk(tree);
50+
}
51+
2252
export default defineConfig({
2353
mdxOptions: {
2454
remarkImageOptions: {
2555
external: false, // Disable fetching external image sizes
2656
},
2757
remarkPlugins: [],
58+
rehypePlugins: [rehypePrependBasePath],
2859
},
2960
});

0 commit comments

Comments
 (0)