@@ -10,7 +10,7 @@ import { type PluginItem, transformAsync } from '@babel/core';
1010import { createRequire } from 'node:module' ;
1111import Piscina from 'piscina' ;
1212import { useBabelLinker } from '../../utils/environment-options.js' ;
13- import { removeSourceMappingURL } from '../../utils/source-map' ;
13+ import { loadInputSourceMap , removeSourceMappingURL } from '../../utils/source-map' ;
1414
1515interface JavaScriptTransformRequest {
1616 filename : string ;
@@ -35,6 +35,51 @@ const textEncoder = new TextEncoder();
3535 */
3636const LINKER_DECLARATION_PREFIX = 'ɵɵngDeclare' ;
3737
38+ async function instrumentCoverage (
39+ filename : string ,
40+ data : string ,
41+ useInputSourcemap : boolean ,
42+ ) : Promise < string > {
43+ try {
44+ let resolvedPath = 'istanbul-lib-instrument' ;
45+ try {
46+ const requireFn = createRequire ( filename ) ;
47+ resolvedPath = requireFn . resolve ( 'istanbul-lib-instrument' ) ;
48+ } catch {
49+ // Fallback to pool worker import traversal
50+ }
51+
52+ const { createInstrumenter } = ( await import (
53+ resolvedPath
54+ ) ) as typeof import ( 'istanbul-lib-instrument' ) ;
55+ const instrumenter = createInstrumenter ( {
56+ produceSourceMap : useInputSourcemap ,
57+ esModules : true ,
58+ } ) ;
59+
60+ const inputSourceMap = useInputSourcemap ? loadInputSourceMap ( filename , data ) : undefined ;
61+ const instrumentedCode = instrumenter . instrumentSync (
62+ data ,
63+ filename ,
64+ inputSourceMap as Parameters < typeof instrumenter . instrumentSync > [ 2 ] ,
65+ ) ;
66+ const lastMap = instrumenter . lastSourceMap ( ) ;
67+
68+ if ( useInputSourcemap && lastMap ) {
69+ const inlineMap = Buffer . from ( JSON . stringify ( lastMap ) ) . toString ( 'base64' ) ;
70+
71+ return instrumentedCode + `\n//# sourceMappingURL=data:application/json;base64,${ inlineMap } ` ;
72+ }
73+
74+ return removeSourceMappingURL ( instrumentedCode ) ;
75+ } catch ( error ) {
76+ throw new Error (
77+ `The 'istanbul-lib-instrument' package is required for code coverage but was not found. Please install the package.` ,
78+ { cause : error } ,
79+ ) ;
80+ }
81+ }
82+
3883export default async function transformJavaScript (
3984 request : JavaScriptTransformRequest ,
4085) : Promise < unknown > {
@@ -62,58 +107,43 @@ async function transformJavaScriptImpl(
62107 options . sourcemap &&
63108 ( ! ! options . thirdPartySourcemaps || ! / [ \\ / ] n o d e _ m o d u l e s [ \\ / ] / . test ( filename ) ) ;
64109
65- const babelPlugins : PluginItem [ ] = [ ] ;
110+ let code = data ;
66111
67112 if ( options . instrumentForCoverage ) {
68- try {
69- let resolvedPath = 'istanbul-lib-instrument' ;
70- try {
71- const requireFn = createRequire ( filename ) ;
72- resolvedPath = requireFn . resolve ( 'istanbul-lib-instrument' ) ;
73- } catch {
74- // Fallback to pool worker import traversal
75- }
76-
77- const istanbul = await import ( resolvedPath ) ;
78- const programVisitor = istanbul . programVisitor ?? istanbul . default ?. programVisitor ;
79-
80- if ( ! programVisitor ) {
81- throw new Error ( 'programVisitor is not available in istanbul-lib-instrument.' ) ;
82- }
83-
84- const { default : coveragePluginFactory } =
85- await import ( '../babel/plugins/add-code-coverage.js' ) ;
86- babelPlugins . push ( coveragePluginFactory ( programVisitor ) as unknown as PluginItem ) ;
87- } catch ( error ) {
88- throw new Error (
89- `The 'istanbul-lib-instrument' package is required for code coverage but was not found. Please install the package.` ,
90- { cause : error } ,
91- ) ;
92- }
113+ code = await instrumentCoverage ( filename , code , useInputSourcemap ) ;
93114 }
94115
95- let code = data ;
96-
97116 if ( shouldLink ) {
98117 if ( useBabelLinker ) {
99118 const { createEs2015LinkerPlugin } = await import ( '@angular/compiler-cli/linker/babel' ) ;
100119 const { ConsoleLogger, LogLevel } = await import ( '@angular/compiler-cli' ) ;
101120
102- babelPlugins . push (
103- createEs2015LinkerPlugin ( {
104- fileSystem : {
105- exists : ( ) => false ,
106- readFile : ( ) => '' ,
107- resolve : ( ...paths : string [ ] ) => paths . join ( '/' ) ,
108- dirname : ( path : string ) => path . split ( '/' ) . slice ( 0 , - 1 ) . join ( '/' ) ,
109- relative : ( _from : string , to : string ) => to ,
110- } as never ,
111- logger : new ConsoleLogger ( LogLevel . info ) ,
112- linkerJitMode : options . jit ,
113- // This is a workaround until https://github.com/angular/angular/issues/42769 is fixed.
114- sourceMapping : false ,
115- } ) as PluginItem ,
116- ) ;
121+ const result = await transformAsync ( code , {
122+ filename,
123+ inputSourceMap : ( useInputSourcemap ? undefined : false ) as undefined ,
124+ sourceMaps : useInputSourcemap ? 'inline' : false ,
125+ compact : false ,
126+ configFile : false ,
127+ babelrc : false ,
128+ browserslistConfigFile : false ,
129+ plugins : [
130+ createEs2015LinkerPlugin ( {
131+ fileSystem : {
132+ exists : ( ) => false ,
133+ readFile : ( ) => '' ,
134+ resolve : ( ...paths : string [ ] ) => paths . join ( '/' ) ,
135+ dirname : ( path : string ) => path . split ( '/' ) . slice ( 0 , - 1 ) . join ( '/' ) ,
136+ relative : ( _from : string , to : string ) => to ,
137+ } as never ,
138+ logger : new ConsoleLogger ( LogLevel . info ) ,
139+ linkerJitMode : options . jit ,
140+ // This is a workaround until https://github.com/angular/angular/issues/42769 is fixed.
141+ sourceMapping : false ,
142+ } ) as PluginItem ,
143+ ] ,
144+ } ) ;
145+
146+ code = result ?. code ?? code ;
117147 } else {
118148 oxcLinkerModule ??= await import ( '../angular/linker/oxc-linker.js' ) ;
119149 const result = oxcLinkerModule . linkWithOxc ( filename , code , {
@@ -130,21 +160,6 @@ async function transformJavaScriptImpl(
130160 }
131161 }
132162
133- // If Babel is needed for code coverage or babel linker fallback, run it
134- if ( babelPlugins . length > 0 ) {
135- const result = await transformAsync ( code , {
136- filename,
137- inputSourceMap : ( useInputSourcemap ? undefined : false ) as undefined ,
138- sourceMaps : useInputSourcemap ? 'inline' : false ,
139- compact : false ,
140- configFile : false ,
141- babelrc : false ,
142- browserslistConfigFile : false ,
143- plugins : babelPlugins ,
144- } ) ;
145- code = result ?. code ?? code ;
146- }
147-
148163 // Run advanced optimizations using our fast oxc-transform
149164 if ( options . advancedOptimizations ) {
150165 const { transform } = await import ( '../babel/plugins/oxc-transform.js' ) ;
0 commit comments