kentcdodds / babel-plugin-macros

🎣 Allows you to build simple compile-time libraries
https://npm.im/babel-plugin-macros
MIT License
2.62k stars 135 forks source link

Failure with ts-jest and babel #160

Open dpovey opened 3 years ago

dpovey commented 3 years ago

I am trying to use babel-plugin-macros (specifically with the twin macro and tailwind) and while it works fine when running normally it fails with jest. This is the relevant part of my jest config:

module.exports = {
  preset: 'ts-jest',
  globals: {
    'ts-jest': {
      babelConfig: {
        plugins: ['macros'],
        presets: [['@babel/preset-env'], '@babel/preset-react'],
      },
    },
  },

I have also tried setting babelConfig to my babel.config.js (plus tried babelrc and babel.config.json). All results in the following error when I run jest on an application test:

 FAIL  tests/App.test.tsx
  ● Test suite failed to run

    MacroError: The macro you imported from "undefined" is being executed outside the context of compilation with babel-plugin-macros. This indicates that you don't have the babel plugin "babel-plugin-macros" configured correctly. Please see the documentation for how to configure babel-plugin-macros properly: https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/user.md

      82 |
      83 |   #desktop-nav & {
    > 84 |     ${tw`flex items-center px-2 py-2 text-xs leading-5 transition ease-in-out duration-150`};
         |       ^
      85 |     div {
      86 |       ${tw`mx-auto`}
      87 |     }

      at Object.macroWrapper [as default] (node_modules/babel-plugin-macros/dist/index.js:60:13)
      at Object.<anonymous> (src/chrome/AppChrome.tsx:84:7)
      at Object.<anonymous> (src/App.tsx:20:1)
      at Object.<anonymous> (tests/App.test.tsx:3:1)

I am not exactly sure what to do from here, it seems most likely that the error is with the check in babel-plugin-macros. I'm relatively sure that babel is running, or at least it chokes if I add a plugin that doesn't exist:

module.exports = {
  preset: 'ts-jest',
  globals: {
    'ts-jest': {
      babelConfig: {
        plugins: ['macros', 'no-such-plugin'],
        presets: [['@babel/preset-env'], '@babel/preset-react'],
      },
    },
  },

Fails with:

 FAIL  tests/App.test.tsx
  ● Test suite failed to run

    Cannot find module 'babel-plugin-no-such-plugin' from '/Users/dpovey/src/ppcsamurai/shogun-react'

      at Function.resolveSync [as sync] (node_modules/resolve/lib/sync.js:90:15)
      at resolveStandardizedName (node_modules/@babel/core/lib/config/files/plugins.js:101:31)
      at resolvePlugin (node_modules/@babel/core/lib/config/files/plugins.js:54:10)
      at loadPlugin (node_modules/@babel/core/lib/config/files/plugins.js:62:20)
      at createDescriptor (node_modules/@babel/core/lib/config/config-descriptors.js:154:9)
      at node_modules/@babel/core/lib/config/config-descriptors.js:109:50
          at Array.map (<anonymous>)
      at createDescriptors (node_modules/@babel/core/lib/config/config-descriptors.js:109:29)
      at createPluginDescriptors (node_modules/@babel/core/lib/config/config-descriptors.js:105:10)
      at node_modules/@babel/core/lib/config/config-descriptors.js:63:53

Any help would be greatly appreciated. It seems to be a variation on a common problem, based on googling the error.

dpovey commented 3 years ago

The only workaround I could find was to move from ts-jest to using babel for transpiling typescript. It means I lose typechecking but I am guessing that's the only reasonable workaround for now.

kentcdodds commented 3 years ago

it seems most likely that the error is with the check in babel-plugin-macros

I'm confident this is not the issue. But if you can figure out what the issue is and contribute a fix that would be great. I don't have the bandwidth to work on this I'm afraid.

conartist6 commented 3 years ago

Are you remembering to jest --clearCache when you make changes to jest's internal transpilation? It can be easy to pass over a configuration that does really work that way. Also you might try creating a custom transformer. Here is an example from one of my projects. Then in jest.config.js you can use your transformer:

module.exports = {
  transform: {
    '.*': '<rootDir>/path/to/transformer',
  },
};
lucas-janon commented 3 years ago

@dpovey I'm having the same issue, can't move to babel-jest because of our app's setup, did you find a workaround?

conartist6 commented 3 years ago

If you're willing to debug through the issue with with me I could probably help you over the next few days. This sat because we never got down to a root cause.

lucas-janon commented 3 years ago

If you're willing to debug through the issue with with me I could probably help you over the next few days. This sat because we never got down to a root cause.

Sure, thank you! As a workaround, I used your solution of a custom transformer and it worked, so, thanks for it. I can write you an email (to the one you have in your profile) and we can debug it next week if you want.

My workaround:

const tsJest = require("ts-jest");

const tsJestTransformer = tsJest.createTransformer();

/**
 * @todo (lucas): this is far from ideal, it removes every tailwind/tw statement to prevent errors in jest
 * that means all our tailwind styles won't be applied in the tests
 * @see https://github.com/kentcdodds/babel-plugin-macros/issues/160
 */
module.exports = {
  ...tsJestTransformer,
  process: (src, filename, jestConfig, ...rest) => {
    const procesedSrc = src.replace(/\$\{tailwind`.*`\}/g, "").replace(/\$\{tw`.*`\}/g, "");

    return tsJestTransformer.process(procesedSrc, filename, jestConfig, ...rest);
  },
};
conartist6 commented 3 years ago

Cool. Yeah I'm happy to look at it, and I think any email I've listed is fine.

dpovey commented 3 years ago

@dpovey I'm having the same issue, can't move to babel-jest because of our app's setup, did you find a workaround?

Alas no I ended up just moving to Babel for transpilation and used the fork-ts-checker plugin for type checking with tsc.

theladyjaye commented 3 years ago

I ran into this as well. I ended up with a jest config like this to work around it:

const { defaults: tsjPreset } = require("ts-jest/presets");

module.exports = {
  "//": "... the rest of the config ...",
  transform: {
    // this one file uses a babel macro
    // it's a generated file that combines
    // typescript and the macro. So that 1 file
    // we compile with `babel-jest` and the rest we
    // compile with `ts-jest`
    "src\/mypath\/myfile\.tsx": [
      "babel-jest",
      {
        plugins: ["macros"],
        presets: ["@babel/preset-typescript"],
     },
    ],
    ...tsjPreset.transform,
  },
}

Since that one file, in my case, is a tool generated file, there is no need to worry about typescript related type checking at compile time which we lose with the babel compile:

See: https://devblogs.microsoft.com/typescript/typescript-and-babel-7/#caveats

As we mentioned above, the first thing users should be aware of is that Babel won’t perform type-checking on TypeScript code; it will only be transforming your code, and it will compile regardless of whether type errors are present.

tareqdayya commented 2 years ago

The problem I had was with ts-jest (tried replacing it with babel-jest and it worked fine).

All I did to get ts-jest to work was to update its config by passing useESM: true. Here's the section in question in the jest.config.js:

globals: {
    'ts-jest': {
      tsconfig: '<rootDir>/tsconfig.json',
      babelConfig: 'babel.config.js',
      useESM: true  // added this
    }
}

Notice I'm also passing the babelConfig option which is disabled by default.

Hope that helps someone.

kopax-polyconseil commented 2 years ago

What's the point of using ts-jest if behind you still use babel for transpiling your code ?

mariojankovic commented 1 year ago

I thought I'd chime in with what worked for me - instead of specifying the transform I've added this into my jest.config.js:

moduleNameMapper: {
  '\\.macro$': '<rootDir>/node_modules/babel-plugin-macros/dist/index.js',
},
StJohn3D commented 1 year ago

Thanks @mariojankovic , your workaround didn't work for me but it did inspire me to try this

moduleNameMapper: {
  '@emotion/styled/macro': '@emotion/styled'
}

This way the bundler still uses the macros but the test run doesn't.