[heft-sass-plugin] Add opt-in bare specifier resolution options - #6094
Merged
Ian Clanton-Thuon (iclanton) merged 2 commits intoSep 28, 2026
Merged
Ian Clanton-Thuon (iclanton) merged 2 commits into
Ian Clanton-Thuon (iclanton) merged 2 commits into
Conversation
Since the move to the `pkg:` importer, a bare specifier such as `@use '@scope/pkg/theme'` only ever resolved relative to the importing file, so it failed with "Can't find stylesheet to import". There was no configuration option to change this, and the failing import is often inside a third-party package that the consuming project cannot edit. - Bare specifiers now fall back to the new `loadPaths` option and then to `node_modules` when they do not resolve relative to the importing file. Relative resolution still takes precedence, matching Sass semantics. - Package resolution failures return null instead of throwing, so Sass reports its usual error pointing at the offending line. - The legacy `~` rewrite is applied in the resolver rather than only by the `@use`/`@import`/`@forward` preprocessor, so it also works in constructs such as `meta.load-css()`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b66c2e44-e9ac-4611-8c45-85cb60478f7b
Ian Clanton-Thuon (iclanton)
force-pushed
the
fix-sass-bare-specifier-resolution
branch
from
September 25, 2026 01:09
47c1903 to
d4e4123
Compare
Bharat Middha (bmiddha)
approved these changes
Sep 25, 2026
David Michon (dmichon-msft)
requested changes
Sep 25, 2026
Addresses review feedback: per the Sass specification the target of `@use`/`@import`/`@forward` is a URL, so `@use '@scope/pkg/theme'` is a relative path and resolving it from node_modules is a deviation. That deviation is now opt-in rather than automatic. - Add `resolveBareSpecifiersAsPackages`, defaulting to false. Package resolution of a bare specifier happens only when it is enabled. - `loadPaths` is likewise consulted only when configured. - The legacy `~` rewrite is unchanged by this commit; `~` is an explicit package reference, so it is not gated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b66c2e44-e9ac-4611-8c45-85cb60478f7b
David Michon (dmichon-msft)
approved these changes
Sep 28, 2026
Ian Clanton-Thuon (iclanton)
deleted the
fix-sass-bare-specifier-resolution
branch
September 28, 2026 19:48
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.
Summary
Adds two opt-in options for resolving bare specifiers in Sass stylesheets, e.g.
@use '@scope/pkg/theme', and fixes the legacy~rewrite in constructs other than@use/@import/@forward.Motivation
Some other Sass toolchains — the Dart Sass CLI's
--load-path,sass-loader, Vite, the Angular CLI — resolve bare specifiers fromnode_modules. Stylesheets authored for those toolchains rely on it, and the import is frequently inside a third-party package, where a consuming project cannot rewrite it. Today there is no way to consume such a package:loadPathswas never exposed, andsass.jsonisadditionalProperties: false, so there is no escape hatch.These options provide one without changing the default behavior for anyone else. Reported downstream against SPFx 1.23 in microsoft/sp-dev-docs#11030.
Changes
loadPaths— folders, resolved against the project folder, searched when a bare specifier does not resolve relative to the importing file. Analogous to the Sass compiler option of the same name.resolveBareSpecifiersAsPackages— whentrue, a bare specifier that resolves neither relatively nor fromloadPathsis additionally resolved as a package via Node module resolution. Defaults tofalse.nullrather than propagating, so Sass reports its usualCan't find stylesheet to importpointing at the offending line, instead of an internalCannot find package "...".~handled in the resolver. Previously the~→pkg:rewrite was a regex over@import/@use/@forwardonly, and any surviving tilde threwUnexpected tilde in URL. The rewrite now also happens during canonicalization, so~works in@include meta.load-css('~@scope/pkg').~is an explicit package reference, so it is not gated behindresolveBareSpecifiersAsPackages.Testing
heft test— 65 passing, 8 new.New coverage: resolution from
node_moduleswhen enabled; resolution inside a dependency stylesheet (the reported scenario);~insidemeta.load-css(); and resolution vialoadPaths.Four are guard tests pinning behavior that must not change:
false;I verified the tests fail for the right reasons by mutation: flipping the
resolveBareSpecifiersAsPackagesdefault totruefails exactly the default-behavior guard and nothing else, and reverting the resolver change fails exactly the four fix-targeting tests — reproducingUnexpected tilde in URL: ~plain-styles— while the guards stay green.Because
node_modules/is gitignored, the package fixture tree is generated at test time under the project'stemp/test/, so snapshots stay checkout-independent.Docs
README options table plus a rewritten "Sass import resolution" section documenting the resolution order, why bare specifiers are not resolved as packages by default, and when to prefer
pkg:.templates/sass.jsongains commented entries for both options.