Skip to content

[Feature]: Support declarationMap for "Go to Definition" navigation #3

@baldyeagle

Description

@baldyeagle

Prerequisites

  • I have searched existing issues to ensure this hasn't been requested
  • I have checked the documentation to ensure this feature doesn't exist

Problem Statement

When declarationMap is set to true in a tsconfig, tsc emits .d.ts.map files that support "Go to Definition" directly to the source files in IDEs.

In /src/typescript-compiler.ts, tsc or tsgo respects declarationMap and will create .d.ts.map files, if appropriate.

Then in /src/generate.ts, the map files are not included in bundledFiles and ultimately rm'd 👋 .

That means doing "Go to Definition" sends you to the appropriate generated .d.ts file instead of your source file.

Proposed Solution

I've implemented two patches in my codebase where I'm using dts: { inferTypes: true } in my bunup config. Apologies in advance for only working with dist instead of the source files.

Note

The bunup change works as-is, but the @bunup/dts change only covers inferTypes: true. The full solution would absolutely need to consider the branch that handles isolatedDeclarations.

If options.declarationMap is true, the map file should be passed along via the bundledFiles array with a dtsMap key. Here's a working patch for bunup in src/build.ts:

--- a/dist/shared/bunup-yf5cw3mx.js
+++ b/dist/shared/bunup-yf5cw3mx.js
@@ -613,6 +613,12 @@
           const pathRelativeToRootDir = cleanPath(`${options.outDir}/${pathRelativeToOutdir}`);
           const fullPath = path4.join(rootDir, pathRelativeToRootDir);
           await Bun.write(fullPath, file.dts);
+          if (file.dtsMap) {
+            const mapObj = JSON.parse(file.dtsMap);
+            const mapDir = path4.dirname(fullPath);
+            mapObj.sources = mapObj.sources.map((s) => path4.relative(mapDir, path4.resolve(rootDir, s)));
+            await Bun.write(`${fullPath}.map`, JSON.stringify(mapObj));
+          }
           buildOutputFiles.push({
             fullPath,
             pathRelativeToRootDir,

For @bunup/dts, we can check for declarationMap in the tsconfig options, pull in the declaration map, update the relative source paths, and append the source map link in the .d.ts file.

--- a/dist/index.js
+++ b/dist/index.js
@@ -1011,12 +1011,27 @@

 ${JSON.stringify(treeshakedDts.errors, null, 2)}`);
       }
+      let dts = options.minify ? minifyDts(treeshakedDts.code) : treeshakedDts.code;
+      let dtsMap;
+      if (entrypoint && tsCompiledDist && tsconfig.config?.compilerOptions?.declarationMap) {
+        try {
+          const tscMapPath = replaceExtension(path3.join(tsCompiledDist, entrypoint), ".d.ts") + ".map";
+          const tscMap = JSON.parse(await Bun.file(tscMapPath).text());
+          const tscMapDir = path3.dirname(tscMapPath);
+          tscMap.sources = tscMap.sources.map((s) => path3.relative(cwd, path3.resolve(tscMapDir, s)));
+          const dtsFileName = path3.basename(replaceExtension(outputPath, getDeclarationExtensionFromJsExtension(getExtension(output.path))));
+          tscMap.file = dtsFileName;
+          dtsMap = JSON.stringify(tscMap);
+          dts += `\n//# sourceMappingURL=${dtsFileName}.map\n`;
+        } catch {}
+      }
       bundledFiles.push({
         kind: output.kind === "entry-point" ? "entry-point" : "chunk",
         entrypoint,
         chunkFileName,
         outputPath,
-        dts: options.minify ? minifyDts(treeshakedDts.code) : treeshakedDts.code,
+        dts,
+        dtsMap,
         pathInfo: {
           outputPathWithoutExtension: deleteExtension(outputPath),
           ext: getExtension(outputPath)

It's possible a restructuring of the declaration branch could be done or some simplifications like setting declarationPath for both branches, which would limit the scope of the changes required for the declaration map feature.

Alternatives Considered

No response

Code Examples

API Design (if applicable)

Additional Context

No response

Priority

Medium - Would significantly improve my workflow

Contribution

  • I'm willing to submit a PR to implement this feature

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions