Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jest attempts to transform my custom transform leading in "TypeError: Jest: a transform must export a process function" #7710

Closed
kentcdodds opened this issue Jan 26, 2019 · 10 comments · Fixed by #7752

Comments

@kentcdodds
Copy link
Contributor

Sorry, I'll add more details later. Just getting this down now so I don't forget what I've learned so far before I wrap up for the weekend.

🐛 Bug Report

I have a custom transform that uses babel-jest to create a transformer based on a certain babel config that does not exist in the typical place (this is for a toolkit where the config resides inside a package, not in the project).

To Reproduce

Steps to reproduce the behavior:

I'll create a reproduction repo next week.

Expected behavior

In jest-runtime/build/ScriptTransformer.js in _getTransformer I'm getting:

TypeError: Jest: a transform must export a `process` function

Adding some console logs around shows me that it goes through that function twice with the same file (my custom transform). Here's an outline of where the code gets to:

    if (transformPath) {
      const transformer = this._transformCache.get(transformPath);

      if (transformer != null) {
        return transformer;
      } // $FlowFixMe
      transform = require(transformPath); // 1️⃣ gets to this line the first time
      // following the code path here actually creates a module in the require.cache
      // but in the process of actually loading the module, this function is
      // called and run again!
      // which means that the second time when we try to require `transformPath`
      // a second time it comes back with a module that has a module.exports of `{}`

      if (typeof transform.createTransformer === 'function') {
        transform = transform.createTransformer();
      }
      if (typeof transform.process !== 'function') {
        // 2️⃣ gets to this line the second time
        throw new TypeError(
          `Jest: a transform must export a \`process\` function`
        );
      }
      this._transformCache.set(transformPath, transform);
    }

This is pretty unusual I think. It's not entirely clear to me why the _getTransform function is getting called twice, but my assumption is that Jest is attempting to transform the transform file. Efforts to configure jest to ignore the transform file failed so I'm missing something else.

Link to repl or repo (highly encouraged)

I'll try to add something next week.

Run npx envinfo --preset jest

Paste the results here:

  System:
    OS: macOS 10.14.3
    CPU: (8) x64 Intel(R) Core(TM) i7-4980HQ CPU @ 2.80GHz
  Binaries:
    Node: 8.15.0 - ~/code/paypal/paypal-scripts/node_modules/.bin/node
    Yarn: 1.12.3 - /usr/local/bin/yarn
    npm: 6.5.0 - ~/n/bin/npm
  npmPackages:
    jest: 24.0.0 => 24.0.0

Thanks! Looking forward to upgrading!

@SimenB
Copy link
Member

SimenB commented Jan 26, 2019

Do you use globalSetup/globalTeardown? If so, this might come from #7562. In that case, it should work to say transformIgnorePatterns: ['/node_modules/', '/my-custom-transform/']

@kentcdodds
Copy link
Contributor Author

Yes, we are using globalSetup but from my testing that file wasn't the one causing the trouble. I'll try to make a reproduction today (unless that PR will be released soon in which case I'd prefer to just wait and see if it magically fixes my problems 😅).

@kentcdodds
Copy link
Contributor Author

Ok, yep, I was able to reproduce this: https://github.com/kentcdodds/jest-transform-self-transform-bug

So the key here is that you have both globalSetup and transform. If you comment either of those out of the config then things work fine.

So my guess is that #7562 solves this. Any ideas as to when that'll be released? I've not learned how to locally link a bunch of lerna packages so I'm not sure of the best way to test that current master solves the problem. But it does seem related.

@SimenB
Copy link
Member

SimenB commented Jan 28, 2019

That PR has been released, that's what's causing your issue. 🙂 You need to include your setup or teardown in transformIgnorePattern.

This should be documented somewhere, not sure where's the best fit, though

@kentcdodds
Copy link
Contributor Author

Ooooh! I see 😅 Hmmm... Interesting. So it's just a limitation or something? I feel like this is something that jest should be able to detect and prevent? Maybe I'm misunderstanding what's going on though.

When you mentioned transformIgnorePattern before, I thought you were saying I should include my custom transform in that pattern, but indeed including the global setup in the pattern fixes the issue! Thanks @SimenB!

I'd be willing to contribute the docs, though it's still unclear to me why this is happening and why it's not a bug that should be fixed instead 🤔

@SimenB
Copy link
Member

SimenB commented Jan 28, 2019

Not sure if it's functionally a bug, but we should definitely provide a better error.

The real fix is to preload all transformers, and then install the require hook. It would still possibly break if there are lazy requires inside the transform function or something, but that's way less likely to happen

@SimenB
Copy link
Member

SimenB commented Jan 28, 2019

Doing this should fix it, as it will make sure to load and cache the transformer before installing require hooks:

diff --git i/packages/jest-cli/src/runGlobalHook.js w/packages/jest-cli/src/runGlobalHook.js
index cd5a4ff06..4c6b3ed0d 100644
--- i/packages/jest-cli/src/runGlobalHook.js
+++ w/packages/jest-cli/src/runGlobalHook.js
@@ -49,6 +49,10 @@ export default ({
 
       const transformer = new Runtime.ScriptTransformer(projectConfig);
 
+      // Load the transformer to avoid a cycle where we need to load a
+      // transformer in order to transform it in the require hooks
+      transformer._getTransformer(modulePath);
+
       const revertHook = addHook(
         (code, filename) =>
           transformer.transformSource(filename, code, false).code || code,

transformer._getTransformer will load the transformer matching the filename, and stick it in a cache (it's the function that throws the error in the OP).

Of course, that calls a private API, so we should make a non-private one. But that should fix the issue.

https://github.com/facebook/jest/blob/d7ca8b23acf2fdd1d070496efb2b2709644a6f4f/packages/jest-runtime/src/ScriptTransformer.js#L144

@borodayev
Copy link

@kentcdodds, can you provide your final version of jest config please, because I've followed all instructions above and I'm still getting this error. 😕

@kentcdodds
Copy link
Contributor Author

This branch has the fix: https://github.com/kentcdodds/jest-transform-self-transform-bug/tree/context-issue

Though there was a new bug in watch mode that this revealed as well (#7740) which has been fixed but not yet released.

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 12, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants