undercoat 0.12.0

Native Rust compiler for Less and Sass/SCSS — byte-compatible with lessc and dart-sass, no Node runtime
Documentation

undercoat

A native Rust CSS preprocessor. One engine compiles both Less and Sass/SCSS, and its output is byte-compatible with the reference implementations — no Node runtime.

The name is the paint layer beneath the finish: undercoat takes Less / Sass sources, flattens them into plain CSS, and hands them to the browser.

$ undercoat app.scss -o app.css --source-map
$ undercoat app.less  -o app.css

Why

  • Two dialects, one pipeline. @import / @use / @forward assembly, evaluation, @extend resolution and source-map generation share one implementation. A dialect differs only in its value model and six output-shape switches.
  • Byte-compatible with the references. Less targets lessc 4.1.3; SCSS targets dart-sass language semantics at 1.93.2 and output shape at 1.35.2 (same input ⇒ the same CSS text). Bootstrap 4/5, Foundation, Bulma and element-plus are part of the regression corpus.
  • Source maps you can trust. Multi-line selector lists, comment continuations and rules nested in @media all map line by line, matching dart-sass coverage.
  • No Node.js. A single binary or a plain library call — embeddable in build tools, bundlers and CI.

Status

Both dialects are usable today: .less, .scss, .sass and plain .css / .wxss. Known deviations are limited to text shape of colour/calc() rounding (numeric equivalents) and a few edge-case constructs that are reported as errors instead of silently emitting wrong CSS.

CLI

$ undercoat <file.(less|scss|css|wxss)> [--source-map] [--project-root DIR] [-o OUT]

Library

use std::path::Path;
use undercoat::{compile_file, Options};

let opts = Options { paths: vec!["/abs/styles".into()], ..Default::default() };
let out = compile_file(
    Path::new("/src/app.scss"),
    "@use 'sass:math'; a { width: math.div(1px, 3) }",
    &opts,
)
.unwrap();

println!("{}", out.css);

compile_file dispatches on the file extension: .less goes to the Less channel; .scss / .sass / .css / .wxss go to the SCSS channel (.sass indented syntax is folded into equivalent SCSS text at load time). Output carries css, an optional source map and the absolute paths of every dependency; errors are a CompileError with file / line / column.

Repository layout

src/                  the library (one module per concept, shared across dialects)
  doc.rs              output IR + `Style` (the only place dialect shapes differ) + printer
  merge.rs            merged-source string and offset segment table (imports, maps)
  text.rs             byte-level scanning primitives
  less/               Less front end: env, eval, selector, prelude, imports, comments, emit
  scss/               SCSS front end: decls, flow, module_use, selector, prelude, emit
tests/                golden / conformance / source-map suites
docs/architecture.md  module responsibilities and the boundaries between dialects
tools/undercoat-diff  semantic CSS comparator used for conformance runs

Development

$ cargo test            # unit + golden + conformance suites
$ cargo run --bin undercoat -- tests/fixtures/<case>/main.scss -o /tmp/out.css

The conformance corpus (bootstrap, foundation, bulma, …) is not vendored; suites that need it take a directory via environment variable (for example SCSS_SASS_CORPUS) and are skipped when it is absent.

License

MIT. See LICENSE.