@bernierllc/validators-html-syntax
v0.3.0
Published
HTML syntax validation primitive - malformed tags, nesting, duplicate IDs, unclosed tags
Readme
@bernierllc/validators-html-syntax
HTML syntax validation primitive - validates HTML for well-formedness, proper nesting, duplicate IDs, unclosed tags, and malformed attributes.
Installation
npm install @bernierllc/validators-html-syntaxUsage
Basic Validation
import { validateHtmlSyntax } from '@bernierllc/validators-html-syntax';
import { parseHtml } from '@bernierllc/validators-utils';
const html = `
<!DOCTYPE html>
<html>
<body>
<div id="header">Header</div>
<div id="header">Duplicate ID!</div>
</body>
</html>
`;
const utils = { parseHtml, /* ... other utils */ };
const result = await validateHtmlSyntax(html, {}, utils);
if (result.problems.length > 0) {
console.log('HTML syntax issues found:');
result.problems.forEach(problem => {
console.log(`- ${problem.message}`);
console.log(` Severity: ${problem.severity}`);
console.log(` Rule: ${problem.ruleId}`);
});
}Creating a Configured Validator
import { createHtmlSyntaxValidator } from '@bernierllc/validators-html-syntax';
const validator = createHtmlSyntaxValidator({
checkDuplicateIds: true,
checkUnclosedTags: true,
checkInvalidNesting: true,
checkMalformedAttributes: true,
});
// Reuse the validator
const result1 = await validator.validate(html1, utils);
const result2 = await validator.validate(html2, utils);
// Get validator metadata
const meta = validator.getMeta();
console.log(`Validator: ${meta.name} v${meta.version}`);
console.log(`Enabled rules: ${meta.enabledRules.join(', ')}`);Using Individual Rules
import { duplicateIdsRule, unclosedTagsRule } from '@bernierllc/validators-html-syntax';
import { createRuleContext } from '@bernierllc/validators-core';
const problems = [];
const context = createRuleContext(
'html-syntax/duplicate-ids',
{},
utils,
{},
(problem) => problems.push(problem)
);
const validator = duplicateIdsRule.create(context);
await validator(html);
console.log(`Found ${problems.length} duplicate ID issues`);API Reference
validateHtmlSyntax(html, options?, utils)
Validates HTML syntax and returns validation results.
Parameters:
html(string): The HTML content to validateoptions(HtmlSyntaxOptions): Validation options (optional)utils(SharedUtils): Utility functions for parsing and validation
Returns: Promise<ValidationResult>
Example:
const result = await validateHtmlSyntax(html, {
checkDuplicateIds: true,
checkUnclosedTags: true,
}, utils);createHtmlSyntaxValidator(options?)
Creates a reusable HTML syntax validator with configured options.
Parameters:
options(HtmlSyntaxOptions): Validation options (optional)
Returns: Object with validate() and getMeta() methods
Example:
const validator = createHtmlSyntaxValidator({
checkInvalidNesting: false, // Disable nesting validation
});HtmlSyntaxOptions
Configuration options for HTML syntax validation:
interface HtmlSyntaxOptions {
/** Check for duplicate IDs (default: true) */
checkDuplicateIds?: boolean;
/** Check for unclosed tags (default: true) */
checkUnclosedTags?: boolean;
/** Check for invalid nesting (default: true) */
checkInvalidNesting?: boolean;
/** Check for malformed attributes (default: true) */
checkMalformedAttributes?: boolean;
/** Check for invalid self-closing tags (default: true) */
checkSelfClosingTags?: boolean;
/** Check for invalid entity references (default: true) */
checkEntityReferences?: boolean;
/** Check for missing or invalid DOCTYPE (default: false) */
checkDoctype?: boolean;
}Validation Rules
html-syntax/duplicate-ids
Detects duplicate ID attributes in the HTML document. Each ID must be unique.
Example violations:
<!-- INVALID: Duplicate IDs -->
<div id="main">First</div>
<div id="main">Second</div>
<!-- VALID: Unique IDs -->
<div id="header">Header</div>
<div id="content">Content</div>html-syntax/unclosed-tags
Detects unclosed HTML tags and mismatched closing tags.
Example violations:
<!-- INVALID: Unclosed tag -->
<div>
<p>Unclosed paragraph
</div>
<!-- INVALID: Mismatched tags -->
<div>
<span>Content</div>
</span>
<!-- VALID: Properly closed -->
<div>
<p>Closed paragraph</p>
</div>html-syntax/invalid-nesting
Detects invalid HTML element nesting per HTML5 specification.
Example violations:
<!-- INVALID: Block element in paragraph -->
<p>
<div>Block element</div>
</p>
<!-- INVALID: Nested paragraphs -->
<p>Outer <p>Inner</p></p>
<!-- INVALID: Nested anchors -->
<a href="/outer">
<a href="/inner">Nested link</a>
</a>
<!-- VALID: Proper nesting -->
<div>
<p>Paragraph</p>
<a href="/link">Link</a>
</div>html-syntax/malformed-attributes
Detects malformed or invalid HTML attributes.
Example violations:
<!-- INVALID: Duplicate attributes -->
<div class="first" class="second">Content</div>
<!-- INVALID: Multiple equals signs -->
<div id=="broken">Content</div>
<!-- INVALID: Invalid attribute name -->
<div 123name="value">Content</div>
<!-- VALID: Proper attributes -->
<div class="test" id="main" data-value="123">Content</div>Validation Results
The validation result includes detailed problem information:
interface ValidationResult {
problems: Problem[];
stats: {
targets: number;
durationMs: number;
rulesApplied: string[];
};
}
interface Problem {
ruleId: string; // e.g., "html-syntax/duplicate-ids"
message: string; // Human-readable description
severity: 'error' | 'warn' | 'info' | 'off';
domain: 'parsing';
location?: {
file?: string;
line?: number;
column?: number;
selector?: string;
};
suggestion?: string; // How to fix the issue
fixable?: boolean; // Whether auto-fix is available
tags?: string[]; // e.g., ['html', 'duplicate-id']
evidence?: {
snippet?: string; // Code snippet showing the issue
context?: Record<string, unknown>;
};
}Examples
Comprehensive Validation
import { validateHtmlSyntax } from '@bernierllc/validators-html-syntax';
const html = `
<div id="container">
<p>
<div id="container">Invalid nesting and duplicate ID</div>
</p>
<span class="test" class="test">Duplicate attribute</span>
</div>
<section>
<p>Unclosed paragraph
</section>
`;
const result = await validateHtmlSyntax(html, {}, utils);
// Group problems by severity
const errors = result.problems.filter(p => p.severity === 'error');
const warnings = result.problems.filter(p => p.severity === 'warn');
console.log(`Found ${errors.length} errors and ${warnings.length} warnings`);
console.log(`Validated in ${result.stats.durationMs}ms`);Selective Validation
// Only check for duplicate IDs and unclosed tags
const validator = createHtmlSyntaxValidator({
checkDuplicateIds: true,
checkUnclosedTags: true,
checkInvalidNesting: false,
checkMalformedAttributes: false,
});
const result = await validator.validate(html, utils);Integration with CI/CD
import { validateHtmlSyntax } from '@bernierllc/validators-html-syntax';
import * as fs from 'fs';
async function validateHtmlFiles(files: string[]) {
let hasErrors = false;
for (const file of files) {
const html = fs.readFileSync(file, 'utf-8');
const result = await validateHtmlSyntax(html, {}, utils);
const errors = result.problems.filter(p => p.severity === 'error');
if (errors.length > 0) {
console.error(`❌ ${file}: ${errors.length} errors`);
errors.forEach(error => {
console.error(` ${error.message}`);
});
hasErrors = true;
} else {
console.log(`✅ ${file}: No errors`);
}
}
if (hasErrors) {
process.exit(1);
}
}
// Run validation
validateHtmlFiles(process.argv.slice(2));Performance
This validator is designed for high performance:
- Incremental parsing: Only parses HTML once
- Efficient rule execution: Rules run in parallel where possible
- Sub-100ms target: Targets sub-100ms validation for typical HTML files
- Memory efficient: Minimal memory overhead for large documents
Architecture
This package follows the BernierLLC validators architecture:
- Pure validation: Returns structured results without enforcing policy
- Composable rules: Individual rules can be used independently
- Framework-agnostic: Works in Node.js, browsers, CI/CD, etc.
- Evidence-rich: Provides actionable information for debugging
Integration Status
- Logger integration: not-applicable - Pure validation primitive with no logging needs. This is a stateless validator that returns structured results without side effects. Consumers can integrate @bernierllc/logger if needed for their use case.
- Docs-Suite: ready - Full TypeDoc documentation exported for docs-suite compatibility
- NeverHub integration: not-applicable - Stateless validation primitive with no service discovery or event bus needs. This package uses detectNeverHub() pattern but is designed as a pure function validator that operates independently.
See Also
- @bernierllc/validators-core - Core types and interfaces
- @bernierllc/validators-utils - Shared utilities for parsing
- @bernierllc/validators-runner - Validation orchestration
License
Copyright (c) 2025 Bernier LLC. All rights reserved.
