[typings-generator] Emit declaration source maps for generated typings - #5907
Open
mikedelgaudio wants to merge 1 commit into
Open
[typings-generator] Emit declaration source maps for generated typings#5907mikedelgaudio wants to merge 1 commit into
mikedelgaudio wants to merge 1 commit 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.
mikedelgaudio
requested review from
apostolisms,
bmiddha,
dmichon-msft,
iclanton,
jxanthony and
octogonz
as code owners
July 29, 2026 18:08
Author
|
@microsoft-github-policy-service agree company="Microsoft" |
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 #5906
Generated typings are merged into the source tree via the TypeScript
rootDirsoption, so thelanguage service only ever sees the generated
.d.ts. As a result, "go to definition" on alocalized string — or on its import specifier — navigates to the generated declaration file instead
of the
.resxthat actually declares the string. The generated file is a build artifact, so this isa dead end for the developer.
This adds opt-in declaration source map (
.d.ts.map) emit to@rushstack/typings-generator, whichis the mechanism TypeScript already uses to redirect "go to definition" from a
.d.tsto realsource. With it enabled, alt-clicking a localized string opens the
.resxon the<data name="...">element that declares it.The feature is off by default and no map is emitted unless it is enabled and the parser supplies
source positions, so existing behavior is unchanged.
Details
The change is made in
typings-generatorrather than in each consuming plugin, because thegenerator composes its output line by line and therefore already knows the exact position of every
declaration it emits. Solving it downstream would require re-parsing the generator's own output with
a regex, which would silently break whenever the emit format changed.
Scope note: this PR wires the feature up for localization typings only.
@rushstack/heft-static-asset-typings-pluginalready builds ontypings-generator, so it should beable to adopt this by supplying positions and exposing the option.
@rushstack/heft-sass-pluginis a separate case — it emits its own.d.tsdirectly fromSassProcessorand does not depend ontypings-generator, so SCSS would need a follow-up change.serializeDeclarationMapis exported so that plugin can reuse the encoder rather than reimplementit. I'm happy to send that follow-up if the approach here looks right.
@rushstack/typings-generatorDeclarationMap.ts(source map v3 + Base64 VLQ encoding) andserializeDeclarationMap. New opt-ingenerateDeclarationMapsoption onITypingsGeneratorBaseOptions. Parsers may now returnIGeneratedTypings(typings text plus declaration positions) instead of a barestring.StringValuesTypingsGeneratorrecords each declaration's position as it emits the line.@rushstack/localization-utilitiesILocalizedString.sourcePosition, populated byparseResxfrom thexmldocelement.@rushstack/heft-localization-typings-plugingenerateDeclarationMapsoption, plus its config schema entry.Notes for reviewers:
string | undefinedtostring | IGeneratedTypings | undefined, so existing parsers that return a string continue tocompile and behave identically.
sourcePositionandgenerateDeclarationMapsare both optional.parseResxnow always reportssourcePosition, regardless of whether declaration maps areenabled. This keeps the parser free of any dependency on generator configuration, at the cost of
adding an optional field to its output (hence the snapshot update below). Happy to gate it behind
an option instead if you would prefer the parsed output to stay unchanged by default.
back to the source is correct for secondary folders, not just the primary one.
//# sourceMappingURL=comment is emitted rather than relying on tsserver'sadjacent-file probing. This is deliberate — it makes resolution unambiguous — but it does mean the
comment appears in the generated output, so please flag it if you would prefer the implicit form.
column: 0is used for.resxpositions.xmldocreportscolumnat the end of theelement's open tag rather than its start (verified empirically), so the start of the line is used
instead. The line number is exact, which is what determines where the editor lands.
How it was tested
Validated against
mainate9ed0088on Linux / Node 22.Added
DeclarationMap.test.ts— 7 new unit tests covering VLQ encoding, mapping serialization,multi-folder relative paths, and the disabled / no-positions cases.
rush buildandrush testare clean for all three packages:@rushstack/typings-generatorStringValuesTypingsGenerator, unmodified)@rushstack/localization-utilities@rushstack/heft-localization-typings-pluginSnapshot update:
parseResxnow reportssourcePositionfor each string, which changes theparsed output shape, so the 10 snapshots in
libraries/localization-utilities/src/parsers/test/__snapshots__/parseResx.test.ts.snapwereregenerated. The only delta in each is the added
sourcePositionobject — no values changed.End-to-end against a real
.resxusing the built library, then queried a live tsserver againsta fixture configured with
rootDirs:Verified the multi-folder case resolves to
../src/Strings.resxfor the primary output folder and../../src/Strings.resxfor a secondary one.Confirmed that with
generateDeclarationMapsunset, no.d.ts.mapis emitted and the generated.d.tsis byte-identical to before this change.Impacted documentation
heft-localization-typings-plugingains agenerateDeclarationMapsconfig option, so the plugin'sconfiguration docs would need a new entry. The JSON schema in
src/schemas/heft-localization-typings-plugin.schema.jsonhas been updated in this PR.rush changefiles are included for all three packages, and the API review files(
common/reviews/api/typings-generator.api.md,localization-utilities.api.md) are updated.