Sunday, 16 August 2015

npm outdated + npm update is your friend

In the last year or two I've been getting my head around the whole world of front-end web development with AngularJS and its associated infrastructure (nodejs, npm, bower, gulp, etc).  A lot of that has to do with the fact that with my current client, I've been working on projects that use all that technology (where they have Microsoft's ASP.NET WebApi on the back-end, and all this other stuff on the front-end).

It seems to me a lot of .Net developers shy away from really learning all that stuff, or they just learn what they need to know to get the job done, but I think all this stuff is pretty cool and I've been trying to learn it all on a slightly deeper level.

So when you get into the whole npm thing you quickly learn that development tools, libraries, packages, plug-ins etc. that are required by your project are stored in a file called package.json.  And that the versions of those packages are managed with symbols using a syntax called semver.

So, having inherited these projects I started looking at the choice of plug-ins used by my predecessors and after a bit of research I decided they seemed generally like pretty good choices.  You specify those plug-ins by using the devDependencies object in package.json.  Here's an example copied from the IDE I'm also learning, which is WebStorm:

{
  "name": "dBWeb",
  "version": "1.0.0",
  "private": true,
  "description": "Dave Barrows AngularJS Web Project Boilerplate",
  "scripts": {
    "init": "npm install",
    "install": "bower install",
    "test": "karma start"  },
  "devDependencies": {
    "angular-jsdoc": "~0.3.8",
    "cli-color": "~0.3.3",
    "commander": "~2.5.0",
    "del": "~1.2.0",
    "gulp": "^3.8.8",
    "gulp-angular-filesort": "^1.0.4",
    "gulp-angular-htmlify": "~2.3.0",
    "gulp-angular-templatecache": "^1.3.0",
    "gulp-autoprefixer": "~2.3.0",
    "gulp-bytediff": "^0.2.0",
    "gulp-concat": "^2.3.3",
    "gulp-docco": "^0.0.4",
    "gulp-filelog": "~0.4.0",
    "gulp-filter": "~3.0.0",
    "gulp-htmlmin": "~1.1.3",
    "gulp-imagemin": "~2.3.0",
    "gulp-inject": "^1.0.1",
    "gulp-jscs": "~2.0.0",
    "gulp-jsdoc": "~0.1.4",
    "gulp-jshint": "^1.7.1",
    "gulp-load-plugins": "^0.6.0",
    "gulp-load-utils": "^0.0.4",
    "gulp-minify-css": "~1.2.0",
    "gulp-minify-html": "~1.0.4",
    "gulp-msbuild": "^0.2.4",
    "gulp-newer": "^0.5.0",
    "gulp-ng-annotate": "~1.1.0",
    "gulp-rev": "^1.1.0",
    "gulp-rev-replace": "^0.3.1",
    "gulp-sourcemaps": "^1.1.5",
    "gulp-task-listing": "^0.3.0",
    "gulp-uglify": "^1.0.1",
    "gulp-util": "^3.0.1",
    "gulp-watch": "^1.0.7",
    "gulp-zip": "^2.0.2",
    "handlebars": "~2.0.0",
    "jshint-stylish": "^0.4.0",
    "karma": "^0.12.24",
    "karma-chai": "^0.1.0",
    "karma-chrome-launcher": "^0.1.5",
    "karma-cli": "0.0.4",
    "karma-mocha": "^0.1.9",
    "karma-ng-html2js-preprocessor": "^0.1.2",
    "karma-phantomjs-launcher": "^0.1.4",
    "karma-sinon-chai": "^0.2.0",
    "karma-xml-reporter": "^0.1.4",
    "lodash": "~2.4.1",
    "merge-stream": "^0.1.6",
    "mocha": "^1.21.5",
    "q": "~1.0.1",
    "yargs": "^3.11.0"  }


So if you go to the command line (I usually work on Windows machines at work, but at home, as I study this stuff, I'm working on a Mac.  But the commands work the same once you've got node installed).  And I issue the following command ("npm outdated"), and it gives me the following output:

davids-mbp:dBWeb davidwbarrows$ npm outdated
Package                   Current   Wanted      Latest  Location
cli-color                   0.3.3    0.3.3       1.0.0  cli-color
commander                   2.5.1    2.5.1       2.8.1  commander
gulp-load-plugins           0.6.0    0.6.0  1.0.0-rc.1  gulp-load-plugins
gulp-rev                    1.1.0    1.1.0       5.1.0  gulp-rev
gulp-rev-replace            0.3.4    0.3.4       0.4.2  gulp-rev-replace
gulp-task-listing           0.3.0    0.3.0       1.0.1  gulp-task-listing
gulp-watch                  1.2.1    1.2.1       4.3.4  gulp-watch
gulp-zip                    2.0.3    2.0.3       3.0.2  gulp-zip
handlebars                  2.0.0    2.0.0       3.0.3  handlebars
jshint-stylish              0.4.0    0.4.0       2.0.1  jshint-stylish
karma                     0.12.37  0.12.37      0.13.9  karma
karma-chrome-launcher      0.1.12   0.1.12       0.2.0  karma-chrome-launcher
karma-cli                   0.0.4    0.0.4       0.1.0  karma-cli
karma-mocha                0.1.10   0.1.10       0.2.0  karma-mocha
karma-phantomjs-launcher    0.1.4    0.1.4       0.2.1  karma-phantomjs-launcher
karma-sinon-chai            0.2.0    0.2.0       1.0.0  karma-sinon-chai
lodash                      2.4.2    2.4.2      3.10.1  lodash
merge-stream                0.1.8    0.1.8       1.0.0  merge-stream
mocha                      1.21.5   1.21.5       2.2.5  mocha
q                           1.0.1    1.0.1       1.4.1  q
davids-mbp:dBWeb davidwbarrows$ 
So that's nice; now I can make decisions about whether I want to update to the latest version.  In the course of this I decided, for my stuff, I will generally use the tilde as opposed to the carat (see this for more info).  So with the above output, for example, I can say things like, okay, I want to update the "gulp-rev" plug-in to its latest version.  So I go back into WebStorm and change this:

"gulp-rev": "^1.1.0",

to this:

"gulp-rev": "~5.1.0",

And then I go back to the command line and issue the "npm update" command:

davids-mbp:dBWeb davidwbarrows$ npm update
gulp-rev@5.1.0 node_modules/gulp-rev
├── object-assign@2.1.1
├── rev-hash@1.0.0
├── through2@0.6.5 (xtend@4.0.0, readable-stream@1.0.33)
├── sort-keys@1.1.1 (is-plain-obj@1.0.0)
├── rev-path@1.0.0 (modify-filename@1.1.0)
└── vinyl-file@1.2.1 (graceful-fs@4.1.2, strip-bom-stream@1.0.0, strip-bom@2.0.0, vinyl@0.5.1)
davids-mbp:dBWeb davidwbarrows$ 
And npm sees that the current version of that package is now later in package.json, so it updates it to the latest version.  (In some cases, obviously, you might decide you actually want to be on an earlier version for whatever reason; say the tool you're using has its own set of dependencies and if you upgrade your tool it might break something else.  The whole semver thing lets you control that as desired).

The point of all this is sort of two-fold:  

a) if you happen to be responsible for managing such applications, this is a way of ensuring you go through them all one by one, so (by googling the package name for example), you begin to really understand the purpose of each plug-in used by your application; and 

b) that modern apps consist of many moving parts, and it's not enough to just blindly accept them; these lists of tools (like package.config in the NuGet world) or package.json in the npm world, require some care and feeding.

Maybe this kind of maintenance task would not happen once you were in the middle of a project.  If it ain't broke, don't fix it.  But if you're in between projects, say, or you are in more of an architecture role and it's your job to stay on top of this stuff, then it seems like a good idea to periodically go through your list of developer dependencies and ensure they're updated to the latest version that makes sense for your particular situation.  Developers are out there working on these tools, and improving them, so chances are you're better off with a newer version than an old outdated one.  (Obviously also, you've got to test your app when you make such a change, make sure upgrading is the right thing to do in your case, etc. etc.)

How do you manage such issues?  Feedback is welcome.