Ts-jest: React Native - SyntaxError: Unexpected token import

Created on 3 Jul 2018  ·  12Comments  ·  Source: kulshekhar/ts-jest

Issue

SyntaxError: Unexpected token import

Jest throw an error when he try to parse React Navigation package who has ES2015 imports.

Expected behavior

Following dedicated React Native documentation and ES2015 documentation I expect that .js files under node_modules to be processed by babel-jest.

Jest config

module.exports = {
    preset: "react-native",
    transform: {
        "^.+\\.jsx?$": "<rootDir>/node_modules/babel-jest",
        "^.+\\.tsx?$": "ts-jest"
    },
    testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
    moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
    globals: {
        "ts-jest": {
            useBabelrc: true
        }
    }
};

Babel config

{
    "presets": ["react-native"],
    "sourceMaps": "inline"
}

Error

    /Users/nicolas/Projects/react-native-typescript/node_modules/react-navigation/src/navigators/createStackNavigator.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import React from 'react';
                                                                                             ^^^^^^

Example

You can find an example with a simple test.

Thanks !

Most helpful comment

Ahhhhh I must be too tired, going back to my project I realized I made a mistake in the ignore pattern...

  • added the right regexp to jest config:

no, it was wrong! So I fixed it:

  transformIgnorePatterns: [
    "/node_modules/(?!react-native)/.+"
  ]

...because yeah, we want ti to NOT ignore processing the react-native's files

Now it complains about some React thing, so there you go that's not for me :-D

console.error node_modules/fbjs/lib/warning.js:33
      Warning: AsyncButton(...): No `render` method found on the returned component instance: you may have forgotten to define `render`.
    console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6309
      The above error occurred in the <AsyncButton> component:
          in AsyncButton
...

@mattyclarkson if you find out it's related to ts-jest, please re-open this issue.

For others landing here, you might be interested in:
https://github.com/babel/babel/issues/7627#issuecomment-409776114 and https://github.com/facebook/react-native/issues/19859#issuecomment-402621849

All 12 comments

I am having the same issue. Started happening when I tried to upgrade to RN 0.56

What version of Babel are you using? Babel 7 now handles .babelrc files different (i.e. doesn't handle them). See facebook/jest#6229. This means that the instructions for setting up ts-jest with React Native and Babel 7 do not work. The workaround to change .babelrc to babel.config.js doesn't work for me.

Also see my comment in facebook/jest#6229

@huafu, @kulshekhar this is blocking me use ts-jest ^23 with any of our React Native projects. I'm happy to investigate this if you can point me in the right direction?

You can reproduce it on ef-carbon/react-native-async-button.

Upgrading ts-jest to ^23 results in the following error when yarn test is ran:

 FAIL  test/ContextAsyncButton.test.tsx
  ● Test suite failed to run

    TypeError: Cannot read property 'bindings' of null

      at Scope.moveBindingTo (node_modules/@babel/core/node_modules/@babel/t
raverse/lib/scope/index.js:869:13)
      at BlockScoping.updateScopeInfo (node_modules/babel-plugin-transform-e
s2015-block-scoping/lib/index.js:364:17)
      at BlockScoping.run (node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:330:12)
      at PluginPass.BlockStatementSwitchStatementProgram (node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:70:24)
      at newFn (node_modules/@babel/core/node_modules/@babel/traverse/lib/visitors.js:193:21)
      at NodePath._call (node_modules/@babel/core/node_modules/@babel/traverse/lib/path/context.js:53:20)
      at NodePath.call (node_modules/@babel/core/node_modules/@babel/traverse/lib/path/context.js:40:17)
      at NodePath.visit (node_modules/@babel/core/node_modules/@babel/traverse/lib/path/context.js:88:12)
      at TraversalContext.visitQueue (node_modules/@babel/core/node_modules/@babel/traverse/lib/context.js:118:16)
      at TraversalContext.visitSingle (node_modules/@babel/core/node_modules/@babel/traverse/lib/context.js:90:19)

I assume this is because something in ts-jest is loading Babel 7, but something else expects Babel 6. If I upgrade babel-core to 7.0.0-bridge.0. Still get the same error, so upgrade babel-preset-react-native. I then start getting the SyntaxError as stated:

 FAIL  test/ContextAsyncButton.test.tsx
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest canno
t parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    SyntaxError: /Users/mattyclarkson/git/github/ef-carbon/react-native-async-button/node_modules/react-native/Libraries/StyleSheet/StyleSheet.js: Unexpected token (18:12)

      16 | const flatten = require('flattenStyle');
      17 |
    > 18 | import type {
         |             ^
      19 |   ____Styles_Internal,
      20 |   ____DangerouslyImpreciseStyle_Internal,
      21 |   ____DangerouslyImpreciseStyleProp_Internal,

      at Parser.raise (node_modules/@babel/core/node_modules/babylon/lib/index.js:776:15)
      at Parser.unexpected (node_modules/@babel/core/node_modules/babylon/lib/index.js:2079:16)
      at Parser.expectContextual (node_modules/@babel/core/node_modules/babylon/lib/index.js:2047:41)
      at Parser.parseImport (node_modules/@babel/core/node_modules/babylon/lib/index.js:5205:12)
      at Parser.parseStatementContent (node_modules/@babel/core/node_modules/babylon/lib/index.js:4043:27)
      at Parser.parseStatement (node_modules/@babel/core/node_modules/babylon/lib/index.js:3962:17)
      at Parser.parseBlockOrModuleBlockBody (node_modules/@babel/core/node_modules/babylon/lib/index.js:4513:23)
      at Parser.parseBlockBody (node_modules/@babel/core/node_modules/babylon/lib/index.js:4500:10)
      at Parser.parseTopLevel (node_modules/@babel/core/node_modules/babylon/lib/index.js:3938:10)
      at Parser.parse (node_modules/@babel/core/node_modules/babylon/lib/index.js:5304:17)

So I then add the following to the jest configuration:

    "transformIgnorePatterns": [
      "/node_modules/(?!react-native).+\\.js$"
    ],

Still get the same error. I've tried all kinds of different transform patterns and other fruitless investigation.

Not sure if this is a problem with ts-jest anymore or it's a result of babel-preset-react-native. Or if I should be using babel-preset-env instead.

Thank @mattyclarkson for the investigation. Here is some quick info I can give you know:

First of all, when you try something else, be sure to run jest --clearCache to ensure jest will run with the new settings/packages. I've reported the bug in jest, jest --no-cache does use some of the cache so it's safer to jest --clearCache.

For babel, if you install babel 7, ts-jest will use it. Else it'll use the 6.

For the repository link you gave, it's pointing to a no more existing hash. If the issue is in the master of this repo as well, lemme know and I'll try to investigate when I'll have a bit more time.

Ooo, thanks for the tip. That helps.

I've updated the link in the above comment, it was suppose to just point at master, sorry about that 🤦‍♂️ . It's also here for convienence.

I wrote step by step what I did, maybe it could help you investigate later some other bugs:

  • I removed babel-core, @babel/core and babel-jest to be sure jest will use the defaults sipped with it.
  • I upgraded ts-jest (it was 22).
  • I ran yarn why @babel/core to see if it was still installed, and if so why. Turns out babel 7 WILL be chosen and installed because of react-native (@babel/core => metro => react-native).
  • I finally ran the tests to see what was going on
  • Issue is in a babel AST transformer's visitor, so it's a babel plugin visiting each node of the JS AST to do some modifications: babel-plugin-transform-es2015-block-scoping. It's trying to move a let to parent scope which apparently doesn't exist.
  • I put a breakpoint to see why exactly, as this plugin ships with babel and should work out of the box (I use ndb for debugging, it's the easiest and coolest tool to debug a NodeJS app, especially when it's one having child-processes)
  • ...
  • At the end this plugin is part of the transformation from es2015+ to es5, so I changed the target in test/tsconfig.json to es5, so that this part of the process would be done by TypeScript and not babel. It's weird, but it sounds like a babel 7 bug, or maybe a bug due to the combination of some babel plugins.
  • 🎉 Test were running but I got millions of deprecation from babel related to its plugins
  • ...
  • I then did something I should have done earlier 🤪, searched babel-plugin-transform-es2015-block-scoping in babel issues and boom https://github.com/babel/babel/issues/7627#issuecomment-409776114
  • what's the preset in .babelrc which is bringing some v6 plugins? oh react-native
  • searched in babel-preset-react-native issues and found this: https://github.com/facebook/react-native/issues/19859
  • so I installed [email protected] and @babel/core (yes, back in for this last one, I shouldn't have removed it...)
  • it's complaining about babel-jest now, ok I shouldn't have removed that one too
  • oh I am at the same point of you Jest encountered an unexpected token!
  • added the right regexp to jest config:
    js transformIgnorePatterns: [ "<rootDir>/node_modules/react-native/.+" ]
  • OMFG there is some flow in there node_modules/react-native/Libraries/StyleSheet/StyleSheet.js
  • yarn add --dev @babel/preset-flow
  • update the presets array of .babelrc to prepend with @babel/preset-flow
  • sounds like the preset did not change anything, flow typings is still in there...

My time on this is over sorry, I'll let you follow here, maybe tomorrow or another day if you still stuck I'll continue to drop an eye, I hope these last few steps will help you

Oh @mattyclarkson look at this https://github.com/facebook/react-native/issues/19859#issuecomment-402621849

Also something to note: removing all node_modules and re-running yarn is sometimes good thing to do after messing up with multiple yarn add ... and yarn remove ...

Ahhhhh I must be too tired, going back to my project I realized I made a mistake in the ignore pattern...

  • added the right regexp to jest config:

no, it was wrong! So I fixed it:

  transformIgnorePatterns: [
    "/node_modules/(?!react-native)/.+"
  ]

...because yeah, we want ti to NOT ignore processing the react-native's files

Now it complains about some React thing, so there you go that's not for me :-D

console.error node_modules/fbjs/lib/warning.js:33
      Warning: AsyncButton(...): No `render` method found on the returned component instance: you may have forgotten to define `render`.
    console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6309
      The above error occurred in the <AsyncButton> component:
          in AsyncButton
...

@mattyclarkson if you find out it's related to ts-jest, please re-open this issue.

For others landing here, you might be interested in:
https://github.com/babel/babel/issues/7627#issuecomment-409776114 and https://github.com/facebook/react-native/issues/19859#issuecomment-402621849

Finally works fine!

Jest config in package.json:

{
    "devDependencies": {
        "babel-jest": "^23.6.0",
        "jest": "^23.6.0",
        "ts-jest": "^23.1.4",
        "typescript": "^3.2.1"
        ...
    },
    "jest": {
        preset: "react-native",
        transform: {
            "^.+\\.jsx?$": "<rootDir>/node_modules/react-native/jest/preprocessor.js",
            "^.+\\.tsx?$": "ts-jest"
        },
        testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
        moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
        globals: {
            "ts-jest": {
                "tsConfigFile": "tsconfig.jest.json"
            }
        }
    }
    ...
}

TS config in tsconfig.json:

{
    "compilerOptions": {
        // https://blogs.msdn.microsoft.com/typescript/2018/08/27/typescript-and-babel-7/
        "target": "esnext",
        "moduleResolution": "node",
        "allowJs": true,
        "noEmit": true,
        "isolatedModules": true,
        "esModuleInterop": true,
        "noImplicitAny": true,
        "allowSyntheticDefaultImports": true,
        "jsx": "react-native",
        "lib": [
            "esnext"
        ],
        "types": [ // fix declaration collision between react-native and ts-jest
            "react",
            "react-native",
            "react-redux",
            "redux"
            ...
        ]
    },
    "include": [
        "src"
    ]
}

TS config in tsconfig.jest.json:

{
    "extends": "./tsconfig",
    "compilerOptions": {
        "jsx": "react",
        "module": "commonjs"
    }
}

Babel config in .babelrc:

{
    "presets": [
        "module:metro-react-native-babel-preset"
    ],
    "env": {
        "test": {
            "presets": [
                "react-native",
                [
                    "@babel/preset-env"
                ]
            ]
        }
    }
}

Thanks 🙏

Was this page helpful?
0 / 5 - 0 ratings