Switch the default Sass transpiler to dart-sass - #2724
Merged
Conversation
Collaborator
Author
|
Review artifacts: one-shot libsass vs dart-sass structural CSS diff (same branch code, dev-profile builds of docsy.dev, only the
|
chalin
marked this pull request as ready for review
August 18, 2026 18:24
sass-embedded@1.102.0, exact-pinned in the root package.json beside hugo-extended: the dart-sass compiler Hugo's dartsass transpiler spawns (the transpiler flip follows in the next commit). The compiler binary ships via npm's optional-deps platform dispatch (the script-free alternative to postinstall downloads), so install:safe drops --omit=optional: a no-op until now (the lock had zero optional entries), and afterward exactly one optional package installs, the script-free platform binary. Supply-chain audit updates, both proven red first: - @parcel/watcher joins the install-script inventory: lock-only, an optional dep of the pure-JS sass fallback shipped for platforms without a prebuilt binary; never installs on any platform we run. allowScripts denies it for defense in depth. - install:safe's pinned form tracks the flag drop.
toCSS in head-css.html now passes transpiler: dartsass, with silenceDependencyDeprecations (Bootstrap's import/if-function/ global-builtin/color-functions warnings; verified the full set) and silenceDeprecations: import for Docsy's own ~40-@import tree until its @use refactor (a later increment). dart-sass forbids Sass @imports nested under @if or a selector, which _code-dark.scss relied on for the generated chroma styles. The generator now wraps each style in a mixin (side-effect-free import), and _code-dark.scss imports at top level and @includes under the scheme selectors. Works identically under libsass, keeping the consumer escape hatch (override head-css.html back to libsass) intact. Build is warning-free. Visual suite now red on exactly the 6 shots the dual-compiler probe predicted (desktop full-page light/dark x home/single/blog, 26-35 px): dart-sass renders Bootstrap's color math at full precision. Golden refresh follows with the structural-diff review artifacts.
Exactly the 6 shots the dual-compiler probe predicted (desktop full-page light/dark x home/single/blog); the 24 others pass unchanged at threshold 0. Structural CSS diff and per-shot diff PNGs attached to the PR as review artifacts. Linux goldens follow from the CI artifact.
The dartsass transpiler shells out to the sass CLI, which a freshly scaffolded smoke site doesn't have; provide the repo-installed, locked one, the same sourcing pattern as HUGO. Red-proven by the first CI run (all new-site jobs failed at the site build); consumer sites install their own per the changelog entry.
…apper A site-supplied chroma file holding a raw hugo-gen-chromastyles dump (the previously documented form) now hits a bare 'Undefined mixin' under the mixin-shaped theme files, its rules also landing unscoped. Red-proven with a raw tango dump at the documented override path: - _code-dark.scss guards with mixin-exists and errors with the required wrapper + docs link; the wrapped form builds green with the custom style landing scoped (verified). - lookandfeel.md documents the wrapper with an example; changelog entry carries the action. - silenceDeprecations gains global-builtin: mixin-exists is one of the deprecated global built-ins, and the interim @import tree cannot host a sass:meta @use; the @use refactor clears both entries.
Supersedes the repo-PATH fix, which made the smoke false-clean: it injected the maintainer repo's compiler into a job that exists to simulate a consumer install, hiding exactly the failure mode the swap introduces (adversarial round 1, finding 3; round 2, P1). _npm_install now installs sass-embedded in the generated site and puts the site's own bin dir on PATH, for every Docsy source: the documented consumer action, exercised end to end. Placed after the --omit=dev DEPS install, which prunes devDependencies (red-proven: install-first left no sass binary and the build failed TOCSS-DART; reordered run is green with the site's sass resolving under the generated .npmrc's release cooldown).
Selector-nested Sass @imports compile fine under dart-sass (_nav.scss does it); only control-directive nesting errors. The generated files now carry both header comments above the mixin wrapper instead of one inside its body.
Every documented deployment path ran hugo where npm's bin dir is not on PATH, so a consumer following the changelog remedy alone still failed to build (adversarial round 1, finding 1): - Prerequisites gain an Install Dart Sass section (sass-embedded for npm sites, Hugo's install guide for the rest) and lose the stale 'extended supports SCSS' rationale. - GH Pages workflow exports node_modules/.bin via GITHUB_PATH before the bare hugo step; Netlify's documented build commands carry an inline PATH prefix (Netlify has no GITHUB_PATH equivalent; round 2, P2). - Official-support matrix names the Dart Sass version: the repo's sass-embedded pin, with 1.74.0 as the hard floor (first release with the deprecation-silencing options the build uses).
Leads with the sass requirement (linking the new prerequisite and deployment docs), states the color change accurately (serialization form, not perceptible shift, with the string-matching caveat), and names the escape hatch's expiry alongside the fact that Hugo will never bundle Dart Sass (adversarial round 1, finding 4).
Terse bullets linking their docs homes, per the repo's separation of concerns; the chroma wrapper becomes its own breaking-change bullet. The evicted release narrative (what to re-check, escape hatch and its expiry, BSD caveat, CF Pages snippet) moves to the 0.17 upgrade-post material at release prep; the post link gets woven in then, per the 0.16.0 entry pattern.
The tarball, registry, and clone consumer simulations inherited the test process PATH, whose repo node_modules/.bin silently supplied the compiler the scratch sites never installed: a false-clean on exactly the defect class the smoke exists to catch (adversarial round 3, finding 3; red-proven by sanitizing PATH alone, which failed all consumer builds with the missing-Dart-Sass error). Consumer Hugo builds now run with the site's own bin dir first and this checkout's directories stripped, and each simulated consumer installs sass-embedded from its site root, the documented action.
The submodule, clone, and npm-package guides kept their own prerequisite section and bare-hugo build commands, so a consumer following any of them built without a sass provider and failed (adversarial round 3, finding 1; reproduced with a packed tarball in a clean-PATH site): - other-options prerequisites gain an Install Dart Sass pointer with the direct-invocation PATH form; the option 1 and 3 hugo commands carry it; option 3 installs sass-embedded beside the theme. - Setup overview: the npm option's 'no extra toolchain' claim now names the compiler as part of the npm-only toolchain. - Root README: Dart Sass prerequisite added; stale 'extended supports SCSS' rationale dropped (both spots).
The README renders into the live site, where lychee checks the absolute URL's fragment against production; the section only exists once this PR deploys. Page link + named section instead.
chalin
force-pushed
the
chalin-m24-dartsass-swap-2026-0818
branch
from
August 18, 2026 21:09
98bddff to
ae14ae7
Compare
- netlify: the build-command lead-in described the PATH prefix as an action to take, while the commands below already carry it; now descriptive. - prerequisites: the Dart Sass floor was stated here and in the support matrix; the matrix owns versions, this page now points. - get-started overview: colon for the elaboration; dependency grammar. - make-site/smoke: comment trims (what-restating clause; duplicate rationale now a pointer).
Probe-verified semantics: Hugo feeds the entry via stdin and loads every other file (Docsy's own tree included) through its importer, which dart-sass counts as dependency loading. So silenceDependencyDeprecations already covers _code-dark.scss's mixin-exists; only the entry file's own import warning needs the explicit ID. Build verified warning-free without it.
Dependency semantics probe-verified: dart-sass treats files loaded through importers as dependencies (sass-lang.com Options#quietDeps), and Hugo resolves every non-entry file through its ImportResolver, so only the stdin-fed entry is first-party.
The dict lost the option in the previous commit while the comment kept documenting it; the entry's import warning was back. Build re-verified warning-free.
chalin
force-pushed
the
chalin-m24-dartsass-swap-2026-0818
branch
from
August 18, 2026 22:08
b7b9abf to
83ed5be
Compare
The probe guards Hugo API deprecations (its original scope, google#2647); with the transpiler swap keeping Dart Sass language deprecations visible as fix-me reminders, its bare /deprecated/i filter tripped on every Sass line. Red-proven before the exemption, green after; the Hugo-API signal is unchanged.
sass:meta's load-css is the module-system replacement for nested Sass @import: an @include, legal under @if, that emits the loaded file's CSS in place. Probe-verified that an @import-loaded file may carry @use sass:meta and still read the importer's globals. Reverts the mixin machinery it obsoletes: generator wrapper, raw chroma dumps restored, mixin-exists guard, docs wrapper instruction, and the chroma changelog bullet. The documented consumer path (a raw hugo-gen-chromastyles dump at the override mount) is no longer a breaking change: red-proven before this branch's guard, green now with the custom style landing scoped. Compiled CSS: rule-identical; the only diff is the generator header comments now emitting inside the scheme blocks. LibSass escape-hatch cost: libsass has no sass:meta, so the hatch grows to a third override (_code-dark.scss, original nested-@import form); carried in the 0.17 upgrade-post material.
One home for the instructions (the prerequisites page); the README names the requirement and links.
A pinned allow is load-bearing (a bump's new script must fail the install until re-reviewed); a pinned deny is bump churn with no security value, since the answer is false for every version. Matches otel.io's committed form (fsevents: false). Red-proven via the audit test's exact-map assertion, which now encodes the principle.
deepEqual already prints the expected list; the message states the invariant.
Sweep of the messages inherited with google#2725, per the repo's assert-message guidance: expected-condition phrasing (no 'still', 'unexpected', 'must'), and values that assert.equal already prints on failure dropped from messages. The parser fixture regexes that match these messages are updated in step; the full suite is the proof.
The install-script list reads as if it claimed execution; the comment states the invariant (platform gating is package-controlled metadata, so lock presence alone earns review). Includes an owner spacing tweak in head-css.html.
Source-verified against the current build system: the build command runs under @netlify/build's execa call with preferLocal: true, which puts the base directory's node_modules/.bin on the child PATH (the archived focal image's broken 'npm bin' export misled the earlier fix). With sass-embedded declared per the prerequisites, bare hugo finds sass. GHA keeps its GITHUB_PATH step: Actions has no preferLocal equivalent.
sass-embedded also gains code font in the link text, per the link conventions.
Verified against the rendered HTML: both pages emit id=install-dart-sass without the annotation; fragment links intact (link check green).
Best-practice consolidation of the Dart Sass PATH story: npm-route sites define build/serve scripts (example in the prerequisites) and run Hugo through them; non-npm sites have sass globally per Hugo's guide. Removes every inline PATH prefix; the GH Pages workflow's GITHUB_PATH step collapses into npm run build.
chalin
force-pushed
the
chalin-m24-dartsass-swap-2026-0818
branch
from
August 18, 2026 23:32
2363bce to
9aee86c
Compare
One unopinionated script ('hugo': 'hugo') replaces the build/serve
pair: every documented Hugo invocation becomes npm run hugo -- ARGS,
and the docs stop legislating flag choices. Matches the passthrough
pattern docsy.dev and docsy-example already use.
Mirrors the documented consumer pattern: the site carries the hugo passthrough script and builds via npm run hugo, with sass-embedded resolving through npm's own script PATH. Harness twists, each commented: the script body is "$HUGO" (the borrowed repo binary by absolute path; a bare name would need the repo bin dir on PATH), and the scaffolded .npmrc pins script-shell=bash for the expansion on Windows. Known limit: npm run PATH-walks ancestor node_modules/.bin, so for sites scaffolded inside the repo tree (CI's tmp/) the repo's sass remains reachable; provenance enforcement lives in the smoke.mjs consumer legs, which build outside the repo under a sanitized PATH.
npm run PATH-walks ancestor node_modules/.bin dirs, so a site scaffolded under the repo's tmp/ can silently borrow the repo's sass, false-cleaning the consumer legs. The scaffold moves to runner.temp (referenced via the runner context; a GITHUB_ENV write would trip the audit's env-isolation rule), and the toolchain-install step is renamed for what it provides: the pinned Hugo that make-site borrows.
…revert follows)" This reverts commit e75fc7b.
One SCAFFOLD definition exported via GITHUB_ENV, three env.SCAFFOLD uses (two working-directories, the artifact path); the runner-temp literal appears once. The audit's blanket GITHUB_ENV denial becomes an exact-pinned per-line allowance mirroring its GITHUB_PATH pattern, with the variable's assignment pinned too (the echo pin alone would leave the value free). Red-proven: the audit tripped on the write before the pin, green after.
- smoke.mjs: extract installSiteSass(), the consumer sass install duplicated verbatim across the tarball and clone legs - changelog: drop the 1.74.0 floor claim; its rationale (silencing options the build uses) went stale with the silencing reversal. The support matrix names only the pinned version; the floor moves to release-post guidance. Prerequisites pointer follows suit. - supply-chain audit: each GITHUB_* sink's rationale above its own loop; comment dash fixes here and in make-site.sh - make-site.sh: prune the trailing what-comment on the build line - smoke workflow: unbend the checkout-role comment's inverted sentence
Prune restatements (workflow header restating the matrix, the create_site_directory banner, the VERBOSE aside), dead commented-out code, a cross-file parenthetical, a stale cSpell word, and the passthrough comment's closing overclaim: npm run also walks ancestor bin dirs, so 'only it' was wrong, and the true part is standard npm knowledge. Trim the HUGO comment to intent only. smoke workflow: prune the toolchain-step comment Dialog residue promoted to a comment: the Hugo borrow is homed at make-site.sh's HUGO fallback, and the Docsy-from-GitHub sentence restates the matrix.
Construct-first dash fixes (colons, comma appositive, 'from' in progress lines; the extended-Hugo assert message also flips to the affirmative form), two comments pruned to their non-obvious fact. Includes the installSiteSass helper extraction. Full smoke suite green.
Review feedback: the Dart Sass exclusion assumes Hugo's log-line prefix. Record that a prefix change turns this gate red rather than silently widening it -- the safe direction, so no structural match is needed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
head-css.html):transpiler: dartsass, with no deprecation silencing: Dart Sass warnings (Bootstrap's@imports and Docsy's own tree, pending its@userefactor) stay visible as fix-me reminders. Hugo deprecated its embedded LibSass in v0.153.0; the consumer escape hatch is overriding back to libsass for as long as their Hugo carries it -- now a three-file override (head-css.html, plusmain.scssand_code-dark.scss, which use module features libsass lacks).@importnested under@if/selectors, which_code-dark.scssrelied on; it now loads chroma styles withmeta.load-css, so rawhugo gen chromastylesdumps keep working unchanged. The entrymain.scssmoves to@use. Compiled CSS unchanged (verified).sass-embeddedexact-pinned in the rootpackage.jsonbesidehugo-extended; the compiler binary arrives via npm's optional-deps platform dispatch (script-free), soinstall:safedrops--omit=optional(a no-op until now: the lock had zero optional entries). The supply-chain audit inventories the one lock-only install-script package this adds (@parcel/watcher, never installed on platforms we run) and pins the newinstall:safeform; both changes were proven red first.make-site.shscaffolds install a site-ownedsass-embeddedand run Hugo through annpm run hugopassthrough script. The smoke suite sanitizes the consumer site's PATH (consumerEnv()) and the CI scaffold moves to$RUNNER_TEMP, so a consumer site can't silently borrow the repo'ssass.td-breadcrumbrules diverge; the only selector-list changes are dart-sass pruning redundant@extendpermutations shadowed within the same rule).install:safe, which now providessasson the npm-script PATH.