I'm building a modular build system that ideally is going to be its own npm package soon enough. However right now, its just a parallel directory:
/build-system
/project
/src
One of the goals is to separate out all the build dependencies. I want to everything to just work, so I'm not using a .babelrc
file and instead linking to babel presets inside the webpack config:
babel: {
presets: [
'babel-preset-es2015',
'babel-preset-react',
'babel-preset-stage-0',
].map(require.resolve),
},
So I was hoping that somehow I could configure and run Jest on the a targeted directory without specified configurations that doesnt rely on directory structure.
Would it be possible to get a config here to set these babel options? What do you think would be the best practice for this?
Would love to know the answer on this here as well
You can wrap babel-jest and create your own preprocessor by just passing in the options you want. That should be sufficient.
On Fri, Aug 19, 2016 at 9:04 PM +0200, "Josh Story" <[email protected]notifications@github.com> wrote:
Would love to know the answer on this here as well
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHubhttps://github.com/facebook/jest/issues/1468#issuecomment-241106867, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AAA0KEhHwHWLdV4HMHF4-ck3CRpb7QsQks5qhf4VgaJpZM4Joskp.
and how do I get that into jest?
@ccorcos you can specify your custom preprocessor in the config
http://facebook.github.io/jest/docs/api.html#scriptpreprocessor-string
@dmitriiabramov I'm in a similar situation. Working on a modular build system and need to be able to pass in some runtime configuration to the scriptPreprocessor. But since scriptPreprocessor
is expected as a string
, it's impossible to pass in any runtime configuration without resorting to some global shared state mutation.
If, instead, it were to accept a string
_or_ function
I think that would solve the problem, but I'm not sure if that would be incompatible with some of Jest's internals? If not, I'd be happy to submit a PR to allow for this. Please advise.
Thanks!
You can make your own preprocessor that simply does this:
require('babel-jest').createTransformer(babelOptions)
and then point scriptPreprocessor to that file.
See https://github.com/facebook/jest/blob/master/packages/babel-jest/src/index.js#L53
This makes the simple case simple and the hard case possible. I don't think we'll make scriptPreprocessor a function and the current solution should work well enough.
@cpojer thanks for taking the time to reply! Sorry but maybe I'm not doing a good job of explaining what's happening. So, what you have suggested is what I am doing. However, I don't see a way of passing in (at run-time) parameters that would affect babelOptions
.
/* jestConfig.js */
module.exports = {
...
scriptPreprocessor: path.resolve('./myCustomPreprocessor.js') // no way to pass in runtime options!
...
};
/* myCustomPreprocessor.js */
// pseudo-code, this doesn't actually work obviously
module.exports = (options /* <-- how to specify?? */) => {
const babelOptions = options; // assuming some merging, conditional logic, etc.
return babelJest.createTransformer(babelOptions);
}
$ npm run testScript --optionThatAffectsBabelOptions
Assuming testScript
is what eventually runs Jest, I don't see a way of passing in optionThatAffectsBabelOptions
. Maybe modifying some global
shared state or using a singleton pattern and requiring the preprocessor early in the run-time (although I'm not sure that would even work since I believe Jest spawns threads right?). Any ideas? I'd be happy to create a sample repo to help illustrate the problem more if it's still unclear. Or maybe I'm just missing something obvious (possible!). Thanks for your help.
@ccorcos right, but notice that CRA is not passing in any runtime options. It's all just loading static files.
I don't understand what you are saying with "runtime". The preprocessor is required by Jest and is used to transform files. When it is being required you can do whatever you want to create your babel options object that you pass on to createTransfomer
. I'm pretty sure there is just some confusion here because this API gives you all the power you need and if it's not enough then there is some confusion left.
@tizmagik as i understand you're trying to pass a parameter from your build process (e.g. grunt
or gulp
pipeline) into jest transform, so that jest transformation is affected by outer process?
as @cpojer said, you can do anything inside your custom preprocessor, but if you really need to pass some external parameters you can probably use environment variables.
e.g.
spawnProcess('jest', {env: {ADD_CUSTOM_TRANSFORMATION: 1}});
then in your customPreprocessor:
if (process.env.ADD_CUSTOM_TRANSFORMATION === 1) {
plugins.push(require('my-custom-transformation-plugin');
}
Ah yes, I see what you mean @tizmagik. You want to be able to something like this:
scriptPreprocessor: require('config/jest/transform.js')({development: true, some_feature: false}),
but all we have is this:
scriptPreprocessor: path.resolve('config/jest/transform.js'),
@ccorcos yes, exactly!
What @dmitriiabramov suggested would work, but I would rather avoid having to simulate/affect some global state.
Would you accept a PR for this change?
I still don't fully follow here. If you pass in the options to the scriptPreprocessor from the outside I see no problem to instead create them from the inside. Just factor out the piece of code that creates your babel config:
// babelConfig.js
module.exports = {babel options};
// transformer
module.exports = babelJest.createTransformer(require('./babelConfig'));
// other build scripts
require('./babelConfig');
we are using this approach all over at FB and it works well for us.
Yeah but that not a runtime configuration.
On Mon, Sep 12, 2016 at 11:53 Christoph Pojer [email protected]
wrote:
I still don't fully follow here. If you pass in the options to the
scriptPreprocessor from the outside I see no problem to instead create them
from the inside. Just factor out the piece of code that creates your babel
config:// babelConfig.js
module.exports = {babel options};// transformer
module.exports = babelJest.createTransformer(require('./babelConfig'));// other build scripts
require('./babelConfig');we are using this approach all over at FB and it works well for us.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/facebook/jest/issues/1468#issuecomment-246452539, or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABth342ubUzRMvRHVHW3IiJtvleGMhKqks5qpZ_CgaJpZM4Joskp
.
@tizmagik i think the biggest thing here is being able to pass config to child processes, when we run tests in parallel.
I don't think there's an easy way to pass a function to another process right now, but i might be wrong.
see https://github.com/facebook/jest/blob/master/packages/jest-cli/src/TestRunner.js#L255
oh interesting. you accept only file path for parallelism... seems like we should be able to solve this issue if the args are serializable. then we have something kind of like how webpack loaders work:
{
loader: 'babel',
query: {
presets: ['es2015'],
}
}
@ccorcos can you show a real example of why my proposal won't work? In practice it worked really well for us. It requires you to only invert a few things and will probably lead to a better separation of concerns.
Here's a pseudocode example:
const buildOptions = parseCliArgs(process.argv)
const babelConfig = generateBabelConfig(buildOptions)
runJest(babelConfig)
The buildOptions
are not static -- they're generated at runtime based. runJest
doesnt accept a babel config, but to a file that exports a babel config. So perhaps a solution would be to write the babelConfig to a file in /tmp
and point to it with this scriptProcessor option, but thats certainly hacky.
Why do your build options change so much and why are they generated? I feel like there must be a simpler way with more static build configuration that's stored in a file somewhere.
because I have a complicated (legacy) build system and have to support several different ways of doing things.
For those landing on this issue and wondering what scriptPreprocessor
is (it's since been removed), here is how I got jest to work with babel without using .babelrc
.
// Custom Jest transform implementation that wraps babel-jest and injects our
// babel presets, so we don't have to use .babelrc.
module.exports = require('babel-jest').createTransformer({
presets: ['node7', 'react', 'stage-2'], // or whatever
});
package.json
):"transform": {
"^.+\\.js$": "<rootDir>/jest.transform.js"
},
Now run jest with these options:
jest --config jest.config.json --no-cache
The --no-cache
option bit me hard - when you're messing with transforms, jest will often skip your custom transformer entirely if it thinks it's already transformed it. Once you have things working smoothly, you can drop --no-cache
.
Other option could be to keep .babelrc
and use webpack babel-loader babelrc
option to true
.
Then at least you can share babel settings in one place,
or if .babelrc
is not an option, here is an example based on @nfarina code,
but reading the babel options directly from your webpack.config.js.
Of course the webpackConfig.module.rules[1].query
is ugly and should be replaced by a function that iterates your rules and finds loader: 'babel-loader'
, to return query property.
var webpackConfig = require('../../webpack.config.js')
var babelConfig = webpackConfig.module.rules[1].query
module.exports = require('babel-jest').createTransformer(babelConfig)
@nfarina thank you so much for your solution above. I struggled for hours this morning trying to get Jest to recognize import
statements in a setup using Webpack and babel-loader, but no .babelrc
.
Trying the solution posted by @nfarina, but it appears as if Jest is ignoring my transform. I even tried manually deleting the Jest cache with --clearCache.
@sparkbuzz not sure if you're still blocked, but here's how I used @nfarina 's idea:
jest.config.js
module.exports = {
verbose: true,
transform: { '^.+\\.js$': '<rootDir>/jestPreprocess.js' },
};
jestPreprocess.js
const babelOptions = { presets: ['env'] };
module.exports = require('babel-jest').createTransformer(babelOptions);
package.json
"scripts": {
"test": "jest --config jest.config.js --no-cache",
// ...
Hope that helps or sparks a solution for you. The issue might be that you're using --clearCache
instead of --no-cache
.
For anyone stuck on this - the approach we took was:
// transformer.js
const { util, transform } = require('babel-core');
module.exports = {
process(src, filename, config) {
return util.canCompile(filename) ?
transform(src, Object.assign({}, { filename }, config.globals.BABEL_OPTIONS)) :
src;
}
};
And then pass BABEL_OPTIONS
to runCLI
via globals
.
But beware we also had to stringify the options passed to runCLI
or they were silently ignored (see #5429).
I have a use case to be able to call runCLI(config)
where config contains inline transforms (not required from a separate file).
To make this work I opened the following PR: #7398
Sorry for reviving this thread, but I would really like to see an official documented way to provide babel options to jest outside of .babelrc
. Both Webpack and Rollup both have the ability to do this, and it makes a lot of sense to me because I very often use different babel options for my bundling vs my unit-testing.
@onlywei you don't have to use .babelrc
to configure jest to work with babel.
You can setup a custom transform in your jest config that can do whatever you want (with the caveat that it has to point to a file or package that does the transform).
If you provide a little more information about what you're trying to do I can try and be a little more specific in suggesting a solution... ;-)
Another possibility (if using Jest 24, currently in beta) is to check if caller.name === 'babel-jest' || caller.name === 'jest-runtime'
in your babel.config.js
. See https://babeljs.io/docs/en/config-files#apicallercb
If you _really_ don't want a config file, using createTransformer
from babel-jest
is the way to go, see https://jestjs.io/docs/en/tutorial-react#custom-transformers
What a mess; so many configuration files and/or nasty hacks necessary.
What a mess; so many configuration files and/or nasty hacks necessary.
This is the Javascript world.
@SimenB
Another possibility (if using Jest 24, currently in beta) is to check ifcaller.name === 'babel-jest' || caller.name === 'jest-runtime'
in yourbabel.config.js
. See https://babeljs.io/docs/en/config-files#apicallercb
Thanks.
I could not get the same way as caller
(exactly, api.caller((caller) => caller.name)
) was undefined
.
But I've solved the problem as follows:
// babel.config.js
module.exports = function (api) {
const isJestEnv = api.env() === 'test'
// This line is required AFTER any calls of api's method.
api.cache.forever()
// Nothing changed? TRY `jest --no-cache` as @nfarina mentioned above.
return {
presets: [
'@vue/app'
],
plugins: (
// Activate the require-context-hook plugin at the only Jest env,
// to avoid `fs.readdirSync is not defined` error
// on the Webpack-generated code.
isJestEnv ? [
'require-context-hook'
] : []
)
}
}
Most helpful comment
For those landing on this issue and wondering what
scriptPreprocessor
is (it's since been removed), here is how I got jest to work with babel without using.babelrc
../jest.transform.js
./jest.config.json (or "jest" entry in
package.json
):Now run jest with these options:
jest --config jest.config.json --no-cache
The
--no-cache
option bit me hard - when you're messing with transforms, jest will often skip your custom transformer entirely if it thinks it's already transformed it. Once you have things working smoothly, you can drop--no-cache
.