Structured issue collection and reporting system with multiple severity levels, precise position tracking, and comprehensive evidence capture for HTML validation results.
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;
}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;
};
}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"
}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
);
}
});
}
};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('---');
});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}`);
});
});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`);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('---');
});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));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));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);
}
});
}
};Always use the exact line and column from parsing events for accurate issue location.
Write clear, actionable messages that help developers understand and fix issues.
The evidence field provides code context - ensure raw content accurately represents the problematic HTML.