@vellora/lint
v0.5.0
Published
Check and auto-fix HTML templates for the vellora HTML-to-PDF renderer, in your editor or CI.
Maintainers
Readme
@vellora/lint
Catch template problems while you write them, not when a customer opens a broken PDF.
vellora renders HTML to PDF without a browser, and it
supports a documented subset of HTML and CSS. @vellora/lint checks your templates against that
subset and rewrites the constructs that have a safe automatic fix. It's meant for your editor, your
tests and CI. It never runs on your production render path unless you ask for it.
Install
npm install --save-dev @vellora/lintCheck a template
import { diagnose } from "@vellora/lint";
const report = diagnose(`<table><tr><td style="display:grid">a</td></tr></table>`);
report.conformant; // false
report.findings[0];
// {
// rule: "flex-grid-in-td",
// severity: "warning",
// autoFixable: true,
// location: { line: 1, col: 12 },
// suggestedFix: "...",
// snippet: "...",
// compatLink: "...",
// }diagnose(html) only reads. It never changes your HTML, touches the file system or goes to the
network, so it's safe to call anywhere.
Fix it automatically
import { fix } from "@vellora/lint";
const { html, report } = fix(template);
// html: the rewritten template. Here the grid cell becomes a nested table,
// which vellora lays out reliably.
// report.findings[i].applied tells you which fixes ran.The fixes are deterministic. Running fix() on its own output changes nothing, so it's safe to run
on every commit.
Use it in CI
Fail the build when a template drifts out of the supported subset:
import { readFileSync } from "node:fs";
import { diagnose } from "@vellora/lint";
for (const file of ["templates/invoice.html", "templates/receipt.html"]) {
const { conformant, findings } = diagnose(readFileSync(file, "utf8"));
for (const f of findings) {
console.log(`${file}:${f.location.line}:${f.location.col} ${f.severity} ${f.rule}`);
}
if (!conformant) process.exitCode = 1;
}Prefer the terminal? @vellora/cli runs the same
checks: vellora lint templates/invoice.html (exit code 1 when there are findings) and
vellora fix templates/invoice.html --write.
The report format
interface Report {
conformant: boolean;
findings: Finding[];
}
interface Finding {
rule: string; // stable id, e.g. "flex-grid-in-td"
severity: "error" | "warning";
autoFixable: boolean;
location: { line: number; col: number };
suggestedFix: string; // for humans
snippet: string; // the offending source
compatLink: string; // where the rule is documented
feature?: string; // the strict render's rejection id, e.g. "css:transform"
applied?: boolean; // set by fix()
}People read suggestedFix and snippet. Scripts, CI and AI agents should rely on rule,
severity, autoFixable, location, compatLink and feature.
feature is set exactly when a strict renderPdf rejects the construct, and it is the same id the
VelloraUnsupportedError carries (element:canvas, css:transform, css:grid, …). A finding
without it (an inline SVG, display:flex in a cell) renders.
How it relates to rendering
renderPdf(html)in vellora is strict by default: it never imports this package and never changes your HTML. Out-of-subset input throws aVelloraUnsupportedErrorwith the line and column.renderPdf(html, data, { strict: false })loads this package and appliesfix()before rendering, for when you'd rather get a PDF than an error.
The linter reports every element and CSS feature the strict render rejects: <script>
(script-element), animation (css-animation), undrawn @page margin boxes (page-margin-box),
the other denied elements (unsupported-element) and transform, transition, filter,
backdrop-filter, perspective and display: grid (unsupported-css), and
<link rel="stylesheet"> / @import, which vellora never loads (external-stylesheet). It cannot see rejections
that depend on render options (images, fonts), on the @page cascade
(css:page-varying-width) or on the engine's nesting limit, so a strict renderPdf per template in
CI is still the final check. The full list is in
COMPATIBILITY.md.
License
MIT. See LICENSE.
