1. Bower
Bower is the most popular front-end package manager. You can install most of the front-end libraries from Bower. However, the biggest weakness of bower is that its functionality is just like a downloader. Each Bower package has a different structure and it is not a very good solution for us when we have to manually include the script files for each new library that we install. In this post, I will demonstrate the solution for that problem. This is not a fully automated method, only for the js part, but that still saves you a lot of time. Also, all the solutions presented in this post are defined in Gulp.
2. Install Bower packages with Gulp
Before processing to this part, make sure that you have Bower and Gulp command line installed already. You may need a .bowerrc file in your project root directory (or the directory that you want bower to run from), but this is optional. If the .bowerrc file is not presented, the default setting will be loaded. You can read more information about Bower configuration here.
Now we will create a Gulp task for installing the libraries describes in bower.json using the configuration in .bowerrc automatically.
var gulp = require('gulp');
var bower = require('bower');
gulp.task('bower', function(cb){
bower.commands.install([], {save: true}, {})
.on('end', function(installed){
cb(); // notify gulp that this task is finished
});
});
If you are install the library for the first time, use bower command line to write the dependencies into the bower.json file
$ bower install --save library_name
Later, when the other developers fetch the new code to their local computers, they can run the bower task
$ gulp bower
Actually, you can just run bower install
to install all packages listed in
bower.json. However, the advantage of using Gulp is that you can chain the
tasks together, specify which task should run after finishing installing bower
packages.
3. Auto bundle Bower libraries
Each Bower library has its own bower.json file, which specifies the dependencies of that library. We will need a Gulp task that parse these files and add them to a new file in the right order (dependencies first and then the library itself after). The explanation is in the next part.
var underscore = require('underscore');
var underscoreStr = require('underscore.string');
var concat = require('gulp-concat');
var gulp = require('gulp');
var exclude = ['lib1', 'lib2'];
gulp.task('bundle-libraries-auto', ['bower'], function(){
var bowerFile = require('./bower.json');
var bowerPackages = bowerFile.dependencies;
var bowerDir = './bower_components';
var packagesOrder = [];
var mainFiles = [];
// Function for adding package name into packagesOrder array in the right order
function addPackage(name){
// package info and dependencies
var info = require(bowerDir + '/' + name + '/bower.json');
var dependencies = info.dependencies;
// add dependencies by repeat the step
if(!!dependencies){
underscore.each(dependencies, function(value, key){
if(exclude.indexOf(key) === -1){
addPackage(key);
}
});
}
// and then add this package into the packagesOrder array if they are not exist yet
if(packagesOrder.indexOf(name) === -1){
packagesOrder.push(name);
}
}
// calculate the order of packages
underscore.each(bowerPackages, function(value, key){
if(exclude.indexOf(key) === -1){ // add to packagesOrder if it's not in exclude
addPackage(key);
}
});
// get the main files of packages base on the order
underscore.each(packagesOrder, function(bowerPackage){
var info = require(bowerDir + '/' + bowerPackage + '/bower.json');
var main = info.main;
var mainFile = main;
// get only the .js file if mainFile is an array
if(underscore.isArray(main)){
underscore.each(main, function(file){
if(underscoreStr.endsWith(file, '.js')){
mainFile = file;
}
});
}
// make the full path
mainFile = bowerDir + '/' + bowerPackage + '/' + mainFile;
// only add the main file if it's a js file
if(underscoreStr.endsWith(mainFile, '.js')){
mainFiles.push(mainFile);
}
});
// run the gulp stream
return gulp.src(mainFiles)
.pipe(concat('libs.js'))
.pipe(gulp.dest('./dist'));
});
Explanation
It’s a bit long. Let me break down the code and explain it.
First, the exclude
array is for you to specify the library name that you don’t
want to bundle into the main library file.
var exclude = ['lib1', 'lib2'];
Next, we get the information about the dependencies (list of libraries) that our project needs
var bowerFile = require('./bower.json');
var bowerPackages = bowerFile.dependencies;
And next, we define some variables. packagesOrder
will store the right order
of the libraries (dependencies first). mainFiles
will store the list of all
main files of the libraries (in the right order) to add to the bundle file
var bowerDir = './bower_components';
var packagesOrder = [];
var mainFiles = [];
Next, iterate through all bower packages that the project needs and add it to
the packagesOrder
array using the addPackage
function except the ones listed
in exclude
.
// calculate the order of packages
underscore.each(bowerPackages, function(value, key){
if(exclude.indexOf(key) === -1){ // add to packagesOrder if it's not in exclude
addPackage(key);
}
});
The addPackage
function parses the bower.json file of the library passed
into it, get all the dependencies and repeat the add step by calling itself.
function addPackage(name){
// package info and dependencies
var info = require(bowerDir + '/' + name + '/bower.json');
var dependencies = info.dependencies;
// add dependencies by repeat the step
if(!!dependencies){
underscore.each(dependencies, function(value, key){
if(exclude.indexOf(key) === -1){ // add only it's not in exclude
addPackage(key);
}
});
}
// and then add this package into the packagesOrder array if they are not exist yet
if(packagesOrder.indexOf(name) === -1){
packagesOrder.push(name);
}
}
After that, iterate through the packagesOrder
. For each library, read its
bower.json to get the main
property. Add that main file to mainFiles
if
that is a .js file
underscore.each(packagesOrder, function(bowerPackage){
var info = require(bowerDir + '/' + bowerPackage + '/bower.json');
var main = info.main;
var mainFile = main;
// get only the .js file if mainFile is an array
if(underscore.isArray(main)){
underscore.each(main, function(file){
if(underscoreStr.endsWith(file, '.js')){
mainFile = file;
}
});
}
// make the full path
mainFile = bowerDir + '/' + bowerPackage + '/' + mainFile;
// only add the main file if it's a js file
if(underscoreStr.endsWith(mainFile, '.js')){
mainFiles.push(mainFile);
}
});
Finally, concatenate all the files in mainFiles
into one file
return gulp.src(mainFiles)
.pipe(concat('libs.js'))
.pipe(gulp.dest('./dist'));
The result when you run this task is a libs.js file in dist folder. All you need to do is to include that libs.js in you website layout.