Bower and Grunt: match made in heaven

Bower is great! But it only does one thing, that is to manage packages. We'll need a bit help from Grunt to reach perfection. Here's how to install and use Bower, and how to automate the copying of files from bower_components to lib.

Note: I have a follow up article on how to do all this with Gulp: Bower and Gulp: match made in heaven (also)

Bower, “a package manager for the web”, a tool that makes installing packages a breeze (e.g. Zepto, Scut or whatever library or plugin desired). Meaning, that it’s now possible to download any package from the comfort of your command line, past are the ‘right click on a link, choose save as‘ times. Bower can also update packages with the bower update command.

Install Bower

Start by Installin Bower globally (node/npm is needed):

$ npm install -g bower

cd into your project and run:

$ bower init

The init is not mandatory if Bower is only used to pull in packages, it just creates bower.json file that we need later on.

It’ll prompt you with various questions, most of them are very self explanatory. Skip any of them with enter. See the comments below for explanations.

[?] name: (test)                   # This defaults to the directory name
[?] version: (0.0.0)               # In which version is your project
[?] description:                   # Short description of your project
[?] main file:                     # The main file of your project, in jQuery that would be the `jquery.js`
[?] what types of modules does this package expose? (Press <space> to select)
❯⬡ amd                             # More: http://requirejs.org/docs/whyamd.html
 ⬡ es6                             # More: http://wiki.ecmascript.org/doku.php?id=harmony%3amodules
 ⬡ globals                         #
 ⬡ node                            # More: http://nodejs.org/api/modules.html
 ⬡ yui                             # Or just press enter to skip this
[?] keywords:                      # Like tags, if you publish your project
[?] authors: (bob <bob@exmpl.com>) # I suppose Bower pulls these from you Git configuration
[?] license: (MIT)                 # MIT is the default and sufficient in most cases
[?] homepage:                      # Project home
[?] set currently installed components as dependencies? (Y/n) # You most likely want to say Y to this
[?] add commonly ignored files to ignore list? (Y/n)          # Solid Y also
[?] would you like to mark this package as private which prevents it from being accidentally published to the registry? (y/N)

bower.json file is created in the project root, check it out. It can be edited in normal fashion if needed.

bower.json file

This is the heart of your Bower, it contains mundane information about the project (name, homepage etc.), along with list of dependencies.

It’s needed only when:

  • Publishing packages
  • Using the grunt-bower-task (more on that later)

Example Bower manifest file could look something like this:

{
  "name": "Test Project",
  "version": "2.0",
  "homepage": "https://github.com/bob/test-project",
  "authors": [
    "bob <bob@example.com>"
  ],
  "description": "This is a test project",
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "jquery": "~2.1.0",
    "scut": "~0.10.2",
    "unveil": "~1.3.0",
    "zeptojs": "~1.1.3",
    "yepnope": "~1.5.4"
  }
}

Only really interesting bit is the dependencies, the packages a project needs to function.

Installing packages with Bower

The syntax for installing is:

$ bower install <endpoint> [<endpoint> ..] [<options>]

If jQuery is desired, it can be first searched:

$ bower search jquery

Choose suitable jQuery package and issue:

$ bower install jquery --save

The --save flag saves is as a dependency to the project. Here are all the options it takes.

  • -F, --force-latest: Force latest version on conflict
  • -p, --production: Do not install project devDependencies
  • -S, --save: Save installed packages into the project’s bower.json dependencies
  • -D, --save-dev: Save installed packages into the project’s bower.json devDependencies

Now, bower_components directory is created into project root, and jQuery is pulled in from it’s Git repo.

bower_components/
└── jquery

Btw, bower.json file is not needed for installing packages.

Install all the project dependencies

It’s really easy to pull in packages with Bower, but the real strength lays in its ability to pull in all the needed dependencies for a given project. Say, clone a project from GitHub, none of the dependencies are included into it, they’re in their respective repositories. Just run:

$ bower instal

And Bower will look into the bower.json file and pull in all the listed dependencies.

Using the packages

Bower is only a package manager, not a module loader. Reference packages in normal fashion:

<script src="bower_components/jquery/dist/jquery.min.js"></script>

This is where the trouble starts.

The package dilemma

“Bower package” equals exactly to: “its git repository”

Which is how it should be, but, we have problem now. For example, jQuery that was pulled in, created a directory tree like this:

jquery/
├── MIT-LICENSE.txt
├── bower.json
├── dist
│   ├── jquery.js
│   ├── jquery.min.js
│   └── jquery.min.map
└── src
    ├── ajax
    │   ├── jsonp.js
    │   ├── load.js
    │   ├── parseJSON.js
    │   ├── parseXML.js
    │   ├── script.js
    │   ├── var
    │   │   ├── nonce.js
    │   │   └── rquery.js
    │   └── xhr.js
    ├── ajax.js
    ├── attributes
    │   ├── attr.js
    │   ├── classes.js
    │   ├── prop.js
    │   ├── support.js
    │   └── val.js
    ├── attributes.js
    ├── callbacks.js
    ├── core
    │   ├── access.js
    │   ├── init.js
    │   ├── parseHTML.js
    │   ├── ready.js
    │   └── var
    │       └── rsingleTag.js
    ├── core.js
    ├── css
    │   ├── addGetHookIf.js
    │   ├── curCSS.js
    │   ├── defaultDisplay.js
    │   ├── hiddenVisibleSelectors.js
    │   ├── support.js
    │   ├── swap.js
    │   └── var
    │       ├── cssExpand.js
    │       ├── getStyles.js
    │       ├── isHidden.js
    │       ├── rmargin.js
    │       └── rnumnonpx.js
    ├── css.js
    ├── data
    │   ├── Data.js
    │   ├── accepts.js
    │   └── var
    │       ├── data_priv.js
    │       └── data_user.js
    ├── data.js
    ├── deferred.js
    ├── deprecated.js
    ├── dimensions.js
    ├── effects
    │   ├── Tween.js
    │   └── animatedSelector.js
    ├── effects.js
    ├── event
    │   ├── alias.js
    │   └── support.js
    ├── event.js
    ├── exports
    │   ├── amd.js
    │   └── global.js
    ├── intro.js
    ├── jquery.js
    ├── manipulation
    │   ├── _evalUrl.js
    │   ├── support.js
    │   └── var
    │       └── rcheckableType.js
    ├── manipulation.js
    ├── offset.js
    ├── outro.js
    ├── queue
    │   └── delay.js
    ├── queue.js
    ├── selector-native.js
    ├── selector-sizzle.js
    ├── selector.js
    ├── serialize.js
    ├── sizzle
    │   └── dist
    │       ├── sizzle.js
    │       ├── sizzle.min.js
    │       └── sizzle.min.map
    ├── traversing
    │   ├── findFilter.js
    │   └── var
    │       └── rneedsContext.js
    ├── traversing.js
    ├── var
    │   ├── arr.js
    │   ├── class2type.js
    │   ├── concat.js
    │   ├── hasOwn.js
    │   ├── indexOf.js
    │   ├── pnum.js
    │   ├── push.js
    │   ├── rnotwhite.js
    │   ├── slice.js
    │   ├── strundefined.js
    │   ├── support.js
    │   └── toString.js
    └── wrap.js

Ummm…

Sure I don’t need all of that?

As we see, Bower does one thing, and it does it well. It’s not trying to be anything else than a tool that pulls in packages.

From jQuery repo, only one file is really needed:

jquery/
└── dist
    └── jquery.js

grunt-bower-task to rescue

What it does:

  • grunt-bower-task copies the main file to /lib
    • Main file is the file that is specified in bower.json: "main": "dist/jquery.js",
  • bower_components dir can be now excluded in .gitignore
  • Link to files in lib instead
    • <script src="lib/jquery/jquery.js"></script>

grunt-bower-task usage

Configuring is easy:

grunt.initConfig({
  bower: {
    install: {
       //just run $ grunt bower:install and you'll see files from your Bower packages in lib directory
    }
  }
});

See more usage and installation instruction here.

After setting it up, try it out:

$ grunt bower

Now the task copies the main files from each package to /lib. The dir structure could looks something like this:

lib
├── jquery
├── zepto
└── yepnope

When grunt-bower-task is set up, install Bower packages like so:

$ bower install jquery && grunt bower

And off you go, happy pappy.

What if no main file

If there is no main file, here’s what grunt-bower-task does:

  • If "main" files are empty then the whole package directory will be copied to ./lib.
  • When you define "exportsOverride" only asset types and files specified by you will be copied to ./lib.

This happens. For example with Zepto, cause it assumes that every user builds their own version of it, therefore no main file exists, and the whole tree is copied. Bummer. But as alway, there’s a way.

Copying specified files only

After Zepto is custom built, (heres how to do that), only zeptojs/dist/zepto.js is desired. Pop open the bower.json from the project root and define exportsOverride there, like this:

{
  "name": "Test Project",
  "version": "2.0",
  "homepage": "https://github.com/bob/test-project",
  "authors": [
    "bob <bob@example.com>"
  ],
  "dependencies": {
    "jquery": "~2.1.0",
    "scut": "~0.10.2",
    "unveil": "~1.3.0",
    "zeptojs": "~1.1.3",
    "yepnope": "~1.5.4"
  },
  "exportsOverride": {
    "zeptojs": {
      "js": "dist/zepto.js"
    },
    "yepnope": {
      "js": "yepnope.js"
    }
  }
}

It breaks down to:

  • "zeptojs" the package dir in bower_components
  • "js" target dir in ./lib
  • "dist/zepto.js" is path to the wanted file in bower_components

Now if $ grunt bower is issued, it’ll create the following directory structure:

lib
├── jquery
│   └── jquery.js
├── js
│   ├── yepnope
│   │   └── yepnope.js
│   └── zeptojs
│       └── zepto.js
├── scut
│   └── _scut.scss
└── unveil
    └── jquery.unveil.js

Nice! We got that problem solved.

Or grunt-contrib-copy would also do the job, it’s a task that copies specified files.

Other useful Bower commands

Uninstall:

$ bower uninstall <packagename>

Publish packages:

To register a new package:

  • There must be a valid manifest JSON in the current working directory.
  • Your package should use semver Git tags.
  • Your package must be available at a Git endpoint (e.g., GitHub); remember to push your Git tags!
  • Then use the following command:
    $ bower register <my-package-name> <git-endpoint>

Conclusion

Bower is pretty amazing, and couple in with grunt it reaches divine heights.

Comments

Club-Mate, the beverage → club-mate.fi