aelbore / esbuild-jest

A Jest transformer using esbuild
519 stars 51 forks source link

Cannot transform the imported binding xxxxx since it's also used in a type annotation #57

Open davisford opened 3 years ago

davisford commented 3 years ago

I'm getting this error when trying to execute a Jest test:

import React, { ReactElement } from 'react';
import TestRenderer, { ReactTestRenderer } from 'react-test-renderer';

    SyntaxError: /Users/dford/git/project/test/floorplan/Circle.unit.test.tsx: Cannot transform the imported binding "ReactTestRenderer" since it's also used in a type annotation. Please strip type annotations using @babel/preset-typescript or @babel/preset-flow.
      14 |
      15 | describe('Circle', () => {
    > 16 |   let component: ReactTestRenderer;
         |                  ^^^^^^^^^^^^^^^^^
      17 |   let spy: jest.SpyInstance<boolean, [Event]>;
      18 |   const transformRef = {
      19 |     nodes: jest.fn(),

I thought esbuild will strip out all type annotations, so I'm not sure what is wrong here?

Here is my jest.config.js transform:

  transform: {
    '^.+\\.(tsx?|jsx?)$': ['esbuild-jest', {target: 'es2020'} ],
    '.+\\.(css|styl|less|sass|scss)$': '<rootDir>/node_modules/jest-css-modules-transform',
  },
davisford commented 3 years ago

This is happening here -- called from here (i.e. related to the special handling for jest mocks)...but the test file itself has type annotations (.tsx test), which causes babel-jest to throw. If I just run the test file itself through esbuild that works fine.

bvanjoi commented 2 years ago

I have the same problem, have you solved it?

davisford commented 2 years ago

@bvanjoi a colleague solved it by adding @babel/preset-typescript as a dependency and add this snippet to package.json:

"babel": {
    "presets": [
        "@babel/preset-typescript"
    ]
},

One other workaround we had to do to get past the error ReferenceError: React is not defined in tests was to replace the import React from 'react'; with import * as React from 'react';

Hope that helps someone....

kevinhalliday commented 2 years ago

thanks @davisford for coming back and posting your solution. I ran into the same issues you did (SynatxError when leaving type annotations in test files, and ReferenceError: React is not defined when using jsx in files that also include jest.mock). I'm curious, did you also only get the ReferenceError in files that included a jest.mock call? Based on the esbuild-jest source you linked to, it seems like that would make sense.

It's a bit of a bummer to have to add a babel configuration to my project when there are no explicit references to babel anywhere in my project. If not for a comment linking back to this issue, it would seem like my babel config was doing nothing.

davisford commented 2 years ago

@khalliday7 I don't recall 100% if it occurred just on files that do jest.mock, but I suspect your assumption is correct.

ahlec commented 2 years ago

Locally, I found that the ReferenceError: React is not defined in TSX files was because the babel transformation would transform the import from import React from 'react' to var _react = _interopRequireDefault(require("react")), but the JSX syntax was being left intact for ESBuild to transform, and it wasn't finding the React symbol.

If you add @babel/plugin-transform-react-jsx to your package.json and then include

"plugins": [
  "@babel/transform-react-jsx"
]

in your Babel configuration, it fixed the problem for me without needing to change import styles to import * as React from 'react'.

Since only a subset of the files go through the Babel transformation and therefore only some of the files will have React renamed to _react, I don't think you'd be able to fix this by specifying the jsxFactory option on the transformation.

petebacondarwin commented 2 years ago

So the simplest workaround is to run the babel-typescript transform but of course that defeats the point of this plugin which is to use esbuild to transform the typescript.

Another workaround is to do both the following:

ErikSchierboom commented 2 years ago

I think the workarounds are not that appealing. I think the code that looks for the mock calls should probably not do a string search, which feels a bit brittle to me.

AndrewSouthpaw commented 2 years ago

I tried the workaround (install the plugin @babel/preset-typescript, add to package.json#babel.presets) didn't do the trick, though we don't otherwise have babel set up for anything so maybe that's our issue? Is there any further integration required with jest or esbuild or esbuild-jest?

For us, I can reproduce the problem with a minimal repro, which gives us a SyntaxError, even without type annotations in the test file:

// bar.ts
// =====
export type Bar = {
  a: string;
};

// foo.ts
// =====
import { Bar } from "./bar";  // <-- important to import a type

export const returnBar = (): Bar => ({ a: "foo" }); // use as annotation

// ock(  <-- just put in this bogus text to trigger the transform

// foo.test.ts
// ========
import { add } from "./foo";

// no type annotations
it("does something", () => {
  expect(add(1, 2)).toEqual(3);
});

I'm not very familiar with esbuild, I was mostly seeing if I could use it to speed up my Jest test runs, but wouldn't passing the code through a babel transform counteract any speed gains we'd find by using esbuild? Or does it still make a difference?

If it does make a difference, any ideas why the workaround isn't working on my end?

nojvek commented 2 years ago

I'm encoutering the same issues as well

SyntaxError: ../mongo.test.ts: Cannot transform the imported binding "MongoClient" since it's also used in a type annotation. Please strip type annotations using @babel/preset-typescript or @babel/preset-flow.

This plugin worked great in another repo but it seems it has real trouble when files use jest.Mock or jest.Mocked

Gonna try using the swc plugin to see if it does any better.

AndrewSouthpaw commented 2 years ago

We ended up going with swc, worked much better.

krystofbe commented 2 years ago

avoid the letters ock( in any file that does have type annotations

this worked for me and i had a good laugh when searching my code and finding this text fragment

    <button
      onClick={() => {
        setBlock(block);
      }}
...

my jest tests ran again after renaming setBlock to setBlck. yet, this does not give me a good feeling to use esbuild-jest in production and i'm probably going to move away from esbuild and use swc like @AndrewSouthpaw

jmikrut commented 1 year ago

Any updates on this issues? We'd like to use esbuild-jest in Payload and I've followed the fixes above, which are great, but I agree that it feels weird to have to add a Babel config to our package.json when we're completely leveraging esbuild at this point.

ziogas commented 1 year ago

So the simplest workaround is to run the babel-typescript transform but of course that defeats the point of this plugin which is to use esbuild to transform the typescript.

Another workaround is to do both the following:

  • avoid type annotations in files that need to be transformed. As mentioned above esbuild-jest decides whether to run this transform by checking to see if the content of the code contains the string ock(. (Look for those characters in your file.)
  • avoid the letters ock( in any file that does have type annotations

Thanks, was going crazy just to realize that one of React components was named function Block()...

To repeat, you can't have ock( in your codebase. So the code like function AnythingBlock() immediately triggers the unwanted behavior.

jeremyhewett commented 10 months ago

you can't have ock( in your codebase

LOL, that is bizarre! Who wrote this thing?!

klausbadelt commented 7 months ago

We also had to move to swc. Using Babel to pre-transform, which seems foundational concept here, seems defeating the purpose of esbuild.