Skip to content

Commit 896228e

Browse files
committed
fix image edgecase bug
1 parent d6081b0 commit 896228e

File tree

2 files changed

+77
-70
lines changed

2 files changed

+77
-70
lines changed

src/components/docImage.tsx

Lines changed: 75 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,54 @@ import {serverContext} from 'sentry-docs/serverContext';
44

55
import {ImageLightbox} from './imageLightbox';
66

7+
// Helper function to safely parse dimension values
8+
const parseDimension = (value: string | number | undefined): number | undefined => {
9+
if (typeof value === 'number' && value > 0 && value <= 10000) return value;
10+
if (typeof value === 'string') {
11+
const parsed = parseInt(value, 10);
12+
return parsed > 0 && parsed <= 10000 ? parsed : undefined;
13+
}
14+
return undefined;
15+
};
16+
17+
// Helper function to parse dimensions from URL hash
18+
const parseDimensionsFromHash = (url: string): number[] => {
19+
try {
20+
const urlObj = new URL(url, 'https://example.com');
21+
const hash = urlObj.hash.slice(1);
22+
23+
// Only parse hash if it looks like dimensions (e.g., "800x600", "100x200")
24+
// Must be exactly two positive integers separated by 'x'
25+
const dimensionPattern = /^(\d+)x(\d+)$/;
26+
const match = hash.match(dimensionPattern);
27+
28+
if (match) {
29+
const width = parseInt(match[1], 10);
30+
const height = parseInt(match[2], 10);
31+
return width > 0 && width <= 10000 && height > 0 && height <= 10000
32+
? [width, height]
33+
: [];
34+
}
35+
36+
return [];
37+
} catch (_error) {
38+
return [];
39+
}
40+
};
41+
42+
// Helper function to clean URL by removing hash
43+
const cleanUrl = (url: string): string => {
44+
try {
45+
const urlObj = new URL(url);
46+
// For external URLs, reconstruct without hash
47+
urlObj.hash = '';
48+
return urlObj.toString();
49+
} catch (_error) {
50+
// If URL parsing fails, just remove hash manually
51+
return url.split('#')[0];
52+
}
53+
};
54+
755
export default function DocImage({
856
src,
957
width: propsWidth,
@@ -16,83 +64,41 @@ export default function DocImage({
1664
return null;
1765
}
1866

19-
// Handle external images early - pass through without processing
20-
if (src.startsWith('http')) {
21-
// For external images, let ImageLightbox decide whether to use Next.js Image or regular img
22-
// Parse dimensions if provided
23-
const width =
24-
typeof propsWidth === 'number'
25-
? propsWidth
26-
: typeof propsWidth === 'string'
27-
? parseInt(propsWidth, 10) || undefined
28-
: undefined;
29-
const height =
30-
typeof propsHeight === 'number'
31-
? propsHeight
32-
: typeof propsHeight === 'string'
33-
? parseInt(propsHeight, 10) || undefined
34-
: undefined;
35-
36-
return (
37-
<ImageLightbox
38-
src={src}
39-
imgPath={src} // For external images, imgPath should be the same as src
40-
width={width}
41-
height={height}
42-
alt={props.alt ?? ''}
43-
{...props}
44-
/>
45-
);
46-
}
47-
48-
// If the image src is not an absolute URL, we assume it's a relative path
49-
// and we prepend /mdx-images/ to it.
50-
if (src.startsWith('./')) {
51-
src = path.join('/mdx-images', src);
52-
}
53-
// account for the old way of doing things where the public folder structure mirrored the docs folder
54-
else if (!src?.startsWith('/') && !src?.includes('://')) {
55-
src = `/${pagePath.join('/')}/${src}`;
56-
}
67+
const isExternalImage = src.startsWith('http') || src.startsWith('//');
68+
let finalSrc = src;
69+
let imgPath = src;
5770

58-
// parse the size from the URL hash (set by remark-image-size.js)
59-
let srcURL: URL;
60-
let imgPath: string;
61-
let dimensions: number[] = [];
71+
// For internal images, process the path
72+
if (!isExternalImage) {
73+
if (src.startsWith('./')) {
74+
finalSrc = path.join('/mdx-images', src);
75+
} else if (!src?.startsWith('/') && !src?.includes('://')) {
76+
finalSrc = `/${pagePath.join('/')}/${src}`;
77+
}
6278

63-
try {
64-
srcURL = new URL(src, 'https://example.com');
65-
imgPath = srcURL.pathname;
66-
dimensions = srcURL.hash // #wxh
67-
.slice(1)
68-
.split('x')
69-
.map(s => {
70-
const parsed = parseInt(s, 10);
71-
return isNaN(parsed) ? 0 : parsed;
72-
});
73-
} catch (_error) {
74-
// Failed to parse URL, fallback to using src directly
75-
imgPath = src;
76-
dimensions = [];
79+
// For internal images, imgPath should be the pathname only
80+
try {
81+
const srcURL = new URL(finalSrc, 'https://example.com');
82+
imgPath = srcURL.pathname;
83+
} catch (_error) {
84+
imgPath = finalSrc;
85+
}
86+
} else {
87+
// For external images, strip hash from both src and imgPath
88+
finalSrc = cleanUrl(src);
89+
imgPath = finalSrc;
7790
}
7891

79-
// Helper function to safely parse dimension values - only return valid numbers or undefined
80-
const parseDimension = (value: string | number | undefined): number | undefined => {
81-
if (typeof value === 'number' && value > 0) return value;
82-
if (typeof value === 'string') {
83-
const parsed = parseInt(value, 10);
84-
return parsed > 0 ? parsed : undefined;
85-
}
86-
return undefined;
87-
};
92+
// Parse dimensions from URL hash (works for both internal and external)
93+
const hashDimensions = parseDimensionsFromHash(src);
8894

89-
// Use parsed dimensions, fallback to props - let ImageLightbox decide on defaults
90-
const width = dimensions[0] > 0 ? dimensions[0] : parseDimension(propsWidth);
91-
const height = dimensions[1] > 0 ? dimensions[1] : parseDimension(propsHeight);
95+
// Use hash dimensions first, fallback to props
96+
const width = hashDimensions[0] > 0 ? hashDimensions[0] : parseDimension(propsWidth);
97+
const height = hashDimensions[1] > 0 ? hashDimensions[1] : parseDimension(propsHeight);
9298

9399
return (
94100
<ImageLightbox
95-
src={src}
101+
src={finalSrc}
96102
imgPath={imgPath}
97103
width={width}
98104
height={height}

src/components/imageLightbox/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ interface ImageLightboxProps
2222
}
2323

2424
// Helper functions
25-
const isExternalImage = (src: string): boolean => src.startsWith('http');
25+
const isExternalImage = (src: string): boolean =>
26+
src.startsWith('http') || src.startsWith('//');
2627

2728
const getImageUrl = (src: string, imgPath: string): string =>
2829
isExternalImage(src) ? src : imgPath;

0 commit comments

Comments
 (0)