Rollup-plugin-typescript2: Resolve path alias in declaration files (d.ts)

Created on 1 Jan 2020  ·  16Comments  ·  Source: ezolenko/rollup-plugin-typescript2

When applying a path alias to a typescript project, the path is resolved in the emitted javascript files, but not in the declaration files.

The TypeScript team have explicitly stated that path resolution is not something that will be included in the compiler (ever) and is the domain of utilities like rollup/webpack.

Versions

  • typescript: 3.7.4
  • rollup: 1.27.14
  • rollup-plugin-typescript2: 0.25.3

In the following project I have aliased ~/* to ./src/*. Allowing for imports using absolute paths from the base directory.

https://github.com/alshdavid-sandbox/rollup-typescript-library

npm install && make
cat dist/a/index.d.ts | grep "~"

cat should print nothing, however it currently prints

import { B } from '~/b';

This illustrates that we are not applying path resolution to the declaration files.

bug controversial enhancement help wanted

Most helpful comment

Yeah, I am achieving this using ttsc as well. I was unable to get the transformers working directly with rollup-plugin-typescript2 transformers options, so I opted to use ttsc

My rollup config looks like:

import typescript from 'rollup-plugin-typescript2'

export default {
  input: `src/index.ts`,
  preserveModules: true,
  output: {
    format: 'esm',
    dir: './dist'
  },
  external: [],
  watch: {
    include: 'src/**',
  },
  plugins: [
    typescript({ 
      typescript: require('ttypescript'),
      tsconfigDefaults: {
        compilerOptions: {
          plugins: [
            { "transform": "typescript-transform-paths" },
            { "transform": "typescript-transform-paths", "afterDeclarations": true }
          ]
        }
      }
    }),
  ],
}

All 16 comments

According to typescript people, you are using it backwards:

Our general take on this is that you should write the import path that works at runtime, and set your TS flags to satisfy the compiler's module resolution step, rather than writing the import that works out-of-the-box for TS and then trying to have some other step "fix" the paths to what works at runtime.

https://github.com/microsoft/TypeScript/issues/15479

Currently this plugin doesn't post process or even parse type declarations once they are emitted by typescript.

It might be possible to do something slightly better than search/replace, but to solve this in general we'll basically need to reimplement rollup for type declarations (unless they already added language service API for generating bundled declarations while I wasn't looking)

Thanks for the reply. Is there a "post emit" hook that runs on both watch and build modes which could be used to attach a crude search/replace post processing step?

d.ts files are emitted directly into rollup pipeline (when useTsconfigDeclarationDir: false option is used), so you could make a rollup plugin that transforms them.

They get emitted when bundle is written though, so you might have to use generateBundle hook instead of transform.

If that doesn't work. I can add a pre-write hook to rpt2 itself.

Encounter the same issue, found a walkaround here
currently using ttsc with typescript-transform-paths to generate declaration files after rollup emitted ts files, kind of redundant but works...

Yeah, I am achieving this using ttsc as well. I was unable to get the transformers working directly with rollup-plugin-typescript2 transformers options, so I opted to use ttsc

My rollup config looks like:

import typescript from 'rollup-plugin-typescript2'

export default {
  input: `src/index.ts`,
  preserveModules: true,
  output: {
    format: 'esm',
    dir: './dist'
  },
  external: [],
  watch: {
    include: 'src/**',
  },
  plugins: [
    typescript({ 
      typescript: require('ttypescript'),
      tsconfigDefaults: {
        compilerOptions: {
          plugins: [
            { "transform": "typescript-transform-paths" },
            { "transform": "typescript-transform-paths", "afterDeclarations": true }
          ]
        }
      }
    }),
  ],
}

Thanks man @alshdavid ! You've saved my life! Yesterday I had the same issue and by using typescript-transform-paths I solved it! Thanks 💪

// webpack.config.js
module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        use: [
          {
            loader: require.resolve('ts-loader'),
            options: {
              compiler: 'ttypescript',
            },
          },
          {
            loader: require.resolve('react-docgen-typescript-loader'),
          },
        ],
        exclude: /(node_modules)/,
      },
    ],
  },
// tsconfig.json
compilerOptions: {
  "plugins": [
     { "transform": "typescript-transform-paths" },
     { "transform": "typescript-transform-paths", "afterDeclarations": true }
   ]
}

this solution has worked for me in the past but I'm finding many situations now where I get an error like this:

UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'text' of undefined

if I move away from using aliases (and these plugins) then it works without issue. Anyone else have this issue?

this solution has worked for me in the past but I'm finding many situations now where I get an error like this:

UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'text' of undefined

if I move away from using aliases (and these plugins) then it works without issue. Anyone else have this issue?

I have the same error

this solution has worked for me in the past but I'm finding many situations now where I get an error like this:

UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'text' of undefined

if I move away from using aliases (and these plugins) then it works without issue. Anyone else have this issue?

Hi, i'm issueing the same problem, but in a different context: I'm using rollup to bundle a components library written with Vue and Typescript. This is the rollup.config.js file:

import path from "path";

import alias from "@rollup/plugin-alias";
import cleaner from "rollup-plugin-cleaner";
import css from "rollup-plugin-css-only";
import scss from "rollup-plugin-scss";
import typescript from "rollup-plugin-typescript2";
import vue from "rollup-plugin-vue";

export default [
  {
    input: "./src/index.ts",
    preserveModules: true,
    output: {
      format: "esm",
      dist: "./dist"
    },
    plugins: [
      cleaner({ targets: ["dist/"] }),
      alias({
        resolve: [".ts", ".vue"],
        entries: { "@/": path.resolve(__dirname, "./src/") }
      }),
      css({ output: "dist/bundle.css" }),
      scss(),
      typescript({
        typescript: require("ttypescript"),
        tsconfigDefaults: {
          compilerOptions: {
            plugins: [
              { transform: "typescript-transform-paths" },
              {
                transform: "typescript-transform-paths",
                afterDeclarations: true
              }
            ]
          }
        }
      }),
      vue({ css: false })
    ]
  }
];

I already use the ttsc --emitDeclarationOnly command after the rollup --config one, in order to resolve aliases, but .vue.d.ts files generated by rollup are not processed (of course).

Same issue with ttypescript and typescript-transofrm-paths, it didn't work :disappointed. When running build:

[!] (plugin rpt2) TypeError: Cannot read property 'text' of undefined

But using @zerollup/ts-transform-paths and ttypescript it works 🎉

There is an awesome setup here: https://www.npmjs.com/package/@zerollup/ts-transform-paths#setup-for-rollup-plugin-typescript2

_rollup.config.js_

import ttypescript from 'ttypescript'
import tsPlugin from 'rollup-plugin-typescript2'

export default {
    input: 'src/lib.ts',
    output: [{ file : 'dist/lib.js', name : 'mylib', format : 'iife', sourcemap : true }],
    plugins: [
        tsPlugin({
            typescript: ttypescript
        })
    ]
}

_tsconfig.json_

{
    "compilerOptions": {
        "baseUrl": ".",
        "paths": {
            "my-lib/*": ["src/*"]
        },
        "plugins": [
            {
                "transform": "@zerollup/ts-transform-paths",
                "exclude": ["*"]
            }
        ]
    }
}

Great! I solved in a different way, but it is not related to this plugin. I removed typescript-transform-path and ttypescript and leaved current plugin as is (typescript()). I used tsc-alias after rollup to solve the problem. It replaces even vue files because it processes .d.ts files after their generation.

I used tsc-alias after rollup to solve the problem. It replaces even vue files because it processes .d.ts files after their generation.

@mantlebee Do you mind posting an example of your tsconfig.json as well as your rollup config?

But using @zerollup/ts-transform-paths and ttypescript it works 🎉

@luanorlandi doesn't ts-transform-path work at runtime? Probably not a great idea for library code.

I used tsc-alias after rollup to solve the problem. It replaces even vue files because it processes .d.ts files after their generation.

@mantlebee Do you mind posting an example of your tsconfig.json as well as your rollup config?

Important properties are declaration, declarationDir and outDir

// tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "declaration": true,
    "declarationDir": "dist/src",
    "baseUrl": ".",
    "outDir": "dist/src",
    "types": [
      "webpack-env",
      "jest"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

Simple rollup.config.js file

import path from "path";

import alias from "@rollup/plugin-alias";
import cleaner from "rollup-plugin-cleaner";
import scss from "rollup-plugin-scss";
import typescript from "rollup-plugin-typescript2";
import vue from "rollup-plugin-vue";

export default [
  {
    input: "src/index.ts",
    output: {
      format: "esm",
      file: "dist/elegere-ui.js"
    },
    plugins: [
      cleaner({ targets: ["dist/"] }),
      alias({
        resolve: [".ts", ".vue"],
        entries: { "@/": path.resolve(__dirname, "src/") }
      }),
      scss(),
      typescript({
        useTsconfigDeclarationDir: true
      }),
      vue()
      //{ css: false }
    ]
  }
];

Then I put everything into the prepublishOnly command (just because it's usefull to me)

"prepublishOnly": "npm run rollup && tsc-alias && npm run docs"

TSC-ALIAS will perform the replace inside the dist/src folder; I choose to put everything inside that because ts-alias will replace aliases thinking to be inside the src folder.

Not an optimal conclusion, but it works. I'll checkout the solution from @luanorlandi asap.

Of course, every other lib must be configured to avoid dist/src folder, like jest

But using @zerollup/ts-transform-paths and ttypescript it works 🎉

@luanorlandi doesn't ts-transform-path work at runtime? Probably not a great idea for library code.

I don't think so, the transformer is just for build step, as the library describes it:

_...tsconfig baseUrl + paths alias rewriting in bundles and declaration files. All them will be rewritted to relative in transpiled js and in d.ts files._

https://www.npmjs.com/package/@zerollup/ts-transform-paths

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vwxyutarooo picture vwxyutarooo  ·  15Comments

Kerumen picture Kerumen  ·  21Comments

PavaniVaka picture PavaniVaka  ·  12Comments

freeman picture freeman  ·  6Comments

jansiegel picture jansiegel  ·  9Comments