CASOON Open Source
HTML conformance checks inside your Rust build.
html-conform parses a document the way browsers do, then checks it against the W3C HTML schema, attribute microsyntaxes, ARIA and structural rules. It is tested against the Nu Html Checker's own test corpus and runs without a JVM, subprocess or network access.
cargo add html-conformerror 10:9 schema.html5 invalid value `rows` for attribute `scope` at 10:9 error 10:9 assertion.tables.th-scope-enum A th element's scope attribute must be one of row, col, rowgroup, or colgroup. error 11:23 tables.integrity Table cell is overlapped by later table cell. error 12:9 tables.integrity Table cell overlaps an earlier table cell. 4 errors, 0 warnings
- vnu test fixtures in the differential test
- 4,655
- false positives on that corpus
- 0
- false negative, documented: CSS inside <style>
- 1
- finding sources in one report
- 6
What it does
Six layers, one report
Parser diagnostics, the RELAX NG schema with SVG and MathML, attribute microsyntaxes, Schematron co-constraints, script and CSP checks, and the table cell grid all return the same Finding: rule ID, severity, message and position.
Measured against vnu
The differential test runs every vendored vnu fixture through check() on each push and fails if false positives or false negatives rise above the recorded baseline.
A library, not a service
No JVM, no subprocess, no HTTP. The schema and the rules are embedded in the crate, so check() works the same in a test, a CLI or a server.
Serializable findings
CheckReport, Finding, Severity and SourceLocation implement serde Serialize and Deserialize. The showcase on this site is built from that JSON.
Generated at build time
All examples →error 7:3 scripts.import-map A "script" element with a "type" attribute whose value is "importmap" must contain a JSON object with no properties other than "imports", "scopes", and "integrity". error 11:3 scripts.speculation-rules The "urls" property in a speculation rule must be a JSON array. warning 7:3 csp.meta-enforcement Inline script violates Content Security Policy (meta tag): blocked by "script-src" directive (missing "'unsafe-inline'" or nonce/hash). warning 10:3 csp.meta-enforcement Inline script violates Content Security Policy (meta tag): blocked by "script-src" directive (missing "'unsafe-inline'" or nonce/hash). warning 11:3 csp.meta-enforcement Inline script violates Content Security Policy (meta tag): blocked by "script-src" directive (missing "'unsafe-inline'" or nonce/hash). 2 errors, 3 warnings
Findings for examples/showcase/csp-and-scripts.html, written by cargo run --example findings and formatted for this page. The showcase has six more samples.
examples/showcase/csp-and-scripts.jsonQuickstart
Three steps from a dependency to a failing build on invalid markup. The documentation covers options, rule IDs and the comparison with vnu.
- Add the crate with
cargo add html-conform. - Pass the document to
html_conform::check(). - Read
report.findings, or askreport.has_errors().
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let html = std::fs::read_to_string("index.html")?;
let report = html_conform::check(&html)?;
for finding in &report.findings {
let at = finding.location.map(|l| l.to_string()).unwrap_or_default();
println!("{:?} {at} {} {}", finding.severity, finding.rule_id, finding.message);
}
if report.has_errors() {
std::process::exit(1);
}
Ok(())
}