0
# Babel Plugin Integration
1
2
Integration functionality for incorporating coverage instrumentation directly into Babel transformation pipelines. This approach is used by babel-plugin-istanbul and enables seamless coverage tracking during existing build processes.
3
4
## Capabilities
5
6
### Program Visitor
7
8
Creates a Babel AST visitor for instrumenting code during Babel transformation. Returns an object with `enter` and `exit` methods for Program-level AST manipulation.
9
10
```javascript { .api }
11
/**
12
* Creates a babel adaptor for instrumentation
13
* @param types - An instance of babel-types (typically from Babel's API)
14
* @param sourceFilePath - The path to source file for coverage tracking
15
* @param opts - Additional instrumentation options
16
* @returns Object with enter and exit functions for Babel Program visitor
17
*/
18
function programVisitor(
19
types: object,
20
sourceFilePath?: string,
21
opts?: ProgramVisitorOptions
22
): ProgramVisitorResult;
23
24
interface ProgramVisitorOptions {
25
/** The global coverage variable name (default: "__coverage__") */
26
coverageVariable?: string;
27
/** Report boolean value of logical expressions (default: false) */
28
reportLogic?: boolean;
29
/** The global coverage variable scope (default: "this") */
30
coverageGlobalScope?: string;
31
/** Use an evaluated function to find coverageGlobalScope (default: true) */
32
coverageGlobalScopeFunc?: boolean;
33
/** Names of methods to ignore by default on classes (default: []) */
34
ignoreClassMethods?: string[];
35
/** The input source map that maps uninstrumented code back to original code */
36
inputSourceMap?: object;
37
}
38
39
interface ProgramVisitorResult {
40
/** Function to call when entering Program AST node */
41
enter(path: object): void;
42
/** Function to call when exiting Program AST node */
43
exit(path: object): ExitResult;
44
}
45
46
interface ExitResult {
47
/** The file coverage object created for the source file */
48
fileCoverage: object;
49
/** Any source mapping URL found when processing the file */
50
sourceMappingURL?: string;
51
}
52
```
53
54
**Usage Examples:**
55
56
```javascript
57
const { programVisitor } = require("istanbul-lib-instrument");
58
const { transformSync } = require("@babel/core");
59
const types = require("@babel/types");
60
61
// Create a simple Babel plugin using programVisitor
62
function createCoveragePlugin() {
63
return function({ types }) {
64
const visitor = programVisitor(types, 'example.js', {
65
coverageVariable: '__coverage__',
66
reportLogic: true
67
});
68
69
return {
70
visitor: {
71
Program: {
72
enter: visitor.enter,
73
exit(path) {
74
const result = visitor.exit(path);
75
console.log('Coverage data:', result.fileCoverage);
76
if (result.sourceMappingURL) {
77
console.log('Source map URL:', result.sourceMappingURL);
78
}
79
}
80
}
81
}
82
};
83
};
84
}
85
86
// Use the plugin with Babel
87
const code = `
88
function greet(name) {
89
if (name) {
90
return "Hello, " + name;
91
}
92
return "Hello, World!";
93
}
94
`;
95
96
const result = transformSync(code, {
97
plugins: [createCoveragePlugin()],
98
filename: 'greet.js'
99
});
100
101
console.log(result.code);
102
```
103
104
### Babel Plugin Integration Pattern
105
106
The programVisitor is designed to integrate with existing Babel pipelines without requiring separate instrumentation steps.
107
108
```javascript { .api }
109
// Typical babel-plugin-istanbul usage pattern
110
function babelPluginIstanbul(babel) {
111
const { types } = babel;
112
113
return {
114
visitor: {
115
Program: {
116
enter(path, state) {
117
const visitor = programVisitor(types, state.filename, state.opts);
118
visitor.enter(path);
119
},
120
exit(path, state) {
121
const visitor = programVisitor(types, state.filename, state.opts);
122
const result = visitor.exit(path);
123
// Store coverage data for later use
124
state.coverageData = result.fileCoverage;
125
}
126
}
127
}
128
};
129
}
130
```
131
132
**Usage Examples:**
133
134
```javascript
135
const { programVisitor } = require("istanbul-lib-instrument");
136
137
// Custom Babel transformation with coverage
138
function transformWithCoverage(code, filename, options = {}) {
139
const { transformSync } = require("@babel/core");
140
141
let coverageData = null;
142
143
const result = transformSync(code, {
144
filename,
145
plugins: [
146
function({ types }) {
147
const visitor = programVisitor(types, filename, options);
148
149
return {
150
visitor: {
151
Program: {
152
enter: visitor.enter,
153
exit(path) {
154
const exitResult = visitor.exit(path);
155
coverageData = exitResult.fileCoverage;
156
}
157
}
158
}
159
};
160
}
161
]
162
});
163
164
return {
165
code: result.code,
166
coverage: coverageData,
167
map: result.map
168
};
169
}
170
171
// Usage
172
const { code, coverage } = transformWithCoverage(
173
'function add(a, b) { return a + b; }',
174
'math.js',
175
{ reportLogic: true }
176
);
177
```
178
179
### Advanced Integration Scenarios
180
181
Integration with complex Babel transformation pipelines and build tools.
182
183
```javascript { .api }
184
// Integration with multiple Babel plugins
185
function createInstrumentationPipeline(options = {}) {
186
return [
187
// Pre-instrumentation plugins (e.g., TypeScript, JSX)
188
'@babel/plugin-transform-typescript',
189
'@babel/plugin-transform-react-jsx',
190
191
// Coverage instrumentation
192
function({ types }) {
193
const visitor = programVisitor(types, options.filename, {
194
coverageVariable: options.coverageVariable,
195
reportLogic: options.reportLogic,
196
inputSourceMap: options.inputSourceMap
197
});
198
199
return {
200
visitor: {
201
Program: {
202
enter: visitor.enter,
203
exit: visitor.exit
204
}
205
}
206
};
207
},
208
209
// Post-instrumentation plugins
210
'@babel/plugin-transform-modules-commonjs'
211
];
212
}
213
```
214
215
**Usage Examples:**
216
217
```javascript
218
const { transformSync } = require("@babel/core");
219
220
// Transform TypeScript with coverage
221
const tsCode = `
222
interface User {
223
name: string;
224
age: number;
225
}
226
227
function createUser(name: string, age: number): User {
228
if (age < 0) {
229
throw new Error("Age cannot be negative");
230
}
231
return { name, age };
232
}
233
`;
234
235
const result = transformSync(tsCode, {
236
filename: 'user.ts',
237
plugins: createInstrumentationPipeline({
238
filename: 'user.ts',
239
reportLogic: true,
240
coverageVariable: '__coverage__'
241
})
242
});
243
```
244
245
### Source Map Integration
246
247
Handling source maps during Babel transformation with coverage instrumentation.
248
249
```javascript { .api }
250
// Source map preservation with instrumentation
251
function instrumentWithSourceMap(code, filename, existingSourceMap) {
252
const { transformSync } = require("@babel/core");
253
const { programVisitor } = require("istanbul-lib-instrument");
254
255
let finalCoverage = null;
256
257
const result = transformSync(code, {
258
filename,
259
inputSourceMap: existingSourceMap,
260
sourceMaps: true,
261
plugins: [
262
function({ types }) {
263
const visitor = programVisitor(types, filename, {
264
inputSourceMap: existingSourceMap
265
});
266
267
return {
268
visitor: {
269
Program: {
270
enter: visitor.enter,
271
exit(path) {
272
const exitResult = visitor.exit(path);
273
finalCoverage = exitResult.fileCoverage;
274
}
275
}
276
}
277
};
278
}
279
]
280
});
281
282
return {
283
code: result.code,
284
map: result.map,
285
coverage: finalCoverage
286
};
287
}
288
```
289
290
## Configuration Options
291
292
### Basic Options
293
294
- **coverageVariable**: Global variable name for storing coverage data
295
- **reportLogic**: Whether to track logical expression evaluation (&&, ||)
296
- **coverageGlobalScope**: Scope attachment for coverage variable
297
298
### Advanced Options
299
300
- **ignoreClassMethods**: Array of class method names to exclude from coverage
301
- **inputSourceMap**: Source map for mapping coverage back to original source
302
- **coverageGlobalScopeFunc**: Use function evaluation for scope determination
303
304
## Integration Best Practices
305
306
### Build Tool Integration
307
308
```javascript
309
// Webpack integration example
310
module.exports = {
311
module: {
312
rules: [{
313
test: /\.js$/,
314
use: {
315
loader: 'babel-loader',
316
options: {
317
plugins: [
318
function({ types }) {
319
return {
320
visitor: {
321
Program: {
322
enter(path, state) {
323
const visitor = programVisitor(types, state.filename);
324
visitor.enter(path);
325
},
326
exit(path, state) {
327
const visitor = programVisitor(types, state.filename);
328
visitor.exit(path);
329
}
330
}
331
}
332
};
333
}
334
]
335
}
336
}
337
}]
338
}
339
};
340
```
341
342
### Development vs Production
343
344
```javascript
345
// Conditional instrumentation based on environment
346
function createConditionalPlugin() {
347
if (process.env.NODE_ENV === 'test') {
348
return function({ types }) {
349
const visitor = programVisitor(types, 'unknown.js', {
350
reportLogic: true,
351
debug: true
352
});
353
354
return {
355
visitor: {
356
Program: {
357
enter: visitor.enter,
358
exit: visitor.exit
359
}
360
}
361
};
362
};
363
}
364
365
// Return no-op plugin for production
366
return function() {
367
return { visitor: {} };
368
};
369
}
370
```
371
372
## Error Handling
373
374
The programVisitor handles errors during AST transformation and provides meaningful error messages.
375
376
```javascript
377
const { programVisitor } = require("istanbul-lib-instrument");
378
379
function safeInstrumentation(types, filename, options) {
380
try {
381
return programVisitor(types, filename, options);
382
} catch (error) {
383
console.error(`Failed to create program visitor for ${filename}:`, error);
384
// Return no-op visitor
385
return {
386
enter() {},
387
exit() {
388
return { fileCoverage: null };
389
}
390
};
391
}
392
}
393
```