If you’ve used Angular in production, you’ve probably learned the importance of annotating your dependencies when using a minifier like Uglify: when parameter names get mangled, Angular’s implicit annotation feature breaks. The ng-annotate package does a great job at alleviating us of this responsibility, but it may need a little hint when you’re using something like Browserify to organize your code as CommonJS modules.

Let’s describe the problem with a little bit of code. First, a simple Angular app authored as a CommonJS module:

var angular = require('angular');

angular
  .module('GreeterApp', [])
  .controller('greeterController', require('./greeter-controller'))
  .factory('greeter', require('./greeter'));

We’ve already installed Angular through npm, so this is all ready to be Browserified. Here’s the Gulp pipeline that does just that:

var
    gulp = require('gulp'),
    browserify = require('browserify'),
    buffer = require('vinyl-buffer'),
    source = require('vinyl-source-stream'),
    ngAnnotate = require('gulp-ng-annotate'),
    uglify = require('gulp-uglify');

gulp.task('default', function() {
  return browserify('./app.js')
    .bundle()
    .pipe(source('app.js'))
    .pipe(buffer())
    .pipe(ngAnnotate())
    .pipe(uglify())
    .pipe(gulp.dest('./dist'));
});

It packages up app.js with Browserify > ng-annotate > Uglify and then drops it into the dist directory.

The greeter-controller.js doesn’t have much to it, either:

module.exports = function($scope, greeter) {
  $scope.greeting = greeter.getGreeting('english');
};

The controller gets an English greeting from the greeter service and puts it in the scope for the view to display. Notice the lack of explicit annotations here; we’re hoping ng-annotate will take care of that for us.

But when we build and run the app, we get the following error in the browser console:

Error: [$injector:unpr] Unknown provider: eProvider <- e <- greeterController
http://errors.angularjs.org/1.4.1/$injector/unpr?p0=eProvider%20%3C-%20e%20%3C-%20greeterController
    at app.js:1
    at app.js:1
    at Object.r [as get] (app.js:1)
    at app.js:1
    at r (app.js:1)
    at Object.i [as invoke] (app.js:1)
    at $get.f.instance (app.js:2)
    at v (app.js:1)
    at s (app.js:1)
    at s (app.js:1)

The documentation page for the error decodes this for us:

This error results from the $injector being unable to resolve a required dependency.

Investigating our ./dist/app.js, we notice the controller has been turned to:

t.exports=function(e,t){e.greeting=t.getGreeting("english")}

No annotations! Angular is looking for a provider for whatever e is, rather than $scope!

What looks like ng-annotate failing to do its job is really just us failing to read the docs very closely. It turns out the CommonJS module pattern is one of those un-common cases that the ng-annotate documentation loudly warns us about:

ng-annotate works by using static analysis to identify common code patterns. There are patterns it does not and never will understand and for those you can use an explicit ngInject annotation instead, see section further down.

The section it refers to gives us a few options that can solve our problem. My favorite is prepending the function declaration with /*@ngInject*/:

module.exports = /*@ngInject*/ function($scope, greeter) {
  $scope.greeting = greeter.getGreeting('english');
};

When we re-run the build, our output includes all of the annotations we were missing:

t.exports=["$scope","greeter",function(e,t){e.greeting=t.getGreeting("english")}]

And all is well.

I’ve been wanting to get into blogging about web and software development for a while now, at least since 2010, when I got my first full-time developer job.

That’s when I noticed just how much of our work involves learning through the vast accumulated knowledge of others who have written about it. Tutorials, deep-dives, thoughtful opinions, and quick tips from bloggers like Jeff Atwood, Joel Spolsky, Chris Coyier, Phil Haack, Luke Wroblewski, and Rob Dodson have shaped me into the developer I am today. I’d like to continue that tradition, and try my best to contribute back to this amazing ecosystem, in whatever small way I can.

I hope you enjoy watching me stumble through my attempts to emulate all of my favorite bloggers. And even more, I hope you eventually find me interesting.

Thanks for reading!