ezolenko/rollup-plugin-typescript2

Declarations not generated for type-only files not explicitly specified in `tsconfig`

slavafomin opened this issue · 9 comments

Hello, again!

I've noticed the following behavior: TypeScript declarations are not generated for source modules from which only types are imported and which are not explicitly added to the project using files or include options in tsconfig.json.

Project configuration

// tsconfig.json
{
  "compilerOptions": {
    "outDir": "dist/",
    "declarationDir": "dist/types/",
    "declaration": true
  },
  
  // specifying only entry-point
  "files": ["src/index.ts"]
}
// rollup.config.js

import typeScriptPlugin2 from 'rollup-plugin-typescript2';

export default {
  input: 'src/index.ts',
  output: {
    file: 'dist/index.js',
    format: 'esm',
  },
  plugins: [
    typeScriptPlugin2({
      verbosity: 2,
      clean: true,
      useTsconfigDeclarationDir: true,
    }),
  ],
};

Source files

// src/index.ts

// importing only type symbol from './foo.ts'
import { Foo } from './foo';

export class Main {
  constructor(foo: Foo) {
    console.log(foo);
  }
}
// src/foo.ts

// type symbol
export type Foo = 'foo';

// constant symbol (not imported)
export const defaultFoo: Foo = 'foo';
// src/unused.ts

export type Unused = 'unused';

Generated files

// dist/index.js

var Main = /** @class */ (function () {
    function Main(foo) {
        console.log(foo);
    }
    return Main;
}());

export { Main };
// dist/types/index.d.ts

// Error: TS2307: Cannot find module './foo'.
import { Foo } from './foo';

export declare class Main {
    constructor(foo: Foo);
}
  • the dist/types/unused.d.ts is not generated, which is GOOD.
  • however, the dist/types/foo.d.ts is not generated, which is BAD.

And if I would set "include": ["src/"], the dist/types/unused.d.ts would be generated, however, it shouldn't, because it's not imported anywhere.


Our goal here is to specify only the entry point of our project and let the compiler to handle only source files that are actually used in the project and avoid processing, generating declarations and reporting errors for files that are not used (redundant).

That's supposed to work after #175, mostly anyway.

Could you run it with verbosity 4 and see if it correctly finds files to import and correctly ignores files from tsconfig that are not imported by anything.

You should see what imports what when and things like skipping declarations for unused 'file' or generating missed declarations for 'file' or emitting declarations for 'file'.

Sure, here's the output:

$ rm -rf ./dist; npx rollup -c ./rollup.config.js

loaded rollup.config.js with warnings
(!) Unused external imports
default imported from external module 'rollup-plugin-typescript' but never used

src/index.ts → dist/index.js...
rpt2: built-in options overrides: {
    "noEmitHelpers": false,
    "importHelpers": true,
    "noResolve": false,
    "noEmit": false,
    "inlineSourceMap": false,
    "outDir": "…/ts-gen-types/node_modules/.cache/rollup-plugin-typescript2/placeholder",
    "moduleResolution": 2,
    "allowNonTsExtensions": true,
    "module": 5
}
rpt2: parsed tsconfig: {
    "options": {
        "outDir": "…/ts-gen-types/node_modules/.cache/rollup-plugin-typescript2/placeholder",
        "declarationDir": "…/ts-gen-types/dist/types",
        "declaration": true,
        "configFilePath": "…/ts-gen-types/tsconfig.json",
        "noEmitHelpers": false,
        "importHelpers": true,
        "noResolve": false,
        "noEmit": false,
        "inlineSourceMap": false,
        "moduleResolution": 2,
        "allowNonTsExtensions": true,
        "module": 5
    },
    "fileNames": [
        "…/ts-gen-types/src/index.ts"
    ],
    "typeAcquisition": {
        "enable": false,
        "include": [],
        "exclude": []
    },
    "raw": {
        "compilerOptions": {
            "outDir": "dist/",
            "declarationDir": "dist/types/",
            "declaration": true
        },
        "files": [
            "src/index.ts"
        ],
        "compileOnSave": false
    },
    "errors": [],
    "wildcardDirectories": {},
    "compileOnSave": false,
    "configFileSpecs": {
        "filesSpecs": [
            "src/index.ts"
        ],
        "excludeSpecs": [
            "dist/",
            "dist/types/"
        ],
        "validatedExcludeSpecs": [
            "dist/",
            "dist/types/"
        ],
        "wildcardDirectories": {}
    }
}
rpt2: typescript version: 3.8.3
rpt2: tslib version: 1.10.0
rpt2: rollup version: 1.32.0
rpt2: rollup-plugin-typescript2 version: 0.26.0
rpt2: plugin options:
{
    "verbosity": 4,
    "clean": true,
    "useTsconfigDeclarationDir": true,
    "check": true,
    "cacheRoot": "…/ts-gen-types/node_modules/.cache/rollup-plugin-typescript2",
    "include": [
        "*.ts+(|x)",
        "**/*.ts+(|x)"
    ],
    "exclude": [
        "*.d.ts",
        "**/*.d.ts"
    ],
    "abortOnError": true,
    "rollupCommonJSResolveHack": false,
    "tsconfigOverride": {},
    "transformers": [],
    "tsconfigDefaults": {},
    "objectHashIgnoreUnknownHack": false,
    "cwd": "…/ts-gen-types",
    "typescript": "version 3.8.3"
}
rpt2: rollup config:
{
    "chunkGroupingSize": 5000,
    "experimentalCacheExpiry": 10,
    "external": [],
    "inlineDynamicImports": false,
    "input": "src/index.ts",
    "perf": false,
    "plugins": [
        {
            "name": "rpt2"
        },
        {
            "name": "stdin"
        }
    ],
    "strictDeprecations": false
}
rpt2: tsconfig path: …/ts-gen-types/tsconfig.json
rpt2: included:
[
    "*.ts+(|x)",
    "**/*.ts+(|x)"
]
rpt2: excluded:
[
    "*.d.ts",
    "**/*.d.ts"
]
rpt2: transpiling '…/ts-gen-types/src/index.ts'
rpt2: generated declarations for '…/ts-gen-types/src/index.ts'
rpt2: generating target 1
rpt2: rolling caches
rpt2: emitting declarations for '…/ts-gen-types/src/index.ts' to '…/ts-gen-types/dist/types/index.d.ts'
created dist/index.js in 945ms

Yep, I see it...

The file shows up in allImportedFiles, but it is ignored by both rollup (because type-only) and tsconfig. tsc run still generates d.ts for it.

We need to somehow include such files in _onwrite, but exclude all the node_modules stuff. Might have to parse d.ts after all...

wegry commented

To get around this for now, I've been running yarn tsc --emitDeclarationOnly && yarn rollup -c to get the declaration files without a runtime component and then use this plugin's output for everything else.

@wegry I've also been thinking about letting declarations to be generated in a separate process.

The above comment worked for me too! I've been having troubles running this command - rollup -c && npx dts-bundle-generator -o dist/index.d.ts dist/types/index.d.ts. It created the types under each file within my lib folder (located outside of the dist folder). Instead of in the typings folder. But running the declaration files separately done the trick. Thank you @wegry !

I wrote up a root cause analysis and solution proposal for this in #298 (comment).
Will need to a good bit of testing for the solution, but think I have a working approach outlined at least!

We need to somehow include such files in _onwrite, but exclude all the node_modules stuff. Might have to parse *.d.ts after all...

Per the proposed solution, I don't think we need to parse declaration files, but we do need to parse the import chain in rpt2 itself (as we can't rely on Rollup to find imports of type-only files), as mentioned in #280 (comment)

This has been fixed by #406 and released in 0.34.0 🎉

I've also pushed my test case project here:
https://github.com/slavafomin/rollup-plugin-typescript2--issue-211

I also confirmed that, with the files: ["src/index.ts"] configuration and rpt2 0.34.0, foo.d.ts gets generated and unused.d.ts is not created 👍