nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
22.64k stars 2.26k forks source link

How to set transformIgnorePatterns to fix "Jest encountered an unexpected token" #812

Closed luchsamapparat closed 5 years ago

luchsamapparat commented 5 years ago

First of all, thanks for bringing Jest to Angular!

I previously had configured Jest in my Angular project by myself. In order to use lodash-es, I had to set transformIgnorePatterns to inlude the path to lodash-es:

  "jest": {
    "preset": "jest-preset-angular",
...
    "transformIgnorePatterns": [
      "<rootDir>/node_modules/(?!lodash-es/.*)"
    ],

Now, after migrating to the Jest config provided by Nx, I don't know where I can set this option. My tests currently fail with this error:

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot 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:

    C:\Users\Marvin\Projekte\hypershop-ng\node_modules\lodash-es\lodash.js:10
    export { default as add } from './add.js';
    ^^^^^^

    SyntaxError: Unexpected token export

      1 | import { ComponentFixture } from "@angular/core/testing";
    > 2 | import { isUndefined } from "lodash-es";
        | ^
      3 |
      4 | export function expectElementFromFixture<T>(fixture: ComponentFixture<T>, domQuery?: string): jasmine.Matchers<{} | null> {
      5 |     return expect(elementFromFixture(fixture, domQuery));

      at ScriptTransformer._transformAndBuildScript (../../node_modules/jest-runtime/build/script_transformer.js:403:17)
      at Object.<anonymous> (../../libs/common/src/test/expect.ts:2:1)
      at Object.<anonymous> (../../libs/common/src/test/index.ts:1:1)

Thanks for your help

luchsamapparat commented 5 years ago

Nevermind, obviously it's ./jest.config.json … 🙄

However, the problem is still there even when adding that config 🤔

llwt commented 5 years ago

I had to remove the <rootDir> from ours:

const esModules = ['@agm', 'ngx-bootstrap'].join('|');
// ...
module.exports = {
  //...
    transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
  // ...
};
luchsamapparat commented 5 years ago

Yes, that confirms what I just discovered while debugging Jest. The <rootDir> is resolved to the lib/app directory being tested, not the workspace root...

`/absolute/path/to/project/libs/libname/node_modules//(?!lodash-es/.*)

luchsamapparat commented 5 years ago

I've been debugging this for the last hour. lodash-es is definitely being passed to transformSource in jest-runtime/build/script_transformer.js, however the transformedSource that is returned by that function still contains non-ES5 statements.

luchsamapparat commented 5 years ago

I finally fixed the issue by adding "allowJs": true to the compilerOptions of each lib/app's tsconfig.spec.json (or alternatively to the root tsconfig.json). Of course in addition to setting transformIgnorePatterns as @llwt suggested. Without that option the TypeScript compiler just skipped the lodash files.

llwt commented 5 years ago

One other option is to pull in babel-jest and tell it to transpile those js files.

From the jest-preset-angular docs:

Transpile js files through babel-jest

Some vendors publish their sources without transpiling. You need to say jest to transpile such files manually since typescript (and thus ts-jest used by this preset) do not transpile them.

  1. Install babel-preset-env and add .babelrc (or modify existing if needed) with that contents:
    {
    "presets": ["env"]
    }
  2. Update Jest configuration (by default TypeScript process untranspiled JS files which is source of the problem):
    {
    "jest": {
    "transform": {
      "^.+\\.(ts|html)$": "<rootDir>/node_modules/jest-preset-angular/preprocessor.js",
      "^.+\\.js$": "babel-jest"
    },
    }
    }

We took that and tweaked it to only pass the js files needed through:

const esModules = ['@agm', 'ngx-bootstrap', 'lodash-es'].join('|');

module.exports = {
  // ...
  transform: {
    [`(${esModules}).+\\.js$`]: 'babel-jest',
    '^.+\\.(ts|js|html)$': 'jest-preset-angular/preprocessor.js',
    // ...
  },
  transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
  // ...
};
skorunka commented 5 years ago

I have the similar problem when trying to debug jest tests in VS Code:

 FAIL  apps/el/admin/src/app/app.component.spec.ts
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot 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:

    <project_path>\apps\el\admin\src\app\app.component.html:2
    <div style="text-align:center">
    ^

    SyntaxError: Unexpected token <

      at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)
      at apps/el/admin/src/app/app.component.ts:26:15
      at Object.<anonymous> (apps/el/admin/src/app/app.component.ts:30:2)

It is a nrwl workspace with one app and one lib, no code changes.

Debugger works, the breakpoint is hit. But got the error from above.

npm run test works - all tests are green.

{
    "type": "node",
    "request": "launch",
    "name": "Jest All",
    "program": "${workspaceFolder}\\node_modules\\jest\\bin\\jest",
    "args": ["--runInBand", "--config=${workspaceFolder}\\jest.config.js"],
    "console": "integratedTerminal",
    "internalConsoleOptions": "neverOpen"
},

Any idea what might be wrong? Thank you.

luchsamapparat commented 5 years ago

@skorunka have a look at https://github.com/nrwl/nx/blob/master/packages/builders/src/jest/jest.builder.ts

When you run your tests via npm run test, the globals config is set by Nx' Jest builder. You need to somehow also provide these globals in order to debug your tests with VS Code. I added that config to temporarily to my Jest config while debugging, but that's certainly not the nicest option...

skorunka commented 5 years ago

@luchsamapparat Hi, could you please share your modified jest config file you use for debugging? Thanks.

skorunka commented 5 years ago

I have added __TRANSFORM_HTML__: true to globals in ./jest.config.js file, like

    globals: {
        'ts-jest': {
            tsConfigFile: './tsconfig.json',
        },
        __TRANSFORM_HTML__: true,
    },

and now there is no problem with HTML. But the easy test for checking that the components can be created fails. It works from command line.

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { LoginInlineComponent } from './login-inline.component';

describe('FooterComponent', () => {
    let component: LoginInlineComponent;
    let fixture: ComponentFixture<LoginInlineComponent>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [FormsModule, ReactiveFormsModule],
            declarations: [LoginInlineComponent],
        }).compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(LoginInlineComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });
});

The breakpoint is hit, but the component is undefined

image

Command line works es expected:

image

Any idea what I'm still missing in here? This drives me crazy. I love jest, but making it work/debug in Angular + VS Code is painful.

skorunka commented 5 years ago

After some debugging, I can actually see that there is an exception when executing:

fixture = TestBed.createComponent(LoginInlineComponent);

It goes in queue_runner.js:

image

danieldanielecki commented 5 years ago

Solved this without allowJs, the only 1 file which changed it was jest.config.js (local lib file, not in global jest.config.js) by adding transformIgnorePatterns lijke below

module.exports = {
  name: 'contact',
  preset: '../../jest.config.js',
  coverageDirectory: '../../coverage/libs/contact',
  transformIgnorePatterns: ['../../node_modules/(?!${ng2-tel-input})']
};

Where ng2-tel-input was the problematic module, getting it for the following environment: Jest 23.6.0, @nrwl/builders 7.5.1, @nrwl/nx 7.5.1, @nrwl/schematics 7.5.1. jest-preset-angular 6.0.1.

danieldanielecki commented 5 years ago

Solved this without allowJs, the only 1 file which changed it was jest.config.js (local lib file, not in global jest.config.js) by adding transformIgnorePatterns lijke below

module.exports = {
  name: 'contact',
  preset: '../../jest.config.js',
  coverageDirectory: '../../coverage/libs/contact',
  transformIgnorePatterns: ['../../node_modules/(?!${ng2-tel-input})']
};

Where ng2-tel-input was the problematic module, getting it for the following environment: Jest 23.6.0, @nrwl/builders 7.5.1, @nrwl/nx 7.5.1, @nrwl/schematics 7.5.1. jest-preset-angular 6.0.1.

Update: apologizes for the mistake, you have to include allowJsas well. The reason why I got it working is I didn't clean cache. Before testing remember to do so.

whizkidwwe1217 commented 4 years ago

@llwt Where can I find the jest-preset-angular/preprocessor.js? It's not available in jest-preset-angular@8.0.0

ahnpnl commented 4 years ago

@llwt Where can I find the jest-preset-angular/preprocessor.js? It's not available in jest-preset-angular@8.0.0

8.0.0 no longer has preprocessor.js in source . It is now delegated fully to ts-jest. Jest-preset-angular only applies custom ast transformers to ts-jest to alter the compiled output.

Lonli-Lokli commented 3 years ago

Still same issue with anagular, jest-babel does not work and I actually do not want to meess both of them just because of this lodash-es stuff... wtf

shayll-herolo commented 3 years ago

the only thing that worked for me was to add this to the main jest.config.js

moduleNameMapper: {
  "^lodash-es$": "lodash"
}

make sure you have lodash in you devDependencies

zhangchi1 commented 3 years ago

Solved this without allowJs, the only 1 file which changed it was jest.config.js (local lib file, not in global jest.config.js) by adding transformIgnorePatterns lijke below

module.exports = {
  name: 'contact',
  preset: '../../jest.config.js',
  coverageDirectory: '../../coverage/libs/contact',
  transformIgnorePatterns: ['../../node_modules/(?!${ng2-tel-input})']
};

Where ng2-tel-input was the problematic module, getting it for the following environment: Jest 23.6.0, @nrwl/builders 7.5.1, @nrwl/nx 7.5.1, @nrwl/schematics 7.5.1. jest-preset-angular 6.0.1.

Hi @danieldanielecki thank you so much for your answer. I'm wondering how did you find out which module was the problematic module? Thanks~

danieldanielecki commented 3 years ago

Solved this without allowJs, the only 1 file which changed it was jest.config.js (local lib file, not in global jest.config.js) by adding transformIgnorePatterns lijke below

module.exports = {
  name: 'contact',
  preset: '../../jest.config.js',
  coverageDirectory: '../../coverage/libs/contact',
  transformIgnorePatterns: ['../../node_modules/(?!${ng2-tel-input})']
};

Where ng2-tel-input was the problematic module, getting it for the following environment: Jest 23.6.0, @nrwl/builders 7.5.1, @nrwl/nx 7.5.1, @nrwl/schematics 7.5.1. jest-preset-angular 6.0.1.

Hi @danieldanielecki thank you so much for your answer. I'm wondering how did you find out which module was the problematic module? Thanks~

Sorry, but don't remember now... For sure it was one of those which caused errors on Server-Side Rendering (SSR)/Angular Universal.

shivaaudam commented 3 years ago

I had to remove the <rootDir> from ours:

const esModules = ['@agm', 'ngx-bootstrap'].join('|');
// ...
module.exports = {
  //...
    transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
  // ...
};

I am using deepdash in place of loadash added below in jest.preset.js

transform: { '^.+\.(ts|js|html)$': 'babel-jest', }, transformIgnorePatterns: [ "/node_modules/(?!deepdash-es/.*)" ],

and created a new file babel.config.json and added

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

and inside individual lib jest.config.js added

transformIgnorePatterns: ['../../node_modules/(?!${deepdash-es})'], transform: { "^.+\.jsx?$": "babel-jest" }, moduleNameMapper: { "^deepdash-es$": "deepdash" },

in tsconfig.spec.json added "allowJs": true,

nothing is working, still the issue persists, could you please help on this.

umairhm commented 3 years ago

This is what worked for me. We were having issues consuming imask and countup.js libs and used the configs mentioned here: https://github.com/thymikee/jest-preset-angular#transpile-js-files-through-babel-jest

Thanks @llwt 🙂

One other option is to pull in babel-jest and tell it to transpile those js files.

From the jest-preset-angular docs:

Transpile js files through babel-jest

Some vendors publish their sources without transpiling. You need to say jest to transpile such files manually since typescript (and thus ts-jest used by this preset) do not transpile them.

  1. Install babel-preset-env and add .babelrc (or modify existing if needed) with that contents:
{
  "presets": ["env"]
}
  1. Update Jest configuration (by default TypeScript process untranspiled JS files which is source of the problem):
{
  "jest": {
    "transform": {
      "^.+\\.(ts|html)$": "<rootDir>/node_modules/jest-preset-angular/preprocessor.js",
      "^.+\\.js$": "babel-jest"
    },
  }
}

We took that and tweaked it to only pass the js files needed through:

const esModules = ['@agm', 'ngx-bootstrap', 'lodash-es'].join('|');

module.exports = {
  // ...
  transform: {
    [`(${esModules}).+\\.js$`]: 'babel-jest',
    '^.+\\.(ts|js|html)$': 'jest-preset-angular/preprocessor.js',
    // ...
  },
  transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
  // ...
};
carlnc commented 3 years ago

Keywords: monorepo jest typescript create-react-app

Works: cd packages/somePackage && yarn jest --no-cache --coverage Fails: yarn jest --no-cache --coverage packages/somePackage

Error:

    Details:

    /Users/…/development/work/monorepo-root/node_modules/babel-preset-react-app/node_modules/@babel/runtime/helpers/esm/slicedToArray.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import arrayWithHoles from "@babel/runtime/helpers/esm/arrayWithHoles";
                                                                                             ^^^^^^

    SyntaxError: Cannot use import statement outside a module

Workaround: in: jest.config.js

    transformIgnorePatterns: [
        `[/\\\\]node_modules[/\\\\](?!${[
            'babel-preset-react-app',
            '@babel',
        ].join('|')})`,
    ],
ahnpnl commented 3 years ago

the only thing that worked for me was to add this to the main jest.config.js

moduleNameMapper: {
  "^lodash-es$": "lodash"
}

make sure you have lodash in you devDependencies

Actually this one is better

'^lodash-es/(.*)$': '<rootDir>/node_modules/lodash/$1',

Because mapping to lodash will resolve in LodashWrapper object instead of the function itself

rikriki commented 3 years ago

for next release of nx(11.5.2) you can do this to fix the issue. Install babel-jest and @babel/preset-env

create babel.config,js in the root of the workspace and add this module.exports = { presets: ['@babel/preset-env'] };

then in your jest,config.js add this transformIgnorePatterns: [/node_modules/?!lodash-es], transform: { '^.+\.(ts|html)$': 'ts-jest', '^.+\.js$': 'babel-jest' }

austinthedeveloper commented 2 years ago

I was not able to get this solution to work.

'^lodash-es/(.*)$': '<rootDir>/node_modules/lodash/$1',

Instead I wrote it like this and it worked:

moduleNameMapper: {
    '^lodash-es/(.*)$': 'lodash/$1',
  },
dholms commented 2 years ago

For anyone that is still running into this issue in the context of a monorepo (yarn workspace in my case), make sure that you are not hoisting the package that needs to be transpiled.

The solution that worked for me:

babel.config.js in the package root (not the workspace root):

module.exports = {
  presets: [['@babel/preset-env']],
}

Jest config:

const esModules = ['ipld-hashmap'].join('|') // can add other packages here

module.exports = {
  ...
  transform: {
    '^.+\\.ts$': 'ts-jest',
    "^.+\\.js?$": "babel-jest"
  },
  transformIgnorePatterns: [`<rootDir>/node_modules/(?!${esModules})`],
  ...
}

Root package.json

  "workspaces": {
    "packages": ["packages/*"],
    "nohoist": ["**/ipld-hashmap", "**/ipld-hashmap/**"]
  }

Any one of those four things (nohoist, transform, transformIgnorePatterns, and babel config) missing & it won't work.

hoi4 commented 1 year ago

I had this issue with Swiper and was able to get it to work by using the following in the app's jest.config.js file:

transformIgnorePatterns: ['node_modules/(?!(.*\\.mjs$|swiper|ssr-window|dom7))'],
modulePaths: ['<rootDir>/../../node_modules/swiper/angular/'],

Swiper depends on ssr-window and dom7 which is why I had to add these modules to the transformIgnorePatterns as well.

Note that modulePaths need to be absolute, therefore the usage of <rootDir>.

david-morris commented 1 year ago

I had this issue with react-merge-refs while using @swc/jest and setting transformIgnorePatterns wasn't helping.

The problem? If you're setting this up for an error coming from a .mjs file, you probably need to change your pattern under transform in your jest config, since its recommended pattern does not match .mjs files. Depending on what you have in your options on your @swc/jest transformer entry, it might make sense to add a new transformer entry, like this:

transform: {
...
    "node_modules\\/.+\\.(js)|(mjs)$": "@swc/jest",
...
}

This is in addition to the transformIgnorePatterns.

asztal commented 1 year ago

In case it helps anyone - in my case, using a monorepo with babel-jest, I needed to add:

transform: {
    '\\.[tj]sx?$': ['babel-jest', { rootMode: 'upward' }]``
}

without the rootMode option, babel-jest was giving syntax errors when importing other libraries in the monorepo.

McGiogen commented 1 year ago

asztal solution works for me with Nx 15.x, Angular 15.x and Jest 28.x

github-actions[bot] commented 1 year ago

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.