Using Flow, Babel and gulp to type-check ES6 code (with sourcemaps)

TL;DR – this post shows a method to use Flow, Babel and Gulp to transpile and type-check ES6 code, with support for sourcemaps in the resulting messages.

Babel, flow

A lot of the annoyances of developing with Javascript are starting to go away. In particular, ES6 has removed a lot of the fluff from JS and made the language a lot more elegant and fun to code in.

The final missing piece of the Javascript puzzle are types. Javascript’s dynamic typing is both a strength and a weakness – its extremely expressive and powerful, allowing for very a lot of interesting abstractions. At the same time, things are so flexible that its very easy to shoot yourself in the foot without realising it.

Flow, babel

The boffins at Facebook have come to the same conclusion and have been working on Flow, which is a static typechecker for Javascript. I find it very nice for a number of reasons, including these:

  • You turn it on for a particular file using a /* @flow */ annotation. This means that you can implement typing bit by bit, and if a particular file is very dynamic then there is no need to type it at all.
  • Similarly to Typescript (and in stark constrast to Java-style languages) you can use structural types for objects. This means that you can say that some function requires an object with property name and function sayHello. This means that any object satisfying these requirements can be passed into the function, whatever other properties it might have. This is at the heart of duck-typing and gives us the flexibility of dynamic typing along with some of the safety of strict typing.
  • Flow commands and annotations can be put into comment blocks meaning that they don’t cause your IDE to plaster your code with red squiggly lines.
  • And finally, it runs some kind of caching server which means that once it has warmed up it runs very fast!

Putting it all together

My goal was to integrate flow typechecking into my build process (in my case this is gulp, but I am sure that the techniques here can be ported to grunt or whatever else you might be using). Here is my final gulpfile.js showing the flow and flow:watch tasks. Note that this assumes that your ES6 source code is in a directory called src.

var gulp = require('gulp'),
    notify = require('gulp-notify'),
    babel = require('gulp-babel'),
    flow = require('gulp-flowtype'),
    sourcemaps = require('gulp-sourcemaps'),
    sourcemapReporter = require('jshint-sourcemap-reporter');

var clientSrcDir = "src", flowDest = "tmp_build_flow";

gulp.task('flow:babel', function(cb) {
	gulp.src(clientSrcDir + '/**/*.js')
		.pipe(sourcemaps.init())
		.pipe(babel({ blacklist: ['flow'] }))
		.on('error', notify.onError('<%= error.message %>'))
		.pipe(sourcemaps.write('.'))
		.pipe(gulp.dest(flowDest))
		.on('end', cb);
});

gulp.task('flow', ['flow:babel'], function() {
	gulp.src(flowDest + '/**/*.js')
		.pipe(flow({
			all: false,
			weak: false,
			killFlow: false,
			beep: true,
			abort: false,
			reporter: {
				reporter: function(errors) {
					return sourcemapReporter.reporter(errors, { sourceRoot: '/' + clientSrcDir + '/' });
				}
			}
		}));
});

gulp.task('flow:watch', function() {
	gulp.watch(clientSrcDir + '/**/*.js', ['client:flow']);
});

gulp-flowtype is a gulp plugin that runs flow as part of a gulp pipeline. It can be installed from npm using:

npm install gulp-flowtype --save

I have also built a simple reporter that supports sourcemaps. Furthermore if you happen to be using IntellJ or WebStorm and run the task through the builtin Gulp runner you should be able to click on the error messages from gulp and be taken directly to the position in the original, un-transpiled, file. Install the reporter with:

npm install jshint-sourcemap-reporter --save

Finally it is necessary to add the ES6 transpilation directory to Flow’s source, and to ignore the ES6 source directory by editing the .flowconfig (created when you run flow init). Therefore this should read:

[ignore]
.*/src/.*

[include]
./build_flow

[libs]

[options]

Note that if you fancy you can extends this pipeline further to do browerifying, concatentation, uglfying, etc, but personally I leave these tasks like this and have separate tasks for other purposes.

Announcing the first release of ember.hx

I am proud to announce the release of Ember.hx, a library that enables ember.js programming via Haxe.  This brings the structure, code completion and compile-time error checking of a modern statically typed language to the best HTML web application framework around.  When I get some time I’ll be writing a few tutorials, but in the meantime check out the code and documentation, plus an example and live demo at https://github.com/ccapndave/ember.hx

Adobe, Apple, iPhones, iPads and the Flash Player – my two cents

There have been some nasty fights recently between Apple and Adobe regarding allowing the Flash Player to run on mobile Apple platforms, which is starting to descend into massive criticism of the Flash platform itself.  I guess its obvious which side of the debate I am going to be on and here is some of my reasoning:

  • One part of Apple’s beef with Flash is that it munches performance and battery.  In fact, Flash isn’t inherently a CPU hog, but is a flexible and powerful tool which means that a coder who doesn’t optimize his code well can end up using more resources than they need to.  Apple seem to think that HTML 5 canvas and Javascript will somehow perform better, but it seems pretty obvious to me that bad (or high performance) code in HTML is going to munch just as many resources as its equivalent Flash app.  As a case in point check out the very cool JSNES HTML emulator at http://benfirshman.com/projects/jsnes/ – its an awesome project, but just listen to your fan step up as it runs…
  • Another of Apple’s problems is that Flash is a closed platform.  Its true, and I as much as anyone would be happy to see the AVM open up; however, Apple are being pretty hypocritical here – the iPhone/iPad platform (as opposed to Android) is also completely closed.  Furthermore Apple own patents in H.264 (the video codec they are promoting in concert with HTML 5 as the replacement for Flash video) and stand to make a mint out of their ‘open’ replacement.  This sounds like a business tactic to me, rather than any high morals to advance the open source community.
  • As a case in point Apple are quick to mention that even on high powered OS X machines Flash performs badly.  Well, the main reason for this is that Apple block access to certain API required for higher performance, no doubt so that they can then claim that Flash performs badly on high powered OS X machines.
  • In almost all the chat around the web Apple seem to be complaining about one thing (although without specifically saying it); streaming video.  Flash is of course used heavily to deliver video and streaming over the web, but this is a tiny part of what Flash can and does do.  What about Flex, RIAs, applications, etc?  Flash is a fully featured platform which happens to be ok at delivering video but by taking this stance Apple will be blocking everything else that is good about Flash.

Apple seem to think that everything that Flash can do will be replicable in HTML 5 using Canvas, Javascript and CSS.  Even apart from the fact that HTML 5 does not have anything like the feature set of the AVM, lets think about this from a developer perspective for a second and we can pretty quickly see some of the downfalls:

  • Javascript is one of the most horrible programming languages about; in fact its the same as Actionscript 1 which was released in 2000 and then outdated in 2003.  The thought of developing fully featured RIAs in AS1 is horrible!
  • Despite the fact that HTML 5 is a standard, history strongly suggests that its going to perform differently in IE, Firefox, Safari, Opera and every other browser increasing development and maintenance time.  Flash is a plugin, and although this certainly has its negative points at least you can be sure that it will do the same thing in every browser and platform.
  • If there is a bug in the Flash Player, Adobe release a new version which updates itself from within the player.  Imagine if something turns out to be wrong with HTML 5?  Its a standard which means that the bug fix will need to be implemented as a upgrade to the standard, then every browser will need to implement the update off their own steam.  We will certainly end up with a situation where browsers support different states of HTML 5 at the same time.  Adding new features will face a similar issue.

The whole thing feels like Apple playing off consumer loyalties and technical ignorance to try and control as much of the web as they can.  I believe that ultimately this will result either in developers falling away from the Apple mobile platform in favour of devices that support the cross-platform model which will end up damaging their business model.  At which point, no doubt, Flash will suddenly be supported again 🙂