coffeehaml
v0.10.1
Published
Haml structure. CoffeeScript semantics. React runtime. Zero runtime. Vite-native.
Maintainers
Readme
☕ CoffeeHaml
Haml structure. CoffeeScript semantics. React runtime.
CoffeeHaml is a compiler that transforms an indentation-based, Haml-inspired
authoring language into JavaScript using the modern React JSX runtime
(react/jsx-runtime). It is not a UI framework. React remains the runtime.
CoffeeHaml replaces JSX — with 50% fewer tokens.
%SimulatorPanel{source: "gyro", width: "auto", height: 500}
- for servo in servos
%ServoGraph{channel: servo.channel}
%ServoPanel{servo: servo}
%Button{onClick: save, disabled: !connected}
Save↓ compiles to ↓
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
jsxs(SimulatorPanel, { source: "gyro", width: "auto", height: 500 },
...servos.map(servo => jsxs(Fragment, {},
jsx(ServoGraph, { channel: servo.channel }),
jsx(ServoPanel, { servo: servo })
)),
jsx(Button, { onClick: save, disabled: !connected }, "Save")
);Why
| JSX | CoffeeHaml |
|-----|------------|
| <div className="box"><h1>Hello</h1><p>World</p></div> | %div.box%h1 Hello%p World |
| Closing tags echo the opener verbatim | Indentation is structure |
| 17 structural tokens | 8 structural tokens |
| Angle brackets, braces, delimiters everywhere | Only semantic characters |
Every character carries weight. No syntactic ceremony — only meaning.
The Indentation Renaissance
Modern LLMs are increasingly trained on indentation-based languages — Python, YAML, Haml, Sass. Their tokenizers have internalized significant whitespace as structural signal. CoffeeHaml enters this cycle at precisely the right moment: the community is ready for a leap in expressiveness that removes boilerplate rather than adding another layer of abstraction.
Install
npm install coffeehamlGitHub: dantiel/CoffeeHaml
CoffeeScript is an optional peer dependency — install it to compile CoffeeScript expressions in attributes and control flow:
npm install coffeescriptWithout CoffeeScript, expressions pass through as-is (valid for most JS
expressions like {onClick: handler} or {disabled: !connected}).
Usage
TextMate Bundle
A complete TextMate bundle ships with the repo at CoffeeHaml.tmbundle/.
Install via symlink:
ln -sfn "$(pwd)/CoffeeHaml.tmbundle" \
~/"Library/Application Support/TextMate/Bundles/CoffeeHaml.tmbundle"Includes syntax highlighting, Prettier formatting (⌃⌥F), compilation commands,
snippets, and symbol navigation. See bundle README.
CLI
# Scaffold a new project (interactive wizard)
npx coffeehaml init
# One-shot compile
npx coffeehaml compile app.chaml -o app.js --wrap component
# Watch mode — recompiles on every change
npx coffeehaml watch src/ --wrap observer| Flag | Description |
|------|-------------|
| init | Interactive project scaffold (Vite, dependency, samples) |
| -o, --output | Write output to file |
| --source-map | Emit inline source map |
| --wrap <mode> | Wrap mode: component, observer, or comma-separated HOC list |
Accepts .coffeehaml, .cohaml, and .chaml extensions.
Vite plugin
// vite.config.ts
import coffeehaml from 'coffeehaml/vite';
export default {
plugins: [coffeehaml()],
};Now import .chaml files directly — they compile to React components with HMR.
Node API
import { compile, compileFile } from 'coffeehaml';
const result = compile('%div Hello', { filename: 'app.chaml' });
console.log(result.code);
// → import { jsx } from "react/jsx-runtime";
// jsx("div", null, "Hello");Syntax
| CoffeeHaml | Description |
|------------|-------------|
| %tag | HTML element (%div, %span, %button) |
| %Component | React component (uppercase) |
| .class | CSS class |
| #id | Element ID |
| {attr: val} / (attr: val) | Attributes (CoffeeScript expressions) |
| {onClick} | Shorthand — onClick={onClick} |
| = expression | Inline output (escaped); continuation via indented lines |
| - if, - unless | Conditional |
| - code | Arbitrary CoffeeScript statements; continuation via indented lines |
| - for x in xs | Loop → .map() |
| - else, - else if | Chain conditionals |
| --- … --- | Fenced CoffeeScript block — render-time statements (multiline -) |
| === … === | Fenced yield block — CoffeeScript whose final value renders (escaped) |
| ~~~ … ~~~ | Fenced preamble block — module-scope code (imports, helpers, setup) |
| -# comment | Haml comment (stripped) |
| / comment | HTML comment |
| .wrapper | Implicit div |
Full grammar: docs/grammar.md
Design Principles
- Zero runtime — the compiler is the only artifact
- Emitter-agnostic AST — future backends possible (Solid, Vue, Mithril)
- Source-located errors — line/column hints for every compile failure
- Source maps — debug in CoffeeHaml, not generated JS
- Incremental compilation — Vite HMR out of the box
- No invention — reuse Haml and CoffeeScript conventions
Status
v0.10.0 — production beta. The compiler pipeline (Lexer → Parser → Emitter) is complete. The Prettier plugin provides 16 deactivatable formatting options including attribute style preservation, statement merging, blank line handling, and CoffeeScript code formatting.
| Feature | Status |
|---------|--------|
| Elements, components, implicit divs | ✅ |
| .class / #id modifiers | ✅ |
| {attr: val} / (attr: val) + spread {props...} | ✅ |
| attribute style preservation (braces/parens/bare) | ✅ |
| Inline = expression output | ✅ |
| = expression continuation (indented) | ✅ |
| - statement continuation (indented) | ✅ |
| - if / - unless / - else / - else if | ✅ |
| - for item in items → .map() | ✅ |
| - while | ✅ |
| Statement merging (- blocks) | ✅ |
| Haml/HTML comments | ✅ |
| :filter blocks | ✅ |
| Prologue passthrough (import, @decorator) | ✅ |
| --- render-time CoffeeScript block | ✅ |
| === yield block (fenced output) | ✅ |
| ~~~ module-scope preamble block | ✅ |
| Component / HOC wrapping (wrap: 'observer') | ✅ |
| Vite plugin with HMR | ✅ |
| CLI --wrap flag | ✅ |
| CLI watch mode | ✅ |
| CLI init scaffold | ✅ |
| = expr -> arrow continuation | ✅ |
| Source-located error messages | ✅ |
| Multi-error parser recovery | ✅ |
| Source maps | ✅ |
| CoffeeScript expression compilation | ✅ (requires peer dep) |
| React Fast Refresh annotations | ✅ |
| Prettier formatter plugin (16 options) | ✅ |
| TextMate bundle (commands, snippets, syntax) | ✅ |
License
MIT
Haml structure. CoffeeScript semantics. React runtime. Nothing more, nothing less.
