Skip to content

Commit

Permalink
Modularise code, add batch to npm version info requests
Browse files Browse the repository at this point in the history
  • Loading branch information
alanshaw committed Jan 21, 2017
1 parent e203432 commit d84fe00
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 146 deletions.
36 changes: 36 additions & 0 deletions lib/batch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
function Batch () {
if (!(this instanceof Batch)) return new Batch()
this._batch = {}
}

/**
* Create or add a callback function to the end of a batch for a given key.
* @param key
* @param cb
*/
Batch.prototype.push = function (key, cb) {
this._batch[key] = this._batch[key] || []
this._batch[key].push(cb)
}

/**
* Determine if there is a batch for the given key.
* @param {String} key
* @returns {boolean}
*/
Batch.prototype.exists = function (key) {
return this._batch[key] != null
}

/**
* Call all the batched callback functions for a key and remove them.
* @param {String} key ID of the batch operation
* @param {Function} fn Function to call for each
*/
Batch.prototype.call = function (key, cb) {
var cbs = this._batch[key]
this._batch[key] = null
cbs.forEach(cb)
}

module.exports = Batch
133 changes: 9 additions & 124 deletions lib/david.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,119 +6,10 @@
* Licensed under the MIT license.
*/

var npm = require('npm')
var parallel = require('async/parallel')
var semver = require('semver')

var unstablePattern = /[a-z+-]/i

/**
* Determine if a version is a stable version or not.
* @param {String} version
* @return {Boolean}
*/
function isStable (version) {
return !unstablePattern.test(version || '')
}

/**
* Determine if a version is a SCM URL or not.
* @param {String} version
* @return {Boolean}
*/
function isScm (version) {
var scmPrefixes = ['git:', 'git+ssh:', 'https:', 'git+https:']
var blacklisted = scmPrefixes.filter(function (prefix) {
return version.indexOf(prefix) === 0
})
return !!blacklisted.length
}

/**
* Get the latest stable version from a list of versions in ascending order.
* @param {Array} versions
* @return {String}
*/
function getLatestStable (versions) {
versions = versions.slice()
while (versions.length) {
var version = versions.pop()
if (isStable(version)) {
return version
}
}
return null
}

/**
* Get info about the versions for this dependency. Returns an object with
* `current` and `versions` properties. Where `current` is the version you'd
* get when you `npm install [package]` and `versions` is a sorted array of
* available versions for the dependency.
* @param {String} name Dependency name
* @param {Object} opts Options
* @param {Object} [opts.npm] npm configuration options
*/
function getVersionsInfo (name, opts, cb) {
npm.load(opts.npm || {}, function (err) {
if (err) return cb(err)

npm.commands.view([name, 'versions', 'time'], true, function (err, data) {
if (err) return cb(err)

var currentVersion = Object.keys(data)[0]
var versions = null

// `npm view 0 versions` returns {}
if (!currentVersion) {
return cb(new Error('Failed to get versions for ' + name))
}

// Some packages simply don't have a time object
if (data[currentVersion].time) {
versions = data[currentVersion].versions.sort(function (a, b) {
a = data[currentVersion].time[a]
b = data[currentVersion].time[b]
return (a < b ? -1 : (a > b ? 1 : 0))
})
} else {
versions = data[currentVersion].versions
}

cb(null, {current: currentVersion, versions: versions})
})
})
}

/**
* Get the latest version and latest stable version
* @param {String} current The version you get when you `npm install [package]`
* @param {Array} versions All versions available
* @param {Object} opts Options
* @param {Boolean} [opts.loose] Use loose option when querying semver
*/
function getLatestVersionInfo (current, versions, opts) {
var stable = current
var latest = versions[versions.length - 1]

if (!isStable(stable)) {
stable = getLatestStable(versions)
}

// getLatestStable might not have found a stable version
if (stable) {
// Latest is the most recent version with higher version than stable
for (var i = versions.length - 1; i >= 0; i--) {
// If !opts.loose then this may throw
if (semver.gt(versions[i], stable, opts.loose)) {
latest = versions[i]
break
}
}
}

return {latest: latest, stable: stable}
}
var Version = require('./version')
var Npm = require('./npm')

// Convert dependencies specified as an array to an object
function normaliseDeps (deps) {
Expand Down Expand Up @@ -165,13 +56,7 @@ function isUpdated (dep, opts) {
return false
}

module.exports.isUpdated = isUpdated

function getVersionsInRange (range, versions, loose) {
return versions.filter(function (v) {
return semver.satisfies(v, range, loose)
})
}
exports.isUpdated = isUpdated

/**
* Given the options passed to david, figure out the dep type.
Expand Down Expand Up @@ -250,7 +135,7 @@ function getDependencies (manifest, opts, cb) {
return cb()
}

if (isScm(deps[depName])) {
if (Version.isScm(deps[depName])) {
err = new Error('SCM dependency: ' + deps[depName])
err.code = 'ESCM'

Expand All @@ -262,7 +147,7 @@ function getDependencies (manifest, opts, cb) {
return cb()
}

getVersionsInfo(depName, opts, function (err, versionsInfo) {
Npm.getVersionsInfo(depName, opts, function (err, versionsInfo) {
if (err) {
if (!opts.error.E404 && err.code === 'E404') {
if (err === '404 Not Found') {
Expand All @@ -280,7 +165,7 @@ function getDependencies (manifest, opts, cb) {
}

try {
var latestVersionInfo = getLatestVersionInfo(versionsInfo.current, versionsInfo.versions, opts)
var latestVersionInfo = Version.getLatest(versionsInfo.current, versionsInfo.versions, opts)

pkgs[depName] = {
required: deps[depName],
Expand All @@ -293,7 +178,7 @@ function getDependencies (manifest, opts, cb) {
}

if (opts.rangeVersions) {
pkgs[depName].rangeVersions = getVersionsInRange(deps[depName], versionsInfo.versions, opts.loose)
pkgs[depName].rangeVersions = Version.getVersionsInRange(deps[depName], versionsInfo.versions, opts.loose)
}
} catch (err) {
error = err
Expand All @@ -307,7 +192,7 @@ function getDependencies (manifest, opts, cb) {
parallel(tasks, function () { cb(error, pkgs) })
}

module.exports.getDependencies = getDependencies
exports.getDependencies = getDependencies

/**
* Get a list of updated packages for the passed manifest.
Expand All @@ -325,7 +210,7 @@ module.exports.getDependencies = getDependencies
* @param {Array} [opts.ignore] List of dependency names to ignore
* @param {Function} cb Function that receives the results
*/
module.exports.getUpdatedDependencies = function (manifest, opts, cb) {
exports.getUpdatedDependencies = function (manifest, opts, cb) {
if (!cb) {
cb = opts
opts = {}
Expand Down
56 changes: 56 additions & 0 deletions lib/npm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
var npm = require('npm')
var Batch = require('./batch')

var batch = new Batch()

/**
* Get info about the versions for this dependency. Returns an object with
* `current` and `versions` properties. Where `current` is the version you'd
* get when you `npm install [package]` and `versions` is a sorted array of
* available versions for the dependency.
* @param {String} name Dependency name
* @param {Object} opts Options
* @param {Object} [opts.npm] npm configuration options
*/
function getVersionsInfo (name, opts, cb) {
if (batch.exists(name)) {
return batch.push(name, cb)
}

batch.push(name, cb)

npm.load(opts.npm || {}, function (err) {
if (err) return batch.call(name, function (cb) { cb(err) })

npm.commands.view([name, 'versions', 'time'], true, function (err, data) {
if (err) return batch.call(name, function (cb) { cb(err) })

var currentVersion = Object.keys(data)[0]
var versions = null

// `npm view 0 versions` returns {}
if (!currentVersion) {
return batch.call(name, function (cb) {
cb(new Error('Failed to get versions for ' + name))
})
}

// Some packages simply don't have a time object
if (data[currentVersion].time) {
versions = data[currentVersion].versions.sort(function (a, b) {
a = data[currentVersion].time[a]
b = data[currentVersion].time[b]
return (a < b ? -1 : (a > b ? 1 : 0))
})
} else {
versions = data[currentVersion].versions
}

batch.call(name, function (cb) {
cb(null, { current: currentVersion, versions: versions })
})
})
})
}

exports.getVersionsInfo = getVersionsInfo
87 changes: 87 additions & 0 deletions lib/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
var semver = require('semver')

var unstablePattern = /[a-z+-]/i

/**
* Determine if a version is a stable version or not.
* @param {String} version
* @return {Boolean}
*/
function isStable (version) {
return !unstablePattern.test(version || '')
}

exports.isStable = isStable

/**
* Determine if a version is a SCM URL or not.
* @param {String} version
* @return {Boolean}
*/
function isScm (version) {
var scmPrefixes = ['git:', 'git+ssh:', 'https:', 'git+https:']
var blacklisted = scmPrefixes.filter(function (prefix) {
return version.indexOf(prefix) === 0
})
return !!blacklisted.length
}

exports.isScm = isScm

/**
* Get the latest stable version from a list of versions in ascending order.
* @param {Array} versions
* @return {String}
*/
function getLatestStable (versions) {
versions = versions.slice()
while (versions.length) {
var version = versions.pop()
if (isStable(version)) {
return version
}
}
return null
}

exports.getLatestStable = getLatestStable

/**
* Get the latest version and latest stable version
* @param {String} current The version you get when you `npm install [package]`
* @param {Array} versions All versions available
* @param {Object} opts Options
* @param {Boolean} [opts.loose] Use loose option when querying semver
*/
function getLatest (current, versions, opts) {
var stable = current
var latest = versions[versions.length - 1]

if (!isStable(stable)) {
stable = getLatestStable(versions)
}

// getLatestStable might not have found a stable version
if (stable) {
// Latest is the most recent version with higher version than stable
for (var i = versions.length - 1; i >= 0; i--) {
// If !opts.loose then this may throw
if (semver.gt(versions[i], stable, opts.loose)) {
latest = versions[i]
break
}
}
}

return { latest: latest, stable: stable }
}

exports.getLatest = getLatest

function getVersionsInRange (range, versions, loose) {
return versions.filter(function (v) {
return semver.satisfies(v, range, loose)
})
}

exports.getVersionsInRange = getVersionsInRange
Loading

0 comments on commit d84fe00

Please sign in to comment.