bower-files
![node.js compatibility](https://img.shields.io/badge/node.js-compatible-brightgreen.svg?style=flat)
![Coverage Status](http://img.shields.io/codeclimate/coverage/github/ksmithut/bower-files.svg?style=flat)
Help you dynamically include your bower components into your build process.
The Problem
Bower is a great tool to bring in your front-end dependencies (and their
dependencies) to your project. But if you want them to be included in your build
process, you need to manually enter them in to your build process. If you add
or remove dependencies, you need to modify your build process configuration
files.
The Solution
bower-files
aims to simplify your build process setup by dynamically getting
the library files for you to include in whatever build process you use. It
splits up the files by extension, and puts them in the order they need to be in,
in order to work correctly in the browser.
3.x
There are breaking changes in 3.x. A few features were requested, but with the
way that the code was organized, it was going to be pretty difficult and it
would make the codebase even more complicated. In the end, I refactored most
every piece to follow a more modular approach. I am much happier with the code
base than I was, but it's not perfect. I'm going to be slowly refactoring litte
pieces here and there, but the api should not change much from here.
For those of you who want the old 2.x api, just use it the same way, but adding
a .old
before the function call.
var lib = require('bower-files').old(options);
With that you get the benefit of using the new modular code, and it still passes
all of the old tests (which had 100% api and code coverage).
2.x Docs
Installation
npm install bower-files --save-dev
Usage
gulp example...
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var lib = require('bower-files')();
gulp.task('default', function () {
gulp.src(lib.ext('js').files)
.pipe(concat('lib.min.js'))
.pipe(uglify())
.pipe(gulp.dest('public/js'));
});
or a grunt example...
var lib = require('bower-files')();
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
dist: {
files: {
'build/lib.min.js': lib.ext('js').files
}
}
},
cssmin: {
dist: {
files: {
'build/lib.min.css': lib.ext('css').files
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.registerTask('default', ['uglify', 'cssmin']);
};
Other Solutions
There are other modules that do this same thing, but in different ways:
Options
The following gives you an instance of your bower files.
var lib = require('bower-files')();
Those options are as follows:
options.cwd
{String}
Default: process.cwd()
Your current working directory where your bower.json
lives. This is also where
it will start looking at .bowerrc
. MUST be an absolute path.
options.json
{String}
Default: 'bower.json'
The relative path to your bower.json
. This is relative to your options.cwd
.
options.componentJson
{String}
Default: '.bower.json'
When you run bower install jquery
it installs the jquery
component into a
folder bower_components/jquery/
. In that directory, there is a .bower.json
file put there by bower, which essentially is a copy of the other bower.json
,
but it's what is officially used by bower. If for some reason you are using a
different package manager that uses a different file, then you can change it
with this option. Otherwise, leave it as is.
options.overrides
{Object}
Default: {}
An overrides object to override specific package main files. Occasionally,
you'll find a bower component that has no defined main
property. So here, you
pass in an object that looks like this:
var lib = require('bower-files')({
overrides: {
jQuery: {
main: './dist/jquery.min.js',
dependencies: {}
}
}
});
Note that you can also add this POJO into bower.json
in JSON
form, but if
you specify an overrides directly when calling bower-files
, it will override
the bower.json
version.
options.dir
{String}
Default: 'bower_components'
The directory that your bower_components directory is set. Note that this module
will automatically read your .bowerrc
file, so if you already have it being
set there, you don't need to set this option. It also follows the same
.bowerrc
rules that bower
follows
options.camelCase
{Boolean}
Default: true
When you get a dependency hash using the .deps
, by default, it will return
the components in camelCase. So if you have angular-route
as a dependency,
it will be returned in the dependency hash as angularRoute
. To prevent this
from happening, pass false to this option. Example:
var lib = require('bower-files')({camelCase: false}).deps;
lib['angular-route'];
API
Getting the files and filtering through them can be a pain without this module.
This API is designed to be easy to understand. If you don't like it, PRs are
welcome.
First, you need your BowerFiles instance:
var lib = require('bower-files')();
At this point, bower-files
has gotten a list of all the components and their
dependencies. Now it's up to you to use the API to filter those files.
Chainable Methods
The following methods are chainable. At the end, you will need to get the
files
property to get the array of files.
lib.files;
You may also get the deps
property, which will be an object hash with all of
the bower components as keys.
lib.deps;
lib.self()
By default the array of files you get only contain your dependencies defined in
the dependencies
property in your bower.json
. This allows you to also get
the files defined in the main
property in your bower.json
.
lib.self().files;
lib.self().deps;
lib.dev()
This throws in your devDependencies
. Right now they come before the normal
dependencies, but I may be convinced to change that if someone runs into issues.
lib.dev().files;
lib.dev().deps;
lib.ext('js')
Gives you the files with the given extension(s). Accepts a string, array of
strings, or a boolean. The strings are extensions that you would like to filter
by (without the '.'). If you pass true
, it will return an object whose
properties are all of the extensions.
lib.ext('js').files;
lib.ext(['css', 'less']).files;
lib.ext('css').ext('less').files;
lib.ext(true).files;
lib.ext('js').deps;
lib.ext(true).deps;
lib.match('!**/*.min.js')
Allows you to glob match the files. Accepts a string, or array of strings. The
files have to match all of the given glob strings to make it through.
lib.match('**/*.js').match('!**/*.min.js').files;
lib.join({font: ['eot', 'woff', 'ttf', 'svg']})
Allows you to join files of a certain extension into another extension. Accepts
an object as formed above and in the example below. It's only really useful if
you plan on using .ext(true)
to split by extension, otherwise you can just use
lib.ext(['eot', 'woff', 'ttf', 'svg']).files
to get the files you need.
lib.join({font: ['eot', 'woff', 'ttf', 'svg']}).files
Non-Chainable
There's really only one method, and it could be made chainable really easy, but
this method is really a catch all of all of the above methods.
lib.filter({/* options */})
The object given have all of the above options given through the chainable
methods. Below is a full example.
lib.filter({
self: true,
dev: true,
ext: 'js',
match: ['!**/*.min.js'],
join: {
js: ['js', 'jsx']
}
});
lib.self()
.dev()
.ext('js')
.match('!**/*.min.js')
.join({js: ['js', 'jsx']})
.files;
The above returns all of the .js
files, including the ones in your
bower.json
, and the devDependencies
, and they aren't min.js
files, and it
joins all of the 'js' and 'jsx' files into the 'js' extension. That join could
be used in the extensions instead, but it's just there to show you how you
would do it.
Questions
I know it's trying to solve for a lot of different use cases, so if you have any
questions about how to implement this in your specific setup, feel free to open
an issue. I usually get back to you pretty quickly, but usually no later than
24 hours, as long as I have access to email.
Development
PRs welcome! Make sure you have an updated npm
or the npm scripts won't work.
Tests that run fail if not all the tests pass, if you don't have 100% coverage,
or if the code doesn't pass jshint. I'd like to keep everything the same style,
so feel free to call me out on stuff. If you don't like the coding style, I'm
open for a discussion on it.
To run tests, run npm test
.