@@ -11,7 +11,7 @@ import fs from 'node:fs';
1111import { createRequire } from 'node:module' ;
1212import path from 'node:path' ;
1313import Piscina from 'piscina' ;
14- import { removeSourceMappingURL } from '../../utils/source-map' ;
14+ import { loadInputSourceMap , removeSourceMappingURL } from '../../utils/source-map' ;
1515
1616interface JavaScriptTransformRequest {
1717 filename : string ;
@@ -36,6 +36,51 @@ const textEncoder = new TextEncoder();
3636 */
3737const LINKER_DECLARATION_PREFIX = 'ɵɵngDeclare' ;
3838
39+ async function instrumentCoverage (
40+ filename : string ,
41+ data : string ,
42+ useInputSourcemap : boolean ,
43+ ) : Promise < string > {
44+ try {
45+ let resolvedPath = 'istanbul-lib-instrument' ;
46+ try {
47+ const requireFn = createRequire ( filename ) ;
48+ resolvedPath = requireFn . resolve ( 'istanbul-lib-instrument' ) ;
49+ } catch {
50+ // Fallback to pool worker import traversal
51+ }
52+
53+ const { createInstrumenter } = ( await import (
54+ resolvedPath
55+ ) ) as typeof import ( 'istanbul-lib-instrument' ) ;
56+ const instrumenter = createInstrumenter ( {
57+ produceSourceMap : useInputSourcemap ,
58+ esModules : true ,
59+ } ) ;
60+
61+ const inputSourceMap = useInputSourcemap ? loadInputSourceMap ( filename , data ) : undefined ;
62+ const instrumentedCode = instrumenter . instrumentSync (
63+ data ,
64+ filename ,
65+ inputSourceMap as Parameters < typeof instrumenter . instrumentSync > [ 2 ] ,
66+ ) ;
67+ const lastMap = instrumenter . lastSourceMap ( ) ;
68+
69+ if ( useInputSourcemap && lastMap ) {
70+ const inlineMap = Buffer . from ( JSON . stringify ( lastMap ) ) . toString ( 'base64' ) ;
71+
72+ return instrumentedCode + `\n//# sourceMappingURL=data:application/json;base64,${ inlineMap } ` ;
73+ }
74+
75+ return removeSourceMappingURL ( instrumentedCode ) ;
76+ } catch ( error ) {
77+ throw new Error (
78+ `The 'istanbul-lib-instrument' package is required for code coverage but was not found. Please install the package.` ,
79+ { cause : error } ,
80+ ) ;
81+ }
82+ }
83+
3984export default async function transformJavaScript (
4085 request : JavaScriptTransformRequest ,
4186) : Promise < unknown > {
@@ -59,50 +104,26 @@ async function transformJavaScriptImpl(
59104 data : string ,
60105 options : Omit < JavaScriptTransformRequest , 'filename' | 'data' > ,
61106) : Promise < string > {
62- const shouldLink = ! options . skipLinker && ( await requiresLinking ( filename , data ) ) ;
63107 const useInputSourcemap =
64108 options . sourcemap &&
65109 ( ! ! options . thirdPartySourcemaps || ! / [ \\ / ] n o d e _ m o d u l e s [ \\ / ] / . test ( filename ) ) ;
66110
67- const babelPlugins : PluginItem [ ] = [ ] ;
111+ let code = data ;
68112
69113 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- babelPlugins . 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- }
114+ code = await instrumentCoverage ( filename , code , useInputSourcemap ) ;
95115 }
96116
117+ const shouldLink = ! options . skipLinker && ( await requiresLinking ( filename , code ) ) ;
118+ const babelPlugins : PluginItem [ ] = [ ] ;
119+
97120 if ( shouldLink ) {
98121 // Lazy load the linker plugin only when linking is required
99122 const linkerPlugin = await createLinkerPlugin ( options ) ;
100123 babelPlugins . push ( linkerPlugin as unknown as PluginItem ) ;
101124 }
102125
103- let code = data ;
104-
105- // If Babel is needed, run it first
126+ // If Babel is needed (e.g. for linking), run it
106127 if ( babelPlugins . length > 0 ) {
107128 const result = await transformAsync ( code , {
108129 filename,
0 commit comments