@@ -4,6 +4,54 @@ import {serverContext} from 'sentry-docs/serverContext';
44
55import { 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+
755export 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 }
0 commit comments