ts-jest: Does not resolve path from tsconfig.js 'paths' option.

  • Issue

[describe the issue here]

Seems to not use path mappings from tsconfig.js that work fine with the rest of my Typescript project. Instead, I have to implement the babel module resolver so I have to maintain two mappings which is not fun.

tsconfig.js:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2017",
        "jsx": "react",
        "moduleResolution": "node",
        "allowJs": true,
        "importHelpers": true,
        "allowSyntheticDefaultImports": true,
        "isolatedModules": false,
        "noUnusedLocals": true,
        "noImplicitReturns": true,
        "noImplicitThis": true,
        "suppressImplicitAnyIndexErrors": true,
        "pretty": true,
        "sourceMap": false,
        "skipLibCheck": true,
        "baseUrl": "src",
        "paths": {
            "*": "../node_modules_local/*",
            "~/*": "./*"
        },
        "typeRoots": [
            "node_modules/@types"
        ]
    }
}
  • Expected behavior

[describe the expected behavior here]

  • Link to a minimal repo that reproduces this issue

[If you haven’t already, create the smallest possible repo that reproduces this issue by running npm install and npm test. This will speed up any fixes that this issue might need]

  • Optional (but highly recommended) - Configure Travis (or your favorite system) with the minimal repo

This allows potential solutions to be tested against the minimal repo. This saves everyone time and avoids a lot of back and forth.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 35
  • Comments: 18 (2 by maintainers)

Commits related to this issue

Most upvoted comments

I faced this as well, however I didn’t think it was an issue. Although it does make sense that the paths in tsconfig are respected automatically from ts-jest.

I used the moduleNameMapper jest config property and set the same paths. You could try that as a workaround for the time being?

Bear in mind the syntax is different for setting the same paths in moduleNameMapper such that:

"my-path/*": ["src/my-path/*"]

would be:

"my-path/(.*)": "<rootDir>/src/my-path/$1"

Hope this helps for the time-being.

I am using the moduleNameMapper, after stugling to make it work, I finally had to pass: { prefix: '<rootDir>/' } to the second parameter. Here below is my working jest-e2e.jsfile.

const { pathsToModuleNameMapper } = require('ts-jest/utils');
// In the following statement, replace `./tsconfig` with the path to your `tsconfig` file
// which contains the path mapping (ie the `compilerOptions.paths` option):
const { compilerOptions } = require('./tsconfig.e2e.json');

const { defaults } = require('jest-config');
module.exports = {
    'moduleFileExtensions': [
        ...defaults.moduleFileExtensions,
    ],
    'rootDir': '.',
    'testEnvironment': 'node',
    'testRegex': '.*spec.ts$',
    'transform': {
        '^.+\\.(t|j)s$': 'ts-jest',
    },
    moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths , { prefix: '<rootDir>/' } ),

    globals: {
        "ts-jest": {
            "tsConfig": "./tsconfig.e2e.json"
        }
    }
};

For anyone who might run into the same problem, you may want to specify the absolute path to the mapper in jest.config.js

const {
  compilerOptions
} = require('./tsconfig');

const {
  resolve
} = require('path');


module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  "collectCoverageFrom": [
    "**/*.{ts,tsx}",
    "!**/node_modules/**",
    "!**/lib/**",
    "!**/vendor/**"
  ],
  moduleNameMapper: {
    '^@core/(.*)$': resolve(__dirname, './src/modules/core/$1'),
    '^@components/(.*)$': resolve(__dirname, './src/components/$1'),
    '^@products/(.*)$': resolve(__dirname, './src/modules/products/$1'),
    '^@layouts/(.*)$': resolve(__dirname, './src/modules/layouts/$1'),

  },
};

The way I approached this was to just map the values from the tsconfig.json into the jest.config.js file like so:

const tsconfig = require('./tsconfig.json')
const paths = tsconfig.compilerOptions.paths

const moduleNameMapper = Object.keys(paths).reduce((acc, curr) => {
  return {
    ...acc,
    [curr]: '<rootDir>' + paths[curr]
  }
}, {})

module.exports = {
  ...
  moduleNameMapper,
 ...
}

any reason we can’t have ts-jest resolve the tsconfig paths automatically?

@alexanderby you need to add

    moduleNameMapper: {
        "malevic": "<rootDir>/entries",
    },

in jest config.

ts-jest doesn’t pick up or resolve any files. All files are resolved by jest and then passed in to ts-jest for processing. That’s why jest needs to know how to resolve modules - the above code does that

@CanKattwinkel

  • change moduleNameMapper
"@core/(.*)": "<rootDir>/../../core/$1"

I honestly don’t know why ../.. is required but this seemed to work

  • install ts-jest in core’s directory or in one of its ancestor directory

  • implement the core/some-shared-model.ts file

this should get tests passing

In my case I used modulePaths in jest.config.js and it solved the issue.

/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable no-undef */
const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig.json');
const moduleNameMapper = pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' });
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src'],
  testMatch: [
    '**/__tests__/**/*.+(ts|tsx|js)',
    '**/?(*.)+(spec|test).+(ts|tsx|js)',
  ],
  transform: {
    '^.+\\.(t|j)s$': 'ts-jest',
  },
  moduleNameMapper,
  modulePaths: ['<rootDir>/src']
};

And my tsconfig.json:

{
  "compilerOptions": {
    "target": "es2019",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./build",
    "incremental": true,
    "moduleResolution": "node",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "lib": [
      "ES6"
    ],
    "noImplicitAny": true,
    "baseUrl": "src",
    "paths": {
      "src/*": [
        "src/*"
      ]
    }
  },
  "include": [
    "src"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ]
}

So then I can import import { foo } from 'core/parser';

root 
|____src
        |____core
                |____parser
                        |____index.ts 

If you don’t want to expend cycles trying to understand the regex patterns required to port your tsconfig.json paths key into jest.config.js’s moduleNameMapper key, I found an npm module to do it for you.

Assuming you have thee tsconfig.json and jest.config.js both defined in the root of your project, from your project root run:

npm install jest-module-name-mapper
node
> let mapper = require('jest-module-name-mapper')
mapper()
// '^common/(.*)': '<rootDir>/./common/src/main/ts/$1'

Then add the output as the keys of your jest’s moduleNameMapper field.

https://www.npmjs.com/package/jest-module-name-mapper

Here’s another example for folks specifically trying to get ~/* to work since copy-pasta is helpful:

If you had this tsconfig.json

{
  "compilerOptions": {
    "paths": {
      "~/*": ["./*"]
    },
    // etc...
  }
}

You can use this jest.config.js:

module.exports = {
  preset: 'ts-jest',
  moduleNameMapper: {
    '^~/(.*)$': '<rootDir>/$1',
  },
  // etc...
};

Thanks, it worked and I used aliases in all my tests. One strange thing is that properties’ order matters https://github.com/alexanderby/malevic/commit/5949e1b8413866d2ff3c3f4f3ae6f91440e2de03

Oh the shared model was a refactoring corpse… But yes after I applied your steps I got it working.

Therefore a very big thank you.

@kulshekhar would you mind to take a look at this minimal reproduction repository?

I’ve a shared core (./core) project that I’m including into my project. It is located next to a nestjs typescript project (./api) that uses Jest for testing. For tsc I’ve added the correct path mapping in my tsconfig file.

After I experienced errors with jest not reading this configuration I found this issue. Therefore a moduleNameMapper was added to the ./api/package.json-File. Maybe my mapping is just wrong but I’m still experiencing errors when running npm run test:

Could not locate module @core/some-model (mapped as /home/.../path-issue/core) (which actually looks good to me - or like the correct path)

Please provide a minimal reproducible repo, this isn’t enough for us to go on.