ezolenko/rollup-plugin-typescript2

Declaration is missing imported type when using `pnpm`?

ubbcou opened this issue · 11 comments

What happens and why it is wrong

When i want to using api-extractor to genderation my d.ts file, i found an error : InternalError: Internal Error: Unable to follow symbol for "Ref"

In the source code, I imported type. And then it lost in my rollup output code.

source code

// origin
import { ref, Ref } from 'vue'

export function useTmp() {
  const bl = ref(false) as Ref<boolean>
  return {
    bl
  }
}
// output
export declare function useTmp(): {
    bl: Ref<boolean>;
};

I think there should be something like this in the output code: import { Ref } from 'vue', but it didn't.

What should I do about it? Thanks.

Environment

Versions

    rollup: ^2.73.0 => 2.73.0 
    rollup-plugin-typescript2: ^0.31.2 => 0.31.2 
    typescript: ^4.6.4 => 4.6.4 

rollup.config.js

:
const typescript = require('rollup-plugin-typescript2')
const path = require('path')

export default {
  input: 'src/index.ts',
  external: ['vue'],
  output: {
    file: 'dist/index.js',
    format: 'esm',
    globals: {
      vue: 'Vue'
    }
  },
  plugins: [
    typescript({
      clean: true,
      verbosity: 3,
      check: false, // FIXED: https://github.com/ezolenko/rollup-plugin-typescript2/issues/234
      tsconfig: path.resolve(__dirname, 'tsconfig.json'),
      tsconfigOverride: {
        compilerOptions: {
          declaration: true,
          rootDir: path.resolve(__dirname),
        },
      },
    }),
  ]
}

tsconfig.json

:
{
  "compilerOptions": {
    "baseUrl": ".",
    "outDir": "dist",
    "sourceMap": false,
    "target": "es2016",
    "useDefineForClassFields": false,
    "module": "esnext",
    "moduleResolution": "node",
    "allowJs": true,
    "strict": true,
    "noUnusedLocals": true,
    "experimentalDecorators": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "removeComments": false,
    "jsx": "preserve",
    "lib": ["esnext", "dom"],
    "rootDir": "."
  },
  "include": [
    "src"
  ]
}

package.json

:
{
  "name": "tmp-rollup-api-extrator",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "rollup -c",
    "build:dts": "node ./scripts/dts"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@microsoft/api-extractor": "^7.24.0",
    "@types/node": "^17.0.33",
    "rollup": "^2.73.0",
    "rollup-plugin-typescript2": "^0.31.2",
    "typescript": "^4.6.4",
    "vue": "^3.2.33"
  }
}

plugin output with verbosity 3

:
> rollup -c

rpt2: built-in options overrides: {
    "noEmitHelpers": false,
    "importHelpers": true,
    "noResolve": false,
    "noEmit": false,
    "inlineSourceMap": false,
    "outDir": "C:\\Users\\ash\\Desktop\\tmp-rollup-api-extrator\\node_modules\\.cache\\rollup-plugin-typescript2/placeholder",
    "moduleResolution": 2,
    "allowNonTsExtensions": true
}
rpt2: parsed tsconfig: {
    "options": {
        "baseUrl": "C:/Users/ash/Desktop/tmp-rollup-api-extrator",
        "outDir": "C:\\Users\\ash\\Desktop\\tmp-rollup-api-extrator\\node_modules\\.cache\\rollup-plugin-typescript2/placeholder",
        "sourceMap": false,
        "target": 3,
        "useDefineForClassFields": false,
        "module": 99,
        "moduleResolution": 2,
        "allowJs": true,
        "strict": true,
        "noUnusedLocals": true,
        "experimentalDecorators": true,
        "resolveJsonModule": true,
        "esModuleInterop": true,
        "removeComments": false,
        "jsx": 1,
        "lib": [
            "lib.esnext.d.ts",
            "lib.dom.d.ts"
        ],
        "rootDir": "C:/Users/ash/Desktop/tmp-rollup-api-extrator",
        "declaration": true,
        "configFilePath": "C:/Users/ash/Desktop/tmp-rollup-api-extrator/tsconfig.json",
        "noEmitHelpers": false,
        "importHelpers": true,
        "noResolve": false,
        "noEmit": false,
        "inlineSourceMap": false,
        "allowNonTsExtensions": true
    },
    "fileNames": [
        "C:/Users/ash/Desktop/tmp-rollup-api-extrator/src/index.ts"
    ],
    "typeAcquisition": {
        "enable": false,
        "include": [],
        "exclude": []
    },
    "raw": {
        "compilerOptions": {
            "baseUrl": ".",
            "outDir": "dist",
            "sourceMap": false,
            "target": "es2016",
            "useDefineForClassFields": false,
            "module": "esnext",
            "moduleResolution": "node",
            "allowJs": true,
            "strict": true,
            "noUnusedLocals": true,
            "experimentalDecorators": true,
            "resolveJsonModule": true,
            "esModuleInterop": true,
            "removeComments": false,
            "jsx": "preserve",
            "lib": [
                "esnext",
                "dom"
            ],
            "rootDir": "C:\\Users\\ash\\Desktop\\tmp-rollup-api-extrator",
            "declaration": true
        },
        "include": [
            "src"
        ],
        "compileOnSave": false
    },
    "errors": [],
    "wildcardDirectories": {
        "c:/users/ash/desktop/tmp-rollup-api-extrator/src": 1
    },
    "compileOnSave": false
}
rpt2: typescript version: 4.6.4
rpt2: tslib version: 2.4.0
rpt2: rollup version: 2.73.0
rpt2: rollup-plugin-typescript2 version: 0.31.2
rpt2: plugin options:
{
    "clean": true,
    "verbosity": 3,
    "check": false,
    "tsconfig": "C:\\Users\\ash\\Desktop\\tmp-rollup-api-extrator\\tsconfig.json",
    "tsconfigOverride": {
        "compilerOptions": {
            "declaration": true,
            "rootDir": "C:\\Users\\ash\\Desktop\\tmp-rollup-api-extrator"
        }
    },
    "cacheRoot": "C:\\Users\\ash\\Desktop\\tmp-rollup-api-extrator\\node_modules\\.cache\\rollup-plugin-typescript2",
    "include": [
        "*.ts+(|x)",
        "**/*.ts+(|x)"
    ],
    "exclude": [
        "*.d.ts",
        "**/*.d.ts"
    ],
    "abortOnError": true,
    "rollupCommonJSResolveHack": false,
    "useTsconfigDeclarationDir": false,
    "transformers": [],
    "tsconfigDefaults": {},
    "objectHashIgnoreUnknownHack": false,
    "cwd": "C:\\Users\\ash\\Desktop\\tmp-rollup-api-extrator",
    "typescript": "version 4.6.4"
}
rpt2: rollup config:
{
    "external": [
        "vue"
    ],
    "input": "src/index.ts",
    "plugins": [
        {
            "name": "rpt2"
        },
        {
            "name": "stdin"
        }
    ],
    "output": [
        {
            "file": "dist/index.js",
            "format": "esm",
            "globals": {
                "vue": "Vue"
            },
            "plugins": []
        }
    ]
}
rpt2: tsconfig path: C:/Users/ash/Desktop/tmp-rollup-api-extrator/tsconfig.json
rpt2: included:
[
    "*.ts+(|x)",
    "**/*.ts+(|x)"
]
rpt2: excluded:
[
    "*.d.ts",
    "**/*.d.ts"
]
rpt2: not cleaning C:\Users\ash\Desktop\tmp-rollup-api-extrator\node_modules\.cache\rollup-plugin-typescript2/placeholder
rpt2: �[34mtranspiling�[39m 'C:\Users\ash\Desktop\tmp-rollup-api-extrator\src\index.ts'
rpt2: �[34mgenerated declarations�[39m for 'C:/Users/ash/Desktop/tmp-rollup-api-extrator/src/index.ts'
rpt2: generating target 1
rpt2: �[34mrolling caches�[39m
rpt2: �[34memitting declarations�[39m for 'C:/Users/ash/Desktop/tmp-rollup-api-extrator/src/index.ts' to 'src/index.d.ts'

Thanks for providing a minimal repro!

That is a strange output error... DTS generation is done entirely by the TS LanguageService too...

Does this code type-check with tsc?
(Since check: false is set, it's possible there's a type-error that's ignored)

A workaround I might suggest is to use import type for the type specifically, which may get it to work.
(Rollup doesn't see type-only imports as they don't emit any JS, so it's possible a tree-shake occurred before the DTS generation?)

image

check: true and use import type. The result is the same, but the error is checked in advance

Is there a type-error or does it type-check fine?

I'm also curious if tsc emits the same broken DTS file, or if tsc's output is correct

Oh, I'm correcting my story. Exporting semantic error TS2305: Module '"vue"' has no exported member 'ref' when check: true and when i pnpm run build.

tsc's output is wrong. And I'm just using tsc without any arguments. I don't know if that's the right way to use it

Also seems a bit reminiscent of #274 (which has a workaround: #274 (comment)), as well as jaredpalmer/tsdx#967 (which also uses pnpm) and jaredpalmer/tsdx#796

First: declare twice include is doesn't work

    typescript({
      useTsconfigDeclarationDir: true,
      clean: true,
      // verbosity: 3,
      check: true, // FIXED: https://github.com/ezolenko/rollup-plugin-typescript2/issues/234
      tsconfig: path.resolve(__dirname, 'tsconfig.json'),
      tsconfigOverride: {
        compilerOptions: {
          declaration: true,
          rootDir: path.resolve(__dirname),
        },
        include: [path.resolve(__dirname, 'src')],
      },
      include: [path.resolve(__dirname, 'src')],
    }),

image

And I don't understand the other issues clearly.. :(

And I'm just using tsc without any arguments. I don't know if that's the right way to use it

Should be fine without arguments given your tsconfig.json.

tsc's output is wrong

If you mean that tsc outputs the same broken DTS files with the same missing import, then there's a good chance that this is an issue upstream in TS itself.

That being said, looking at your type-errors, this might be "correct" behavior as the import can't be resolved and so TS's behavior is kind of undefined in that case -- the type-error needs to be fixed for it work properly.

Exporting semantic error TS2305: Module '"vue"' has no exported member 'ref' when check: true and when i pnpm run build.

Given this error, it sounds like either Vue or its typings couldn't be found / resolved. Which would explain why tsc has the same type-error and probably why tsc gives the same output.

Normally, the module and its typings are found in node_modules, but with pnpm (or Yarn PnP etc) it's a little bit different... so I suspect that might be the cause here.

First: declare twice include is doesn't work

Thanks for checking this!

And I don't understand the other issues clearly.. :(

No worries -- they're more a note to me and other maintainers of similar issues encountered, which could be useful for figuring out the root cause. In this case, the use of pnpm seems to be a common pattern in some of these issues.

Let me investigate this a bit more. I'd like to see how NPM vs. PNPM usage may differ, some moduleResolution tests (since Vue 3 uses Node's ESM support), and do some tracing. Your repo is a great minimal reproduction of the case, which is very helpful for diagnosing root cause!
It's very possible this is an upstream TS issue with regard to resolution and pnpm; can attempt a fix upstream if I figure out what's going on here as I've read through TS's module resolution code before (though the nodenext changes have made it a lot more complicated)

Ok, I did a quick look and was able to get tsc working fine with pnpm. See my fork and commit of your repo here.
I explained in the commit message the various tsconfig.json settings I changed to get that to work as expected and why rpt2 is still failing.

So now can confirm that this is not an upstream TS issue, and has to do with how pnpm's node_modules are being resolved with Rollup or rpt2.
Notably, as I wrote in the commit message, if you use npm instead, rpt2 will work correctly. I.e. rm -rf node_modules/ && npm install && npm run build will output correctly.
So pnpm resolution is the central issue here, meaning this is a duplicate of #234 basically.

I'll see if I can figure anything out in this minimal repro and will respond here if I can, but going to close this as a duplicate as such.

Worked through your reproduction (and the source code of the TS compiler) and managed to find a fix for this long-standing issue with pnpm monorepos!

See #332 . I actually tested it locally against your repro and confirmed that there was no type-error anymore (so check: false workaround is no longer necessary) and that the declaration file had correct output with import { Ref } from 'vue'.

Thanks for the very minimal repro again -- really shows the value of MCVEs! 🙂

Once that's merged and released, this should be fixed. I'll update here when it's released.

Released in 0.32.0 🎉

Thank you very much!!! 😊