@@ -36,6 +36,61 @@ const textEncoder = new TextEncoder();
3636 */
3737const LINKER_DECLARATION_PREFIX = 'ɵɵngDeclare' ;
3838
39+ function extractInlineSourceMap ( code : string ) : object | undefined {
40+ const match = code . match (
41+ / \/ \/ # s o u r c e M a p p i n g U R L = d a t a : a p p l i c a t i o n \/ j s o n ; (?: c h a r s e t = u t f - 8 ; ) ? b a s e 6 4 , ( .* ) $ / m,
42+ ) ;
43+ if ( ! match ) {
44+ return undefined ;
45+ }
46+ try {
47+ return JSON . parse ( Buffer . from ( match [ 1 ] , 'base64' ) . toString ( ) ) as object ;
48+ } catch {
49+ return undefined ;
50+ }
51+ }
52+
53+ async function instrumentCoverage (
54+ filename : string ,
55+ data : string ,
56+ useInputSourcemap : boolean ,
57+ ) : Promise < string > {
58+ try {
59+ let resolvedPath = 'istanbul-lib-instrument' ;
60+ try {
61+ const requireFn = createRequire ( filename ) ;
62+ resolvedPath = requireFn . resolve ( 'istanbul-lib-instrument' ) ;
63+ } catch {
64+ // Fallback to pool worker import traversal
65+ }
66+
67+ const { createInstrumenter } = ( await import (
68+ resolvedPath
69+ ) ) as typeof import ( 'istanbul-lib-instrument' ) ;
70+ const instrumenter = createInstrumenter ( {
71+ produceSourceMap : useInputSourcemap ,
72+ esModules : true ,
73+ } ) ;
74+
75+ const inputSourceMap = useInputSourcemap ? extractInlineSourceMap ( data ) : undefined ;
76+ const instrumentedCode = instrumenter . instrumentSync ( data , filename , inputSourceMap ) ;
77+ const lastMap = instrumenter . lastSourceMap ( ) ;
78+
79+ if ( useInputSourcemap && lastMap ) {
80+ const inlineMap = Buffer . from ( JSON . stringify ( lastMap ) ) . toString ( 'base64' ) ;
81+
82+ return instrumentedCode + `\n//# sourceMappingURL=data:application/json;base64,${ inlineMap } ` ;
83+ }
84+
85+ return removeSourceMappingURL ( instrumentedCode ) ;
86+ } catch ( error ) {
87+ throw new Error (
88+ `The 'istanbul-lib-instrument' package is required for code coverage but was not found. Please install the package.` ,
89+ { cause : error } ,
90+ ) ;
91+ }
92+ }
93+
3994export default async function transformJavaScript (
4095 request : JavaScriptTransformRequest ,
4196) : Promise < unknown > {
@@ -59,41 +114,18 @@ async function transformWithBabel(
59114 data : string ,
60115 options : Omit < JavaScriptTransformRequest , 'filename' | 'data' > ,
61116) : Promise < string > {
62- const shouldLink = ! options . skipLinker && ( await requiresLinking ( filename , data ) ) ;
63117 const useInputSourcemap =
64118 options . sourcemap &&
65119 ( ! ! options . thirdPartySourcemaps || ! / [ \\ / ] n o d e _ m o d u l e s [ \\ / ] / . test ( filename ) ) ;
66120
67- const plugins : PluginItem [ ] = [ ] ;
68-
121+ let code = data ;
69122 if ( options . instrumentForCoverage ) {
70- try {
71- let resolvedPath = 'istanbul-lib-instrument' ;
72- try {
73- const requireFn = createRequire ( filename ) ;
74- resolvedPath = requireFn . resolve ( 'istanbul-lib-instrument' ) ;
75- } catch {
76- // Fallback to pool worker import traversal
77- }
78-
79- const istanbul = await import ( resolvedPath ) ;
80- const programVisitor = istanbul . programVisitor ?? istanbul . default ?. programVisitor ;
81-
82- if ( ! programVisitor ) {
83- throw new Error ( 'programVisitor is not available in istanbul-lib-instrument.' ) ;
84- }
85-
86- const { default : coveragePluginFactory } =
87- await import ( '../babel/plugins/add-code-coverage.js' ) ;
88- plugins . push ( coveragePluginFactory ( programVisitor ) as unknown as PluginItem ) ;
89- } catch ( error ) {
90- throw new Error (
91- `The 'istanbul-lib-instrument' package is required for code coverage but was not found. Please install the package.` ,
92- { cause : error } ,
93- ) ;
94- }
123+ code = await instrumentCoverage ( filename , code , useInputSourcemap ) ;
95124 }
96125
126+ const shouldLink = ! options . skipLinker && ( await requiresLinking ( filename , code ) ) ;
127+ const plugins : PluginItem [ ] = [ ] ;
128+
97129 if ( shouldLink ) {
98130 // Lazy load the linker plugin only when linking is required
99131 const linkerPlugin = await createLinkerPlugin ( options ) ;
@@ -116,13 +148,13 @@ async function transformWithBabel(
116148 ) ;
117149 }
118150
119- // If no additional transformations are needed, return the data directly
151+ // If no additional transformations are needed, return the code directly
120152 if ( plugins . length === 0 ) {
121153 // Strip sourcemaps if they should not be used
122- return useInputSourcemap ? data : removeSourceMappingURL ( data ) ;
154+ return useInputSourcemap ? code : removeSourceMappingURL ( code ) ;
123155 }
124156
125- const result = await transformAsync ( data , {
157+ const result = await transformAsync ( code , {
126158 filename,
127159 inputSourceMap : ( useInputSourcemap ? undefined : false ) as undefined ,
128160 sourceMaps : useInputSourcemap ? 'inline' : false ,
@@ -133,7 +165,7 @@ async function transformWithBabel(
133165 plugins,
134166 } ) ;
135167
136- const outputCode = result ?. code ?? data ;
168+ const outputCode = result ?. code ?? code ;
137169
138170 // Strip sourcemaps if they should not be used.
139171 // Babel will keep the original comments even if sourcemaps are disabled.
0 commit comments