hsml
v0.5.0
Published
A pug-inspired HTML preprocessor
Maintainers
Readme
HSML - Hyper Short Markup Language
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.
What is it?
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
Quick taste
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>Installation
CLI (via Cargo)
cargo install hsmlWASM (via npm)
npm install -D hsml
# or
pnpm add -D hsml
# or
bun add -D hsmlUsage
CLI
# 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-colorIgnore patterns
When 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/WASM / JavaScript
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', ... }] }HSML Syntax
Tags
h1 Hello World
div
p Some textTags default to div when only a class or id is specified:
.container
.card
.card-body HelloClasses
h1.text-red.font-bold HelloTailwindCSS arbitrary values are fully supported:
.bg-[#1da1f2].lg:[&:nth-child(3)]:hover:underlineIDs
div#app
h1#title HelloAttributes
img(src="/photo.jpg" alt="A photo" width="300")
a(href="https://github.com" target="_blank") GitHub
button(disabled) Click meMultiline attributes work too:
img(
src="/photo.jpg"
alt="A photo"
width="300"
height="200"
)Text blocks
Use a trailing . to start a text block:
p.
This is a multi-line
text block that preserves
line breaks.Comments
// Dev comment (excluded from HTML output)
//! Native comment (rendered as <!-- ... -->)Doctype
doctype htmlVue / Angular support
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()") ClickDiagnostics
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
| ^Error & warning codes
| 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 |
Roadmap
- [x] Parser with TailwindCSS support
- [x] HTML compiler
- [x] CLI with
compilecommand - [x] WASM package for npm
- [x] Diagnostic system with errors and warnings
- [x] JSON diagnostic output (
--report-format json) - [x]
hsml check— standalone linting command - [x] Ignore support (
.gitignore,.hsmlignore,--ignore-pattern) - [ ]
hsml fmt— code formatter - [ ]
hsml parse— AST output as JSON - [x] LSP server with diagnostics and hover
- [x] GitHub/GitLab CI diagnostic formatters
Contributing
See CONTRIBUTING.md for development setup and commands.
License
MIT — go wild.
