Jon Stuebe

Dealing with Bower Files

bower front-end npm gulp

Dealing with Bower Files

Posted by Jon Stuebe on .

If you haven't heard of it before, Bower is a tremendous package (asset) manager for the frontend of your website. Think npm for the front end. It's a great tool, but one of the drawbacks is that it essentially downloads the whole github repo for each package. For instance, if I wanted to include jquery in my project I would simply install Bower:

npm install -g bower

and then install jquery:

bower install jquery

However, there are a couple of problems with this right off the bat. First, it creates a directory called bower_components which I'm guessing no one actually wants in their project folder (location, name, etc). Second, it downloads the whole repo

├── 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
    │   ├── ajax.js
    │   ├── 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

As you can see, this is overkill. Most of the time when downloading a package I simply want the pertinent files. Really I want it to do this:

├── jquery.js
├── jquery.min.js
├── jquery.min.map

When I first started using bower I simply thought this was a unforunate result that I was going to have to live with. However, after doing a little digging, I found this lovely gulp package called main-bower-files. It takes the files from your bower components and pulls the "main" files from each package and copies them to a single folder. To use it you simply use a gulp configuration that looks something like this:

var gulp = require('gulp');
var mainBowerFiles = require('main-bower-files');

gulp.task('TASKNAME', function() {
    return gulp.src(mainBowerFiles())
        .pipe(gulp.dest('./assets/vendor'));
});

This plugin would then output something like this (inside of your output folder):

├── jquery
│   └── dist
│       └── jquery.js

This is so much better than the original, but I still wasn't completely happy with the output. What if I had some packages that delt with css, some with js, or both? I wanted something more type driven rather than just straight copying. Too my surprise someone had already made this as well. This package is called gulp-bower-normalize. It works in harmony with main-bower-files so adding it is super simple.

var gulp = require('gulp');
var mainBowerFiles = require('main-bower-files');
var bowerNormalizer = require('gulp-bower-normalize');

gulp.task('TASKNAME', function() {
    return gulp.src(mainBowerFiles())
        .pipe(bowerNormalizer({bowerJson: './bower.json'}))
        .pipe(gulp.dest('./assets/vendor'));
});

This outputs the following file structure:

├── jquery
│   └── js
│       └── jquery.js

Ah, so much better. Now each package is organized by asset type. This could be your last step, but the only issue is that every time this task is run, gulp doesn't know to clear the old files, so if the package changes filenames, the old files will still remain. To remedy this situation, One more step is needed:

var gulp = require('gulp');
var mainBowerFiles = require('main-bower-files');
var bowerNormalizer = require('gulp-bower-normalize');
var del = require('del');

gulp.task('TASKNAME', function() {
    del([
        'assets/vendor/**'
    ], function(){

        gulp.src(mainBowerFiles(), { base: 'bower_components' })
            .pipe(bowerNormalizer({ bowerJson: './bower.json' }))
            .pipe(gulp.dest('./assets/vendor'));

    });
});

gist

optional

From here, my next optional step would be to take each file that is outputted and minify them and place them next to the original files with a .min extension on each file. The configuration file looks something like this:

var gulp = require('gulp'),
    mainBowerFiles = require('main-bower-files'),
    bowerNormalizer = require('gulp-bower-normalize'),
    del = require('del'),
    uglify = require('gulp-uglify'),
    gulpFilter = require('gulp-filter'),
    minifyCSS = require('gulp-minify-css');

gulp.task('bower:prod', function(){

    del([
        'assets/vendor/**'
    ], function(){

        var jsFilter = gulpFilter('**/*.js'),
            cssFilter = gulpFilter('**/*.css');

        gulp.src(mainBowerFiles(), { base: 'bower_components' })
            .pipe(bowerNormalizer({ bowerJson: './bower.json' }))
            .pipe(jsFilter)
            .pipe(uglify())
            .pipe(jsFilter.restore())
            .pipe(cssFilter)
            .pipe(minifyCSS())
            .pipe(cssFilter.restore())
            .pipe(gulp.dest('./assets/vendor'));            

    });

});

With that configuration, your all set to create production ready code.

user

Jon Stuebe