babel-plugin-rewire does not work in a Typescript project

2.7k Views Asked by At

Note: My goal it bring Rewire like functionally to the project. Be it using the Rewire package, babel-plugin-rewire or any other library that satisfies my goal. With that in mind, here is the detail:

I am trying to setup a new Typescript project with Mocha, and Chai. One of the unit tests require me to use rewire, which does not work with ES6 imports. So, I ended up using babel-plugin-rewire. But, I can't get it to work. For example, the following line:

    const jwtVerify = hello.__get__("hello");

Fails with TypeError: _get__(...).__get__ is not a function.

I have setup a minimalistic reproducible public repo here: https://github.com/naishe/typescript-babel if you want to play with it.

Here is the minimal project setup:

src/hello.ts

export default function(name: string) {
  return `Hello ${name}`;
}

function privateHello(name: string) {
  return `Not exported Hello ${name}`;
}

test/index.spec.ts

import hello from "../src/hello";
import { expect } from "chai";

describe("Typescript + Babel usage suite", () => {
  // This works!
  it("should return string correctly", () => {
    expect(hello("mocha")).to.be.equal("Hello mocha");
  });

  // These fail
  it("should check if jwtVerify function exists", () => {
    //@ts-ignore
    const jwtVerify = hello.__get__("hello");
    expect(jwtVerify).to.be.a("function");
  });

  it("should check if private function exists", () => {
    //@ts-ignore
    const privateHello = hello.__get__("privateHello");
    expect(privateHello).to.be.a("function");
  });

});

test/babel-register.js

const register = require('@babel/register').default;

register({ extensions: ['.ts', '.tsx', '.js', '.jsx'] });

babelrc.json

{
  "plugins": ["rewire"]
}

babel.config.js

module.exports = (api) => {
  // Cache configuration is a required option
  api.cache(false);

  const presets = [
    "@babel/preset-typescript",
    "@babel/preset-env"
  ];

  return { presets };
};

mocharc.json

{
  "extension": ["ts"],
  "spec": "test/**/*.spec.ts",
  "require": "test/babel-register.js"
}

Relevant part of package.json

"scripts": {
    "test": "mocha"
  },
  "devDependencies": {
    "@babel/cli": "^7.12.10",
    "@babel/core": "^7.12.10",
    "@babel/preset-env": "^7.12.11",
    "@babel/preset-typescript": "^7.12.7",
    "@babel/register": "^7.12.10",
    "@types/chai": "^4.2.14",
    "@types/mocha": "^8.2.0",
    "@typescript-eslint/eslint-plugin": "^4.13.0",
    "@typescript-eslint/parser": "^4.13.0",
    "chai": "^4.2.0",
    "eslint": "^7.17.0",
    "mocha": "^8.2.1",
    "ts-node": "^9.1.1",
    "typescript": "^4.1.3"
  },
  "dependencies": {
    "babel-core": "^6.26.3",
    "babel-plugin-rewire": "^1.2.0"
  }

npm test emmits this:

  Typescript + Babel usage suite
    ✓ should return string correctly
    1) should check if jwtVerify function exists


  1 passing (4ms)
  1 failing

  1) Typescript + Babel usage suite
       should check if jwtVerify function exists:
     TypeError: _get__(...).__get__ is not a function
      at Context.<anonymous> (test/index.spec.ts:10:29)
      at processImmediate (internal/timers.js:456:21)

I have raised the concern on the babel-plugin-rewire, but it seems to be woefully silent. So, I am wondering if there is any other way to achieve this?

2

There are 2 best solutions below

4
Zach On

You need to just state the export name, as the function. Because you imported it as:

import hello from "../src/hello";

Just use:

const jwtVerify = hello;
expect(jwtVerify).to.be.a("function");

The reason is because you are trying to see if the object returned is a function (and you exported a function). So you just need to check the whole import name.


Additional side note:

If hello.ts is made like this:

export default function(name: string) {
  return `Hello ${name}`;
}

then to call the function you just:

console.log(hello('John'));

output: Hello John

because in the way you exported it, the import is the function.


Edit after question update:

I'm not familiar with Rewire, but there's a related question on Using Rewire with TypeScript that might be helpful.

If you need to have Babel with Typescript, try using the configurations shown in Microsoft - Typescript-Babel-Starter to begin with to ensure they both are configured to understand each other.

Here's also a nice I found that speaks some more about it Typescript and Babel Article.

0
coffeeking On

I have encountered the same problem. It seems because of the private function must be called somewhere.

You can change src/hello.ts like this:

export default function(name: string) {
  return privateHello(name); //call privateHello here
}

function privateHello(name: string) {
  return `Not exported Hello ${name}`;
}

It can work in my case, but i don't know why. I doubt it may because of tree-shaking, but I disabled babel 'minified', 'compact' cattribute, it does not work.

So call the private function somewhere to make do.