[heft-sass-plugin] Emit declaration source maps for generated typings - #5909
Open
mikedelgaudio wants to merge 2 commits into
Open
[heft-sass-plugin] Emit declaration source maps for generated typings#5909mikedelgaudio wants to merge 2 commits into
mikedelgaudio wants to merge 2 commits into
Conversation
Generated typings such as .resx and .scss declarations are merged into the source tree via "rootDirs", so the TypeScript language service only ever sees the generated .d.ts. Alt-clicking a localized string therefore navigates to the generated declaration rather than the file that declares it. Add opt-in declaration source map generation to TypingsGenerator. The generator already composes its output line by line, so it knows the exact position of every emitted declaration and does not need to parse its own output. The map is serialized per output folder so that the relative path back to the source is correct for secondary folders as well. StringValuesTypingsGenerator records those positions from the new optional IStringValueTyping.sourcePosition, parseResx populates it from the xmldoc element, and heft-localization-typings-plugin exposes a generateDeclarationMaps option. Parsers that do not supply positions are unaffected, and no map is emitted unless the feature is enabled and positions are available.
Sass typings are merged into the source tree via rootDirs, so the language service only sees the generated .d.ts and go-to-definition on a CSS module class stops there instead of opening the rule that declares it. Add an opt-in generateDeclarationMaps option that emits a .d.ts.map beside each generated typings file. Positions are obtained by recording where each class selector appears in the compiled CSS, before postcss-modules rewrites names, and translating that position back through the Sass source map. A class declared in an imported partial therefore resolves into that partial, and a class restated inside a media query still resolves to its top-level rule. The shared pieces live in typings-generator: serializeDeclarationMap now accepts multiple sources, and decodeMappings/originalPositionFor are exported for generators that compile their input. The Sass-specific helpers are exported from heft-sass-plugin so that other Sass typings generators can reuse them rather than reimplement the same chain.
mikedelgaudio
requested review from
apostolisms,
bmiddha,
dmichon-msft,
iclanton,
jxanthony and
octogonz
as code owners
July 29, 2026 22:38
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
Fixes #5908
Sass typings are written into a folder that
rootDirsmerges into the source tree, so the languageservice only ever sees the generated
.d.ts. "Go to definition" on a CSS module class thereforestops at the generated declaration instead of opening the rule that declares it.
This adds an opt-in
generateDeclarationMapsoption that emits a.d.ts.mapbeside each generatedtypings file. It is off by default, and when disabled the output is unchanged.
This is the Sass counterpart to #5906 / #5907, which cover localization typings. It is stacked on
#5907 — the shared declaration-map primitives live in
@rushstack/typings-generator, so thisbranch contains that commit and should merge after it.
Details
The interesting part is obtaining accurate positions. Sass is compiled before PostCSS runs, so
PostCSS positions refer to the compiled CSS rather than the stylesheet. The chain is:
before
postcss-modules(which rewrites class names).createDTSreports the line it emits each declaration on, so the generated side needs no parsingof its own output.
Because the lookup happens in compiled-CSS order, this gets two cases right that a text search over
the stylesheet cannot:
@imported partial resolves into that partial. It does not appear inthe importing file at all.
@mediaor theme block still resolves to its primary rule, with noindentation heuristics.
Shared vs. Sass-specific code
Per the two questions raised in #5908, and to avoid a third copy of this logic appearing downstream:
@rushstack/typings-generatorserializeDeclarationMapnow accepts multiplesources, andIDeclarationMappinggains an optionalsourceIndex— needed because Sass declarations can originate from several files. AddsdecodeMappingsandoriginalPositionForalongside the existing encoder.@rushstack/heft-sass-pluginSassDeclarationMaps.tswith the PostCSS recorder and the resolve-through-Sass-map step, exported from the package index so other Sass typings generators can reuse it. Adds a dependency ontypings-generator.Notes for reviewers:
serializeDeclarationMap'ssourcesparameter widens fromstringtostring | readonly string[], andsourceIndexdefaults to0, so existing callers areunaffected. The
heft-localization-typings-pluginpath is unchanged.sourceMapIncludeSourcesis not forced on. Enabling declaration maps requests a Sass sourcemap, but only embeds sources when
sourceMap: truewas already set, to avoid inflating output.typings-generatorhalf into its own PR if you would prefer to review theshared primitives separately from the Sass plugin.
How it was tested
Validated on Linux / Node 22.
rush buildandrush testclean forheft-sass-plugin,typings-generator, andlocalization-utilities.heft-sass-plugin: 60/60 tests pass, including 3 new cases that decode the emitted map andassert the resolved line:
_partial-with-class.scss, not the importing file;@mediaresolves to its primary rule, not the restatement;.d.ts.mapis emitted when the option is disabled.typings-generator: the 26 existing tests from [typings-generator] Emit declaration source maps for generated typings #5907 still pass unmodified, confirming themulti-source change did not regress the single-source path.
go-to-definition on a CSS module class resolved to the
.scssrule rather than the generated.d.ts.Impacted documentation
heft-sass-plugingains agenerateDeclarationMapsoption; the plugin's configuration docs wouldneed a new entry. The JSON schema is updated in this PR.
rush changefiles are included, andcommon/reviews/api/typings-generator.api.mdis updated.