neutrinojs / neutrino

Create and build modern JavaScript projects with zero initial configuration.
https://neutrinojs.org
Mozilla Public License 2.0
3.95k stars 214 forks source link

jest: Support overriding moduleNameMapper via preset options #1651

Closed davidje13 closed 3 years ago

davidje13 commented 3 years ago

As explained in #1650, this small change makes it possible for users to override default configuration for Jest's moduleNameMapper.

Specifically, this is important for me so that I can integrate svgr into my project correctly;

    jest({
      moduleNameMapper: {
        '\\.svg$': '<rootDir>/mocks/svgr.js',
      },
    }),

(without this change, the above .svg rule is ignored due to the built-in rule taking priority; Jest follows the key ordering of the entries in moduleNameMapper).

I do not expect this to break anybody's config unless they had it misconfigured to begin with. If it does break for anybody, the fix is to remove any user-provided config that was previously being ignored.

constgen commented 3 years ago

I would try https://github.com/vyushin/file-replace-loader . It may look like this.

let fileReplaceLoader = require.resolve('file-replace-loader')
let testEnvironment = process.env.NODE_ENV = 'test'

neutrino.config
   .when(testEnvironment, function(config){
     config.module.rule('replace-svg')
       .use('file-replace')
         .test(/\.svg$/i)
         .loader(fileReplaceLoader)
         .options({
           condition: 'if-replacement-exists',
           replacement: resolve('./tests/mock/svgr.js'),
           async: true,
        })
        .end()
     .end()
  })

I could make some mistakes because didn't run it. You can change it to match your needs

davidje13 commented 3 years ago

@constgen that may be a viable workaround but it's not a proper solution considering Jest has this capability built-in.

For now my preferred workaround is to set jest.config.js to:

const neutrino = require('neutrino');

const config = neutrino().jest();
config.moduleNameMapper = {
  '\\.svg$': config.moduleNameMapper['\\.svg$'],
  ...config.moduleNameMapper,
};
module.exports = config;

(which applies the fix shown here as a post-processing stage)

constgen commented 3 years ago

Proposed change is not a fix but a new feature. Which also applied only to Jest. Other testing frameworks may still stay broken when work with SVGR. So I suggest better develop SVGR middleware which will be testing framework agnostic and doesn't require changes to core middlewres. It is easier to upgrade versions and move forward for both Neutrino and community middlewares in such architecture. Also just look at this example

module.exports = {
  use: [
    svgr(), // future SVGR midleware
    jset()
  ]
}

In you case svgr() has no chane to modify Jest config as it is not a part of webpack-chain. The end user will have to jest({ moduleNameMapper: {'\\.svg$': '<rootDir>/mocks/svgr.js'}}) every time. In my case no extra configuration is required. Everything works out of the box because svgr() will setup everything.

davidje13 commented 3 years ago

Take SVGR out of the equation for a moment; the feature added here is the ability to set part of Jest's configuration which is currently not configurable if using neutrino.

Looked at one way, you could say this is a bug fix because it makes the ability to override configuration more consistent (if you set rootDir, testRegex, testEnvironment, etc. they will all override the default config coming from neutrino due to the deepmerge call. But it is currently only possible for users to override the default moduleNameMapper config if they set the exact same key that neutrino sets. This is because Jest uses the order of the properties (tut tut) to determine priority, but of course deepmerge has no idea that's the case. It merges properties the other way around because really why shouldn't it; no sensible person would ever rely on property ordering in JavaScript. The combination of these two things in the upstream projects causes an observable inconsistency in neutrino. Other configurable fields are not affected by this (except maybe transform but I don't know so I left it as-is for now).

The fact that I discovered this problem while working with svgr is mostly coincidental, and I put those notes in just to give context on why this matters. The core of this is fixing the configuration precedence in the Jest module.

Other modules might have similar bugs which could be fixed separately, but I'd be surprised; the root cause here is Jest's bad choice of using object property ordering to determine priority.

constgen commented 3 years ago

Now I really see what you mean. It makes sense. If I was a maintainer I would confirm this PR.

edmorley commented 3 years ago

This has been released in Neutrino v9.5.0 :-)