With the new dependency tracking CodeKit 3 has for ES Modules, we can write a Hook that triggers on one specific file when it or any of its dependencies change. We can do that by mixing the new dependency tracking with the ability to process files in place (to do ESLinting and minifying) to bundle all our code up with Browserify.

The first thing we need to do is npm install a couple third-party dependencies to use in our Hooks; We’ll need Browserify and babelify. After we have these dependencies added we can easily write a hook that will run browserify on our ES Modules with the added bonus of being able to easily require other JS dependencies from npm.

Setting up the code to Hook into

I prefer using npm scripts and triggering those via Hooks, this allows npm to handle the paths and we don’t have to references things like ./node_modules/.bin/browserify to use browserify on the CLI. So, our first step will be writing an npm script in our package.json file.

It should look similar to the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"name": "codekit_3_browserify",
"version": "1.0.0",
"description": "",
"main": "",
"scripts": {
"build": "browserify js/main.js -t [ babelify --presets [ latest ] ]> js/bundle.js"
},
"author": "",
"license": "ISC",
"dependencies": {},
"devDependencies": {
"babel-preset-latest": "6.16.0",
"babelify": "7.3.0",
"browserify": "13.1.1",
"rxjs": "^5.0.0-rc.2"
}
}

Here we’re

  • executing browserify and passing js/main.js as the entry file.
  • passing a -t babel transform, so that babel will transform our code into ES 5, and modules to CommonJS style.
  • passing presets to babel
  • then passing the bundled code to stdout which is piped into js/bundle.js

You could also include uglifyjs and pipe browserifies content to it, but since we can trigger that via CodeKit I see no reason to add that extra dependency since it’s provided. But there may be some case where you might want this, so it’s completely possible to handle minification here as well.

Run down of file and directory content

1
2
3
4
5
6
7
8
9
10
11
12
js/
bizz.js
bundle.js // <-- exit file
foo.js
main.js // <-- entry file

node_modules/
rxjs/**
[...other dependencies]

index.html
package.json

The above are just simple files to demonstrate small modules that can be imported and bundled up, we do however have rxjs that was installed via npm. I’ve included this to show the ease of including thrid-party libs using this work flow.

1
2
// bizz.js
export const bizz = (buzz='') => `fizz ${buzz} bizz`;
1
2
// foo.js
export const foo = (something='') => `foo ${something}`;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// node_modules/rxjs/Observable.js
"use strict";
var root_1 = require('./util/root');
var toSubscriber_1 = require('./util/toSubscriber');
var observable_1 = require('./symbol/observable');
/**
* A representation of any set of values over any amount of time. This the most basic building block
* of RxJS.
*
* @class Observable<T>
*/

var Observable = (function () {
// and lots more code ...
})()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// main.js
import {foo} from './foo';
import {bizz} from './bizz';

let {console} = window;

console.log(foo('this'));
console.log(bizz('bar'));


import {Observable} from 'rxjs/Observable';
// patch Observable with appropriate methods
import 'rxjs/add/observable/of';

Observable.of(1,2,3).subscribe( a => console.log(a) );

In main.js we can see that we’re requiring our own ES Modules with a relative path ./. This is important so that browserify knowns that the file is relative to the file it’s being imported within and not a dependency that’s within node_modules.

As for Observable we’re importing it with no relative path, doing this browserify will do a lookup in your node_modules folder to find the dependencies needed. You also may notice that we’re using the new ES Module syntax to import the Observable, even though it’s built via the CommonJS pattern. This is possible because Babel’s presets will use the CommonJS style when transforming your code, which is nice since we can code in the standard syntax while still requiring dependencies that are built using the CommonJS format.

Another added benefit is that import {Observable} from 'rxjs/Observable' is converted to var Observable = require('rxjs/Observable').Observable. Note the added .Observable needed using the CommonJS style; It’s a nice benefit of not having to do this with the ES Module syntax.

CodeKit project setup

JS Files

You’ll want to setup each js file to be processed, but, ensure that you set Transpile With: to Nothing; This is because we’re going to be processing this file to itself. You can, however, setup up the linter of your choice, I’ve been moving over the ESLint.

If you’re using ESLint, make sure you’ve set Source Type to be Module and have checked the ES6 box.

In the following image you’ll see that CodeKit is listing our custom ES Module files that main.js is importing but, you wont see the items that are required from node_modules here due to that folder being skipped by default. Which is a nice perk since you’ll really only want to mess with files that you’re writing yourself and leave the third-party dependencies alone.

Earlier I wrote about either piping Browserify’s content to uglifyjs yourself, or, letting CodeKit handle the minification. Here you can see that it’s as simple as checking a box, one thing to note though, source maps will not work due to us working outside of CodeKit’s baked in transpiling. If source maps are important to you, this will probably the moment you want to uglify the content yourself as well and setup Browserify to build out the source maps too.

Now, the step that ties everything together and builds out your bundle anytime a dependencies of your entry file changes; The Hook. As you can see below it’s quite simple, just three words. This is because we’re using npm to handle the rest of the logic, so if you want to make modifications to your bundle processes remember that package.json is where all the work is happening.

If you find yourself in a situation where npm is not working out for you and your team, maybe due to some folks not having npm and/or node; Or maybe you’re experimenting with different versions of node and npm. If that’s the case you can setup a Hook that uses CodeKits bundled version of node and a custom JS file written on your end to setup Browserify.

::Warning:: Using this method relies on that fact that everyone has CodeKit installed in the same location and that CodeKit itself doesn’t change the location of node, which is possible as this is not documented. So… enter at your own risk.

Your Hook would look something like:

1
2
3
node=/Applications/CodeKit.app/Contents/Resources/engines/node/node
$node browserify.js > js/bundle.js
echo 'Built js/bundle.js'

and browserify.js would look something like:

1
2
3
4
5
6
7
// browserify.js

var browserify = require('browserify');
browserify('./js/main.js')
.transform("babelify", {presets: ["latest"]})
.bundle()
.pipe(process.stdout);

Github repo

You can find the sample project with all the above files at https://github.com/subhaze/CodeKit_3_Browserify

When you preview the project you should see the following in your console.

1
2
3
4
5
foo this
fizz bar bizz
1
2
3

Happy coding!