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

Support the "module" package.json field #2702

Closed
quantizor opened this issue Jan 26, 2017 · 26 comments · Fixed by #10393
Closed

Support the "module" package.json field #2702

quantizor opened this issue Jan 26, 2017 · 26 comments · Fixed by #10393

Comments

@quantizor
Copy link
Contributor

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.

@thymikee
Copy link
Collaborator

thymikee commented Jan 26, 2017

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

@quantizor
Copy link
Contributor Author

Working on it right now :)

@thymikee
Copy link
Collaborator

Awesome 😎

@quantizor
Copy link
Contributor Author

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

@mrbinky3000
Copy link

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

@quantizor
Copy link
Contributor Author

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

@canercandan
Copy link

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

@loklaan
Copy link

loklaan commented Oct 20, 2017

@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.

@canercandan
Copy link

@loklaan awesome thanks I will give it a try

@TehShrike
Copy link

@probablyup looks like the maintainers are still interested in merging if you want to re-open the pull request: #2704 (comment)

@quantizor
Copy link
Contributor Author

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

@dan-kez
Copy link

dan-kez commented Mar 24, 2018

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.

@jeysal
Copy link
Contributor

jeysal commented Apr 30, 2018

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',
  },

@dan-kez
Copy link

dan-kez commented Jul 5, 2018

@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',
  },

@eckdanny-osi
Copy link

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

@hedgepigdaniel
Copy link

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.

@bdwain
Copy link

bdwain commented Sep 30, 2019

Is this the code that needs to be changed to support this? https://github.com/facebook/jest/blob/master/packages/jest-resolve/src/defaultResolver.ts#L131

@alesmenzel
Copy link
Contributor

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

@jennale
Copy link

jennale commented Feb 12, 2020

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

@SimenB
Copy link
Member

SimenB commented Feb 13, 2020

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 #2702 (comment) 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

@SimenB
Copy link
Member

SimenB commented Apr 5, 2020

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.

@makotoshimazu
Copy link

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!

@wickkidd
Copy link

@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

@scinos
Copy link
Contributor

scinos commented Aug 11, 2020

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

@hedgepigdaniel
Copy link

thanks @scinos!

@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 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.