Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

init

  • Loading branch information...
commit b77b713dce2f378a6fb9b373dbf4c48aa159d97b 1 parent 1c7cae6
@sindresorhus sindresorhus authored
View
9 .editorconfig
@@ -0,0 +1,9 @@
+# editorconfig.org
+root = true
+
+[*]
+indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
View
1  .gitattributes
@@ -0,0 +1 @@
+* text=auto
View
1  .gitignore
@@ -0,0 +1 @@
+node_modules
View
21 .jshintrc
@@ -0,0 +1,21 @@
+{
+ "node": true,
+ "es5": true,
+ "esnext": true,
+ "bitwise": true,
+ "camelcase": true,
+ "curly": true,
+ "eqeqeq": true,
+ "immed": true,
+ "indent": 4,
+ "latedef": true,
+ "newcap": true,
+ "noarg": true,
+ "quotmark": "single",
+ "regexp": true,
+ "undef": true,
+ "unused": true,
+ "strict": true,
+ "trailing": true,
+ "smarttabs": true
+}
View
4 .npmignore
@@ -0,0 +1,4 @@
+.npmignore
+.editorconfig
+.jshintrc
+.gitattributes
View
1  Procfile
@@ -0,0 +1 @@
+web: node server.js
View
80 component-list.js
@@ -0,0 +1,80 @@
+/*jshint camelcase:false */
+'use strict';
+var request = require('request');
+var Q = require('q');
+
+var REGISTRY_URL = 'https://bower.herokuapp.com/packages';
+
+function createComponentData(name, data) {
+ return {
+ name: name,
+ description: data.description,
+ owner: data.owner.login,
+ website: data.homepage || data.html_url,
+ forks: data.forks,
+ stars: data.watchers,
+ created: data.created_at,
+ updated: data.updated_at
+ };
+}
+
+function fetchComponents() {
+ return Q.fcall(function () {
+ var deferred = Q.defer();
+ request.get(REGISTRY_URL, {json: true}, function(err, response, body) {
+ if (!err && response.statusCode === 200) {
+ deferred.resolve(body);
+ } else {
+ deferred.reject(new Error(err));
+ }
+ });
+ return deferred.promise;
+ }).then(function (list) {
+ var results = list.map(function (el) {
+ var deferred = Q.defer();
+ var re = /github\.com\/([\w\-\.]+)\/([\w\-\.]+)/i;
+ var parsedUrl = re.exec(el.url.replace(/\.git$/, ''));
+
+ // only return components from github
+ if (!parsedUrl) {
+ deferred.resolve();
+ return deferred.promise;
+ }
+
+ var user = parsedUrl[1];
+ var repo = parsedUrl[2];
+ var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo;
+
+ request.get(apiUrl, {
+ json: true,
+ qs: {
+ client_id: process.env.GITHUB_CLIENT_ID,
+ client_secret: process.env.GITHUB_CLIENT_SECRET
+ }
+ }, function (err, response, body) {
+ if (!err && response.statusCode === 200) {
+ deferred.resolve(createComponentData(el.name, body));
+ } else {
+ if (response.statusCode === 404) {
+ // uncomment to get a list of registry items pointing
+ // to non-existing repos
+ //console.log(el.name + '\n' + el.url + '\n');
+
+ // don't fail just because the repo doesnt exist
+ // instead just return `undefined` and filter it out later
+ deferred.resolve();
+ } else {
+ deferred.reject(new Error(err));
+ }
+ }
+ return deferred.promise;
+ });
+ return deferred.promise;
+ });
+
+ console.log('Finished fetching data from Bower registry', '' + new Date());
+ return Q.all(results);
+ });
+}
+
+module.exports = fetchComponents;
View
40 package.json
@@ -0,0 +1,40 @@
+{
+ "name": "bower-component-list",
+ "version": "0.1.0",
+ "description": "Aggregated list of components from the Bower registry and GitHub metadata",
+ "keywords": [
+ "bower",
+ "package",
+ "manager",
+ "web",
+ "component",
+ "package"
+ ],
+ "homepage": "https://github.com/sindresorhus/bower-component-list",
+ "bugs": "https://github.com/sindresorhus/bower-component-list/issues",
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "main": "component-list.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sindresorhus/bower-component-list.git"
+ },
+ "dependencies": {
+ "request": "~2.12.0",
+ "lodash": "~1.0.0-rc.3",
+ "q": "~0.8.12",
+ "connect": "~2.7.2"
+ },
+ "engines": {
+ "node": ">=0.8.0",
+ "npm": ">=1.1.65"
+ },
+ "licenses": [
+ {
+ "type": "MIT"
+ }
+ ]
+}
View
22 readme.md
@@ -0,0 +1,22 @@
+# bower-component-list
+
+Aggregated list of components from the [Bower registry](https://bower.herokuapp.com/packages) and GitHub metadata.
+
+Currently used as the back-end for the [Bower components site](http://sindresorhus.com/bower-components/).
+
+
+### Getting Started
+
+- [Register a new OAuth app](https://github.com/settings/applications/new) on GitHub. This is needed since GitHub allows more API usage for registered apps.
+
+- Set the environment variables `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` from your newly created app. Remember set the `NODE_ENV` to `production` when you deploy.
+
+- Run `node server.js && open http://localhost:8011`
+
+You can change the port by setting the `PORT` environment variable.
+
+
+## License
+
+[MIT License](http://en.wikipedia.org/wiki/MIT_License)
+(c) [Sindre Sorhus](http://sindresorhus.com)
View
116 server.js
@@ -1,61 +1,71 @@
-// Update once every hour
-const UPDATE_INTERVAL_IN_SECONDS = 60 * 60;
-const HTTP_PORT = process.env.PORT || 8001;
-
-var http = require('http');
-var Q = require('q');
-var gruntPlugins = require('./grunt-plugins');
+'use strict';
var crypto = require('crypto');
var connect = require('connect');
+var Q = require('q');
+var fetchComponents = require('./component-list');
+var componentListEntity;
-// pluginListEntity - promise {etag: '', json: ''}
-// using a promise so that clients can connect and wait for the initial entity
-var pluginListEntity = getPluginListEntity();
-
-function getPluginListEntity() {
- var deferred = Q.defer();
- gruntPlugins.fetchPluginList().then(
-
- function(pluginList) {
- var entity = {
- json: JSON.stringify(pluginList)
- };
- var shasum = crypto.createHash('sha1');
- shasum.update(entity.json);
- entity.etag = shasum.digest('hex');
- deferred.resolve(entity);
- // update the entity
- pluginListEntity = deferred.promise;
- }).fail(function(e) {
- deferred.reject(e);
- });
- return deferred.promise;
+var HTTP_PORT = process.env.PORT || 8011;
+var UPDATE_INTERVAL_IN_MINUTES = 60;
+
+
+function getComponentListEntity() {
+ var deferred = Q.defer();
+
+ fetchComponents().then(function (list) {
+ console.log('Finished fetching data from GitHub', '' + new Date());
+
+ // TODO: Find a way for the promise not to return null so this isn't needed
+ list = list.filter(function (el) {
+ return el !== null && el !== undefined;
+ });
+
+ var entity = {json: JSON.stringify(list)};
+ var shasum = crypto.createHash('sha1');
+ shasum.update(entity.json);
+ entity.etag = shasum.digest('hex');
+ deferred.resolve(entity);
+ // update the entity
+ componentListEntity = deferred.promise;
+ }).fail(function (err) {
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+}
+
+function getComponentList(request, response, next) {
+ componentListEntity.then(function (entity) {
+ // allow CORS
+ response.setHeader('ETag', entity.etag);
+ response.setHeader('Access-Control-Allow-Origin', '*');
+ response.setHeader('Content-Type', 'application/json');
+
+ if (request.headers['if-none-match'] === entity.etag) {
+ response.statusCode = 304;
+ response.end();
+ return;
+ }
+
+ response.statusCode = 200;
+ response.end(new Buffer(entity.json));
+ }).fail(function(err) {
+ next(err);
+ });
}
-// Update function
-setInterval(function() {
- getPluginListEntity();
-}, UPDATE_INTERVAL_IN_SECONDS * 1000);
-
-var app = connect().use(connect.logger('dev')).use(connect.errorHandler()).use(connect.timeout(10000)).use(function(request, response, next) {
- // get the plugin list
- pluginListEntity.then(function(entity) {
- // Allow Cross-origin resource sharing
- response.setHeader('Access-Control-Allow-Origin', '*');
- response.setHeader("Content-Type", "application/json");
- response.setHeader('ETag', entity.etag);
- if (request.headers['if-none-match'] === entity.etag) {
- response.statusCode = 304;
- response.end();
- return;
- }
- response.statusCode = 200;
- response.end(new Buffer(entity.json));
- }).fail(function(e) {
- // something went wrong
- next(e);
- })
-}).listen(HTTP_PORT);
+// componentListEntity - promise {etag: '', json: ''}
+// using a promise so that clients can connect and wait for the initial entity
+componentListEntity = getComponentListEntity();
+
+connect()
+ .use(connect.errorHandler())
+ .use(connect.timeout(10000))
+ .use(connect.logger('dev'))
+ .use(getComponentList)
+ .listen(HTTP_PORT);
+
+setInterval(getComponentListEntity, UPDATE_INTERVAL_IN_MINUTES * 1000 * 60);
console.log('Server running on port ' + HTTP_PORT);
Please sign in to comment.
Something went wrong with that request. Please try again.