or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cli-interface.mdcore-validation.mdhtml-parsing.mdindex.mdissue-reporting.mdrules-configuration.md
tile.json

issue-reporting.mddocs/

Issue Reporting

Structured issue collection and reporting system with multiple severity levels, precise position tracking, and comprehensive evidence capture for HTML validation results.

Capabilities

Reporter Class

Core reporting functionality for collecting and managing validation issues during HTML analysis.

/**
 * Reporter class for collecting validation issues during HTML analysis
 */
class Reporter {
  /** Original HTML content being analyzed */
  html: string;
  
  /** HTML content split into individual lines */
  lines: string[];
  
  /** Length of line break characters (\r\n or \n) */
  brLen: number;
  
  /** Ruleset configuration used for validation */  
  ruleset: Ruleset;
  
  /** Array of collected validation messages/hints */
  messages: Hint[];
  
  /**
   * Create a new Reporter instance
   * @param html - HTML content to analyze
   * @param ruleset - Rule configuration for validation
   */
  constructor(html: string, ruleset: Ruleset);
  
  /**
   * Report an informational message
   * @param message - Descriptive message about the issue
   * @param line - Line number where issue occurs (1-based)
   * @param col - Column number where issue occurs (1-based)
   * @param rule - Rule that detected the issue
   * @param raw - Raw HTML content that triggered the issue
   */
  info(message: string, line: number, col: number, rule: Rule, raw: string): void;
  
  /**
   * Report a warning message
   * @param message - Descriptive message about the issue
   * @param line - Line number where issue occurs (1-based)
   * @param col - Column number where issue occurs (1-based)
   * @param rule - Rule that detected the issue
   * @param raw - Raw HTML content that triggered the issue
   */
  warn(message: string, line: number, col: number, rule: Rule, raw: string): void;
  
  /**
   * Report an error message
   * @param message - Descriptive message about the issue
   * @param line - Line number where issue occurs (1-based)
   * @param col - Column number where issue occurs (1-based)
   * @param rule - Rule that detected the issue
   * @param raw - Raw HTML content that triggered the issue
   */
  error(message: string, line: number, col: number, rule: Rule, raw: string): void;
}

Hint Structure

Validation results are returned as Hint objects containing detailed information about each issue found.

/**
 * Represents a validation issue found during HTML analysis
 */
interface Hint {
  /** Severity level of the issue */
  type: ReportType;
  
  /** Human-readable description of the issue */
  message: string;
  
  /** Raw HTML content that triggered the issue */
  raw: string;
  
  /** Evidence string showing the problematic code in context */
  evidence: string;
  
  /** Line number where the issue occurs (1-based) */
  line: number;
  
  /** Column number where the issue occurs (1-based) */
  col: number;
  
  /** Rule that detected this issue (processed with link URL) */
  rule: {
    id: string;
    description: string;
    link: string;
  };
}

Report Types

HTMLHint supports three severity levels for validation issues.

/**
 * Severity levels for validation issues
 */
enum ReportType {
  /** Informational messages - lowest severity */
  info = "info",
  
  /** Warning messages - medium severity */
  warning = "warning",
  
  /** Error messages - highest severity */
  error = "error"
}

Usage Examples

Basic Issue Reporting

import { Reporter, HTMLParser } from "htmlhint";

// Create a custom rule that uses the reporter
const customRule = {
  id: 'custom-validation',
  description: 'Custom validation rule example',
  
  init(parser: HTMLParser, reporter: Reporter, options: unknown) {
    parser.addListener('tagstart', (event) => {
      // Report different severity levels
      if (event.tagName === 'div' && !event.attrs.length) {
        reporter.info(
          'Div element without attributes',
          event.line,
          event.col,
          this,
          event.raw
        );
      }
      
      if (event.tagName === 'img') {
        const hasAlt = event.attrs.some(attr => attr.name === 'alt');
        if (!hasAlt) {
          reporter.warn(
            'Image element missing alt attribute',
            event.line,
            event.col,
            this,
            event.raw
          );
        }
      }
      
      if (event.tagName === 'script' && event.attrs.some(attr => attr.name === 'src' && !attr.value)) {
        reporter.error(
          'Script element has empty src attribute',
          event.line,
          event.col,
          this,
          event.raw
        );
      }
    });
  }
};

Processing Validation Results

import { HTMLHint } from "htmlhint";

const html = `
<div>
  <img src="image.jpg">
  <script src=""></script>
</div>`;

const hints = HTMLHint.verify(html);

// Process hints by severity
const errors = hints.filter(hint => hint.type === 'error');
const warnings = hints.filter(hint => hint.type === 'warning');
const info = hints.filter(hint => hint.type === 'info');

console.log(`Found ${errors.length} errors, ${warnings.length} warnings, ${info.length} info messages`);

// Display detailed information for each hint
hints.forEach(hint => {
  console.log(`${hint.type.toUpperCase()} at ${hint.line}:${hint.col}`);
  console.log(`Rule: ${hint.rule.id}`);
  console.log(`Message: ${hint.message}`);
  console.log(`Evidence: ${hint.evidence}`);
  console.log(`Raw: ${hint.raw}`);
  console.log('---');
});

Filtering and Sorting Results

import { HTMLHint } from "htmlhint";

const html = '<div><img src="test.jpg"><p>Hello</div>';
const hints = HTMLHint.verify(html);

// Sort by line number, then column
const sortedHints = hints.sort((a, b) => {
  if (a.line !== b.line) {
    return a.line - b.line;
  }
  return a.col - b.col;
});

// Group by rule ID
const hintsByRule = hints.reduce((groups, hint) => {
  const ruleId = hint.rule.id;
  if (!groups[ruleId]) {
    groups[ruleId] = [];
  }
  groups[ruleId].push(hint);
  return groups;
}, {} as { [ruleId: string]: Hint[] });

console.log('Issues by rule:');
Object.entries(hintsByRule).forEach(([ruleId, ruleHints]) => {
  console.log(`${ruleId}: ${ruleHints.length} issues`);
  ruleHints.forEach(hint => {
    console.log(`  Line ${hint.line}: ${hint.message}`);
  });
});

Custom Severity Filtering

import { HTMLHint, ReportType } from "htmlhint";

const html = '<div><img><script src=""></script></div>';
const hints = HTMLHint.verify(html);

// Filter by severity level
function filterBySeverity(hints: Hint[], minSeverity: ReportType): Hint[] {
  const severityOrder = { 'info': 1, 'warning': 2, 'error': 3 };
  const minLevel = severityOrder[minSeverity];
  
  return hints.filter(hint => severityOrder[hint.type] >= minLevel);
}

// Get only warnings and errors
const criticalIssues = filterBySeverity(hints, ReportType.warning);
console.log(`Found ${criticalIssues.length} critical issues`);

// Get only errors
const errorIssues = filterBySeverity(hints, ReportType.error);
console.log(`Found ${errorIssues.length} error-level issues`);

Evidence Context Analysis

import { HTMLHint } from "htmlhint";

const html = `
<html>
  <head><title>Test Document</title></head>
  <body>
    <div class="container">
      <img src="image.jpg">
      <p>Some text content goes here</p>
    </div>
  </body>
</html>`;

const hints = HTMLHint.verify(html);

hints.forEach(hint => {
  console.log(`Issue: ${hint.message}`);
  console.log(`Location: Line ${hint.line}, Column ${hint.col}`);
  
  // Evidence shows the problematic code in context
  console.log(`Evidence: ${hint.evidence}`);
  
  // Raw shows the exact HTML that triggered the rule
  console.log(`Raw HTML: ${hint.raw}`);
  
  // Additional context from rule
  console.log(`Rule: ${hint.rule.id} - ${hint.rule.description}`);
  console.log('---');
});

Building Custom Reporters

interface CustomReportFormat {
  timestamp: string;
  file: string;
  issues: Array<{
    severity: string;
    rule: string;
    message: string;
    location: { line: number; column: number };
    context: string;
  }>;
}

function createCustomReport(hints: Hint[], filename: string): CustomReportFormat {
  return {
    timestamp: new Date().toISOString(),
    file: filename,
    issues: hints.map(hint => ({
      severity: hint.type,
      rule: hint.rule.id,
      message: hint.message,
      location: { line: hint.line, column: hint.col },
      context: hint.evidence
    }))
  };
}

// Usage
const html = '<div><img></div>';
const hints = HTMLHint.verify(html);
const report = createCustomReport(hints, 'example.html');

console.log(JSON.stringify(report, null, 2));

Statistics and Metrics

import { HTMLHint, Hint } from "htmlhint";

function generateStatistics(hints: Hint[]) {
  const stats = {
    total: hints.length,
    byType: { error: 0, warning: 0, info: 0 },
    byRule: {} as { [ruleId: string]: number },
    byLine: {} as { [line: number]: number }
  };
  
  hints.forEach(hint => {
    // Count by type
    stats.byType[hint.type]++;
    
    // Count by rule
    const ruleId = hint.rule.id;
    stats.byRule[ruleId] = (stats.byRule[ruleId] || 0) + 1;
    
    // Count by line
    stats.byLine[hint.line] = (stats.byLine[hint.line] || 0) + 1;
  });
  
  return stats;
}

// Usage
const html = '<div><img><p>Text</div>';
const hints = HTMLHint.verify(html);
const stats = generateStatistics(hints);

console.log('Validation Statistics:');
console.log(`Total issues: ${stats.total}`);
console.log(`Errors: ${stats.byType.error}, Warnings: ${stats.byType.warning}, Info: ${stats.byType.info}`);
console.log('Most common rules:', Object.entries(stats.byRule)
  .sort(([,a], [,b]) => b - a)
  .slice(0, 5));

Integration with Rules

The Reporter is automatically passed to all rules during initialization and provides the standardized way for rules to report issues:

const customRule = {
  id: 'example-rule',
  description: 'Example of rule integration with reporter',
  
  init(parser: HTMLParser, reporter: Reporter, options: unknown) {
    parser.addListener('tagstart', (event) => {
      // Rules use reporter methods to report issues
      if (/* some condition */) {
        reporter.error('Error message', event.line, event.col, this, event.raw);
      }
      
      if (/* warning condition */) {
        reporter.warn('Warning message', event.line, event.col, this, event.raw);
      }
      
      if (/* info condition */) {
        reporter.info('Info message', event.line, event.col, this, event.raw);
      }
    });
  }
};

Best Practices

Position Accuracy

Always use the exact line and column from parsing events for accurate issue location.

Message Quality

Write clear, actionable messages that help developers understand and fix issues.

Evidence Context

The evidence field provides code context - ensure raw content accurately represents the problematic HTML.

Severity Guidelines

  • Error: Issues that prevent proper HTML parsing or violate critical standards
  • Warning: Issues that may cause problems but don't break functionality
  • Info: Suggestions for improvement or style guidelines