or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context.mdfile-writing.mdindex.mdtree-traversal.mdxml-generation.md
tile.json

xml-generation.mddocs/

0

# XML Generation

1

2

XML generation utilities in Istanbul Lib Report provide well-formed, indented XML output for creating structured reports. The XMLWriter class wraps content writers and manages proper tag opening, closing, and nesting with automatic indentation.

3

4

## Capabilities

5

6

### XMLWriter Class

7

8

The main XML writing utility that produces well-formed, indented XML with proper tag management and nesting validation.

9

10

```javascript { .api }

11

/**

12

* A utility class to produce well-formed, indented XML

13

*/

14

class XMLWriter {

15

/**

16

* Create an XML writer wrapping a content writer

17

* @param {ContentWriter} contentWriter - The content writer that this utility wraps

18

*/

19

constructor(contentWriter);

20

21

/**

22

* Writes the opening XML tag with the supplied attributes

23

* @param {string} name - Tag name

24

* @param {Object} [attrs] - Attributes for the tag as key-value pairs

25

*/

26

openTag(name, attrs);

27

28

/**

29

* Closes an open XML tag

30

* @param {string} name - Tag name to close (must match currently open tag)

31

* @throws {Error} If tag name doesn't match the currently open tag

32

* @throws {Error} If attempting to close tag when none are open

33

*/

34

closeTag(name);

35

36

/**

37

* Writes a tag and its value, opening and closing it at the same time

38

* @param {string} name - Tag name

39

* @param {Object} [attrs] - Tag attributes as key-value pairs

40

* @param {string} [content] - Optional tag content

41

*/

42

inlineTag(name, attrs, content);

43

44

/**

45

* Closes all open tags and ends the document

46

*/

47

closeAll();

48

}

49

```

50

51

**Usage Examples:**

52

53

```javascript

54

const libReport = require('istanbul-lib-report');

55

56

// Create context and get XML writer

57

const context = libReport.createContext({ dir: 'reports', coverageMap });

58

const contentWriter = context.getWriter().writeFile('coverage.xml');

59

const xmlWriter = context.getXMLWriter(contentWriter);

60

61

// Basic XML structure

62

xmlWriter.openTag('coverage', { version: '1.0', timestamp: Date.now() });

63

xmlWriter.openTag('project', { name: 'my-project' });

64

xmlWriter.inlineTag('metric', { type: 'statements', covered: '85', total: '100' });

65

xmlWriter.inlineTag('metric', { type: 'branches', covered: '78', total: '90' });

66

xmlWriter.closeTag('project');

67

xmlWriter.closeTag('coverage');

68

69

contentWriter.close();

70

```

71

72

### Opening Tags

73

74

Creates opening XML tags with optional attributes and manages the tag stack for proper nesting.

75

76

```javascript { .api }

77

/**

78

* Opens an XML tag with optional attributes

79

* @param {string} name - The tag name

80

* @param {Object} [attrs] - Attributes as key-value pairs

81

*/

82

openTag(name, attrs);

83

```

84

85

**Usage Example:**

86

87

```javascript

88

// Simple tag

89

xmlWriter.openTag('report');

90

91

// Tag with attributes

92

xmlWriter.openTag('file', {

93

path: '/src/utils.js',

94

covered: '12',

95

total: '15'

96

});

97

98

// Tag with multiple attributes

99

xmlWriter.openTag('coverage', {

100

'line-rate': '0.85',

101

'branch-rate': '0.78',

102

'lines-covered': '850',

103

'lines-valid': '1000',

104

complexity: '0',

105

version: '1.9',

106

timestamp: '1640995200'

107

});

108

```

109

110

### Closing Tags

111

112

Closes XML tags with validation to ensure proper nesting and tag matching.

113

114

```javascript { .api }

115

/**

116

* Closes an open XML tag with validation

117

* @param {string} name - Tag name to close

118

* @throws {Error} If tag doesn't match currently open tag

119

*/

120

closeTag(name);

121

```

122

123

**Usage Example:**

124

125

```javascript

126

xmlWriter.openTag('methods');

127

xmlWriter.openTag('method', { name: 'calculateTotal', signature: '()V' });

128

xmlWriter.closeTag('method');

129

xmlWriter.closeTag('methods');

130

131

// Error handling

132

try {

133

xmlWriter.openTag('coverage');

134

xmlWriter.closeTag('report'); // Error: mismatched tag

135

} catch (error) {

136

console.error('XML tag mismatch:', error.message);

137

}

138

```

139

140

### Inline Tags

141

142

Creates self-contained tags that open and close in a single operation, optionally with content.

143

144

```javascript { .api }

145

/**

146

* Creates a complete tag with optional content

147

* @param {string} name - Tag name

148

* @param {Object} [attrs] - Tag attributes

149

* @param {string} [content] - Optional tag content

150

*/

151

inlineTag(name, attrs, content);

152

```

153

154

**Usage Examples:**

155

156

```javascript

157

// Self-closing tag

158

xmlWriter.inlineTag('metric', { type: 'statements', value: '85' });

159

// Produces: <metric type="statements" value="85"/>

160

161

// Tag with content

162

xmlWriter.inlineTag('name', null, 'istanbul-lib-report');

163

// Produces: <name>istanbul-lib-report</name>

164

165

// Tag with attributes and content

166

xmlWriter.inlineTag('description', { lang: 'en' }, 'Coverage reporting library');

167

// Produces: <description lang="en">Coverage reporting library</description>

168

169

// Multiple inline tags for metrics

170

const metrics = {

171

statements: { covered: 850, total: 1000 },

172

branches: { covered: 780, total: 900 },

173

functions: { covered: 95, total: 100 }

174

};

175

176

Object.entries(metrics).forEach(([type, data]) => {

177

xmlWriter.inlineTag('metric', {

178

type,

179

covered: data.covered.toString(),

180

total: data.total.toString(),

181

rate: (data.covered / data.total).toFixed(2)

182

});

183

});

184

```

185

186

### Closing All Tags

187

188

Utility method to close all open tags in the correct order, useful for cleanup and error recovery.

189

190

```javascript { .api }

191

/**

192

* Closes all open tags in reverse order

193

*/

194

closeAll();

195

```

196

197

**Usage Example:**

198

199

```javascript

200

// Safe cleanup pattern

201

try {

202

xmlWriter.openTag('coverage');

203

xmlWriter.openTag('project');

204

xmlWriter.openTag('packages');

205

// ... generate report content

206

207

// Manual closing (normal flow)

208

xmlWriter.closeTag('packages');

209

xmlWriter.closeTag('project');

210

xmlWriter.closeTag('coverage');

211

} catch (error) {

212

// Error recovery - close all remaining tags

213

xmlWriter.closeAll();

214

throw error;

215

} finally {

216

contentWriter.close();

217

}

218

```

219

220

### Complete XML Report Example

221

222

Here's a comprehensive example showing how to generate a complete XML coverage report:

223

224

```javascript

225

const libReport = require('istanbul-lib-report');

226

227

function generateXMLReport(coverageMap) {

228

const context = libReport.createContext({

229

dir: 'coverage',

230

coverageMap,

231

defaultSummarizer: 'nested'

232

});

233

234

const contentWriter = context.getWriter().writeFile('coverage.xml');

235

const xmlWriter = context.getXMLWriter(contentWriter);

236

237

// XML declaration and root element

238

contentWriter.println('<?xml version="1.0" encoding="UTF-8"?>');

239

xmlWriter.openTag('coverage', {

240

'line-rate': '0.85',

241

'branch-rate': '0.78',

242

timestamp: Math.floor(Date.now() / 1000).toString(),

243

version: '1.0'

244

});

245

246

// Get coverage tree and traverse

247

const tree = context.getTree();

248

tree.visit({

249

onStart() {

250

xmlWriter.openTag('packages');

251

},

252

253

onSummary(node) {

254

if (!node.isRoot()) {

255

const summary = node.getCoverageSummary();

256

xmlWriter.openTag('package', {

257

name: node.getQualifiedName(),

258

'line-rate': (summary.lines.pct / 100).toFixed(2),

259

'branch-rate': (summary.branches.pct / 100).toFixed(2)

260

});

261

xmlWriter.openTag('classes');

262

}

263

},

264

265

onDetail(node) {

266

const summary = node.getCoverageSummary();

267

const fileCoverage = node.getFileCoverage();

268

269

xmlWriter.openTag('class', {

270

name: node.getRelativeName(),

271

filename: node.getQualifiedName(),

272

'line-rate': (summary.lines.pct / 100).toFixed(2),

273

'branch-rate': (summary.branches.pct / 100).toFixed(2),

274

complexity: '0'

275

});

276

277

// Add line information

278

xmlWriter.openTag('lines');

279

Object.entries(fileCoverage.getLineCoverage()).forEach(([lineNum, count]) => {

280

xmlWriter.inlineTag('line', {

281

number: lineNum,

282

hits: count.toString(),

283

branch: 'false'

284

});

285

});

286

xmlWriter.closeTag('lines');

287

288

xmlWriter.closeTag('class');

289

},

290

291

onSummaryEnd(node) {

292

if (!node.isRoot()) {

293

xmlWriter.closeTag('classes');

294

xmlWriter.closeTag('package');

295

}

296

},

297

298

onEnd() {

299

xmlWriter.closeTag('packages');

300

xmlWriter.closeTag('coverage');

301

contentWriter.close();

302

}

303

});

304

}

305

```

306

307

## Types

308

309

```javascript { .api }

310

interface XMLWriter {

311

constructor(contentWriter: ContentWriter): XMLWriter;

312

openTag(name: string, attrs?: Record<string, string>): void;

313

closeTag(name: string): void;

314

inlineTag(name: string, attrs?: Record<string, string>, content?: string): void;

315

closeAll(): void;

316

}

317

318

interface XMLAttributes {

319

[key: string]: string | number;

320

}

321

```