Jest: Support the "module" package.json field

Created on 26 Jan 2017  路  25Comments  路  Source: facebook/jest

Similar to how there's an option to use "browser", it would be awesome to specify that Jest respect the "module" package.json field to resolve ES modules when using Babel with Jest.

Enhancement

Most helpful comment

FYI @dmk255 and others who might have the same setup:
I have a lerna setup as well with main set to dist/index.js in the package.json files, while the source files are src/index.ts.
Until this issue is resolved, I currently use the moduleNameMapper option to remap @myscope/x to <rootDir>/packages/x/src in jest.config.js:

  moduleNameMapper: {
    '^@myscope\\/([^/]+)': '<rootDir>/packages/$1/src',
  },

All 25 comments

Are you interested in shipping a PR with this? :)

Working on it right now :)

Awesome 馃槑

@thymikee would you mind reviewing that PR? I asked a few others but they seem to be busy.

Just wondering where we stand on this PR? Jest support for the "module" field would really help me out.

I submitted a PR and it was ignored for a while, so I closed it.

anyone can help unlock this freeze here? I聽am pretty much in the same situation than @probablyup and having this module parameter is very helpful for mono-repo based environment such as lerna

@canercandan Had same case as you. Got around the issue by configuring Jest with a custom resolver that remaps module & main fields: https://gist.github.com/loklaan/9fe7768576466c5a31c2e7af4cfdecd0

Until the fields become configurable, this is the only (?) way to do what you need. You might need to write this resolver anyway, if you end up enabling watched-compilation for interdependent packages in your repo.

@loklaan awesome thanks I聽will give it a try

@probablyup looks like the maintainers are still interested in merging if you want to re-open the pull request: https://github.com/facebook/jest/pull/2704#issuecomment-338410586

I鈥檓 currently away but might have some time to reboot the PR this weekend. If someone else wants to grab it that鈥檚 fine too.

This is a touch of a blocker for me. I'm using lerna to manage my repository. At present I need to run babel to build my dependencies and then run jest. Otherwise jest won't be able to find my import since the path in the main key in package.json doesn't exist until babel is run.

e.g. babel runs and generates files in lib. If babel isn't run, then the module doesn't resolve.

in package.json:

{
"main": "lib/index.js",
"jsnext:main": "src/index.js"
}

I'm really excited about this PR as it will significantly speed up our test times.

FYI @dmk255 and others who might have the same setup:
I have a lerna setup as well with main set to dist/index.js in the package.json files, while the source files are src/index.ts.
Until this issue is resolved, I currently use the moduleNameMapper option to remap @myscope/x to <rootDir>/packages/x/src in jest.config.js:

  moduleNameMapper: {
    '^@myscope\\/([^/]+)': '<rootDir>/packages/$1/src',
  },

@jeysal Thank you! That's a great idea. I actually use the projects feature in lerna so each project has its own jest.config.js.

Due to this I had to tweak your example slightly (I also needed to exclude one package):

 moduleNameMapper: {
    '^@myscope\\/((?!config)[^\\/]+)': '<rootDir>/../$1/src',
  },

So happy I found this thread. Ty @jeysal!

This would be great to mentioned in the jest docs. The Babel site that has a section dedicated for monorepo concerns. If there was mention made some where close to the --projects config section, it'd probably help a lot of people out

Thanks @jeysal for the excellent solution! In my case I also had imports to subdirectories of packages in the monorepo, so I had to do the following:

moduleNameMapper: {
  // Note that the order of these two is important!
  '^@scope\\/([^/]+)\\/(.*)': '<rootDir>/../../packages/$1/src/$2',  // import '@scope/package/sub/dir'
  '^@scope\\/([^/]+)': '<rootDir>/../../packages/$1/src', }, // import '@scope/package'
}

Even though I think jest now does support the module field, it doesn't support other main field names (which you can configure in webpack with the mainFields option). So using this workaround works well if you want to use a field other than main or module.

This feature is still definitely not supported.

Are there any plans on finishing this after 3 years?
Support ESM modules through the "module": "x/y/z.js" field in package.json. (module > main)

See webpack which resolves it by default: https://webpack.js.org/configuration/resolve/#resolvemainfields

Great idea, thanks @jeysal!

Here's another possible mapping, for those who might have packages in nested directories.
For example if

  • @scope/package-a is in /packages/components/a and
  • @scope/package-b is in /packages/utilities/x/b
moduleNameMapper: {
  '^@scope\\/([^/]+)': '@scope/$1/src'  
  //Turns your @scope/package to @scope/package/src
}

This might only work if you are already exposing the /src directory in the package.json alongside the /dist though... ie through "files": ["src", "dist"] or something like that

Jest will never* support ESM through the module entry in package.json out of the box, as it's not in any widely adopted spec and there are no guarantees it's actually ESM (e.g. a single usage of process, require, module, __dirname, __filename anywhere will break), for much the same reason Node chose to introduce a new field rather than reuse it. ESM will be supported through the same way Node itself supports it (track #9430 for implementation status of native ESM support).

When it comes to how you as users can use the module field, I think plugging in a custom resolver that uses resolve and its packageFilter like @loklaan did in https://github.com/facebook/jest/issues/2702#issuecomment-338071583 is the correct approach. That gist can be packaged up in a module and distributed on npm, and then in your Jest config you can say resolver: 'jest-module-field-resolver'. Config wise as simple as module: true except for a single yarn add or npm install first.

(the same can (should) be done for the browser field, but removing it is potentially very breaking)

* of course things might change, but I highly doubt it

Note that the official way of supporting this in node is through conditional exports: https://nodejs.org/api/esm.html#esm_conditional_exports

When we support that (#9771) it might make sense to add an option to define "priorities" from that object so you could say you want browser or some such.

Hi,
I hit this issue like the others and resolved by using @loklaan 's code as a customized resolver as Simen suggested.
Then, I created a npm repo as his suggestion. I hope it'd help others.

Thanks!

@makotoshimazu thank you so much for making that! Worked like a champ.

FYI, I still had to add the es module in question to transformIgnorePatterns

If I'm reading the code of jest-resolve correctly, there is a problem with implementing a custom resolver: when running in watch mode, Jest will clean up the cache of the _default_ resolver on every run, but not the cache of a custom resolver.

Ideally the custom resolver could use options.defaultResolve (as documented in https://jestjs.io/docs/en/configuration#resolver-string) to call the original resolver and preserve the cache functionality. But the original options.defaultResolve does not pass all the options (specifically, it doesn't pass packageFilter) to resolve, so we can't map pkg.module to pkg.main

In other words, I'd like to write a resolver like:

module.exports = ( request, options ) =>
    options.defaultResolver( request, {
        ...options,
        packageFilter: ( pkg ) => {
            return {
                ...pkg,
                main: pkg.module,
            };
        },
    } );

But I don't see how it can be done without changing https://github.com/facebook/jest/blob/master/packages/jest-resolve/src/defaultResolver.ts#L42 to pass down options.packageFilter

thanks @scinos!

Was this page helpful?
0 / 5 - 0 ratings