CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-istanbul-lib-report

Base reporting library for istanbul providing core utilities for generating coverage reports across different formats

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

xml-generation.mddocs/

XML Generation

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.

Capabilities

XMLWriter Class

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

/**
 * A utility class to produce well-formed, indented XML
 */
class XMLWriter {
  /**
   * Create an XML writer wrapping a content writer
   * @param {ContentWriter} contentWriter - The content writer that this utility wraps
   */
  constructor(contentWriter);

  /**
   * Writes the opening XML tag with the supplied attributes
   * @param {string} name - Tag name
   * @param {Object} [attrs] - Attributes for the tag as key-value pairs
   */
  openTag(name, attrs);

  /**
   * Closes an open XML tag
   * @param {string} name - Tag name to close (must match currently open tag)
   * @throws {Error} If tag name doesn't match the currently open tag
   * @throws {Error} If attempting to close tag when none are open
   */
  closeTag(name);

  /**
   * Writes a tag and its value, opening and closing it at the same time
   * @param {string} name - Tag name
   * @param {Object} [attrs] - Tag attributes as key-value pairs
   * @param {string} [content] - Optional tag content
   */
  inlineTag(name, attrs, content);

  /**
   * Closes all open tags and ends the document
   */
  closeAll();
}

Usage Examples:

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

// Create context and get XML writer
const context = libReport.createContext({ dir: 'reports', coverageMap });
const contentWriter = context.getWriter().writeFile('coverage.xml');
const xmlWriter = context.getXMLWriter(contentWriter);

// Basic XML structure
xmlWriter.openTag('coverage', { version: '1.0', timestamp: Date.now() });
  xmlWriter.openTag('project', { name: 'my-project' });
    xmlWriter.inlineTag('metric', { type: 'statements', covered: '85', total: '100' });
    xmlWriter.inlineTag('metric', { type: 'branches', covered: '78', total: '90' });
  xmlWriter.closeTag('project');
xmlWriter.closeTag('coverage');

contentWriter.close();

Opening Tags

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

/**
 * Opens an XML tag with optional attributes
 * @param {string} name - The tag name
 * @param {Object} [attrs] - Attributes as key-value pairs
 */
openTag(name, attrs);

Usage Example:

// Simple tag
xmlWriter.openTag('report');

// Tag with attributes
xmlWriter.openTag('file', {
  path: '/src/utils.js',
  covered: '12',
  total: '15'
});

// Tag with multiple attributes
xmlWriter.openTag('coverage', {
  'line-rate': '0.85',
  'branch-rate': '0.78',
  'lines-covered': '850',
  'lines-valid': '1000',
  complexity: '0',
  version: '1.9',
  timestamp: '1640995200'
});

Closing Tags

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

/**
 * Closes an open XML tag with validation
 * @param {string} name - Tag name to close
 * @throws {Error} If tag doesn't match currently open tag
 */
closeTag(name);

Usage Example:

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

// Error handling
try {
  xmlWriter.openTag('coverage');
  xmlWriter.closeTag('report'); // Error: mismatched tag
} catch (error) {
  console.error('XML tag mismatch:', error.message);
}

Inline Tags

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

/**
 * Creates a complete tag with optional content
 * @param {string} name - Tag name
 * @param {Object} [attrs] - Tag attributes
 * @param {string} [content] - Optional tag content
 */
inlineTag(name, attrs, content);

Usage Examples:

// Self-closing tag
xmlWriter.inlineTag('metric', { type: 'statements', value: '85' });
// Produces: <metric type="statements" value="85"/>

// Tag with content
xmlWriter.inlineTag('name', null, 'istanbul-lib-report');
// Produces: <name>istanbul-lib-report</name>

// Tag with attributes and content
xmlWriter.inlineTag('description', { lang: 'en' }, 'Coverage reporting library');
// Produces: <description lang="en">Coverage reporting library</description>

// Multiple inline tags for metrics
const metrics = {
  statements: { covered: 850, total: 1000 },
  branches: { covered: 780, total: 900 },
  functions: { covered: 95, total: 100 }
};

Object.entries(metrics).forEach(([type, data]) => {
  xmlWriter.inlineTag('metric', {
    type,
    covered: data.covered.toString(),
    total: data.total.toString(),
    rate: (data.covered / data.total).toFixed(2)
  });
});

Closing All Tags

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

/**
 * Closes all open tags in reverse order
 */
closeAll();

Usage Example:

// Safe cleanup pattern
try {
  xmlWriter.openTag('coverage');
  xmlWriter.openTag('project');  
  xmlWriter.openTag('packages');
  // ... generate report content
  
  // Manual closing (normal flow)
  xmlWriter.closeTag('packages');
  xmlWriter.closeTag('project');
  xmlWriter.closeTag('coverage');
} catch (error) {
  // Error recovery - close all remaining tags
  xmlWriter.closeAll();
  throw error;
} finally {
  contentWriter.close();
}

Complete XML Report Example

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

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

function generateXMLReport(coverageMap) {
  const context = libReport.createContext({
    dir: 'coverage',
    coverageMap,
    defaultSummarizer: 'nested'
  });
  
  const contentWriter = context.getWriter().writeFile('coverage.xml');
  const xmlWriter = context.getXMLWriter(contentWriter);
  
  // XML declaration and root element
  contentWriter.println('<?xml version="1.0" encoding="UTF-8"?>');
  xmlWriter.openTag('coverage', {
    'line-rate': '0.85',
    'branch-rate': '0.78',
    timestamp: Math.floor(Date.now() / 1000).toString(),
    version: '1.0'
  });
  
  // Get coverage tree and traverse
  const tree = context.getTree();
  tree.visit({
    onStart() {
      xmlWriter.openTag('packages');
    },
    
    onSummary(node) {
      if (!node.isRoot()) {
        const summary = node.getCoverageSummary();
        xmlWriter.openTag('package', {
          name: node.getQualifiedName(),
          'line-rate': (summary.lines.pct / 100).toFixed(2),
          'branch-rate': (summary.branches.pct / 100).toFixed(2)
        });
        xmlWriter.openTag('classes');
      }
    },
    
    onDetail(node) {
      const summary = node.getCoverageSummary();
      const fileCoverage = node.getFileCoverage();
      
      xmlWriter.openTag('class', {
        name: node.getRelativeName(),
        filename: node.getQualifiedName(),
        'line-rate': (summary.lines.pct / 100).toFixed(2),
        'branch-rate': (summary.branches.pct / 100).toFixed(2),
        complexity: '0'
      });
      
      // Add line information
      xmlWriter.openTag('lines');
      Object.entries(fileCoverage.getLineCoverage()).forEach(([lineNum, count]) => {
        xmlWriter.inlineTag('line', {
          number: lineNum,
          hits: count.toString(),
          branch: 'false'
        });
      });
      xmlWriter.closeTag('lines');
      
      xmlWriter.closeTag('class');
    },
    
    onSummaryEnd(node) {
      if (!node.isRoot()) {
        xmlWriter.closeTag('classes');
        xmlWriter.closeTag('package');
      }
    },
    
    onEnd() {
      xmlWriter.closeTag('packages');
      xmlWriter.closeTag('coverage');
      contentWriter.close();
    }
  });
}

Types

interface XMLWriter {
  constructor(contentWriter: ContentWriter): XMLWriter;
  openTag(name: string, attrs?: Record<string, string>): void;
  closeTag(name: string): void;
  inlineTag(name: string, attrs?: Record<string, string>, content?: string): void;
  closeAll(): void;
}

interface XMLAttributes {
  [key: string]: string | number;
}

docs

context.md

file-writing.md

index.md

tree-traversal.md

xml-generation.md

tile.json