├── store └── .gitkeep ├── .gitignore ├── .travis.yml ├── stats.json ├── wercker.yml ├── index.js ├── .jshintrc ├── task ├── bower.js ├── composer.js ├── base.js ├── history.js └── npm.js ├── .editorconfig ├── .jscsrc ├── test └── github-linker-registries_test.js ├── package.json ├── README.md ├── gulpfile.js └── CHANGELOG.md /store/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | temp/ 3 | coverage/ 4 | npm-debug.log 5 | .DS_Store 6 | .idea 7 | store/ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | after_script: 5 | - npm run coveralls 6 | -------------------------------------------------------------------------------- /stats.json: -------------------------------------------------------------------------------- 1 | { 2 | "total": { 3 | "npm": 140006, 4 | "bower": 30824, 5 | "composer": 63318 6 | }, 7 | "update": { 8 | "npm": 3636, 9 | "bower": 813, 10 | "composer": 1376 11 | } 12 | } -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/nodejs 2 | # Build definition 3 | build: 4 | # The steps that will be executed on build 5 | steps: 6 | # A step that executes `npm install` command 7 | - npm-install 8 | - hgen/gulp: 9 | tasks: default 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * github-linker-cache 3 | * https://github.com/stefanbuck/github-linker-cache 4 | * 5 | * Copyright (c) 2014 Stefan Buck 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = { 12 | bower: require('./store/bower.js'), 13 | npm: require('./store/npm.js'), 14 | composer: require('./store/composer.js') 15 | }; 16 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "unused": true, 11 | "boss": true, 12 | "eqnull": true, 13 | "node": true, 14 | "globals": { 15 | "describe" : false, 16 | "it" : false, 17 | "before" : false, 18 | "beforeEach" : false, 19 | "after" : false, 20 | "afterEach" : false 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /task/bower.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var base = require('./base'); 5 | 6 | module.exports = function(done) { 7 | 8 | var options = { 9 | type: 'bower', 10 | uri: 'https://bower-component-list.herokuapp.com', 11 | jsonStreamPath: '*', 12 | filter: ['name', 'website'], 13 | filePath: path.resolve(__dirname, '../store/bower.js'), 14 | parser: function(item) { 15 | return item.website; 16 | } 17 | }; 18 | base(options, done); 19 | }; 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /task/composer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var githubURLParser = require('github-url-from-git'); 5 | var base = require('./base'); 6 | 7 | module.exports = function(done) { 8 | 9 | var options = { 10 | type: 'composer', 11 | uri: 'https://packagist.org/packages/list.json?fields[]=repository', 12 | jsonStreamPath: 'packages.*', 13 | jsonStreamMap: function (item, path) { 14 | item.name = path[path.length - 1]; 15 | return item; 16 | }, 17 | filter: ['name', 'repository'], 18 | filePath: path.resolve(__dirname, '../store/composer.js'), 19 | parser: function(item) { 20 | return githubURLParser(item.repository); 21 | } 22 | }; 23 | base(options, done); 24 | }; 25 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"], 3 | "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], 4 | "disallowSpaceBeforeBinaryOperators": [",", ":"], 5 | "disallowSpaceAfterBinaryOperators": ["!"], 6 | "requireSpaceBeforeBinaryOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="], 7 | "requireSpaceAfterBinaryOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="], 8 | "disallowImplicitTypeConversion": ["string"], 9 | "disallowKeywords": ["with"], 10 | "disallowMultipleLineBreaks": true, 11 | "disallowKeywordsOnNewLine": ["else"], 12 | "disallowTrailingWhitespace": true, 13 | "requireLineFeedAtFileEnd": true, 14 | "validateJSDoc": { 15 | "checkParamNames": true, 16 | "requireParamTypes": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/github-linker-registries_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var registries = require('../'); 4 | var validUrl = require('valid-url'); 5 | require('should'); 6 | 7 | describe('githubLinkerRegistries', function () { 8 | 9 | it('public properties', function () { 10 | registries.should.have.keys('npm', 'bower', 'composer'); 11 | }); 12 | 13 | it('npm content length', function () { 14 | Object.keys(registries.npm).length.should.be.above(0); 15 | }); 16 | 17 | it('bower content length', function () { 18 | Object.keys(registries.bower).length.should.be.above(0); 19 | }); 20 | 21 | it('composer content length', function () { 22 | Object.keys(registries.composer).length.should.be.above(0); 23 | }); 24 | 25 | it('npm returns a valid uri', function () { 26 | var item = registries.npm[Object.keys(registries.npm)[0]]; 27 | validUrl.isUri(item).length.should.be.above(0); 28 | }); 29 | 30 | it('bower returns a valid uri', function () { 31 | var item = registries.bower[Object.keys(registries.bower)[0]]; 32 | validUrl.isUri(item).length.should.be.above(0); 33 | }); 34 | 35 | it('composer returns a valid uri', function () { 36 | var item = registries.composer[Object.keys(registries.composer)[0]]; 37 | validUrl.isUri(item).length.should.be.above(0); 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /task/base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var _ = require('lodash'); 5 | var reqeust = require('request'); 6 | var JSONStream = require('JSONStream'); 7 | var es = require('event-stream'); 8 | var stats = require('../stats.json'); 9 | 10 | module.exports = function(options, done) { 11 | var total = 0; 12 | var dataPath = options.filePath; 13 | var filter = es.mapSync(function(item) { 14 | if (options.filter && Array.isArray(options.filter)) { 15 | item = _.pick(item, options.filter); 16 | } 17 | return item; 18 | }); 19 | 20 | var repoParser = es.map(function(item, cb) { 21 | var repoURL = options.parser(item); // <-- NEW 22 | if (!repoURL) { 23 | return cb(); 24 | } 25 | total++; 26 | cb(null, [item.name, repoURL]); 27 | }); 28 | 29 | var handleEnd = function() { 30 | var oldTotal = stats.total[options.type]; 31 | done(null, { 32 | total: total, 33 | update: total - oldTotal 34 | }); 35 | }; 36 | 37 | var handleData = function(data) { 38 | console.log(data.name); 39 | }; 40 | 41 | reqeust.get(options.uri) 42 | .pipe(JSONStream.parse(options.jsonStreamPath, options.jsonStreamMap)) 43 | .pipe(filter) 44 | .on('data', handleData) 45 | .pipe(repoParser) 46 | .pipe(JSONStream.stringifyObject('module.exports = {\n', ',\n', '\n};\n')) 47 | .pipe(fs.createWriteStream(dataPath)) 48 | .on('finish', handleEnd); 49 | }; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-linker-cache", 3 | "description": "Module that contains the mapping between a dependency and their related GitHub repository page", 4 | "version": "0.2.34", 5 | "homepage": "https://github.com/octo-linker/cache", 6 | "bugs": "https://github.com/octo-linker/cache/issues", 7 | "license": "MIT", 8 | "main": "index.js", 9 | "author": { 10 | "name": "Stefan Buck", 11 | "email": "github@stefanbuck.com", 12 | "url": "http://stefanbuck.com" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/octo-linker/cache" 17 | }, 18 | "keywords": [], 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "JSONStream": "^0.9.0", 22 | "coveralls": "^2.10.0", 23 | "event-stream": "^3.1.7", 24 | "github-url-from-git": "^1.4.0", 25 | "gulp": "^3.8.8", 26 | "gulp-istanbul": "^0.3.1", 27 | "gulp-jscs": "^1.1.2", 28 | "gulp-jshint": "^1.8.4", 29 | "gulp-load-plugins": "^0.6.0", 30 | "gulp-mocha": "^1.1.0", 31 | "gulp-plumber": "^0.6.5", 32 | "gulp-util": "^3.0.1", 33 | "jshint-stylish": "^0.4.0", 34 | "lodash": "^2.4.1", 35 | "request": "^2.44.0", 36 | "rimraf": "^2.2.8", 37 | "should": "^4.0.4", 38 | "valid-url": "^1.0.9" 39 | }, 40 | "scripts": { 41 | "coveralls": "gulp test && cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", 42 | "build": "gulp && gulp lint", 43 | "test": "gulp test" 44 | }, 45 | "files": [ 46 | "store", 47 | "index.js" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /task/history.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var util = require('util'); 6 | 7 | var updateReadme = function(stats) { 8 | var filePath = path.resolve(__dirname, '../README.md'); 9 | var content = fs.readFileSync(filePath, 'utf-8'); 10 | 11 | content = content.replace(/(npm: )[0-9]*/g, '$1' + stats.npm) 12 | .replace(/(bower: )[0-9]*/g, '$1' + stats.bower) 13 | .replace(/(composer: )[0-9]*/g, '$1' + stats.composer) 14 | .replace(/(badge\/npm-)[0-9]*/g, '$1' + stats.npm) 15 | .replace(/(badge\/bower-)[0-9]*/g, '$1' + stats.bower) 16 | .replace(/(badge\/composer-)[0-9]*/g, '$1' + stats.composer); 17 | 18 | fs.writeFileSync(filePath, content, 'utf-8'); 19 | }; 20 | 21 | var updateChangelog = function(stats) { 22 | var pkgFilePath = path.resolve(__dirname, '../package.json'); 23 | var filePath = path.resolve(__dirname, '../CHANGELOG.md'); 24 | 25 | var content = fs.readFileSync(filePath, 'utf-8'); 26 | var pkg = fs.readFileSync(pkgFilePath, 'utf-8'); 27 | pkg = JSON.parse(pkg); 28 | 29 | var version = pkg.version; 30 | var date = new Date().toISOString().slice(0,10); 31 | 32 | var entry = util.format('\n\n## v%s (%s)', version, date); 33 | if (stats.npm) { 34 | entry += '\n\nnpm: ' + stats.npm; 35 | } 36 | if (stats.bower) { 37 | entry += '\n\nbower: ' + stats.bower; 38 | } 39 | if (stats.composer) { 40 | entry += '\n\ncomposer: ' + stats.composer; 41 | } 42 | 43 | var title = '# Changelog'; 44 | content = title + '\n' + entry + content.replace(title, ''); 45 | 46 | fs.writeFileSync(filePath, content, 'utf-8'); 47 | }; 48 | 49 | module.exports = function(stats) { 50 | updateReadme(stats.total); 51 | updateChangelog(stats.update); 52 | }; 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS REPO IS DEPRECATED 2 | This repo represents the "old way" of getting a GitHub URL for a specific package. Check out [octo-linker/live-resolver](https://github.com/octo-linker/live-resolver) instead. 3 | 4 | 5 | # cache 6 | [![Build Status][travis-image]][travis-url] [![NPM version][npm-image]][npm-url] [![Supported npm package][count-npm-image]][count-npm-url] [![Supported bower package][count-bower-image]][count-bower-url] [![Supported composer package][count-composer-image]][count-composer-url] 7 | 8 | Module that contains the mapping between a dependency and their related GitHub repository page. 9 | 10 | ## Install 11 | 12 | ```bash 13 | $ npm install --save github-linker-cache 14 | ``` 15 | 16 | 17 | ## API 18 | 19 | ```javascript 20 | var cache = require('github-linker-cache'); 21 | 22 | console.log(cache.npm['jquery']); 23 | // https://github.com/jquery/jquery 24 | 25 | console.log(cache.bower['lodash']); 26 | // https://github.com/lodash/lodash 27 | 28 | console.log(cache.composer['monolog']); 29 | // https://github.com/Seldaek/monolog 30 | ``` 31 | 32 | 33 | ## Total supported repositories 34 | 35 | npm: 140006 36 | 37 | bower: 30824 38 | 39 | composer: 63318 40 | 41 | 42 | ## License 43 | 44 | Copyright (c) 2015 Stefan Buck. Licensed under the MIT license. 45 | 46 | 47 | 48 | [npm-url]: https://npmjs.org/package/github-linker-cache 49 | [npm-image]: https://badge.fury.io/js/github-linker-cache.svg 50 | [travis-url]: https://travis-ci.org/octo-linker/cache 51 | [travis-image]: https://travis-ci.org/octo-linker/cache.svg?branch=master 52 | [count-npm-url]: https://npmjs.org/ 53 | [count-npm-image]: http://img.shields.io/badge/npm-140006-green.svg 54 | [count-bower-url]: https://bower.io/ 55 | [count-bower-image]: http://img.shields.io/badge/bower-30824-green.svg 56 | [count-composer-url]: https://packagist.org/ 57 | [count-composer-image]: http://img.shields.io/badge/composer-63318-green.svg 58 | -------------------------------------------------------------------------------- /task/npm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var githubURLParser = require('github-url-from-git'); 5 | var base = require('./base'); 6 | 7 | var parseURL = function(url) { 8 | if (typeof url !== 'string') { 9 | return null; 10 | } 11 | 12 | // Remove last trailing slash 13 | if (url.slice(-1) === '/') { 14 | url = url.slice(0, -1); 15 | } 16 | // Fix multiple forward slashes 17 | url = url.replace(/([^:]\/)\/+/g, '$1'); 18 | 19 | // Resolve shorthand url to a qualified URL 20 | if (url.split('/').length === 2) { 21 | url = 'http://github.com/' + url; 22 | } 23 | 24 | // Replace and fix invalid urls 25 | url = url.replace('https+git://', 'git+https://'); 26 | url = url.replace('://www.github.com', '://github.com'); 27 | 28 | // Resolve detail link 29 | url = url.split('/tree/master')[0]; 30 | url = url.split('/blob/master')[0]; 31 | 32 | var githubUrl = githubURLParser(url); 33 | if (githubUrl) { 34 | return githubUrl; 35 | } 36 | }; 37 | 38 | var getRepoURL = function(node) { 39 | if (typeof node === 'string') { 40 | return parseURL(node); 41 | } else if (node.url) { 42 | return parseURL(node.url); 43 | } else if (node.path) { 44 | return parseURL(node.path); 45 | } else if (node.web) { 46 | return parseURL(node.web); 47 | } else if (node.git) { 48 | return parseURL(node.git); 49 | } 50 | }; 51 | 52 | var lookup = function(node) { 53 | if (Array.isArray(node)) { 54 | return getRepoURL(node[0]); 55 | } else { 56 | return getRepoURL(node); 57 | } 58 | }; 59 | 60 | var getURL = function(node) { 61 | var result = null; 62 | 63 | if (node.repository) { 64 | result = lookup(node.repository); 65 | } 66 | if (!result && node.repositories) { 67 | result = lookup(node.repositories); 68 | } 69 | if (!result && node.homepage) { 70 | result = parseURL(node.homepage); 71 | } 72 | 73 | return result; 74 | }; 75 | 76 | module.exports = function(done) { 77 | 78 | var options = { 79 | type: 'npm', 80 | uri: 'https://skimdb.npmjs.com/registry/_all_docs?include_docs=true', 81 | jsonStreamPath: 'rows.*.doc', 82 | filter: ['name', 'repository', 'repositories', 'homepage'], 83 | filePath: path.resolve(__dirname, '../store/npm.js'), 84 | parser: getURL 85 | }; 86 | base(options, done); 87 | }; 88 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var gulp = require('gulp'); 5 | var rimraf = require('rimraf').sync; 6 | var plugins = require('gulp-load-plugins')(); 7 | var stats = require('./stats.json'); 8 | 9 | var paths = { 10 | lint: ['./gulpfile.js', './task/**/*.js', './store/*.js', './index.js', './test/**/*.js'], 11 | tests: ['./test/**/*.js', '!test/{temp,temp/**}', '!test/fixtures/**'], 12 | source: ['./index.js'] 13 | }; 14 | 15 | function rmStoreFiles() { 16 | rimraf('./store/bower.js'); 17 | rimraf('./store/composer.js'); 18 | rimraf('./store/npm.js'); 19 | } 20 | 21 | rmStoreFiles(); 22 | 23 | var writeStats = function(type, result) { 24 | if (stats.total[type] > result.total) { 25 | // TODO revert store file 26 | console.log(type + ' update failed'); 27 | rmStoreFiles(); 28 | process.exit(1); 29 | return; 30 | } 31 | 32 | stats.update[type] = result.update; 33 | stats.total[type] = result.total; 34 | 35 | fs.writeFileSync('./stats.json', JSON.stringify(stats,null, ' ')); 36 | }; 37 | 38 | gulp.task('lint', function () { 39 | return gulp.src(paths.lint) 40 | .pipe(plugins.jshint('.jshintrc')) 41 | .pipe(plugins.plumber()) 42 | .pipe(plugins.jscs()) 43 | .pipe(plugins.jshint.reporter('jshint-stylish')); 44 | }); 45 | 46 | gulp.task('istanbul', function (cb) { 47 | gulp.src(paths.source) 48 | .pipe(plugins.istanbul()) // Covering files 49 | .on('finish', function () { 50 | gulp.src(paths.tests) 51 | .pipe(plugins.plumber()) 52 | .pipe(plugins.mocha()) 53 | .pipe(plugins.istanbul.writeReports()) // Creating the reports after tests runned 54 | .on('finish', function() { 55 | process.chdir(__dirname); 56 | cb(); 57 | }); 58 | }); 59 | }); 60 | 61 | gulp.task('buildNPM', function (cb) { 62 | var npmTask = require('./task/npm.js'); 63 | npmTask(function(err, result) { 64 | if (err) { 65 | return cb(err); 66 | } 67 | writeStats('npm', result); 68 | cb(); 69 | }); 70 | }); 71 | 72 | gulp.task('buildBower', function (cb) { 73 | var bowerTask = require('./task/bower.js'); 74 | bowerTask(function(err, result) { 75 | if (err) { 76 | return cb(err); 77 | } 78 | writeStats('bower', result); 79 | cb(); 80 | }); 81 | }); 82 | 83 | gulp.task('buildComposer', function (cb) { 84 | var composerTask = require('./task/composer.js'); 85 | composerTask(function(err, result) { 86 | if (err) { 87 | return cb(err); 88 | } 89 | writeStats('composer', result); 90 | cb(); 91 | }); 92 | }); 93 | 94 | gulp.task('build', ['buildNPM', 'buildBower', 'buildComposer']); 95 | 96 | gulp.task('test', ['lint', 'istanbul']); 97 | 98 | gulp.task('default', ['build'], function() { 99 | var history = require('./task/history.js'); 100 | history(stats); 101 | }); 102 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## v0.2.33 (2015-07-22) 5 | 6 | npm: 3636 7 | 8 | bower: 813 9 | 10 | composer: 1376 11 | 12 | 13 | ## v0.2.32 (2015-07-08) 14 | 15 | npm: 2673 16 | 17 | bower: 578 18 | 19 | composer: 1043 20 | 21 | 22 | ## v0.2.31 (2015-06-26) 23 | 24 | npm: 8657 25 | 26 | bower: 2069 27 | 28 | composer: 3434 29 | 30 | 31 | ## v0.2.30 (2015-05-18) 32 | 33 | npm: 3270 34 | 35 | bower: 790 36 | 37 | composer: 1255 38 | 39 | 40 | ## v0.2.29 (2015-05-03) 41 | 42 | npm: 2769 43 | 44 | bower: 750 45 | 46 | composer: 1152 47 | 48 | 49 | ## v0.2.28 (2015-04-20) 50 | 51 | npm: 2974 52 | 53 | bower: 677 54 | 55 | composer: 1058 56 | 57 | 58 | ## v0.2.27 (2015-04-07) 59 | 60 | npm: 6145 61 | 62 | bower: 237 63 | 64 | composer: 2677 65 | 66 | 67 | ## v0.2.26 (2015-03-09) 68 | 69 | npm: 3480 70 | 71 | composer: 1628 72 | 73 | 74 | ## v0.2.25 (2015-02-20) 75 | 76 | npm: 1788 77 | 78 | bower: 446 79 | 80 | composer: 870 81 | 82 | 83 | ## v0.2.24 (2015-02-11) 84 | 85 | npm: 1243 86 | 87 | bower: 337 88 | 89 | composer: 591 90 | 91 | 92 | ## v0.2.24 (2015-02-06) 93 | 94 | npm: 2103 95 | 96 | bower: 590 97 | 98 | composer: 964 99 | 100 | 101 | ## v0.2.22 (2015-01-27) 102 | 103 | npm: 879 104 | 105 | bower: 262 106 | 107 | composer: 376 108 | 109 | 110 | ## v0.2.21 (2015-01-24) 111 | 112 | npm: 1335 113 | 114 | bower: 378 115 | 116 | composer: 572 117 | 118 | 119 | ## v0.2.19 (2015-01-18) 120 | 121 | npm: 1023 122 | 123 | bower: 315 124 | 125 | composer: 507 126 | 127 | 128 | ## v0.2.17 (2015-01-12) 129 | 130 | npm: 98290 131 | 132 | bower: 22871 133 | 134 | composer: 46137 135 | 136 | 137 | ## v0.2.16 (2015-01-03) 138 | 139 | npm: 1969 140 | 141 | bower: 559 142 | 143 | composer: 875 144 | 145 | 146 | ## v0.2.16 (2014-12-21) 147 | 148 | npm: 1839 149 | 150 | bower: 554 151 | 152 | composer: 1001 153 | 154 | 155 | ## v0.2.15 (2014-12-11) 156 | 157 | npm: 1230 158 | 159 | bower: 348 160 | 161 | composer: 566 162 | 163 | 164 | ## v0.2.14 (2014-12-04) 165 | 166 | npm: 91776 167 | 168 | bower: 21143 169 | 170 | composer: 43129 171 | 172 | 173 | ## v0.2.12 (2014-12-03) 174 | 175 | npm: 918 176 | 177 | bower: 307 178 | 179 | composer: 443 180 | 181 | 182 | ## v0.2.11 (2014-11-28) 183 | 184 | npm: 1876 185 | 186 | bower: 496 187 | 188 | composer: 878 189 | 190 | 191 | ## v0.2.10 (2014-11-18) 192 | 193 | npm: 2072 194 | 195 | bower: 585 196 | 197 | composer: 1005 198 | 199 | 200 | ## v0.2.9 (2014-11-07) 201 | 202 | npm: 621 203 | 204 | bower: 172 205 | 206 | composer: 312 207 | 208 | 209 | ## v0.2.8 (2014-11-03) 210 | 211 | npm: 1157 212 | 213 | bower: 360 214 | 215 | composer: 554 216 | 217 | 218 | ## v0.2.7 (2014-10-28) 219 | 220 | npm: 1345 221 | 222 | bower: 464 223 | 224 | composer: 721 225 | 226 | 227 | ## v0.2.6 (2014-10-20) 228 | 229 | npm: 811 230 | 231 | bower: 241 232 | 233 | composer: 423 234 | 235 | 236 | ## v0.2.5 (2014-10-15) 237 | 238 | npm: 397 239 | 240 | bower: 140 241 | 242 | composer: 202 243 | 244 | 245 | ## v0.2.4 (2014-10-13) 246 | 247 | npm: 1200 248 | 249 | bower: 329 250 | 251 | composer: 445 252 | 253 | 254 | ## v0.2.3 (2014-10-07) 255 | 256 | npm: 108 257 | 258 | bower: 37 259 | 260 | composer: 56 261 | 262 | 263 | ## v0.2.2 (2014-10-07) 264 | 265 | npm: 833 266 | 267 | bower: 255 268 | 269 | composer: 361 270 | 271 | 272 | ## v0.2.1 (2014-10-01) 273 | 274 | npm: 1199 275 | 276 | bower: 354 277 | 278 | composer: 443 279 | 280 | 281 | ## v0.2.0 (2014-09-24) 282 | 283 | npm: 900 284 | 285 | bower: 295 286 | 287 | composer: 421 288 | 289 | 290 | ## v0.1.0 (2014-09-22) 291 | 292 | npm: 78780 293 | 294 | bower: 17643 295 | 296 | composer: 37521 297 | --------------------------------------------------------------------------------