@@ -93,7 +93,7 @@ export async function prepareBundleForDebugIdUpload(
9393 *
9494 * The string pattern is injected via the debug ID injection snipped.
9595 */
96- function determineDebugIdFromBundleSource ( code : string ) : string | undefined {
96+ export function determineDebugIdFromBundleSource ( code : string ) : string | undefined {
9797 const match = code . match (
9898 / s e n t r y - d b i d - ( [ 0 - 9 a - f A - F ] { 8 } \b - [ 0 - 9 a - f A - F ] { 4 } \b - [ 0 - 9 a - f A - F ] { 4 } \b - [ 0 - 9 a - f A - F ] { 4 } \b - [ 0 - 9 a - f A - F ] { 12 } ) / ,
9999 ) ;
@@ -119,6 +119,95 @@ function addDebugIdToBundleSource(bundleSource: string, debugId: string): string
119119 }
120120}
121121
122+ function setDebugIdOnSourceMap ( map : Record < string , unknown > , debugId : string ) : void {
123+ // For now we write both fields until we know what will become the standard - if ever.
124+ map [ 'debug_id' ] = debugId ;
125+ map [ 'debugId' ] = debugId ;
126+ }
127+
128+ /**
129+ * Stamps the debug ID injected into `bundleSource` into the given source map.
130+ *
131+ * This exists for `sourcemaps.disable: "disable-upload"`: the regular upload path only stamps
132+ * temporary copies of the artifacts (see `prepareBundleForDebugIdUpload`), so without this the
133+ * emitted map would carry no debug ID and a later manual upload could not match it to the bundle.
134+ * Only the map is touched. The bundle's bytes stay as emitted so subresource integrity hashes
135+ * computed on them remain valid.
136+ *
137+ * @returns the stamped source map, or `undefined` if the bundle carries no debug ID or the map
138+ * isn't valid JSON.
139+ */
140+ export function addDebugIdToSourceMap ( bundleSource : string , sourceMapSource : string ) : string | undefined {
141+ const debugId = determineDebugIdFromBundleSource ( bundleSource ) ;
142+ if ( debugId === undefined ) {
143+ return undefined ;
144+ }
145+
146+ let map : unknown ;
147+ try {
148+ map = JSON . parse ( sourceMapSource ) ;
149+ } catch {
150+ return undefined ;
151+ }
152+
153+ if ( ! map || typeof map !== 'object' ) {
154+ return undefined ;
155+ }
156+
157+ setDebugIdOnSourceMap ( map as Record < string , unknown > , debugId ) ;
158+ return JSON . stringify ( map ) ;
159+ }
160+
161+ /**
162+ * Stamps the debug ID of an emitted bundle into its source map file on disk.
163+ *
164+ * Used by bundlers that offer no hook to modify assets before they are written (esbuild).
165+ * Rewrites only the map; the bundle itself is never modified.
166+ */
167+ export async function addDebugIdToEmittedSourceMap (
168+ bundleFilePath : string ,
169+ logger : Logger ,
170+ resolveSourceMapHook : ResolveSourceMapHook | undefined ,
171+ ) : Promise < void > {
172+ let bundleSource : string ;
173+ try {
174+ bundleSource = await fs . promises . readFile ( bundleFilePath , 'utf8' ) ;
175+ } catch ( e ) {
176+ logger . error ( `Could not read bundle to stamp debug ID into its source map: ${ bundleFilePath } ` , e ) ;
177+ return ;
178+ }
179+
180+ const sourceMapPath = await determineSourceMapPathFromBundle (
181+ bundleFilePath ,
182+ bundleSource ,
183+ logger ,
184+ resolveSourceMapHook ,
185+ ) ;
186+ if ( ! sourceMapPath ) {
187+ return ;
188+ }
189+
190+ let sourceMapSource : string ;
191+ try {
192+ sourceMapSource = await fs . promises . readFile ( sourceMapPath , 'utf8' ) ;
193+ } catch ( e ) {
194+ logger . error ( `Could not read source map to stamp debug ID: ${ sourceMapPath } ` , e ) ;
195+ return ;
196+ }
197+
198+ const stampedSourceMap = addDebugIdToSourceMap ( bundleSource , sourceMapSource ) ;
199+ if ( stampedSourceMap === undefined ) {
200+ logger . debug ( `Could not stamp debug ID into source map (no debug ID in bundle or invalid map): ${ sourceMapPath } ` ) ;
201+ return ;
202+ }
203+
204+ try {
205+ await fs . promises . writeFile ( sourceMapPath , stampedSourceMap , 'utf8' ) ;
206+ } catch ( e ) {
207+ logger . error ( `Could not write debug ID into source map: ${ sourceMapPath } ` , e ) ;
208+ }
209+ }
210+
122211/**
123212 * Whether the bundle carries its source map inlined as `sourceMappingURL=data:`
124213 * URI, rather than referencing a separate `.map` file. Such bundles must still
@@ -223,9 +312,7 @@ async function prepareSourceMapForDebugIdUpload(
223312 let map : Record < string , unknown > ;
224313 try {
225314 map = JSON . parse ( sourceMapFileContent ) as { sources : unknown ; [ key : string ] : unknown } ;
226- // For now we write both fields until we know what will become the standard - if ever.
227- map [ 'debug_id' ] = debugId ;
228- map [ 'debugId' ] = debugId ;
315+ setDebugIdOnSourceMap ( map , debugId ) ;
229316 } catch {
230317 logger . error ( `Failed to parse source map for debug ID upload: ${ sourceMapPath } ` ) ;
231318 return ;
0 commit comments