all files / lib/ parsePackage.js

80.41% Statements 78/97
59.09% Branches 13/22
79.17% Functions 19/24
80.21% Lines 77/96
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272                                                                                          11×                                             16×                                                                                                                                                                                                                                                                
'use strict';
 
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.packageParser = undefined;
 
var _toArray2 = require('babel-runtime/helpers/toArray');
 
var _toArray3 = _interopRequireDefault(_toArray2);
 
var _regenerator = require('babel-runtime/regenerator');
 
var _regenerator2 = _interopRequireDefault(_regenerator);
 
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
 
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
 
var _keys = require('babel-runtime/core-js/object/keys');
 
var _keys2 = _interopRequireDefault(_keys);
 
var _fs = require('fs');
 
var _fs2 = _interopRequireDefault(_fs);
 
var _requestPromise = require('request-promise');
 
var _requestPromise2 = _interopRequireDefault(_requestPromise);
 
var _cheerio = require('cheerio');
 
var _cheerio2 = _interopRequireDefault(_cheerio);
 
var _bluebird = require('bluebird');
 
var _bluebird2 = _interopRequireDefault(_bluebird);
 
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
var ERR_NO_PKG = 'Unable find package on NPM';
 
var packageParser = exports.packageParser = {};
/**
* packageParser is the parent for all of our internal package.json parsing
* Pretty much everything is private to the function itself
* To call it, use something like:
* packageParser.matchDependencies('./test/fixtures/package-test.json').then((p) => console.log(p));
* @private
**/
 
packageParser.dependencies = function (path_to_pkg) {
  /**
  * Load a package.json file
  * @param path_to_pkg {string} The path, from root of application (_not_ from this module) to the package.json to load
  * @returns {array} An array of all modules in the dependencies and the devDependencies
  * @private
  **/
  var pj = JSON.parse(_fs2.default.readFileSync(path_to_pkg, 'utf8'));
  return (0, _keys2.default)(pj.dependencies).concat((0, _keys2.default)(pj.devDependencies));
};
 
packageParser.depURL = function (pkg) {
  /**
  * Given a package name, get the npmjs url to that package
  * @param pkg {string} The package name, as seen in package.json
  * @returns {string} A fully-qualified URL to the package
  * @private
  **/
  return 'https://www.npmjs.com/package/' + pkg;
};
 
packageParser.fetchNPM = function () {
  var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(depURL) {
    var npmPage;
    return _regenerator2.default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return (0, _requestPromise2.default)(depURL);
 
          case 2:
            npmPage = _context.sent;
            return _context.abrupt('return', this.parseDependencies(npmPage));
 
          case 4:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this);
  }));
  return function (_x) {
    return ref.apply(this, arguments);
  };
}();
 
packageParser.parseDependencies = function (html) {
  /**
  * Takes the html content of an NPM module's page and parses out the keywords
  * Typically this is called when request returns a given NPM page
  * @param {string} The HTML content of the page
  * @returns {Function} A resolved or rejected Promise
  * @private
  **/
  var $ = _cheerio2.default.load(html);
  var deps = $('.sidebar>p.list-of-links:first-of-type').text().split(',');
  return deps.map(function (el) {
    return el.replace(/\s*(\S.*\S)\s*/g, '$1');
  });
};
 
packageParser.parseDockers = function (dockerJSON) {
  /**
  * Docker makes all the official repos available in a JSON file at one endpoint
  * URL: https://hub.docker.com/v2/repositories/library/
  * JSON from here comes in the format:
  *  {
  *   user: "library",
  *   name: "nginx",
  *   namespace: "library",
  *   status: 1,
  *   description: "Official build of Nginx.",
  *   is_private: false,
  *   is_automated: false,
  *   can_edit: false,
  *   star_count: 3054,
  *   pull_count: 196962844,
  *   last_updated: "2016-05-24T23:08:09.477673Z"
  * },
  * Grab that JSON and parse it into an easy-to-search object of the format:
  * {
  *   nginx: "Official build of Nginx."
  * }
  * @param dockerJSON {string} Stringified JSON from the Docker endpoint
  * @returns {Object} An object of Dockerfiles with the format:
  * { technol}
  * @private
  **/
  var dj = JSON.parse(dockerJSON);
  var dockers = {};
  dj.results.forEach(function (el) {
    dockers[el.name] = el.description;
  });
  return dockers;
};
 
packageParser.fetchDockers = function () {
  var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2(url) {
    var dockerURL, dockers;
    return _regenerator2.default.wrap(function _callee2$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            /**
            * Grabs all Official Docker repositories
            * @param {string} A URL, but usually we won't need this and will use the default
            * @returns {Function} A Promise
            * @private
            **/
            dockerURL = url || 'https://hub.docker.com/v2/repositories/library/?page_size=999';
            _context2.next = 3;
            return (0, _requestPromise2.default)(dockerURL);
 
          case 3:
            dockers = _context2.sent;
            return _context2.abrupt('return', this.parseDockers(dockers));
 
          case 5:
          case 'end':
            return _context2.stop();
        }
      }
    }, _callee2, this);
  }));
  return function (_x2) {
    return ref.apply(this, arguments);
  };
}();
 
packageParser.matchDependencies = function () {
  var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee3(path_to_pkg) {
    var _this = this;
 
    var packageJSON, packageURLs, fetchedPromises, _ref, _ref2, fetchedData, dockerKeywords, depKeywords, depList;
 
    return _regenerator2.default.wrap(function _callee3$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            /**
            * Given a package, return an object with the docker modules needed to launch
            * that package. This method pulls together all other methods in this object.
            * Response is an object that shows the docker module.
            * Example response:
            *   {  mongo : true,
            *   redis: true, }
            * @param {string} The path, from root of application (_not_ from this module) to the package.json to load
            * @returns {Function} A Promise containt an object describing all of the needed docker modules
            **/
            packageJSON = this.dependencies(path_to_pkg);
 
            // From our dependencies, get a list of NPM URLs
 
            packageURLs = packageJSON.map(function (pkg) {
              return _this.depURL(pkg);
            });
 
            // Create an array holding all our Promises, so that we can Promise.all them later
 
            fetchedPromises = [this.fetchDockers()];
 
            // We now have an array of NPM URLs
            // We need to iterate over the array
 
            packageURLs.forEach(function (url) {
              // And for each URL, grab a Promise of the fetch & parse
              // Drop that in our array
              fetchedPromises.push(_this.fetchNPM(url));
            });
 
            // If we separately awaited the dependencies and Docker promises
            // We'd do things serially. Instead, use destructuring and the spread
            // operator to await them in paralell
            _context3.next = 6;
            return _bluebird2.default.all(fetchedPromises);
 
          case 6:
            _ref = _context3.sent;
            _ref2 = (0, _toArray3.default)(_ref);
            fetchedData = _ref2;
 
            // The first item in fetchedData is our Docker module names
            // All remaining items are the dependencies... in multiple nested arrays
 
            dockerKeywords = fetchedData.shift();
            depKeywords = fetchedData.reduce(function (accum, el) {
              return accum.concat(el);
            }, []);
            depList = {};
 
 
            depKeywords.forEach(function (kw) {
              // For each keyword from an npm page
              if (!depList.hasOwnProperty(kw) && dockerKeywords.hasOwnProperty(kw)) {
                // If there's not already a key in depList with that name
                //  And if there's a matching Docker module
                //  Add a key
                depList[kw] = true;
              }
            });
 
            return _context3.abrupt('return', (0, _keys2.default)(depList));
 
          case 14:
          case 'end':
            return _context3.stop();
        }
      }
    }, _callee3, this);
  }));
  return function (_x3) {
    return ref.apply(this, arguments);
  };
}();
 
var bar = packageParser.matchDependencies('../../test/fixtures/package-test.json');
bar.then(function (p) {
  return console.log('p', p);
});