This is a follow-up to the "Making your io.js command line apps compatible with node.js" post. While the previous article deals with running your ES6 command line app through babel-node
when on a node.js platform, this article deals with how you setup your project in a way that an ES5 version of your app is automatically compiled and used when on a node.js platform and the original ES6 version is used on an io.js platform.
This has several advantages, such as not having to require Babel in your production dependencies. Also, performance is better on ES5 platforms when the files are pre-compiled and it is more trivial to run the tests on ES5 platforms if they are also written in ES6.
Introducing our sample project
We will be using the say-hello app we built in the "Making your io.js command line apps compatible with node.js" post. You can find the version for this article on the master branch.
Directory structure
This is how I setup my ES6/ES5 hybrid command line app gitclick:
es5/bin/test/main.jssrc/bin/test/main.jsnode_modules/main.jspackage.json
Some observations:
./main.js
is the main entry point. Here, we either load./src/main
or./es5/main
./es5/
is an ES5-mirror of./src/
and will be built with Babelnode_modules
andpackage.json
live at the project's root
Installing our hybrid dependencies
$ npm i is-iojs -S && npm i babel -D
This command will install is-iojs
as a normal dependency and babel
as a development dependency. We only need Babel to build the ES5-version before we publish our project to npm but our project will not require Babel at runtime.
Setting up our package.json scripts
JSON
"scripts": {"build": "mkdir -p es5/bin && babel src/bin/say-hello --out-file es5/bin/say-hello","prepublish": "npm run build"}
build
is the script that actually compiles our app down to ES5babel src --out-dir es5
would compile everything in oursrc
directory- The previous command skips files without the
js
extension, so we have to runbabel src/bin/say-hello --out-file es5/bin/say-hello
to compile our bin - Before all that, we run
mkdir -p
to make sure the directory exists
prepublish
makes sure that thisbuild
is always run before we publish our package to npm
Building the app
With all the meta-stuff setup, let's build our little say-hello app. You will be able to run say-hello
from your terminal to either display "Hello io.js" or "Hello node.js", depending on which platform you are on.
./src/bin/say-hello
JS
#!/usr/bin/env node'use strict'const iojs = require('is-iojs')const engine = iojs ? 'io.js' : 'node.js'console.log(`Hello ${engine}`)
Now, let's make sure that this app runs on both platforms:
Going hybrid
Depending on our platform, we will either direct our user to the ES6-bin or the ES5-bin:
./bin/say-hello
JS
#!/usr/bin/env node'use strict'require(require('is-iojs') ? '../src/bin/say-hello' : '../es5/bin/say-hello')
Don't forget to declare the path of your bin:
package.json
JSON
"bin": {"gitclick": "bin/gitclick"}
And make sure that the file is executable:
$ chmod +x bin/say-hello
Done
That was pretty much it. Check out the code in the GitHub repository for this little demo.
Bonus: Testing your ES5-compiled app
I didn't implement this in the say-hello
demo but this is how I test ES6 and ES5 versions for gitclick.
To test your app when compiled to ES5, you can use the following scripts:
JSON
"scripts": {"test-es6": "mocha src/test --recursive","test-es5": "mocha es5/test --recursive","test": "if [ \"$(node -e \"console.log(require('is-iojs'))\")\" = \"true\" ]; then npm run test-es6; else npm run test-es5; fi;"}
If you use Travis CI for continuous integration, you could setup your .travis.yml
like this:
YAML
language: node_jsnode_js:- '0.10'- '0.11'- '0.12'- 'iojs'
This will run your tests against multiple version of node.js and the latest io.js.