Mocha + Babel + React Modules - javascript

How can I configure Mocha/Babel to also transform a module in my node_modules folder when required?
I am developing an application in ES6 with react.
I include an external react component which I installed through npm install.
When I run the test (mocha --recursive --compilers js:babel/register) the test fails with an Unexpected token '<' error from the external module.
The reason is that the external react module needs to be transformed when being loaded. This is specified in the modules package.json like so:
"browserify": {
"transform": [
"reactify"
]
}
It runs fine for the browser. The source is compiled with browserify -t babelify. The browserify information from the package.json is used and the module is transformed properly when it is loaded.
How can I configure Mocha/Babel to take this also into account?
Or how can I configure Mocha/Babel to also compile the modules in the node_modules folder?
Or any other way to solve this?

Babel will, by default, not transpile the node_modules directory. If you wish to disable this, you can set the ignore option to false.
On the command line, you can pass --ignore false to either babel or babel-node to allow Babel to transpile the node_modules directory.
For your uses, you can pass Mocha the --compilers js:babel/register option to transpile the files Mocha accesses. In order to allow Babel's register hook to transpile the node_modules directory, you can configure Babel with a .babelrc file. The file should contain the following: { "ignore": false }.
Either of these options should allow you to use this external module (since Babel will transpile it).

Related

How can I run jest on a React project where the babel config file is in a subfolder instead of root?

I'm in a project where the babel config is in a subfolder:
config
|- babel.json
package.json
When I run jest it breaks
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
[...]
If I move babel.json to the root folder as .babelrc then all tests work.
Can I tell jest to look for a specific babel config file or does it need to be in the root?

npm global installed packages and a automated and recursive build process

OK so, I have a WordPress site which a custom theme and several custom plugins (mu-plugins to be exact) which all use the exact same build processes. That is to say, they all have package.json files and use webpack. Technically there are a couple gulpfiles but I am migrating those to webpack configs.
So I want to be able to deploy my app, and after composer install all the plugins and theme, I would have a node script run which would:
Install all the NPM packages the build processes need - but install them globally
CD into the custom theme's src directory and run webpack --config webpack.production.js to build the theme's assets
CD into each of the custom plugins' src directories and run web run webpack --config webpack.production.js to build each plugin's assets in their respective locations
By doing this, I hope to
avoid committing compiled assets to my repos
avoid committing 3rd party packages to my repos
cut down on deployment time (because all packages would be installed globally, and only 1 time vs in each location)
I have already written the node scripts which:
installs the NPM packages
sets the node_path and node_prefix settings so my webpack (and gulp) scripts know where to find things
and CDs into each location and runs something like
This script simply runs child_process('webpack --config webpack.production.js' {cwd:' whatever/plugin/src'})
Now, the issues I having are (and how I have fixed some of them):
while my init node script could find node modules, the spawned processes that ran webpack could not
fixed this by setting NODE_PATH before the npm run command or by adding the node path via export in my .profile scrip
webpack files were unable to find the various loaders
fixed this by adding the global node path to the resolveLoaders option
my scss files couldn't find 3rd party packages like bulma
I fixed this by including the global node_modules path into the includePaths arg for sass-loader
The last, well most current issue I am facing is with webpack and vuejs.
I get this error:
ERROR in (webpack)/node_modules/timers-browserify/main.js
Module not found: Error: Can't resolve 'setimmediate' in '/usr/local/bin/npm-global/lib/node_modules/webpack/node_modules/timers-browserify'
# (webpack)/node_modules/timers-browserify/main.js 54:0-23
# /usr/local/bin/npm-global/lib/node_modules/vue/dist/vue.js
# ./src/js/entry.js
# ./src/entry.js
timers-browserify is apparently a dependency of VueJs. My entry js file has this vue import:
import Vue from 'vue/dist/vue';
and that appears to be throwing the error. If I remove it then webpack compiles everything as expected. If I install my node_modules in that folder (there is a package.json file in there) then it also compiles as expected.
I have already updated my webpack config to "resolve" module and loader dependencies to use their globally installed locations:
resolve: {
// Configure how Webpack finds modules imported with `import`.
modules: [
'/usr/local/bin/npm-global/lib/node_modules',
path.resolve(__dirname, 'node_modules'),
],
},
resolveLoader: {
// Configure how Webpack finds `loader` modules.
modules: [
'/usr/local/bin/npm-global/lib/node_modules',
path.resolve(__dirname, 'node_modules'),
],
},```
So it seems that Vue, or maybe some npm packages in general seem to have some expectations of where certain node_modules live?
This is likely going to be a continuous issue with the path I have decided on so I want to know whats causing it or what to look for?
One way I can fix this is by symlinking my globally install node_module folder into each plugin/theme. But I still want to know what is causing this as this entire process has exposed me to a couple concepts that have just helped me in general so I feel like the answer to this problem might also do the same.

Running js tests - getting "Cannot use import statement outside of a module"

I added the type: module but that didn't help.
I am trying to run mocha or jest tests that use import and export for the source files.
The existing questions about this have specifics that are different from mine and I also find them confusing to follow for someone with my specific situation, especially since I have developed a specific answer with details not relevant to the existing questions, but relevant to other people in my situation.
In the past, you could not use ES modules (i.e. import/export) in Node without transpiling your code using Babel. However, support for ES modules in Node is now a reality, and both Jest and Mocha have also recently added support for using ES modules natively.
It takes more than just adding "type": "module" to your package.json, however.
Steps for using native ES Modules in Jest
As already mentioned, add "type": "module" to your package.json
Install either jest-environment-node or jest-environment-jsdom-sixteen to your development dependencies. For example:
$ npm i -D jest-environment-node
Update the Jest configuration in package.json and add the testEnvironment setting. For example:
"jest": {
"testEnvironment": "jest-environment-jsdom-node"
}
If you are using a version of Node earlier than 13.2, then you will need to add two additional flags when running Node: --experimental-modules and experimental-vm-modules. I use npx to execute the commands, although it's a little verbose:
$ npx --node-arg=--experimental-modules --node-arg=--experimental-vm-modules jest
This will run all your Jest tests using the appropriate Node flags. I'd recommend making this your test script in package.json if this is the way you go.
Now you should be able to use import/export without Babel!
One last important point: when using native ES modules in Node, you have to use the entire import path to your local modules, including the file extensions. For example:
import lib from "./my/lib.js"
Here is the Node documentation on native ES modules, if you want to read about this in more detail: https://nodejs.org/dist/latest-v14.x/docs/api/esm.html
I'd also recommend reading through this Github issue for more details on the Jest implementation of native ES modules: https://github.com/facebook/jest/issues/9430
Per the Jest documentation, if you make sure babel-jest is installed and supply your Babel configuration per the Babel documentation, in a config file or package.json e.g.:
.babelrc.json
{
"presets": [
"#babel/env"
]
}
Then babel-jest will pick it up automatically and you don't need to explicitly pre-build the files to test them. This also means you don't have to set flags on the command line when calling Babel.
Note that if you do want to explicitly pre-build, I'd recommend:
Using a pre<script> script rather than having multiple steps in one line; and
Re-using the build script so you don't have to make changes in two places.
In your case:
"scripts": {
"build": "babel src/ --out-dir lib",
"pretest": "npm run build",
"test": "jest lib/*.test.js"
}
You need to use a compiler and then use the compiled files when running tests.
Many of the references say to add type: module but don't say much more.
To be clear, the basic message:
SyntaxError: Cannot use import statement outside a module
is because you are using import/export and you are trying to run the files directly without compilation.
The mindshift here is getting used to editing the files in one directory and running the tests in another. Alternatively, some solutions offer "in'flight" compilation so this detail is essentially hidden and only the source files are used.
There are a few different approaches to doing this depending on specific needs. Here is one of the simplest approach I've found so far, using Babel for the compilation step:
Install babel npm install babel --save-dev
Add babel commands in package.json scripts, for example:
"scripts": {
"test": "babel src/ --out-dir lib --presets=#babel/env; jest lib/*.test.js",
"build": "babel src/ --out-dir lib --presets=#babel/env"
}, // this was for jest but you can use mocha, etc as needed
// Note that using preset this way eliminates the need for a specific .babel.config.json file
Now, if you run:
jest .
you get SyntaxError: Cannot use import statement outside a module, but if you run npm t you get
Successfully compiled 2 files with Babel.
PASS lib/app.test.js
All tests
✓ Canary test (2ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Going forward, just remember to:
Edit files in src/
Run tests in lib/

How to run ES6 and npm deps in Rhino

Situation: You have a JS module that you wrote yourself or 3rd party. It may have npm dependencies. It may be written with ES6 syntax and may use new ES6 (or 7, etc) features. You want to use it on Rhino.
The problem: Rhino supports require()ing files, but it does not have OOTB support for npm. It only supports JavaScript 5 syntax and features.
How can I run my pretty new JS stuff on Rhino?
You can bundle your JS into a single file and convert it to JS 5.
Solution: Babel supports JS syntax conversion. core-js polyfills fill in missing ES6 features. Browserify supports bundling up a dependency tree (including npm dependencies) into a single standalone file.
The Details: Easier said than done. Read on for a step-by-step guide.
Create a "bundler" directory. Later on, we will put mymodule.js in it, but let's hold off on that.
cd into your bundler directory and run npm init to create a package.json file.
Add your build command to the "scripts" section of package.json
:
"build": "browserify mymodule.js -t babelify --standalone mymodule -o bundle.js"
browserify mymodule.js means that browserify is going to bundle up mymodule.js and all its dependencies. -t babelify means that babel will transpile the code, converting ES6 things to 5. --standalone mymodule means that the things your module exports via module.exports will be preserved. -o bundle.js means that the output file will be bundle.js. You'll want to rename this file back to your module's name when you add it to Rhino.
Create the babel config file babel.config.js
:
module.exports = function (api) {
api.cache(true);
const presets = [
[
"#babel/preset-env",
{
useBuiltIns: "entry",
corejs: 3,
}
],
];
const plugins = [];
return {
presets,
plugins
};
};
The newest version of corejs is currently 3, but use a newer version if there is one. The useBuiltins can be "usage" instead, which may be better depending on your situation.
Install things
:
npm install --global browserify
npm install --save-dev babelify #babel/core #babel/preset-env
npm install core-js
This completes the setup of your "bundler", per se. You can save what you have so far for future use. The following steps now show how to use this bundler on one of your modules...
Put your module file (let's call it mymodule.js) and all of its dependencies into the bundler directory. This includes running npm install ... to install all of mymodule.js's npm dependencies.
Add the line
:
import "core-js/stable";
to the top of mymodule.js. This imports the needed polyfills. To reduce the filesize, you can import the specific JS features that your module (and its dependencies) actually use. For example, import "core-js/stable/number/is-nan"; to import the isNaN method on the Number class. It may be difficult though to know what you specifically use. That's why the "usage" option in tandem with browserslist may be the best way to slim. See core-js for more.
For example, your file may look like:
import "core-js/stable";
import mylib from "my-3rd-party-lib";
module.exports = mylib;
if it is nothing more than a 3rd party library that you want to use in Rhino. If it's a module you wrote yourself, then there will be a bunch of code between the core-js import at the top and the module.exports at the bottom.
Finally, run the build command you wrote in step 4
:
npm run build
You should now have a file called bundle.js. You should be able to copy that file into the place where the Rhino engine will find it, and it should work.
cp bundle.js /path/to/where/to/place/dist/file/mymodule.js
Happy coding.

package npm project written in flow

I have to publish a npm package that is written with Flow and compiled using babel.
What I did was I compiled all my source files. Then I copied compiled files from dist/ and put them into some other directory. I also put package.json there and edited it and then I published the package on npm.
I can then normally install project and require it my project. However when I run my project, it throws error that I need to require babel-core and babel-polyfills (install them as dev-dependencies). The problem is since my new project only requires my own package and does not use babel or something like that, so I see no point in requiring babel dependencies in my new project.
My question is, how can I package my library that is written with Flow and compiled by babel, so that I can then use this package in other places without requiring babel.
Did you add main and files into your package.json?
https://docs.npmjs.com/files/package.json#main
https://docs.npmjs.com/files/package.json#files
Your files should probably be
"files": [
"dist/**"
],
Also to prevent Users who install your package from needing to install the transpilers (i.e babel) add them into your devDependencies
Directly from the https://docs.npmjs.com/files/package.json#dependencies
Please do not put test harnesses or transpilers in your dependencies object. See devDependencies, below.
Also to add flow to your dist add https://github.com/AgentME/flow-copy-source
flow-copy-source -v src dist
The problem was in using incorrectly defined preset
In my babelrc I only had defined
preset: "env" without specifying that it needs to run on node 8 and higher, since the code used async.
Therefore i defined preset as:
preset: ["env", {
targets: {
"node": "8.9.1"
}
}
And the asnyc functions compiles correctly.
Also, thanks to #Kenneth I used flow-copy-source to add type to my library, so it can be seen in intelisense.

Categories

Resources