or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdcollection.mdconfiguration.mdhooks.mdindex.mdinstrumentation.mdreporting.mdstorage.mdtree-summarizer.mdutilities.md
tile.json

instrumentation.mddocs/

0

# Code Instrumentation

1

2

Code instrumentation transforms JavaScript source code to track statement, line, function, and branch coverage during execution. The Instrumenter class provides both synchronous and asynchronous APIs for instrumenting code.

3

4

## Capabilities

5

6

### Instrumenter Class

7

8

Creates an instrumenter instance for transforming JavaScript code to add coverage tracking.

9

10

```javascript { .api }

11

/**

12

* Creates an instrumenter for transforming JavaScript code

13

* @param {InstrumentOptions} options - Configuration options for instrumentation

14

*/

15

class Instrumenter {

16

constructor(options?: InstrumentOptions);

17

18

/**

19

* Synchronously instruments JavaScript code

20

* @param {string} code - The JavaScript source code to instrument

21

* @param {string} filename - The filename/path for the code (used in coverage data)

22

* @returns {string} The instrumented JavaScript code

23

*/

24

instrumentSync(code: string, filename: string): string;

25

26

/**

27

* Asynchronously instruments JavaScript code

28

* @param {string} code - The JavaScript source code to instrument

29

* @param {string} filename - The filename/path for the code

30

* @param {Function} callback - Callback function (err, instrumentedCode) => void

31

*/

32

instrument(code: string, filename: string, callback: (err: Error | null, code?: string) => void): void;

33

34

/**

35

* Instruments an AST directly (advanced usage)

36

* @param {Object} program - The parsed AST program node

37

* @param {string} filename - The filename/path for the code

38

* @param {string} originalCode - The original source code

39

* @returns {string} The instrumented JavaScript code

40

*/

41

instrumentASTSync(program: Object, filename: string, originalCode: string): string;

42

43

/**

44

* Returns coverage object template for the last instrumented file

45

* @returns {Object} Zero-coverage object with all statements, functions, branches, and lines

46

*/

47

lastFileCoverage(): Object;

48

49

/**

50

* Returns source map for the last instrumented file

51

* @returns {Object|null} Source map object or null if not available

52

*/

53

lastSourceMap(): Object | null;

54

55

/**

56

* Filters comments to extract Istanbul ignore hints

57

* @param {Object[]} comments - Array of comment objects from esprima

58

* @returns {Object[]} Filtered array of Istanbul hint comments

59

*/

60

filterHints(comments: Object[]): Object[];

61

62

/**

63

* Extracts the current hint for an AST node based on position

64

* @param {Object} node - AST node to check for hints

65

* @returns {Object|null} Current hint object or null

66

*/

67

extractCurrentHint(node: Object): Object | null;

68

69

/**

70

* Fixes column positions after code wrapping (internal method)

71

* @param {Object} coverState - Coverage state object to fix

72

*/

73

fixColumnPositions(coverState: Object): void;

74

75

/**

76

* Generates the preamble code for coverage tracking (internal method)

77

* @param {string} sourceCode - Original source code

78

* @param {boolean} emitUseStrict - Whether to emit 'use strict'

79

* @returns {string} Preamble code string

80

*/

81

getPreamble(sourceCode: string, emitUseStrict: boolean): string;

82

83

/**

84

* Starts ignoring coverage for subsequent code (internal method)

85

*/

86

startIgnore(): void;

87

88

/**

89

* Ends ignoring coverage for subsequent code (internal method)

90

*/

91

endIgnore(): void;

92

93

/**

94

* Converts AST nodes to block statements (internal method)

95

* @param {Object} node - AST node to convert

96

* @returns {Object} Block statement AST node

97

*/

98

convertToBlock(node: Object): Object;

99

100

/**

101

* Converts arrow function expressions to block form (internal method)

102

* @param {Object} node - Arrow function AST node

103

*/

104

arrowBlockConverter(node: Object): void;

105

106

/**

107

* Ensures try-catch handler compatibility (internal method)

108

* @param {Object} node - Try statement AST node

109

*/

110

paranoidHandlerCheck(node: Object): void;

111

}

112

113

interface InstrumentOptions {

114

/** Name of the global variable to store coverage data (default: '__coverage__') */

115

coverageVariable?: string;

116

117

/** Whether to embed original source code in coverage object (default: false) */

118

embedSource?: boolean;

119

120

/** Whether to preserve comments in instrumented output (default: false) */

121

preserveComments?: boolean;

122

123

/** Whether to emit readable code instead of compact (default: false) */

124

noCompact?: boolean;

125

126

/** Whether the code uses ES6 import/export statements (default: false) */

127

esModules?: boolean;

128

129

/** Whether to skip auto-wrapping code in anonymous function (default: false) */

130

noAutoWrap?: boolean;

131

132

/** Options passed directly to escodegen for code generation */

133

codeGenerationOptions?: Object;

134

135

/** Enable debug mode for instrumenter (default: false) */

136

debug?: boolean;

137

138

/** Enable debug mode for AST walker (default: false) */

139

walkDebug?: boolean;

140

}

141

```

142

143

**Usage Examples:**

144

145

```javascript

146

const fs = require('fs');

147

const { Instrumenter } = require('istanbul');

148

149

// Basic instrumentation

150

const instrumenter = new Instrumenter();

151

const code = fs.readFileSync('app.js', 'utf8');

152

const instrumentedCode = instrumenter.instrumentSync(code, 'app.js');

153

154

// Instrumentation with options

155

const instrumenter2 = new Instrumenter({

156

coverageVariable: '__myCoverage__',

157

embedSource: true,

158

preserveComments: true,

159

esModules: true

160

});

161

162

// Async instrumentation

163

instrumenter.instrument(code, 'app.js', (err, instrumentedCode) => {

164

if (err) throw err;

165

console.log('Code instrumented successfully');

166

167

// Get coverage template

168

const coverageObject = instrumenter.lastFileCoverage();

169

console.log('Coverage template:', coverageObject);

170

});

171

172

// Advanced: Direct AST instrumentation

173

const esprima = require('esprima');

174

const ast = esprima.parseScript(code);

175

const instrumentedFromAST = instrumenter.instrumentASTSync(ast, 'app.js', code);

176

```

177

178

### Coverage Variable Structure

179

180

The instrumented code populates a global coverage variable (default: `__coverage__`) with the following structure:

181

182

```javascript { .api }

183

interface CoverageObject {

184

[filename: string]: FileCoverage;

185

}

186

187

interface FileCoverage {

188

/** File path */

189

path: string;

190

191

/** Statement coverage data */

192

s: { [statementId: string]: number };

193

194

/** Branch coverage data */

195

b: { [branchId: string]: number[] };

196

197

/** Function coverage data */

198

f: { [functionId: string]: number };

199

200

/** Function name mapping */

201

fnMap: { [functionId: string]: FunctionMapping };

202

203

/** Statement location mapping */

204

statementMap: { [statementId: string]: Location };

205

206

/** Branch location mapping */

207

branchMap: { [branchId: string]: BranchMapping };

208

209

/** Line coverage data (derived) */

210

l?: { [lineNumber: string]: number };

211

212

/** Original source code (if embedSource is true) */

213

code?: string[];

214

}

215

216

interface Location {

217

start: { line: number; column: number };

218

end: { line: number; column: number };

219

}

220

221

interface FunctionMapping {

222

name: string;

223

decl: Location;

224

loc: Location;

225

line: number;

226

}

227

228

interface BranchMapping {

229

loc: Location;

230

type: 'if' | 'switch' | 'cond-expr' | 'default-arg';

231

locations: Location[];

232

line: number;

233

}

234

```

235

236

### Instrumentation Process

237

238

The instrumentation process involves:

239

240

1. **Parse**: Code is parsed into an Abstract Syntax Tree (AST)

241

2. **Transform**: AST is traversed and modified to add coverage tracking

242

3. **Generate**: Modified AST is converted back to JavaScript code

243

4. **Template**: Coverage template is created with all trackable elements

244

245

The instrumented code will automatically populate the global coverage variable as it executes, tracking:

246

- **Statement coverage**: Which statements were executed

247

- **Branch coverage**: Which conditional branches were taken

248

- **Function coverage**: Which functions were called

249

- **Line coverage**: Which lines contained executed code

250

251

### Error Handling

252

253

```javascript

254

try {

255

const instrumentedCode = instrumenter.instrumentSync(code, filename);

256

} catch (error) {

257

if (error.name === 'SyntaxError') {

258

console.error('JavaScript syntax error in:', filename);

259

} else {

260

console.error('Instrumentation failed:', error.message);

261

}

262

}

263

```

264

265

Common instrumentation errors include:

266

- **SyntaxError**: Invalid JavaScript syntax in source code

267

- **TypeError**: Invalid options passed to instrumenter

268

- **Error**: AST transformation failures or code generation issues