Skip to content

0.2.0 - 2026-06-11

Choose a tag to compare

@github-actions github-actions released this 12 Jun 03:50

Release Notes

Everything since the initial 0.1.0 crates.io publish. This grew the compiler
from an early vertical slice to 100% of the attempted official sass-spec
suite
(13,896 / 13,896, zero failures — 11,405 byte-exact CSS outputs plus
2,491 error specs correctly rejected; the 8 remaining cases are tagged :todo
for dart-sass itself upstream), matching current dart-sass (1.100) byte-for-byte.
The pass is measured against a conformance harness tightened to reproduce the
official sass-spec comparator (normalizeOutput) exactly — collapse newline
runs only, no extra whitespace leniency — so the count holds under the upstream
comparator, not just a looser local one.

Added

  • Byte-exact diagnostics — errors, @error, @warn, and @debug now
    reproduce dart-sass's stderr byte-for-byte: source-span ╷│╵ snippets with
    carets and right-aligned gutters (tab→4 spaces), aligned stack frames
    (root stylesheet / name() / @import), a --no-unicode flag, and the
    @import deprecation warning (with a per-id cap/dedup deprecation registry).
    238 of the suite's 3,256 stderr expectations now match byte-for-byte (a
    spec/run_spec.py --check-stderr metric tracks it); the rest (other
    deprecations, multi-span layouts) build on this foundation.
  • @use / @forward module system — built-in sass:* modules and user
    files, with configuration, namespacing, @forward prefix/show/hide,
    dash-insensitive member access, forward conflict resolution, and star
    (as *) modules.
  • Indented .sass syntax — a full front-end (Options::with_syntax, the
    CLI --indented flag, .sass extension inference), including cross-syntax
    @import of partials by file extension.
  • CSS Color 4 color spacessrgb/display-p3/lab/lch/oklab/
    oklch/xyz via color(), with modern color serialization.
  • @extend and %placeholders — a faithful port of dart-sass's
    ExtensionStore engine: registration-order extension folding, selector
    weaving/unification/trimming, @use/@forward cross-module visibility, and
    the self-referential :not/:has pseudo cases — closing the suite's
    @extend family to byte-exact parity.
  • Built-in function modulesmeta (first-class function references via
    get-function/call, existence predicates), math (clamp/min/max/
    round/log and friends), list (bracket-preserving join/append),
    map (nested key paths, deep-merge/deep-remove), string
    (split/unique-id), and selector functions.
  • First-class mixinsmeta.get-mixin returns a mixin value and
    meta.apply invokes it (with @content support).
  • CLI — compile multiple input files in one process (sasso a.scss b.scss,
    startup shared across files); --loop <N> for in-process throughput and
    -q/--quiet to suppress stdout (used by the benchmark harness).
  • Benchmark harness — sasso registered as a first-class engine in bench/;
    three-way report bench/three_way.md (sasso vs
    dart-sass vs grass).
  • CODE_OF_CONDUCT.md adopting the
    Sass Community Guidelines, as
    the Sass project asks every implementation to do.
  • This CHANGELOG.

Changed

  • Selector resolution now matches dart-sass on combinator normalization,
    adjacent-compound separation, and bogus-combinator omission.
  • color functions match dart-sass strictness: channel-unit leniency in
    adjust/change, missing/powerless-channel errors, the Microsoft alpha()
    filter overload, and adjust-hue rejecting non-legacy colors.
  • selector functions coerce string/list arguments and validate arity, and
    accept a list of extendees in extend/replace.
  • list builtins validate fixed-arity arguments and preserve list shape.
  • Unquoted string serialization collapses newlines to spaces; custom-property
    values are emitted verbatim — both matching dart-sass.
  • Control-flow blocks use semi-global scoping with a global-write guard.

Performance

Profiling showed the compiler is allocation- and hashing-bound; a series of
hot-path cuts followed, with no behavior change (cumulative ~2× faster on the
large benchmark vs. the original, lifting the lead over grass to ~1.9–2.4× and
over dart-sass to ~16–25×):

  • Selector helpers split_commas/tokenize_complex return borrowed &str
    slices, and copy_name/normalize_selector avoid their intermediate
    String/Vec — no per-part/per-token/per-name heap allocation on the hot
    selector-resolution path.
  • The compiler's internal String-keyed maps (variable scope, function/mixin
    tables, module maps) use a small inline FxHash hasher instead of std's
    DoS-resistant-but-slow SipHash. (Still zero runtime dependencies.)
  • A scoped bump-arena allocator (ScopedAlloc): within each compile() a
    per-thread arena turns every allocation into a pointer bump and frees them
    wholesale (reset) at the end — a further ~1.5×. It is installed as the CLI's
    #[global_allocator]; library/wasm embedders can opt in the same way (it
    forwards to the system allocator outside a compile, so it's safe to install
    unconditionally). This is the library's one audited unsafe module —
    verified by unit tests, Miri (no UB), AddressSanitizer, and the full sass-spec
    suite run through it (zero crashes, byte-identical output); the
    rest of the crate is deny(unsafe_code). Still zero runtime dependencies.
  • Smaller Value — the Color variant's modern-color payload is boxed, so
    the Value enum drops from 128 to 64 bytes (a compile-time size_of
    guard prevents regressions). Halves every scope-map slot, Vec<Value> element
    and lookup clone, with byte-identical output.
  • Zero-dependency Ryū float formatter — a from-scratch d2s shortest-round-
    trip formatter on the float-to-string hot path, replacing core::fmt, with a
    differential fuzz test against a reference.

Tooling

  • The conformance ratchet pins the sass-spec commit (spec/SPEC_VERSION.txt)
    for reproducibility, with a --latest/--canary drift-detection mode.
  • The conformance harness now reproduces the official sass-spec comparator
    (sass-spec/lib/test-case/compare.ts normalizeOutput) exactly, so a "pass"
    means byte-identical under the upstream comparator — no extra local leniency.

Install sasso 0.2.0

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/momiji-rs/sasso/releases/download/v0.2.0/sasso-installer.sh | sh

Install prebuilt binaries via powershell script

powershell -ExecutionPolicy Bypass -c "irm https://github.com/momiji-rs/sasso/releases/download/v0.2.0/sasso-installer.ps1 | iex"

Download sasso 0.2.0

File Platform Checksum
sasso-aarch64-apple-darwin.tar.xz Apple Silicon macOS checksum
sasso-x86_64-apple-darwin.tar.xz Intel macOS checksum
sasso-x86_64-pc-windows-msvc.zip x64 Windows checksum
sasso-aarch64-unknown-linux-gnu.tar.xz ARM64 Linux checksum
sasso-x86_64-unknown-linux-gnu.tar.xz x64 Linux checksum
sasso-aarch64-unknown-linux-musl.tar.xz ARM64 MUSL Linux checksum
sasso-x86_64-unknown-linux-musl.tar.xz x64 MUSL Linux checksum