rollup-plugin-ts icon indicating copy to clipboard operation
rollup-plugin-ts copied to clipboard

Declaration maps do not always work

Open domoritz opened this issue 5 years ago • 5 comments

  • Version: 1.3.4
  • Rollup Version: 2.26.10
  • Operating System and version (if applicable): macOS
  • Node Version (if applicable): 14.9.0
  • Does it work with tsc (if applicable): yes

I might be missing something obvious but unfortunately, declaration maps stopped working for me when I switched to this plugin.

Reproduction

Check out https://github.com/vega/vega-tooltip/tree/fee07c92ce6b1914650a0daf881ef403249e6374 and run yarn && yarn build. Then link from another js module and run

import * as tooltip from 'vega-tooltip';

tooltip.formatValue;

Then use VSCode to follow formatValue.

Expected Behavior

I would expect to end up in https://github.com/vega/vega-tooltip/blob/fee07c92ce6b1914650a0daf881ef403249e6374/src/formatValue.ts#L11

Actual Behavior

VSCode opens up the .d.ts file.

I used to compile declarations with tsc and declaration maps worked fine.

domoritz avatar Sep 07 '20 18:09 domoritz

Hey there. As you may know, rollup-plugin-ts bundles declarations, and it does this as a TypeScript Custom Transformer that visits SourceFiles through multiple steps (the major ones being bundling/merging, deconflicting, and tree-shaking).

TypeScript itself then takes care of diffing the source and output ASTs for each SourceFile and applies transformations to the declaration maps that will be emitted.

It is not that declaration maps doesn't work generally - they do, and we have several tests for them - but unfortunately it is true that they can break quite easily as soon as symbols from various SourceFiles are merged together in one

The primary reason behind it is that for TypeScript to be able to properly trace, say, a Declaration (so it can apply transformations to the declaration maps), it must be able to see a connection between the Declaration that ends up in the merged declaration file all the way back to the place and position where it was originally declared. And, when merging/bundling Nodes from other files, we can't just reference them, we have to clone them (which is why we use @wessberg/ts-clone-node for that. More info here), so this is where that connection is broken, and TypeScript loses track of that Node.

I would love to find a way to work around this, and intuitively I think a solution will require manually generating declaration maps and ignoring those generated by TypeScript, but that won't do since other Custom Transformers may be provided that applies further transformations to the bundled declarations, and these changes too should carry over to the declaration maps. I'll keep this issue open for future reference. And I'm open to suggestions as to how this might be resolved.

For the time being, I recommend disabling declaration maps. Based on what I can see, declaration maps are generally not supported by other declaration bundlers either.

wessberg avatar Sep 09 '20 19:09 wessberg

Thanks for the explanation and I definitely appreciate the complexity of the problem.

I wonder whether it would make sense to provide an option to generate .d.ts and .d.ts.map files for all source files rather than bundling them? What's the benefit of bundling in development?

domoritz avatar Sep 09 '20 23:09 domoritz

I've been thinking about that too, yes. I'm open to the idea of providing an option to opt out of bundling and just fall back to TypeScripts normal declaration mechanism. There are some major gotchas with that approach though!

This plugin not only bundles declarations, but also fully supports code splitting of the declarations and more generally will respect the output structure and filenames produced by Rollup.

TypeScripts normal declaration system expects the source structure to be equivalent to the output structure, and so it is quite easy to end up in a situation where the definitions are placed in a different file than the actual value and then you have the problem of TypeScript suggesting importing from the wrong files and so on. For example, you may instruct Rollup to produce an output structure based on two entry chunks: a (pointing to src/foo.ts) and b (pointing to src/bar.ts). Rollup will then generate two bundles named a and b and split their common dependencies up into shared chunks. Now TypeScript, on the other hand, won't know anything about neither files called a and b nor any shared chunks, so it will complain when clients of that library attempts to import from either a or b.

So ideally, if we were to allow falling back to the built-in declarations system, we would have to do some work to at the very least rewrite import- and export specifiers to align at least the entry chunks with Rollup.

wessberg avatar Sep 10 '20 07:09 wessberg

Again, I appreciate the complexity here. Thank you for the deep dive into the details.

I personally don't need chunks for the library projects I am working on so I will stick with rollup-typescript2 for now and switch over to rollup-plugin-ts later.

domoritz avatar Sep 10 '20 16:09 domoritz

I noticed that declaration maps are no longer generated after typescript 4.2. Not sure if related to this plugin or typescript itself.

NullVoxPopuli avatar Jun 21 '22 02:06 NullVoxPopuli