@@ -4,6 +4,54 @@ import {serverContext} from 'sentry-docs/serverContext';
4
4
5
5
import { ImageLightbox } from './imageLightbox' ;
6
6
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
+
7
55
export default function DocImage ( {
8
56
src,
9
57
width : propsWidth ,
@@ -16,83 +64,41 @@ export default function DocImage({
16
64
return null ;
17
65
}
18
66
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 ;
57
70
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
+ }
62
78
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 ;
77
90
}
78
91
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 ) ;
88
94
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 ) ;
92
98
93
99
return (
94
100
< ImageLightbox
95
- src = { src }
101
+ src = { finalSrc }
96
102
imgPath = { imgPath }
97
103
width = { width }
98
104
height = { height }
0 commit comments