[Declarations] Merge identical namespace declarations created from inlining modules when importing a namespace as a type
Question
For example I have types.ts file with a large amount of types. And a few other modules that imports this file. The plugin duplicates the whole types.ts content for every module which imports it. Is this intended? The output bundle becomes enourmously large because of content duplicated many times.
No, that sounds odd, unless you're importing the types as namespaces or something like that, which can lead to scenarios where the namespaces must be inlined or split into separate modules with imports and exports due to how the internals of TypeScripts declaration system works.
In order for me to be able to investigate, please submit a repro, and I'll look into it when I have time.
https://github.com/HanabishiRecca/Discord-Slim
No, the code itself has no namespaces. Only ES modules as files.
Disable preserveModules in rollup.config.js to build a bundle.
I took a look at the source code, and it is in fact importing the types as namespaces here and there, which is what leads them to be inlined in namespace declarations. For example, inside the files actions.ts, events.ts, tools.ts, and index.ts:
import type * as types from './types';
Here the module is imported as a namespace. That will lead to all of the helpers from that module being inlined as part of that namespace.
The same happens with types from the module helpers.ts, for example inside actions.ts:
import type * as helpers from "./helpers";
To avoid the duplication, for now the best approach is to avoid importing the types as namespaces.
I do think it would make sense to see if these can be merged into single declarations to avoid the duplication where possible, so I'll rename this issue and mark it as a feature request.
To avoid the duplication, for now the best approach is to avoid importing the types as namespaces.
Unfortunately this leads to naming collisions. I've tried some other ts tools, some of them replacing * import by named imports automatically. But this makes declarations very messy with poor names like User$1. Very inconvinient for the library end user.
I do think it would make sense to see if these can be merged into single declarations to avoid the duplication where possible, so I'll rename this issue and mark it as a feature request.
That would be great. Other tools I've tried don't do this job properly.
Alright, I took a stab at this to see if we can avoid inlining the same module-/namespace declarations multiple times in the same bundles.
- When importing/exporting the same namespace under the same binding, only one is emitted
- When importing the same namespace under another binding, one is emitted, and it is then aliased under the correct binding with an
ImportEqualsDeclaration(to make it still behave as a namespace)
I tested these changes directly on your codebase, and it brings your declaration file down to 2906 lines, - so a pretty large reduction in size and redundancy.
However, even with these changes, there are still room for your declaration file to shrink, so I'll look a bit more into this. But looking at your code base, you're not always importing from types.ts or helpers.ts as a namespace. For example, in client.ts, you're importing User as a named imports:
import type { User } from './types';
However, that will certainly lead to duplication, as two separate declarations of User will be emitted - one from within the namespace declaration, and one outside of it. We can't just take the declaration of User from within the imported namespace and make that external to it and then alias it from within the namespace due to how TypeScript works internally. So there I would highly recommend you instead import the namespace and reference it via types.User for minimal redundancy.
Great 👍
So there I would highly recommend you instead import the namespace and reference it via types.User for minimal redundancy.
This is not a problem and can be done easily, thanks.