A pug-inspired HTML preprocessor, written in Rust. Less typing, more shipping.
Still young and growing! Some features are cooking. Check out the roadmap below.
HSML compiles a short, indentation-based syntax into HTML — think Pug, but leaner:
- TailwindCSS-friendly — arbitrary values like
.bg-[#1da1f2]andlg:[&:nth-child(3)]:hover:underlinejust work - No template engine — HSML is to HTML what TypeScript is to JavaScript: a compile-time transformation, no runtime
- Rust-powered — fast native CLI + WASM package for browser and bundler use
- Helpful diagnostics — descriptive errors and warnings with source context, like a good compiler should
doctype html
html
head
title My Page
body
h1.text-xl.font-bold Hello World
.card
img.rounded-full(src="/avatar.jpg" alt="Me")
p.text-gray-500 Nice to meet you!
Compiles to:
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1 class="text-xl font-bold">Hello World</h1>
<div class="card">
<img class="rounded-full" src="/avatar.jpg" alt="Me" />
<p class="text-gray-500">Nice to meet you!</p>
</div>
</body>
</html>cargo install hsmlnpm install -D hsml
# or
pnpm add -D hsml
# or
bun add -D hsml# Compile a single file
hsml compile index.hsml
# Compile to a specific output file
hsml compile index.hsml -o output.html
# Compile all .hsml files in a directory
hsml compile src/
# Check files for errors and warnings without compiling
hsml check src/
# Get diagnostics as JSON (for CI integration)
hsml compile index.hsml --report-format json
hsml check src/ --report-format json
# GitHub Actions annotations
hsml check src/ --report-format github
# GitLab Code Quality report
hsml check src/ --report-format gitlab
# Debug output with timing
hsml compile src/ --debug
# Disable colored output
hsml compile src/ --no-colorWhen compiling or checking a directory, HSML automatically skips:
- Hidden files and directories (e.g.
.git/,.cache/) - Built-in ignores:
node_modules/,target/,dist/,build/,.hg/,.svn/ .gitignorepatterns (works even outside git repositories).hsmlignorepatterns (same format as.gitignore)
You can also pass ignore patterns via CLI:
hsml compile src/ --ignore-pattern "vendor/"
hsml check src/ --ignore-pattern "tmp/" --ignore-pattern "generated/"To re-include a built-in ignored directory, add a negation to .hsmlignore:
# .hsmlignore
!build/import { compileContent, compileContentWithDiagnostics } from "hsml";
// Simple compilation
const html = compileContent("h1.title Hello World\n");
// => '<h1 class="title">Hello World</h1>'
// With diagnostics (errors + warnings)
const result = compileContentWithDiagnostics("h1.foo.foo Hello\n");
// => { success: true, html: '...', diagnostics: [{ severity: 'warning', code: 'W002', ... }] }h1 Hello World
div
p Some text
Tags default to div when only a class or id is specified:
.container
.card
.card-body Hello
h1.text-red.font-bold Hello
TailwindCSS arbitrary values are fully supported:
.bg-[#1da1f2].lg:[&:nth-child(3)]:hover:underline
div#app
h1#title Hello
img(src="/photo.jpg" alt="A photo" width="300")
a(href="https://github.com" target="_blank") GitHub
button(disabled) Click me
Multiline attributes work too:
img(
src="/photo.jpg"
alt="A photo"
width="300"
height="200"
)
Use a trailing . to start a text block:
p.
This is a multi-line
text block that preserves
line breaks.
// Dev comment (excluded from HTML output)
//! Native comment (rendered as <!-- ... -->)
doctype html
HSML supports framework-specific attribute syntax out of the box:
// Vue
button(@click="handleClick" :class="dynamicClass" v-if="show") Click
template(#default)
p Slot content
// Angular
button([disabled]="isDisabled" (click)="onClick()") Click
HSML provides helpful error messages with source context:
error[E001]: Tag name must start with an ASCII letter
--> example.hsml:1:1
|
1 | 42div Hello
| ^
And warnings for common mistakes:
warning[W002]: Duplicate class 'foo'
--> example.hsml:1:12
|
1 | h1.foo.foo Hello
| ^
| Code | Description |
|---|---|
| E001 | Tag name must start with an ASCII letter |
| E002 | Unclosed bracket |
| E003 | Unclosed parenthesis |
| E004 | Unclosed quote in attribute value |
| E005 | Expected quoted attribute value |
| E006 | Invalid attribute key |
| W001 | Duplicate id (only one per element) |
| W002 | Duplicate class |
| W003 | Mixed tabs and spaces in indentation |
| W004 | Duplicate attribute |
| W005 | Void element cannot have content |
| W006 | Empty attribute parentheses |
- Parser with TailwindCSS support
- HTML compiler
- CLI with
compilecommand - WASM package for npm
- Diagnostic system with errors and warnings
- JSON diagnostic output (
--report-format json) -
hsml check— standalone linting command - Ignore support (
.gitignore,.hsmlignore,--ignore-pattern) -
hsml fmt— code formatter -
hsml parse— AST output as JSON - LSP server with diagnostics and hover
- GitHub/GitLab CI diagnostic formatters
See CONTRIBUTING.md for development setup and commands.
MIT — go wild.