Grunt Bower and NPM

Full stack part 3.1 : De coupling dependencies and adding task management

Bill Gooch
HTML5 RIA full stack

--

Reviewing the past

After embarking on our full stack spike we have reached a point whereby the implementation is tightly coupled to our project.

Where we are …..

Where we want to get to …….

FX watch- list component

Consideration will be first placed on abstracting out the fx watch list component. Given the component is an angularJS component and more generically it is javascript we will be using bower to manage the dependency.

Bower

Bower is a package manager for the web and when used in conjunction with the node ecosystem and grunt they provide a powerful solution to managing the development and build of your enterprise rich internet applications.

In our context we require bower to manage our fx watch list component dependency and as we do not have a local bower repository set up we will deploy our dependency to the global bower repo.

Creating a Bower package

As a globally deployed bower dependency our component project will have the following requirements..

  • Store source
  • Store distributed code
  • Demo of component
  • Test the source

This leads to the following project structure

/ fx-watchlist-component
—- / src
—- /dist
—- /demo
—- /test

From here we will want to create our bower.json file, which will store our components definition.

To achieve this we run the following command in the root of our project directory.

bower init

After running through the various configuration options our bower file looks like the following.

{  "name": "gch-fx-watchlist",
"version": "0.0.1",
"author": "Bill Gooch <billgch@gmail.com>",
"description": "An fx watchlist",
"main": "['src/scripts/gch-fx-watch-list.js']",
"ignore": [
"tests",
"demo",
".gitignore",
".jshintrc",
"Gruntfile.js",
"package.json"
],
"keywords": [
"AngularJS",
"fx",
"watch list",
"Forex"
],
"license": "MIT",
"dependencies": {
"angular": "1.2.15",
"socket.io-client": "~0.9.16",
"socket.io": "~0.9.15",
"angular-socket-io": "~0.4.1"
},
"devDependencies": {
"angular-mocks": "latest",
"angular-socket.io-mock": "latest"
}
}

The configuration falls into two main sections project info and project dependencies.

Understanding bower distribution

There a few key points to consider when distributing a package to bower

  1. The version number adheres to semantic versioning and the version number in your bower.json should match a version tag in your Git repository
  2. The reason being is that bower does not host your package as it is built ontop of git. Hence it will look up your package that is hosted in your git repository of choice and use the tag to get to the latest version — hence to release a new version you simply define a new tag in your git repo.
  3. You use the ignore tag to ……….. as it says on the tin …… ignore the directories / files you do not wish to be ditributed by bower BUT more importantly the files / folders you leave out are those that will be distributed. Therefore i make sure that i always leave out the ditribution folder.

Deployment

To ensure our package is set up correctly lets deploy to bower — to deploy a package to bower you run the following command, which names your project and provides the link to your projects git repository.

bower register gch-fx-watch-list git://github.com/Goochie/gch-fx-watch-list.git

After searching for the package…

We see from the screen grab of the command line our package has been successfully deployed to bower.

To get info on the package we use the following command

bower info gch-fx-watch-list 

Summary

So far we have de coupled our component from the project, defined a new project structure, created our bower config and deployed to the global bower repository.

Grunt - automation and task management

Recap : We have our dependency defined and deployed to a repository, we now need to be able to develop, test and distribute our component source.

Grunt is a javascript task runner that integrates and works alongside the node ecosystem — so you need to have node and npm installed in order to run grunt.

If you havent already install node, npm and then grunt

Our requirements

  • Run unit and integration tests
  • Copy files to demo
  • Build source
  • Run demo/dev server

Setting up a Grunt file

There seem to be a million and one yeoman generators now — from complete stack applications to barebones client side apps, which i think is a great thing.

Although in the context of learning how all of these tools fit together i do feel it hamper’s your learning curve some what and that the best way to start getting your head around how all the pieces fit together is to build from the ground up.

So under that premise lets set up a basic grunt file.

Grunt scaffolding

In some what of an ironic fashion i will be using grunt scaffolding to set up the initial file.

First of all we need to install grunt init

npm install -g grunt-init

We then need to following the install instructions on this page to install the template/s we may wish to use.

Once the template has been installed we then run our command to create our grunt file.

grunt-init gruntfile

On completion of this command we will have a basic GruntFile.js and a package.json file.

Grunt tasks

Now we have a GruntFile.js — lets start stubbing out our tasks

  1. Run unit and integration tests

We will need to add two tasks to cover this requirement

test:unit and test:e2e

2. Copy files to demo

deployDemo

3. Build source

build

4. Run demo

devServer

The grunt tasks will now be stubbed out as the following

//------------------------------------------------------
// TASKS
//------------------------------------------------------
/* Build process...
*/
grunt.registerTask('build', [ ]);/* Deploy to demo...
*/
grunt.registerTask('deployDemo', []);/* Run demo / dev ...
*/
grunt.registerTask('devServer', []);/* Testing
*/
grunt.registerTask('test:unit', []);
grunt.registerTask('test:e2e', []);

NPM — Managing and loading dependencies

In order to implement our tasks we require the following dependencies, which are node package modules.

grunt-contrib-jshint
grunt-contrib-clean
grunt-contrib-cssmin
grunt-contrib-uglify
grunt-contrib-watch
grunt-contrib-copy
grunt-express
grunt-protractor-runner
grunt-karma
grunt-html2js

To load these dependencies we will use the following command

npm install grunt-contrib-jshint 

Something to consider when loading npm package is that you need decide if you wish the dependency to be saved to your package.json file — if yes use the following command…..

npm install grunt-contrib-jshint --save

…… if you then need it to be saved as a dev dependency use the following command.

npm install grunt-contrib-jshint --save-dev

package.json

After loading all of our dependencies our package.json file will look like the following…….

{
"engines": {
"node": ">= 0.10.0"
},
"devDependencies": {
"grunt": "~0.4.5",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-cssmin": "~0.10.0",
"grunt-contrib-watch": "~0.6.1",
"grunt-contrib-uglify": "~0.5.0",
"jshint-stylish": "~0.1.3",
"time-grunt": "~0.2.1",
"grunt-contrib-copy": "~0.4.1",
"grunt-express": "^1.4.1",
"karma": "^0.12.17",
"grunt-karma": "^0.8.3",
"karma-jasmine": "^0.1.5",
"karma-chrome-launcher": "^0.1.4",
"grunt-html2js": "^0.2.8"
"jshint-stylish-ex": "^0.2.0",
"grunt-protractor-runner": "^1.0.1"
},
"dependencies": {
"grunt-express": "^1.4.1"
}
}

Loading the tasks into grunt

We use the following method to load the tasks into grunt.

    grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-express');
grunt.loadNpmTasks('grunt-protractor-runner');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-html2js');

For the purpose of this post i will not go into details about the configuration of all of the individual tasks — if your interested you can find the source code here ………

Conclusion

In order to decouple our fx-watchlist component from our main app and to automate some of the process’s when doing so, we have …..

  • Defined a new project/package that holds our component and registered the package with the global bower repository
  • Used grunt to automate the testing, deployment and package management tasks.
  • Used npm to manage our dependencies

Keep up to date on this series by following me on twitter @billgch

Next steps ….

Creating an NPM package for the fx-price-service

--

--