Release Notes
Crate release v0.8.0; ships on npm as sasso@0.11.0 (wasm + native — same
core). Byte-identity, complete: all 20 projects in the real-world corpus
compile byte-identical to dart-sass 1.101
(bench/real-world/real_world.md, 2.9–51×
faster end-to-end). The corpus doubled this release (tabler, AdminLTE,
reveal.js, Font Awesome, video.js, forem, nextcloud server,
jekyll-theme-chirpy, grafana, wagtail) and every divergence it surfaced was
fixed against dart-sass semantics — each pinned by a parity test.
Fixed (@extend engine — dart's per-registration model)
@eachover a single value —nullincluded — iterates once (dart
Value.asList). Bootstrap'svalid-radius(null)relies on it; previously
a css-lessborder-radiusmap key ERRORED the whole compile (tabler).- The application fold runs each registration exactly once — no fixpoint.
Extend cycles resolve the way dart's do:_extensionsByExtender[target]is
a LIVE list, so an extender that itself contains the target self-derives
within its own registration. The old fixpoint chained one extender of an
@extendthrough its sibling of the same rule, emitting selectors dart
never produces. - Original selectors are identity-tracked (dart
_originalsis a
Set.identity()): an extension product value-equal to one of the rule's
own selectors is coverage-trimmed in the original's favor, keeping the
original's position and source line break (.btn-sm,\n.btn-group-sm > .btn;
h1, .h1heading runs). dart's bare fast path (a single-simple compound
replaced wholesale returns the extender object) carries identity too,
scope-gated per module store — dart#1297 keeps the in-store:is()rewrite
alongside its source while the cross-module variant replaces it. - Foreign extensions apply in dart's cross-module store-merge order
(downstream stores merge transitively; sibling stores reverse-first-load;
derived entries follow their trigger's absorption order) — bulma's%block
extender interleaving. - Identical
(target, extender)registrations merge per module store, not
globally, so each keeps its own store-merge rank (chirpy's
#access-lastmod a:hoverposition and line break). - One
@extendhitting several compounds of the same complex uses dart's
pathsorder (first component varies fastest) on the incremental path
too (forem's.crayons-btn + .crayons-btnfile-selector products). - Pre-rule extensions apply one-shot per dart's
addSelectortiming, and
the one-shot gate compares registration indices, not counts. - A placeholder-only module whose rules were all dropped counts as empty,
so its group-separator blank collapses (uswdsplaceholders/).
Fixed (module system & output fidelity)
- The pre-module comment engine matches dart on three fronts (uswds):
registration deep-scans pending comments through invisible module-scope
placeholders; re-emitted clones are fenced and never re-register onto new
module keys; a css-less module built while the shared comment map is
non-empty absorbs pending registrations (dart'stransitivelyContainsCss
includespreModuleComments.isNotEmpty). - Re-emitted pre-module clones stay out of the loader's import-run sweep
(nextcloud's SPDX header no longer jumps to the top of the document). - CSS
@importhoisting keeps the css flow's eval-time grouping — dart's
blank lines come solely from group-end flags, never source gaps; only the
seam the pulled import run vacated is re-derived (forem). meta.load-csscopies re-acquire per-rule group separators — dart
re-visits the combined css node-by-node (reveal.js print styles).- A trailing invisible chain owns the enclosing group's end at any nesting
depth, packing the next group tight (chirpy). @at-rootseparators follow dart's_styleRule == nullgroup-end gate
— rules flattened out of a wrapper rule pack tight (wagtail's sidebar).- Selector line-break parity with dart (pseudo-arg newlines,
parent-resolution flags), loud comment re-indentation per dart's
_loudComment, invisible-last-child groups pack the next group tight,
and an empty module scope no longer anchors a group separator
(bootstrap, quasar, mastodon, govuk-frontend byte-identity).
Added
bench/real-world/: the vetted corpus doubled to 20 projects — every
one compiled standalone with both engines and byte-compared on every run
(node bench/real-world/run.mjs checkis the regression gate).
Install sasso 0.8.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/momiji-rs/sasso/releases/download/v0.8.0/sasso-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/momiji-rs/sasso/releases/download/v0.8.0/sasso-installer.ps1 | iex"Download sasso 0.8.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 |