babel-loader not transpiling packages in node_modules even after specifying in exclude block to ignore the package (lerna) - babeljs

So I am trying out monorepo design with lerna for our react applications.
the idea is to create one repo which will have all the react projects as lerna packages as well as some common modules/components which are shared across the applications.
now all these common modules/components are es6 modules. which are not transpiled. because there is continuous development going on the common modules as well. and if we build/transpile them I am sure react HMR will not work after that (a wild guess). following is my directory structure
package.json
lerna.json
|--packages
|--common
|--react-app
|--constants
|--utilities
common contains common react elements like table,accordion etc. which are exported as default es6 modules.
react-app imports common as dependency. react-app has webpack build configuration set.
now when i import common module into my react-app babel transform fails with this error
Button.component.jsx 7:19
Module parse failed: Unexpected token (7:19)
You may need an appropriate loader to handle this file type.
| const { Search } = Input;
| class TextBoxWithButton extends React.Component {
> static propTypes = {
| placeholder: PropTypes.string.isRequired,
| onAction: PropTypes.func.isRequired,
# ./src/App/Modules/Todo/Components/Header/Header.component.jsx 10:0-111 16:25-41
# ./src/App/Modules/Todo/Todo.component.jsx
# ./src/App/Router/index.jsx
# ./src/App/Layout/index.jsx
# ./src/App/index.jsx
# ./src/App.hot.js
# ./src/index.jsx
which means babel-loader is unable to parse and transpile whats in the node_nodules folder which makes sense because all dependencies are expected to be already transpiled. but not always. if you manage local dependencies you cannot keep them build all the time (that's what i think)
now I have found some solutions online that enable 1bable-loader not to exclude node_modules or ignoring #mypackagein exclude regex. but nothing is working in my case.
here is what i have tried so far.
remove exlucde: /node_modules/ from babel-loader => not working
use require.resolve('babel-loader') => not working
add resolve.symlinks= false.
add resolve.modules='node_modules' or
path.resove(__dirname,'node_modules') => not working
add packages path to babel-loader include include: [srcPath, lernaPackagesPath],
nothing seem to work.
is there something that i am missing ?
here is the link to my git test repo.

babel-loader by default will not transpile anything that is in node_modules. you can explicitly say what to transpile in node_modules but after #babel7.0.0 that doesn't seem to work either.
there is also a scope of .babelrc which was introduced in #babel7.0.0.
according to the research i did in under normal circumstances node_modules expect to have transpiled commonjs or umd modules. which can be imported by any application. in my case my packages/components where all es6 modules which needed to be transpiled. and my webpack build was failing because babel-loader was simply ignoring them.
so i decided to use #babel/cli to transpile each package where my components reside i had to add .babelrc along with other configurations to my component packages and build them with #babel/cli
here is the scripts in my package.json
"scripts": {
"start": "babel src --watch --out-dir dist --source-maps inline --copy-files --ignore spec.js,spec.jsx"
},
and my package.json looks something like this after that
{
"name": "#pkg/components",
"version": "1.0.1",
"description": "a repository for react common components. may or may not be dependent on elements",
"main": "dist/index.js",
"author": "hannad rehman",
"license": "MIT",
"scripts": {
"start": "babel src --watch --out-dir dist --source-maps inline --copy-files --ignore spec.js,spec.jsx"
},
"dependencies": {
"#pkg/constants": "^1.0.1",
"#pkg/elements": "^1.0.1"
},
"peerDependencies": {
"prop-types": "^15.6.2",
"react": "^16.4.2",
"react-router-dom": "^4.3.1"
}
}
with this approach. all of my common packages will be unit tested, linted and built before any application can import them. babel has a watch mode which will make sure transpilation happens always when a change occurs.
lastly and most importantly react HMR works as expected.
UPDATE
the above solution definitely works but after months i changed it by using include property in the babel loader
{
test: /\.js(x?)$/,
include: [/node_modules\/#pkg/],
use: [
'thread-loader',
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
configFile: path.resolve(
__dirname,
'../../../../',
`${env.appConfig.folderSrc}/babel.config.js`,
),
},
},
],
}

Related

How to configure babel to not do anything unless NODE_ENV is set to test?

I'm a bit new to babel, but what I'm trying to do is set it to run only when running npm run test and in no other contexts.
I thought that if I configure .babelrc like below, it will only take effect when I use "scripts"."test": "NODE_ENV=test ./node_modules/.bin/jest --no-cache" in package.json, but babel somehow applies (and messes up things) even with npm run watch, which uses NODE_ENV=development.
{
"env": {
"test": {
"presets":[
["#babel/preset-env"],"#babel/react"
],
"plugins": [
"transform-es2015-modules-commonjs",
"dynamic-import-node"
]
}
}
}
I get a TypeError: this.setDynamic is not a function. I have to remove entire babel dependencies and configurations for project to work again, but then I can't run the jest tests.

Babel: root programmatic options

I seem to absolutely not grasp where to put root programmatic options for the babel.
If I have a monorepo and need to tell the different sub packages that they shall look upwards for my babel.config.js then I should put rootMode: "upwards" into the .babelrc of the sub packages, correct? This does not work, because of the resulting error
Error: .rootMode is only allowed in root programmatic options
Somehow I simply can't find any example of where to put/use root programmatic options... Can anyone point me to the right direction?
If you are using Webpack, you need to put it there.
module: {
[..]
rules: [
// Transpile ES6 Javascript into ES5 with babel loader
{
test: /\.jsx?$/,
exclude: [/node_modules/, /json/],
loader: 'babel-loader',
options: {
rootMode: 'upward'
},
},
[..]
],
[..]
},
Otherwise I had the same issue than you, I can't put it in the package.json file using the key babel.
Any API-related options are called programmatic options. Take a look at my discussion with the primary maintainer of Babel: https://github.com/babel/babel/discussions/14405.
It's when you specify them directly to Babel (babel.transformSync(code, programmaticOptions) or to the Babel integration you are using (e.g. babel-loader, which can pass them to its internal babel.transform call). In other words, not in presets or config files. [...]
by #nicolo-ribaudo - Babel core team.
I got this error using my (probably non-standard) monorepo setup where I have top-level subdirectories for each of my packages. No top-level package. When I upgraded to Babel 7, my Jest tests were no longer transforming packages that were yarn linked into the package where I was running Jest.
I added a top-level babel.config.js as part of Babel's monorepo instructions. I had rootMode: "upwards" in these three places:
ui-package/webpack.config.js for transforming the app.
ui-package/babel-jest.js for the tests, where it appeared like:
module.exports = require("babel-jest").createTransformer({
rootMode: "upward",
})
and was referenced from jest.config.js in that same dir like:
transform: {
"^.+\\.jsx?$": "./babel-jest.js",
},
And in /babel.config.js, the newly added top-level babel conf file.
Removing it from the last one removed the error.

vscode automatic type acquisition for jest

I have vscode 1.9 and I want to have intellisense for jest tests. The problem is that describe, it, expect etc are globally available in jest and you don't need to import them in your test files. So vscode will not show intellisense for them.
Is there any configuration for globals for automatic type acquisition?
You have a few options in this case:
Add jest to your package.json:
"dependencies": {
"jest": "^18.1.0"
}
This only works if you are working JavaScript and do not have a tsconfig.json.
Install #types/jest
$ npm install -D #types/jest
This should work for both JavaScript and TypeScript projects. However #types but may be disabled by a jsconfig.json/tsconfig.json: http://www.typescriptlang.org/docs/handbook/tsconfig-json.html
Create a jsconfig.json file in the root of your workspace to specifically include jest:
{
"typeAcquisition": {
"include": [
"jest"
]
}
}
This will only work for JavaScript projects when automatic typings acquisition is enabled.
All of these should allow VSCode to pick up jest's typings without an import or require
I tried installing the #types/jest, and it did work, but the problem is that it resulted in the jest suggestions appearing in my .js files as well. I couldn't figure out how to get global suggestions for test, expect, etc. in only .test.js files but not .js files.
So I decided to just manually import each jest global I was going to use in each .test.js file, which allowed the suggestions to appear with types but avoided having the suggestions appear in the .js files:
import { test, expect } from '#jest/globals'
npm install -D #types/jest
edit jest.config.js
typeAcquisition: {
include: ['jest'],
},

babel-jest doesn't handle ES6 within modules

I am trying to set up Jest on a React based project which uses ES6 modules. However I seem to be having issues with ES6 modules, I am using babel-jest and believe I have this set up properly (Jest detects it automatically).
Jest doesn't seem to have a problem using ES6 imports however as soon as it hits on an import statement within one of the imported modules it chokes. It's as if it is only transpiling the initial test script and not any of the imported modules. I have tried various configurations and tried searching Google with no luck. Running tests without any imports works fine.
Here is the error:
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import Predications from './predications';
^^^^^^
SyntaxError: Unexpected token import
Here are the relevant bits of config:
jest.conf.json
{
"testRegex": "\/test\/spec\/.*\\.js$",
}
.babelrc
{
"presets": ["es2015", "stage-0", "react"]
}
Test script
import React from 'react';
import { mount, shallow } from 'enzyme';
import Slider from 'react-slick';
import Carousel from '../../client/components/carousel/carousel.js'; // test chokes on when I include this module
describe('carousel component', () => {
it('is a test test case', () => {
expect(1 + 2).toEqual(3);
});
});
Update:
As suggested, I have tried running the test without jest.conf.js, however the testRegex is needed in order for Jest to find my tests, I tried moving tests to the default test directory and they still fail.
I would like to clarify that tests themselves are running fine, the issue seems to be where one of my imported modules uses ES6, in my example above, if I don't import my carousel component the test runs fine, as soon as I import that the test chokes on the import statement within that file. It seems as though the imported modules are not getting transpiled.
Update #2
After some investigation it appears the issue is that babel is not transpiling ES6 within node_modules. I have created an example repo to demonstrate this here: https://github.com/jamiedust/babel-jest-example
I understand that third party modules should be handling their own transpiling, however we have a number of modules which are hosted on our own npm registry and are re-used between projects, in these cases Webpack handles transpiling, for the Jest tests we need these node_modules to be transpiled by Babel, or a way of leveraging our webpack set up to do this for us.
Solution
Add the following config in package.json (or Jest config file).
"jest": {
"transformIgnorePatterns": [
"/node_modules/(?!test-component).+\\.js$"
]
}
By default any code in node_modules is ignored by babel-jest, see the Jest config option transformIgnorePatterns. I've also created a PR on your example repo, so you can see it working.
While this works, I've found it to be extremely slow in real applications that have a lot of dependencies containing ES modules. The Jest codebase has a slightly different approach to this as you can find in babel-jest transforming dependencies. This can also take much longer on Windows, see Taking 10 seconds on an empty repo.
If doing "unit" testing, mocking is probably the better way to go.
You could try adding the transform-es2015-modules-commonjs plugin to your babel config file for testing only. Here is an example config file which tells babel to transpile modules only when in a testing environment. You can put it underneath your presets:
{
"presets": [
"react",
["es2015", {"modules": false, "loose": true}]
],
"env": {
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
}
}
}
You can read about the plugin here:
https://www.npmjs.com/package/babel-plugin-transform-es2015-modules-commonjs
Then, when running your Jest tests on the command line specify NODE_ENV=test (you may need to add the --no-cache flag to the command the first time after making the change to the babel config because Jest caches babel output, but after that you can leave it off:
NODE_ENV=test jest --no-cache
I learned about this issue in a React seminar by Brian Holt at Frontend Masters. https://frontendmasters.com/courses/
faced the same issue, followed the steps to resolve,
install babel-jest
in jest config add this configuration
transform: {
'^.+\\.js?$': require.resolve('babel-jest')
}
make sure you have babel.config.js present (your config might be different than provided below)
module.exports = {
"env": {
"test": {
presets: [
[
'#babel/preset-env',
{
targets: {
node: 'current',
},
},
],
]
}
}
};
I faced the same problem (node_module not transpiled by babel-jest), without being able to solve it.
Instead, I finally succeed by mocking the node_module, like described here https://facebook.github.io/jest/docs/manual-mocks.html
NB: setting mocks in __mocks__ subfolders did not work for me. So I passed the mock as the second parameter of the jest.mock() function. Something like :
jest.mock('your_node_module', () => {})
Another possible cause. Babel now ignores your .babelrc inside node_modules and uses the one provided by the dependency. If you have control of the dependency you would have to add a .babelrc to it and babel would use those settings for it.
this can cause problems though if your dependency and your project use different babel versions or modules.

Using babel-register with AVA and .babelrc's `"ignore": false`, but node_modules are not being transpiled

I'm trying to have source files (and their dependencies in node_modules) be transpiled when running AVA tests. I've configured AVA to require babel-register and inherit my .babelrc file with the following in package.json:
"ava": {
"require": "babel-register",
"babel": "inherit"
}
and this in .babelrc:
{
"presets": [ "es2015" ],
"ignore": false
}
I have a test spec that imports a source file with and that source file imports an ES2015 dependency from node_modules:
However, when running ava I see:
/Users/me/code/esri-rollup-example/node_modules/capitalize-word/index.js:2
export default input => input.replace(regexp, match => match.charAt(0).toUpperCase() + match.substr(1));
^^^^^^
SyntaxError: Unexpected token export
Which tells me that the source file (src/app/utils.js) did transpile, but it's dependency in node_modules (capitalize-string/index) did not.
Both the source modules and dependencies transpile fine when I use babel CLI, so it really seems like the .babelrc's "ignore": false setting is not getting passed to babel-register. I can see from the babel docs that you can explicitly pass an ignore option to babel-register, but I don't see how you can do that from the AVA config. I even tried adding the following to my test file before the line where it imports the source files, but I still see the same error:
require("babel-register")({
ignore: false
});
I suppose I could add a transpile step before testing, but I wanted to make sure that I wasn't just missing some AVA or babel configuration first.
This is related to issue in babel itself – https://phabricator.babeljs.io/T6726
But you can put babel-register require in separate file (let call it .setup.js):
require('babel-register')({
ignore: /node_modules\/(?!capitalize\-word)/i
});
const noop = function () {};
require.extensions['.css'] = noop; // If you want to ignore some CSS imports
And then change "require": "babel-register" to "require": "./.setup.js"