[typings-generator] Emit declaration source maps for generated typings - #5907
Conversation
|
@microsoft-github-policy-service agree company="Microsoft" |
ce4dd1d to
8c5c7d4
Compare
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.
8c5c7d4 to
8045531
Compare
|
Good call — done, and it simplified things more than I expected. Switched to I also replaced the decoder in the unit test with the library's
Verified: 26/26 tests pass, and the end-to-end tsserver check still resolves Two notes in case they matter for how you'd like this to land:
|
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.