How to tell which files are being transpiled by Babel 6? - javascript

I have a project that is using babel-register to dynamically transpile ES6 source to ES5 when requiring that module in a Node 6.6 project. I've read that babel-register hooks into Node's require function in order to transpile a file when you try to load it, but I'm not always clear on which files will be affected by that change.
This question comes up for me a lot when I'm writing tests: is only my production code getting transpiled, or does the test code get transpiled too?This brings me to the more general question, which is the topic of this post:
How can I tell when Babel is actually running, and which files are being transpiled?
Example code
Let's say I have production classes like this that are written in ES6 syntax
//src/greeter.js
export default class Greeter {
sayHello() {
return 'Hello World';
}
}
and Babel is configured to transpile as so (.babelrc)
{
"presets": ["es2015"]
}
and then there's some test code
//features/step_definitions/greeter_steps.js
import Greeter from '../../src/greeter'; //Causes greeter.js to be transpiled
import expect from 'expect';
var stepWrapper = function() {
//Does Babel try to transpile this code too?
this.Given(/^a greeter$/, function() {
this.greeter = new Greeter();
});
this.When(/^I ask it for a general greeting$/, function() {
this.greeting = this.greeter.sayHello();
});
this.Then(/^it should greet the entire world$/, function() {
expect(this.greeting).toEqual('Hello World');
});
};
module.exports = stepWrapper;
and all of that runs on Node like so
cucumberjs --compiler js:babel-core/register
Example code is available here, if that is helpful.

I made a hack to node_modules/babel-register/lib/node.js to do some logging like so
function compile(filename) {
var result = void 0;
var opts = new _babelCore.OptionManager().init((0, _extend2.default)({ sourceRoot: _path2.default.dirname(filename) }, (0, _cloneDeep2.default)(transformOpts), { filename: filename }));
var cacheKey = (0, _stringify2.default)(opts) + ":" + babel.version;
var env = process.env.BABEL_ENV || process.env.NODE_ENV;
console.log('[babel-register::compile] filename=' + filename + '\n'); //Added logging here
if (env) cacheKey += ":" + env;
if (cache) {
var cached = cache[cacheKey];
if (cached && cached.mtime === mtime(filename)) {
result = cached;
}
}
...
}
which then reports that test and production code are at least passing through Babel on some level
$ npm t
> cucumber-js-babel#1.0.0 test /Users/krull/git/sandbox/node/cucumber-js-babel
> cucumberjs --compiler js:babel-core/register
[babel-register::compile] filename=.../node/cucumber-js-babel/features/step_definitions/greeter_steps.js
[babel-register::compile] filename=.../node/cucumber-js-babel/src/greeter.js
...test results...
However, I'm hoping for a better solution that
works by some means of plugins and/or configuration, instead of monkey patching
better distinguishes which files are actually being transpiled, and which ones pass through Babel without modification

Because of this:
cucumberjs --compiler js:babel-core/register
...babel is invoked for both your test and regular source code. Keep in mind that in node, the only way to import JS is through require, so obviously babel-register will always be invoked. Of course, what babel does depends on its configuration, but most likely you have a simple configuration where all files required by require except those under node_modules will be transpiled.

Related

Is it possible to use ES6 modules in Mocha tests?

ES6, Windows 10 x64, Node.js 8.6.0, Mocha 3.5.3
Is it possible to use ES6 modules in Mocha tests? I have the problems with export and import keywords.
/* eventEmitter.js
*/
/* Event emitter. */
export default class EventEmitter{
constructor(){
const subscriptions = new Map();
Object.defineProperty(this, 'subscriptions', {
enumerable: false,
configurable: false,
get: function(){
return subscriptions;
}
});
}
/* Add the event listener.
* #eventName - the event name.
* #listener - the listener.
*/
addListener(eventName, listener){
if(!eventName || !listener) return false;
else{
if(this.subscriptions.has(eventName)){
const arr = this.subscriptions.get(eventName);
arr.push(listener);
}
else{
const arr = [listener];
this.subscriptions.set(eventName, arr);
}
return true;
}
}
/* Delete the event listener.
* #eventName - the event name.
* #listener - the listener.
*/
deleteListener(eventName, listener){
if(!eventName || !listener) return false;
else{
if(this.subscriptions.has(eventName)){
const arr = this.subscriptions.get(eventName);
let index = arr.indexOf(listener);
if(index >= 0){
arr.splice(index, 1);
return true;
}
else{
return false;
}
}
else{
return false;
}
}
}
/* Emit the event.
* #eventName - the event name.
* #info - the event argument.
*/
emit(eventName, info){
if(!eventName || !this.subscriptions.has(eventName)) {
return false;
}
else{
for(let fn of this.subscriptions.get(eventName)){
if(fn) fn(info);
}
return true;
}
}
}
Mocha test:
/* test.js
* Mocha tests.
*/
import EventEmitter from '../../src/js/eventEmitter.js';
const assert = require('assert');
describe('EventEmitter', function() {
describe('#constructor()', function() {
it('should work.', function() {
const em = new EventEmitter();
assert.equal(true, Boolean(em));
});
});
});
I launch the mocha directly through the PowerShell console. The result:
Mocha has support for ESM from version 7.1.0 onward (release: Feb. 26, 2020).
This requires Node 12.11.0 or higher, and is subject to the current restrictions/limitations of using modules in Node:
Either you must use .mjs file extensions for source files that use ES modules, or you must have "type": "module" in your package.json
You can't use named imports when importing from CommonJS modules
Local import statements have to explicitly include the .js file extension
And so on.
Updated answer
I had previously recommended using the esm package as an alternative to Mocha's built-in module support, but it is no longer being mantained, can't handle newer syntactical constructs like ?., and seems to possibly not work at all with newer versions of Mocha.
However, #babel/register seems to work well for this:
mocha -r #babel/register -r regenerator-runtime/runtime
I'm using this with this preset (in .babelrc):
{
"presets": [
"#babel/preset-env"
]
}
This setup requires the following packages:
#babel/core
#babel/register
#babel/preset-env
regenerator-runtime
You can also specify these in your .mocharc.js file instead of on the command line:
module.exports = {
require: [
'#babel/register',
'regenerator-runtime/runtime',
],
};
My personal experience as of yet is that trying to take advantage of Mocha's new, inherent ESM support is still a considerable burden, but using this approach is quite seamless.
Previous answer
Another option is to use the esm package, which is not subject to the above limitations:
mocha -r esm
My personal experience as of yet is that trying to take advantage of Mocha's new, inherent ESM support is still a considerable burden, but using the esm package is quite seamless.
In my case run with:
basic comand:
npx mocha --require esm test_path/
package.json
"scripts": {
// ...
"test": "npx mocha --require esm --reporter spec test_path/"
}
Runing
npm test
It's possible with Babel and Browserfy
https://drublic.de/blog/es6-modules-using-browserify-mocha/
Regarding your main question
Is it possible to use ES6 modules in Mocha tests?
Yes, as of Mocha version 9:
Mocha is going ESM-first! This means that it will now use ESM import(test_file) to load the test files, instead of the CommonJS require(test_file). This is not a problem, as import can also load most files that require does. In the rare cases where this fails, it will fallback to require(...). This ESM-first approach is the next step in Mocha's ESM migration, and allows ESM loaders to load and transform the test file.
You also need to use a Node version which supports import, which would be >= 13.2.0
Regarding the Unexpected token import problem - others here wrote good answers, but here's a better answer from another related question:
How does mocha / babel transpile my test code on the fly?

Using Webpack to add JavaScript module to ASP.NET MVC app

I'm trying to use Webpack to create a couple of simple modules in an ASP.NET MVC 5 Visual Studio 2015 project. Following instructions on the Webpack site, I downloaded the latest version of Node.js. Then using the Node command prompt, changed to my project's folder. There, I ran this command to install Webpack locally:
npm install webpack --save-dev
It created a package.json file in the root of my project:
{
"devDependencies": {
"webpack": "^2.4.1"
}
}
Note that the project already has jQuery and Bootstrap as bundles via the BundleConfig.cs, which are then referenced on _Layout.cshtml; hence they're available on all pages of the app.
Now I'd like to create a very simple test to see how to create and require modules using Webpack; once I understand it better, I can add more complex modules. I've been reading about code-splitting: https://webpack.js.org/guides/code-splitting-async/ but it's still not clear how you do this.
The function test requires function isEmpty. I'd like to define isEmpty as a module and then use it with test.
var test = function(value){
return isEmpty(value);
};
var isEmpty = function(value) {
return $.trim(value).length === 0 ? true : false;
};
This article has been helping: http://developer.telerik.com/featured/webpack-for-visual-studio-developers/
The Webpack documentation mentions import() and also require.ensure(). How do I use Webpack to modularize the isEmpty code and then use it?
Webpack allows you to use the commonJS approach for dependency management which Node.js uses, so if you have experience with Node.js it's very similar.
If not have a look at this article on the module system or the spec for a description of the module system.
For this problem I will assume all files are in the same directory. I think you will need to first move the isEmpty code into a separate file maybe isEmpty.js and change it's structure a bit so that it looks like this:
module.exports = function(value) {
return $.trim(value).length === 0 ? true : false;
};
then your test function can be moved into a separate test.js file and you can require the isEmpty module and use it like this:
var isEmpty = require('./isEmpty');
var test = function(value){
return isEmpty(value);
};
You will probably have to do something about the dependency on $ (I'm guessing jquery?) but I think that can be handled with shimming
If you have a number of functions you can do something like:
someFunctions.js
var self = {};
self.Double = function(value){
return value*2;
}
self.Triple = function(value){
return value*3;
}
module.exports = self;
useFunctions.js
var useFunctions = require('./someFunctions');
var num = 5;
console.log(useFunctions.Double(num));
console.log(useFunctions.Triple(num));

Require another file in gulpfile (which isn't in node_modules)

I've been using gulp for a while now and know how to import another node module, e.g.
var sass = require('gulp-sass');
That's fine, but my gulpfile is filling up with code that I'd like to move into a separate file and "require". Specifically I am writing a postcss plugin, which I already have working when declared as a function inside of the gulpfile. My question is how to put my function in an external file and require it like I do a node module. Do I need to "export" the function in the file being required? Do I need to use ES6 modules or something like that?
As an aside, I realise that if i was doing this probably I would either (A) turn this into a proper node module and put it on a private NPM repository, but that seems unnecessary, or (B) turn it into a proper gulp plugin, but that would require learning how to author a gulp plugin and learning about streams and stuff. Both of these are probably better but would take more time so I've decided to just keep the function simple and local for now.
First create a new js file (here ./lib/myModule.js):
//./lib/myModule.js
module.exports = {
fn1: function() { /**/ },
fn2: function() { /**/ },
}
You could also pass some arguments to your module:
// ./lib/myAwesomeModule.js
var fn1 = function() {
}
module.exports = function(args) {
fn1: fn1,
fn2: function() {
// do something with the args variable
},
}
Then require it in your gulpfile:
//gulpfile.js
var myModule = require('./lib/myModule')
// Note: here you required and call the function with some parameters
var myAwesomeModule = require('./lib/myAwesomeModule')({
super: "duper",
env: "development"
});
// you could also have done
/*
var myAwesomeModuleRequire = require('./lib/myAwesomeModule')
var myAwesomeModule = myAwesomeModuleRequire({
super: "duper",
env: "development"
});
*/
gulp.task('test', function() {
gulp.src()
.pipe(myModule.fn1)
.pipe(myAwesomeModule.fn1)
.gulp.dest()
}
First, you have to add export default <nameOfYourFile> at the end of your file
Then to use it, write import gulp from 'gulp'
If you have an error message, install babel-core and babel-preset-es2015 with NPM, and add a preset "presets": ["es2015"] in your .babelrc config file.
I fix my problem by install:
npm i babel-plugin-add-module-exports
Then i add "plugins": [["add-module-exports"]] to the .babelrc

Gulp with browserify, tsify, and reactify?

I'm using gulp with browserify and tsify in a TypeScript project. The following is an extract from my gulpfile.js:
var browserified = function (filename, debug) {
var b = browserify({
entries: filename,
debug: debug || false
});
b.plugin('tsify', {
noImplicitAny: true,
target: 'ES5'
});
b.transform('debowerify');
return b.bundle();
};
gulp.task('rebuild', ['lint', 'less'], function() {
var b = browserified ('./src/ts/main.ts', true);
return buildSourceMaps (b);
});
This works so far. I want to extend this so I can require React JSX files. First I tried (from one of my TypeScript files):
import Test = require ('../jsx/Test.jsx');
This doesn't work, though, because tsify would complain as it looks for a TypeScript file ../jsx/Test.jsx.ts. So I use the following hack:
declare var require: any;
var Test = require ('../jsx/Test.jsx');
If Test.jsx is plain vanilla JavaScript, this works. If Test.jsx contains TypeScript, it would fail, which is what I expect. So far, so clear.
Now I want to add reactify to my gulp tasks so I can use JSX in these files. Here I am stuck! I tried adding the following to the function browserified in my gulpfile.js:
b.plugin ('reactify', {
extension: 'jsx'
});
I still get the following error when I call gulp rebuild when Test.jsx contains actual JSX:
Unexpected token <
Obviously, gulp chokes on the first JSX-specific term. I think gulp is trying to pass the JSX through the TypeScript compiler. Which isn't a surprise, since I can't think of a way how to tell tsify to ignore my .jsx files. I'm new to gulp, so I am a bit at a loss. Any ideas how to set up gulp to allow for TypeScript with all .ts files and JSX with all .jsx files?
This is the gulp task I use for development. It uses watchify along with browserify and reactify to build your code, provide source mapping, and rebundle any changes you make on the fly. The path.ENTRY_POINT variable is the main component for your react app (often app.js or main.js).
gulp.task('watch', function() {
gulp.watch(path.HTML, ['copy']);
var watcher = watchify(browserify({
entries: [path.ENTRY_POINT],
transform: [reactify],
debug: true,
cache: {}, packageCache: {}, fullPaths: true
}));
return watcher.on('update', function () {
watcher.bundle()
.pipe(source(path.OUT))
.pipe(gulp.dest(path.DEST_SRC))
console.log('Updated');
})
.bundle()
.pipe(source(path.OUT))
.pipe(gulp.dest(path.DEST_SRC));
});
I used this tutorial to set up my gulpfile.js and it provides a good explanation for every gulp task:
http://tylermcginnis.com/reactjs-tutorial-pt-2-building-react-applications-with-gulp-and-browserify/

Why do I see "define not defined" when running a Mocha test with RequireJS?

I am trying to understand how to develop stand-alone Javascript code. I want to write Javscript code with tests and modules, running from the command line. So I have installed node.js and npm along with the libraries requirejs, underscore, and mocha.
My directory structure looks like this:
> tree .
.
├── node_modules
├── src
│   └── utils.js
└── test
└── utils.js
where src/utils.js is a little module that I am writing, with the following code:
> cat src/utils.js
define(['underscore'], function () {
"use strict";
if ('function' !== typeof Object.beget) {
Object.beget = function (o) {
var f = function () {
};
f.prototype = o;
return new f();
};
}
});
and test/utils.js is the test:
> cat test/utils.js
var requirejs = require('requirejs');
requirejs.config({nodeRequire: require});
requirejs(['../src/utils'], function(utils) {
suite('utils', function() {
test('should always work', function() {
assert.equal(1, 1);
})
})
});
which I then try to run from the top level directory (so mocha sees the test directory):
> mocha
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Calling node's require("../src/utils") failed with error: ReferenceError: define is not defined
at /.../node_modules/requirejs/bin/r.js:2276:27
at Function.execCb (/.../node_modules/requirejs/bin/r.js:1872:25)
at execManager (/.../node_modules/requirejs/bin/r.js:541:31)
...
So my questions are:
Is this the correct way to structure code?
Why is my test not running?
What is the best way to learn this kind of thing? I am having a hard time finding good examples with Google.
Thanks...
[sorry - momentarily posted results from wrong code; fixed now]
PS I am using requirejs because I also want to run this code (or some of it) from a browser, later.
Update / Solution
Something that is not in the answers below is that I needed to use mocha -u tdd for the test style above. Here is the final test (which also requires assert) and its use:
> cat test/utils.js
var requirejs = require('requirejs');
requirejs.config({nodeRequire: require});
requirejs(['../src/utils', 'assert'], function(utils, assert) {
suite('utils', function() {
test('should always work', function() {
assert.equal(1, 1);
})
})
});
> mocha -u tdd
.
✔ 1 tests complete (1ms)
The reason your test isn't running is because src/utils.js is not a valid Node.js library.
According to the RequireJS documentation, in order to co-exist with Node.js and the CommonJS require standard, you need to add a bit of boilerplate to the top of your src/utils.js file so RequireJS's define function is loaded.
However, since RequireJS was designed to be able to require "classic" web browser-oriented source code, I tend to use the following pattern with my Node.js libraries that I also want running in the browser:
if(typeof require != 'undefined') {
// Require server-side-specific modules
}
// Insert code here
if(typeof module != 'undefined') {
module.exports = whateverImExporting;
}
This has the advantage of not requiring an extra library for other Node.js users and generally works well with RequireJS on the client.
Once you get your code running in Node.js, you can start testing. I personally still prefer expresso over mocha, even though its the successor test framework.
The Mocha documentation is lacking on how to set this stuff up, and it's perplexing to figure out because of all the magic tricks it does under the hood.
I found the keys to getting browser files using require.js to work in Mocha under Node: Mocha has to have the files added to its suites with addFile:
mocha.addFile('lib/tests/Main_spec_node');
And second, use beforeEach with the optional callback to load your modules asynchronously:
describe('Testing "Other"', function(done){
var Other;
beforeEach(function(done){
requirejs(['lib/Other'], function(_File){
Other = _File;
done(); // #1 Other Suite will run after this is called
});
});
describe('#1 Other Suite:', function(){
it('Other.test', function(){
chai.expect(Other.test).to.equal(true);
});
});
});
I created a bootstrap for how to get this all working: https://github.com/clubajax/mocha-bootstrap
You are trying to run JS modules designed for browsers (AMD), but in the backend it might not work (as modules are loaded the commonjs way). Because of this, you will face two issues:
define is not defined
0 tests run
In the browserdefine will be defined. It will be set when you require something with requirejs. But nodejs loads modules the commonjs way. define in this case is not defined. But it will be defined when we require with requirejs!
This means that now we are requiring code asynchronously, and it brings the second problem, a problem with async execution.
https://github.com/mochajs/mocha/issues/362
Here is a full working example.
Look that I had to configure requirejs (amd) to load the modules, we are not using require (node/commonjs) to load our modules.
> cat $PROJECT_HOME/test/test.js
var requirejs = require('requirejs');
var path = require('path')
var project_directory = path.resolve(__dirname, '..')
requirejs.config({
nodeRequire: require,
paths: {
'widget': project_directory + '/src/js/some/widget'
}
});
describe("Mocha needs one test in order to wait on requirejs tests", function() {
it('should wait for other tests', function(){
require('assert').ok(true);
});
});
requirejs(['widget/viewModel', 'assert'], function(model, assert){
describe('MyViewModel', function() {
it("should be 4 when 2", function () {
assert.equal(model.square(2),4)
})
});
})
And for the module that you want to test:
> cat $PROJECT_HOME/src/js/some/widget/viewModel.js
define(["knockout"], function (ko) {
function VideModel() {
var self = this;
self.square = function(n){
return n*n;
}
}
return new VideModel();
})
Just in case David's answer was not clear enough, I just needed to add this:
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
To the top of the js file where I use define, as described in RequireJS docs ("Building node modules with AMD or RequireJS") and in the same folder add the amdefine package:
npm install amdefine
This creates the node_modules folder with the amdefine module inside.
I don't use requirejs so I'm not sure what that syntax looks like, but this is what I do to run code both within node and the browser:
For imports, determine if we are running in node or the browser:
var root = typeof exports !== "undefined" && exports !== null ? exports : window;
Then we can grab any dependencies correctly (they will either be available already if in the browser or we use require):
var foo = root.foo;
if (!foo && (typeof require !== 'undefined')) {
foo = require('./foo');
}
var Bar = function() {
// do something with foo
}
And then any functionality that needs to be used by other files, we export it to root:
root.bar = Bar;
As for examples, GitHub is a great source. Just go and check out the code for your favorite library to see how they did it :) I used mocha to test a javascript library that can be used in both the browser and node. The code is available at https://github.com/bunkat/later.

Categories

Resources