@@ -82,20 +82,27 @@ const sourceMappingURLRegex = new RegExp(
8282 * @param {string } code source code content
8383 * @returns {string | undefined } source mapping information
8484 */
85- function getSourceMappingURL ( code : string ) : string | undefined {
86- const lines = code . split ( / ^ / m) ;
87- let match : RegExpMatchArray | null | undefined = null ;
85+ export function getSourceMappingURL ( code : string ) : string | undefined {
86+ let searchIndex = code . lastIndexOf ( 'sourceMappingURL' ) ;
87+
88+ while ( searchIndex !== - 1 ) {
89+ const lineStart = code . lastIndexOf ( '\n' , searchIndex ) ;
90+ const lineEnd = code . indexOf ( '\n' , searchIndex ) ;
91+ const line = code . slice (
92+ lineStart + 1 ,
93+ lineEnd === - 1 ? code . length : lineEnd ,
94+ ) ;
95+ const match = line . match ( sourceMappingURLRegex ) ;
8896
89- for ( let i = lines . length - 1 ; i >= 0 ; i -- ) {
90- match = lines [ i ] ?. match ( sourceMappingURLRegex ) ;
9197 if ( match ) {
92- break ;
98+ const sourceMappingURL = match [ 1 ] || match [ 2 ] || '' ;
99+ return sourceMappingURL ? decodeURI ( sourceMappingURL ) : undefined ;
93100 }
94- }
95101
96- const sourceMappingURL = match ? match [ 1 ] || match [ 2 ] || '' : '' ;
102+ searchIndex = code . lastIndexOf ( 'sourceMappingURL' , lineStart - 1 ) ;
103+ }
97104
98- return sourceMappingURL ? decodeURI ( sourceMappingURL ) : sourceMappingURL ;
105+ return undefined ;
99106}
100107
101108export function registerSourceMapURL (
@@ -135,28 +142,36 @@ export async function transformCoverage(
135142 coverageMap : CoverageMap ,
136143 sourcemapUrlCache : Map < string , string | undefined > ,
137144) : Promise < CoverageMap > {
138- await mapWithConcurrency (
139- coverageMap
140- . files ( )
141- // process js/cjs/mjs file only
142- . filter ( ( filename ) => filename . endsWith ( 'js' ) ) ,
143- SOURCE_MAP_SCAN_CONCURRENCY ,
144- async ( filename ) => {
145- let url = sourcemapUrlCache . get ( filename ) ;
146- if ( ! url ) {
147- const { readFile } = await import ( 'node:fs/promises' ) ;
148- url = await readFile ( filename , 'utf8' ) . then (
149- ( content ) => getSourceMappingURL ( content ) ,
150- ( ) => undefined ,
151- ) ;
152- }
153- sourcemapUrlCache . set ( filename , url ) ;
154- } ,
145+ const jsFiles = coverageMap
146+ . files ( )
147+ // process js/cjs/mjs file only
148+ . filter ( ( filename ) => filename . endsWith ( 'js' ) ) ;
149+
150+ const uncachedFiles = jsFiles . filter (
151+ ( filename ) => ! sourcemapUrlCache . has ( filename ) ,
155152 ) ;
156153
154+ if ( uncachedFiles . length ) {
155+ const { readFile } = await import ( 'node:fs/promises' ) ;
156+ await mapWithConcurrency (
157+ uncachedFiles ,
158+ SOURCE_MAP_SCAN_CONCURRENCY ,
159+ async ( filename ) => {
160+ try {
161+ const content = await readFile ( filename , 'utf8' ) ;
162+ sourcemapUrlCache . set ( filename , getSourceMappingURL ( content ) ) ;
163+ } catch {
164+ // Do not cache failed reads. The file may be temporarily unavailable
165+ // during watch-mode rebuilds, so retry it on the next report.
166+ }
167+ } ,
168+ ) ;
169+ }
170+
157171 // Call createSourceMapStore as needed
158172 let store : MapStore | undefined ;
159- for ( const [ filename , url ] of sourcemapUrlCache ) {
173+ for ( const filename of jsFiles ) {
174+ const url = sourcemapUrlCache . get ( filename ) ;
160175 if ( url ) {
161176 if ( ! store ) {
162177 const { createSourceMapStore } =
0 commit comments