Rollup-plugin-typescript2: Why are interface declarations removed ?

Created on 19 Jul 2017  Β·  19Comments  Β·  Source: ezolenko/rollup-plugin-typescript2

Hi,

I'm trying to move from webpack to rollup for a library and I have trouble getting declarations to work correctly.

It seems that interfaces declarations aren't exported, this is a problem when I have things like :

import { RenderedCell } from "../RenderedCell";

export default class MergedCell implements RenderedCell {
    // ...
}

I get the error that RenderedCell can't be found.
I worked in webpack, and I can't understand what's the difference in my configuration that broke this. but I seem to understand that it's normal with Rollup.

BTW. I'm using the latest rollup, rollup-plugin-typescript2 and rollup-plugin-uglify I can post my configurations if needed

Most helpful comment

For what it's worth, I just call tsc --emitDeclarationOnly after rollup, which overrides the declaration files into build folder.
in my package.json

"scripts": {
    "build": "rollup -c",
    "postbuild: "tsc --emitDeclarationOnly"
}

All 19 comments

Is this a runtime error, or typescript error? Could you post how you export the interface from RenderedCell itself?

The thing is that the file isn't generated at all, and it fails when using the library in another project.

Wait, are you expecting RenderedCell interface to be generated? From the code you posted typescript would expect file named RenderedCell.ts to exist one directory above current file and it would need to contain something like this:

export interface RenderedCell 
{ 
    // ...
}

Could you give more details on your project structure? (file tree, tsconfig, rollup config, etc)

yes I'm expecting it to be generated.

Here are my configurations :

running the build : rollup -c -f es -n sq-web-component-table -o dist/sq-web-component-table.es.min.js

I didn't put the whole tree because it's a quite big library and it's more noise than signal. But you can already see that many files aren't generated in the dist.

rollup.config.ts

import typescript from 'rollup-plugin-typescript2';
import uglify from 'rollup-plugin-uglify';
import { minify } from 'uglify-es';

export default {
    entry: './src/index.ts',
    dest: 'dist/sq-web-component-table.min.js',
    sourceMap: true,

    plugins: [
        typescript(),
        uglify({}, minify)
    ]
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "declaration": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "outDir": "dist"
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "dist",
    "node_modules",
    "**/*-test.ts"
  ]
}

RenderedCell.ts

import {RenderedElement} from "./RenderedElement";
import {RenderedRow} from "./RenderedRow";
export interface RenderedCell extends RenderedElement {
    isMergedCell(): boolean;
    getColspan(): number;
    setColspan(colspan: number): void;
    getParent(): RenderedRow;
}

File tree

.
β”œβ”€β”€ src
β”‚Β Β  β”œβ”€β”€ index.ts
β”‚Β Β  └── table
β”‚Β Β      β”œβ”€β”€ extensions
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ base
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ FirstTableExtension.ts
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ LastTableExtension.ts
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ SectionDispatcher.ts
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ SectionManager.ts
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── SizeChangeMonitor.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ InvalidateEvent.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ InvalidateSizesEvent.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ RenderedCell.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ RenderedElement.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ RenderedRow.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ RenderedSection.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ RenderedTable.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ SectionClickedEvent.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ support
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── ...
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ TableAttributes.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ TableExtensionChain.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ TableExtensionList.ts
β”‚Β Β      β”‚Β Β  └── TableExtension.ts
β”‚Β Β      β”œβ”€β”€ model
β”‚Β Β      β”‚Β Β  └── ...
β”‚Β Β      β”œβ”€β”€ TableRenderingOptions.ts
β”‚Β Β      β”œβ”€β”€ Table.ts
β”‚Β Β      β”œβ”€β”€ util
β”‚Β Β      β”‚Β Β  └── ...
β”‚Β Β      └── view
β”‚Β Β          └── ...
β”œβ”€β”€ dist
β”‚Β Β  β”œβ”€β”€ index.d.ts
β”‚Β Β  β”œβ”€β”€ sq-web-component-table.es.min.js
β”‚Β Β  β”œβ”€β”€ sq-web-component-table.es.min.js.map
β”‚Β Β  β”œβ”€β”€ sq-web-component-table.min.js
β”‚Β Β  β”œβ”€β”€ sq-web-component-table.min.js.map
β”‚Β Β  └── table
β”‚Β Β      β”œβ”€β”€ extensions
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ base
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ FirstTableExtension.d.ts
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ LastTableExtension.d.ts
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ SectionDispatcher.d.ts
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ SectionManager.d.ts
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── SizeChangeMonitor.d.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ InvalidateEvent.d.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ InvalidateSizesEvent.d.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ RenderedRow.d.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ SectionClickedEvent.d.ts
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ support
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── ...
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ TableAttributes.d.ts
β”‚Β Β      β”‚Β Β  └── TableExtensionList.d.ts
β”‚Β Β      β”œβ”€β”€ model
β”‚Β Β      β”‚Β Β  └── ...
β”‚Β Β      β”œβ”€β”€ Table.d.ts
β”‚Β Β      β”œβ”€β”€ util
β”‚Β Β      β”‚Β Β  └── ...
β”‚Β Β      └── view
β”‚Β Β          └── ...
β”œβ”€β”€ node_modules
β”‚Β Β  └── ...
β”œβ”€β”€ __tests__
β”‚Β Β  └── ...
β”œβ”€β”€ package.json
β”œβ”€β”€ rollup.config.js
└── tsconfig.json

Ah, I see. Is the lib opensource by any chance?

Try building with those options:

typescript({ verbosity: 2, clean: true }),

Check if all files you expect are being processed. If declarations suddenly start being generated then there is a cache problem somewhere.

If RenderedCell.ts is not mentioned in output (rpt2: transpiling '...'), increase verbosity to 3 and check if files that are supposed to import it are doing that. You should see something like

rpt2: resolving '../RenderedCell'
rpt2:     to '...'

If the problem is not obvious then, I'll add more logging somewhere.

I would love to, might be in the future :)

Thanks for the tricks, I will try this on monday.

If you set declaration: false in the compilerOptions section of your tsconfig and recompile, does the issue go away? If yes, is the error reported by TSC related to one of the files within the dist directory? If yes is your answer to both, does the error go away if you introduce some non-type logic to the file containing RenderedCell file such as an export of a simple function? If yes, then the reason is that the file containing RenderedCell is considered unused and Typescript won't ever prepare declarations for it.

If all of this applies to you, I see 2 options:

  • Stop generating declarations for your bundle.
  • Make sure that roll-up won't tree-shake the file away entirely by introducing changes to the file that isn't type related,. Yes, it's a hack and I hope we find a way to do better

Hi,

sorry for the long delay, I tried your tips and tricks but I still couldn't get the type definitions for interfaces generated.

I changed the configuration to typescript({ verbosity: 3, clean: true }).
In the output of the generated build there is not a single mention of the RenderedCell interface.

I'm 100% sure this file is needed, as the index.d.ts of the library directly requires it and all files but the interfaces are generated, but the interfaces are still in the imports of the index.d.ts file.

I will dive in the code to see if there is a particular reason why those files aren't generated.

@wessberg Stop generating declarations for my bundle is not an option as this is a library that is part of a bigger project all written in TypeScript, so if it doesn't have typings it doesn't work.

So, I made some investigation and created a repository that reproduces my case with a minimal set of configuration : https://github.com/onigoetz/typescript-rollup-experiment

I also included a webpack configuration to compare how webpack makes it work.

My understanding of the problem is that when rollup-plugin-typescript2 requires the compiled version of a file (let's say index.ts) Typescript will return the compiled files and it's imports, but without the interfaces in the imports.

That's why rollup never generates them : it's not aware of their existence.

To run the repository do an npm install && npm run build inside the directory.

If you add:
import "./SomeInterface"; (exactly like that, - without any named bindings) to your index.ts file, it should work. Does it?

Unfortunately, Rollup doesn't invoke the transform handler of plugins for declaration-only files such as files containing only type declarations. The thing above should work since that forces Rollup to evaluate the file. This issue: https://github.com/rollup/rollup-plugin-typescript/issues/28 explains how and why this issue exists. Theoretically, we could manually resolve all dependencies of your application from the entry point and determine those that only contain type declarations, but there is no way of forcing rollup to invoke the transform hook (beside other obvious downsides such as much added build-step complexity).

I'm afraid we can't do much about this without altering rollup itself. The "hack" I described should work for now though.

Yep, just checked, adding import "./SomeInterface"; in the test repo fixes things.

When importing types only, typescript elides import statements from js output and rollup doesn't see those files and thus doesn't send them for transpiling.

I'll try to cheat and monitor which files typescript requested from disk vs which files rollup sent for transpiling. Should be possible to force-transpile the difference with definitions only flag. This might result in too many d.ts files in some cases I though (for things rollup has legitimately shaken from the tree for example)

Thanks for looking into it, I will also check if I can make a contribution for this.

@onigoetz, ok, try your project with master. Check if right declarations are generated, but also check that no unexpected ones are :)

Awesome, you litteraly fixed it while I was sleeping :)

I tested it and it almost works fine.

There is just a small issue, but I think it's something we can't do anything against, the webpack loader does exactly the same and the file list is generated by TypeScript itself.

As an example; If I have three files in my repo :

  • index.ts importing SomeInterface.ts
  • SomeInterface.ts
  • nongenerated.ts importing SomeInterface.ts

nongenerated.ts is never referenced but its types are generated anyway.

Cool, thanks for testing. I'll release it a bit later after another issue is confirmed.

Yeah not much we can do when bypassing rollup. You should be able to exclude those files manually in exclude option, but that's a poor workaround.

Ok, in 0.5.1 now

work well ,just config tsconfig.json

{
    "compilerOptions": {
        "module": "es2015",
        "target": "es5",
        "declaration":true,
        "declarationDir": "lib",
        "typeRoots": [
            "node_modules/@six006",
            "node_modules/@types"
        ],
        "lib": [
            "es2015",
            "es2017",
            "es5"
        ],
    },
    "include": [
        "src/index.ts",
        "src/module/**/*.ts"
    ],
    "exclude": [
        "node_modules"
    ]
}

include section tell you what to generate

YMMV but I got it to work like this ...

before

export type MyType = { ... };

after

type MyType = { ... };

export { MyType }

For what it's worth, I just call tsc --emitDeclarationOnly after rollup, which overrides the declaration files into build folder.
in my package.json

"scripts": {
    "build": "rollup -c",
    "postbuild: "tsc --emitDeclarationOnly"
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

brandon-leapyear picture brandon-leapyear  Β·  7Comments

mikob picture mikob  Β·  4Comments

stakx picture stakx  Β·  6Comments

eddow picture eddow  Β·  14Comments

freeman picture freeman  Β·  6Comments