rollup-plugin-typescript2: Declaration generated in wrong folder when using object as `input` (multi-entry)
What happens and why it is wrong
Possibly this is a misconfiguration on my end, but it looks like a bug. When you use an object
as the input
property for the rollup configuration, the typings file for the input files are generated in the root folder of the repo (same folder as the rollup config file) and not the target output directory (here, ./dist
) along with the bundled JS file.
Versions
- typescript: 3.2.4
- rollup: 1.1.2
- rollup-plugin-typescript2: 0.19.2
rollup.config.js
{
...
input: {
Lib1: './src/Lib1.tsx',
Lib2: './src/Lib2.tsx'
},
output: {
dir: './dist',
format: 'cjs',
sourcemap: true,
entryFileNames: '[name].js'
}
}
tsconfig.json
{
...
"compilerOptions": {
"outDir": "./dist"
},
}
Result:
// These files are created
./Lib1.d.ts
./Lib2.d.ts
// instead of (expected):
./dist/Lib1.d.ts
./dist/Lib2.d.ts
Curiously, if you enter entryFileNames: 'dist/[name].js'
it creates a superfluous subfolder called dist
within the dist
folder and creates them there.
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 14
- Comments: 35 (11 by maintainers)
Commits related to this issue
- - not generating typings for files that are not actually imported #162, #136 — committed to ezolenko/rollup-plugin-typescript2 by ezolenko 5 years ago
Yup. that seems to be the only way unless you let
tsc
do the bundling end-to-end. These rollup plugins seem to be effecively asking tsc to do two different tasks:The results of 1 and 2 don’t, and can’t match when you’re using an “input map” and code-splitting, etc. – AFAICT.
…unless you make the rollup plugin a whole lot more involved in the type-declaration generation process.
Thing is, I’m exporting a set of standalone modules (
import tool1 from 'myTools/tool1';
etc…) so there’s no single entry point to use inpkg.types
. Each definition file must reside next to its*.js
counterpart for VSCode to pick it up.Worse still, if I have
I get:
And now there’s no way for TypeScript to pair up
dist/fooscript.js
anddist/foo/index.d.ts
.…
Is there no way your plugin could feed TypeScript the destination/output path for each JavaScript bundle file? It seems that tsc is receiving the original entry filename and holding on to it tight when generating the declarations. …or for each of the input files hunt for the corresponding
*.d.ts
file and move+rename it to the input-file map destination. (You may notice I am talking out of my ass here, so… 😉This is something I’m bound to try manually after Rollup has finished - but it would be so much nicer if the plugin would handle it automatically.
For any struggling soul out there that has the same src folder structure as @maranomynet and me.
I came across a quick fix for it, may be another ugly way but works for me.
I’m using rollup-plugin/typescript2 so I’ll go with its config in this answer: Define your own declarationDir in your tsconfig.json so your project can dump all *.d.ts files in its own path in the bundled dist. And make sure to set the useTsConfigDeclarationDir to true in your typescript plugin in rollup config file. Also, where you define the output paths for your individual component bundles in your rollup.config.js (and package.json), change those paths to be the same as your ‘declarationDir’ + ‘how your src component route is’. So if your component in src is like:
And your declarationDir is:
So your output path needs to be like:
This way, rollup will include your types in the same directory as your component main.js and your TS consumer project will pick up the typings.
So the bundle will look something like:
Moreover it seems like it creates declaration files for every .ts file it finds, not just the entry points.
I’ve tried scoping
options.tsconfigOverride.input
to just the entry points, but that seems to have no effect whatsover. In fact I’m not even sure iftsconfigOverride.input
works at all.This is all a bit confusing.
…
I mean, fair enough if it needs to create a declaration file for
utils/bar-helper.ts
but at leastwat.ts
should be left out of the whole thing.My custom workaround build script (see above) is gradually creeping into more and more of my projects. o_O
That would work, yeah.
I have a couple of ideas (when I get around to them), for example generating
<bundlename>.d.ts
that has bunch of/// <reference types=""/>
for each d.ts involved.First I might have to fix related bug of generating too many type declarations and limit that to only root file and anything it actually imports.
Hi again. For now, I’m setting a custom
declarationDir
fortsc
to dump all the*.d.ts
into, and then run a standalone script that imports the same entry-file-to-output-file object as I feed to Rollup, and it generates bundle-destination-level declaration files that simplyexport * from
the actual declaration file.Essentially, I generate
dist/fooscript.d.ts
which contains only:It’s an ugly standalone hack, but it provides predictably correct results.
I wonder if
rollup-plugin-typescript2
could do something similar?No, plugin uses
LanguageService
(same typescript API IDEs are usually using), so I have some control on what to write and where.To write one type definition per rollup input we’ll need to merge definitions for all imports for that input. Rollup might be seeing one input ts file, but it could be importing and bundling any number of source files when building that, those need types generated as well.
Currently I’m erring on the side of generating definitions for everything typescript finds when looking at tsconfig file. If I generate types only for transpiled modules, type only files will be ignored.
I might have to revisit that part, API changed considerable since that part was done…
I’ll have to look at how to make your example work. Might need better support for multiple bundles in one rollup config.
How do you consume that package after building it though? It is not something you can publish on npm and import by package name…
Ah yeah that’s sort of what I figured, thanks! Gave you a 👍 for your helpful answer, and the 👎 is for how I feel about it.
I updated the example above, as it seems that default exports have to be explicitly re-exported.
Preliminary testing indicates that blindly re-exporting
default
from a declaration file that has nodefault
export is harmless, and silently ignored. (At least by VSCode.)A few thoughts:
IMO, the only truly ugly part of my hack is the fact that it stands apart from the Rollup process. The intermediary
*.d.ts
are actually a pretty neat and idiomatic way to bridge between Rollup’s generated bundles andtsc
’s definitions.Since the
tsc
-generated declaration files reference each other via relative paths, that file/folder structure is probably best left alone – lest you end up re-implementing parts of Rollup’s functionality, for a custom language syntax.And if
rollup-plugin-typescript2
sets something likeoutput.dir + '__types'
as the defaultdeclarationDir
, then any mess caused bytsc
’s over-eagerness is neatly swept out-of-sight inside a clearly-named folder. That way the extraneous definition files become a very minor concern IMO.FWIW, here’s the meat of my script:
…I’ve been playing with
tsc
on the commandline a bit now, and I think I see now the limitations you’re dealing with.I noticed, however, that if you run tsc and set the module/output format to either
"amd"
or"system"
, it generates a single*.d.ts
file with a neat list of all the necessarydeclare module "..."
blocks inlined.I wonder if this behavior could be exploited somehow - via file rename/move and unwrapping/rewriting the last (main) module declaration?
Side note: I also notice that
tsc
doesn’t seem to put much effort into tree-shaking that declaration file. In one case I see a main (entry point) module that exports a single, very simple, function signature, and yet the declaration file exposes declaration blocks for all the private modules used “behind the scenes”. Funny. 😃The full scenario where this breaks down is: src/dir1/foo.ts src/dir2/foo.ts
dir1/foo.ts us set as rollup input and it imports dir2/foo.ts. Rollup will create dist/foo.js bundle that will contain relevant parts of both source files, but typescript needs to create 2 type definition files with the same name somewhere.
Easy solution is to use subfolders, mirroring source layout. I think that’s what
tsc
does as well, unless you tell it to merge type definitions (rpt2 doesn’t support definition merging).And yes, typescript will know where to find type definitions. So aside from being a bit messy, this shouldn’t be a problem. If you want to deploy package with types on npm, set
"types"
inpackage.json
to d.ts file for your entry point. Typescript will find things from there.Since it is a definition for
dist/foo.js
I had assumed it would end up next to it - just like the source map file – i.e. indist/foo.d.ts
.Will TypeScript know to pair up
dist/foo.js
anddist/lorem/foo.d.ts
?Yeah, it generates declarations for all files found by tsconfig, if you don’t want particular file touched, make sure it is excluded or not included in tsconfig. To see list of files found by typescript, run plugin with verbosity 3 and look for “parsed tsconfig” entry.
Deciding if subdirectories should be used based on uniqueness of the paths would be a nightmare in an evolving project, suddenly your typing would move to a subfolder only because you added a new file with the same name in a different folder.
Normally if you want pretty typings you will want to merge them into one file anyway (there was an npm module I keep forgetting the name of for that)
Works for me on 0.20.1 with
useTsconfigDeclarationDir: false
– d.ts files go into the folders specified inoutput.[].dir
. @benkeen could you try again?@AntonPilyak that is by design – if d.ts were going into one folder without sub paths, then if you had
subFolder1/one.ts
andsubFolder2/one.ts
, one of those would be overwriting another.@benkeen when using
useTsconfigDeclarationDir: true
plugin option you also need to havedeclarationDir
value intsconfig.json
This might be fixed by #142, released in 0.20.0