npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-syntax

Usage

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 validate
  • options (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

License

Copyright (c) 2025 Bernier LLC. All rights reserved.