@bernierllc/validators-markdown-structure
v0.3.0
Published
Markdown structure validation primitive - heading hierarchy, links, lists, code blocks
Readme
@bernierllc/validators-markdown-structure
Primitive validator for Markdown document structure validation - heading hierarchy, link references, list formatting, and code block syntax.
Installation
npm install @bernierllc/validators-markdown-structureUsage
Basic Validation
import { validateMarkdownStructure } from '@bernierllc/validators-markdown-structure';
import { createSharedUtils } from '@bernierllc/validators-utils';
const markdown = `
# Project Title
## Introduction
### Getting Started
- Feature 1
- Feature 2
\`\`\`javascript
const example = true;
\`\`\`
`;
const utils = createSharedUtils();
const result = await validateMarkdownStructure(markdown, {}, utils);
if (result.problems.length > 0) {
console.log('Structure issues found:');
result.problems.forEach(problem => {
console.log(`${problem.severity}: ${problem.message} (line ${problem.location?.line})`);
});
}Custom Configuration
import { createMarkdownStructureValidator } from '@bernierllc/validators-markdown-structure';
const validator = createMarkdownStructureValidator({
checkHeadingHierarchy: true,
checkLinkReferences: true,
checkListFormatting: true,
checkCodeBlocks: true,
allowAnyFirstHeading: false, // Require h1 first
maxHeadingLevel: 4, // Only allow h1-h4
flavor: 'gfm', // GitHub Flavored Markdown
});
const result = await validator.validate(markdown, utils);Features
Heading Hierarchy Validation
Ensures proper heading level progression:
<!-- ✓ Valid -->
# Title
## Section
### Subsection
<!-- ✗ Invalid - skipped level -->
# Title
### Subsection (should be h2)
<!-- ✗ Invalid - first heading not h1 -->
## Starting with h2Rules:
- First heading should be h1 (configurable)
- No skipping heading levels
- Respects maximum heading level setting
Link Reference Validation
Validates link and image references:
<!-- ✓ Valid -->
[Link text](https://example.com)

[Reference link][ref]
[ref]: https://example.com
<!-- ✗ Invalid - undefined reference -->
[Broken link][missing-ref]
<!-- ✗ Invalid - empty URL -->
[Link]()
<!-- ⚠ Warning - missing alt text -->
Rules:
- Reference links must have matching definitions
- URLs cannot be empty
- Images should have alt text (accessibility)
- Anchor links validated for structure
List Formatting Validation
Checks list structure and consistency:
<!-- ✓ Valid -->
- Item 1
- Item 2
- Nested item
- Another nested
- Item 3
<!-- ✗ Invalid - inconsistent markers -->
- Item 1
* Item 2 (mixed - and *)
- Item 3
<!-- ✗ Invalid - inconsistent indentation -->
- Item 1
- Nested 2 spaces
- Nested 3 spaces (inconsistent)Rules:
- Consistent markers within a list (-, *, or +)
- Consistent indentation for nested items
- Proper spacing after markers
Code Block Validation
Validates fenced code blocks:
<!-- ✓ Valid -->
```javascript
const x = 1;
```
<!-- ✗ Invalid - unclosed block -->
```javascript
const x = 1;
<!-- ⚠ Warning - missing language -->
```
plain code
```
<!-- ⚠ Warning - invalid language specifier -->
```java script with spaces
code
```Rules:
- Code blocks must be properly closed
- Language specifiers recommended
- Valid language identifier format
API Reference
validateMarkdownStructure(markdown, options, utils)
Validates Markdown document structure.
Parameters:
markdown(string): Markdown content to validateoptions(MarkdownStructureOptions): Validation optionsutils(SharedUtils): Shared utilities from validators-utils
Returns: Promise<ValidationResult>
createMarkdownStructureValidator(options)
Creates a configured validator instance.
Parameters:
options(MarkdownStructureOptions): Validation options
Returns: Validator object with validate() and getMeta() methods
MarkdownStructureOptions
interface MarkdownStructureOptions {
checkHeadingHierarchy?: boolean; // Default: true
checkLinkReferences?: boolean; // Default: true
checkListFormatting?: boolean; // Default: true
checkCodeBlocks?: boolean; // Default: true
allowAnyFirstHeading?: boolean; // Default: false
maxHeadingLevel?: number; // Default: 6
flavor?: 'commonmark' | 'gfm' | 'github'; // Default: 'commonmark'
}Parser Utilities (Advanced)
Low-level parsing functions for advanced use cases:
import {
parseHeadings,
parseLinks,
parseListItems,
parseCodeBlocks
} from '@bernierllc/validators-markdown-structure';
const headings = parseHeadings(markdown);
const links = parseLinks(markdown);
const listItems = parseListItems(markdown);
const codeBlocks = parseCodeBlocks(markdown);Individual Rules (Advanced)
Access individual validation rules:
import {
headingHierarchyRule,
linkReferencesRule,
listFormattingRule,
codeBlocksRule
} from '@bernierllc/validators-markdown-structure';Validation Rules
markdown-structure/heading-hierarchy
Validates proper heading level progression without skipping levels.
Severity: error (first heading), warn (skipped levels)
markdown-structure/link-references
Validates that link and image references are properly defined.
Severity: error (undefined references, empty URLs), warn (missing alt text)
markdown-structure/list-formatting
Validates consistent list formatting and indentation.
Severity: warn (inconsistent markers/indentation), info (odd indentation)
markdown-structure/code-blocks
Validates fenced code block syntax and closure.
Severity: error (unclosed blocks), warn (invalid language), info (missing language)
Integration Status
Logger Integration
- Status: not-applicable
- Justification: Pure validation primitive with no logging requirements. As a stateless validator, all diagnostic information is returned in the
ValidationResultstructure. Users can integrate with@bernierllc/loggerin their consuming code if needed.
Docs-Suite
- Status: ready
- Format: Markdown format with TypeDoc API documentation
- Compatibility: All documentation is exportable and compatible with docs-suite standards
NeverHub Integration
- Status: not-applicable
- Justification: Stateless validation primitive with no service discovery or event bus requirements. This package performs synchronous validation with no external dependencies or state. Consuming services may integrate this validator with
@bernierllc/neverhub-adapterif needed for distributed validation workflows.
Examples
Strict Documentation Validation
const validator = createMarkdownStructureValidator({
checkHeadingHierarchy: true,
checkLinkReferences: true,
checkListFormatting: true,
checkCodeBlocks: true,
allowAnyFirstHeading: false, // Must start with h1
maxHeadingLevel: 4, // Limit depth
flavor: 'gfm',
});
const result = await validator.validate(docContent, utils);
const errors = result.problems.filter(p => p.severity === 'error');
if (errors.length > 0) {
throw new Error(`Documentation validation failed: ${errors.length} errors`);
}Lenient Blog Post Validation
const result = await validateMarkdownStructure(blogPost, {
checkHeadingHierarchy: true,
checkLinkReferences: true,
checkListFormatting: false, // Allow flexible lists
checkCodeBlocks: true,
allowAnyFirstHeading: true, // Any starting level OK
maxHeadingLevel: 6,
}, utils);CI/CD Integration
import { validateMarkdownStructure } from '@bernierllc/validators-markdown-structure';
import { readFileSync } from 'fs';
import { glob } from 'glob';
const files = await glob('docs/**/*.md');
let totalErrors = 0;
for (const file of files) {
const content = readFileSync(file, 'utf-8');
const result = await validateMarkdownStructure(content, {}, utils);
const errors = result.problems.filter(p => p.severity === 'error');
if (errors.length > 0) {
console.error(`${file}: ${errors.length} errors`);
errors.forEach(err => {
console.error(` Line ${err.location?.line}: ${err.message}`);
});
totalErrors += errors.length;
}
}
if (totalErrors > 0) {
process.exit(1);
}Architecture
This is a primitive validator in the BernierLLC validators architecture:
- Pure validation logic - No policy enforcement
- Composable rules - Each rule has single responsibility
- Evidence-rich results - Provides actionable error information
- Tool-agnostic - Works in CLI, web, CI/CD, IDE plugins
Principles
- Validators return problems, never throw - Results are structured data
- Single responsibility per rule - Each rule checks one aspect
- Configurable severity - Implementors decide what constitutes failure
- Performance budgets - Fast validation for real-time feedback
Performance
- Parsing: Single-pass with minimal overhead
- Validation: Parallel rule execution where possible
- Memory: Streaming-friendly for large documents
- Target: <100ms for typical documentation files
Related Packages
- @bernierllc/validators-core - Core validation types and utilities
- @bernierllc/validators-runner - Validation orchestration
- @bernierllc/validators-reporters - Result formatting
- @bernierllc/markdown-detector - Markdown flavor detection
Contributing
See the BernierLLC validators principles for architecture guidelines.
License
Copyright (c) 2025 Bernier LLC. All rights reserved.
This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
