| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 | /*npm outdated [pkg]Does the following:1. check for a new version of pkgIf no packages are specified, then run for all installedpackages.--parseable creates output like this:<fullpath>:<name@wanted>:<name@installed>:<name@latest>*/module.exports = outdatedoutdated.usage = 'npm outdated [[<@scope>/]<pkg> ...]'outdated.completion = require('./utils/completion/installed-deep.js')const os = require('os')const url = require('url')const path = require('path')const readPackageTree = require('read-package-tree')const asyncMap = require('slide').asyncMapconst color = require('ansicolors')const styles = require('ansistyles')const table = require('text-table')const semver = require('semver')const npa = require('libnpm/parse-arg')const pickManifest = require('npm-pick-manifest')const fetchPackageMetadata = require('./fetch-package-metadata.js')const mutateIntoLogicalTree = require('./install/mutate-into-logical-tree.js')const npm = require('./npm.js')const npmConfig = require('./config/figgy-config.js')const figgyPudding = require('figgy-pudding')const packument = require('libnpm/packument')const long = npm.config.get('long')const isExtraneous = require('./install/is-extraneous.js')const computeMetadata = require('./install/deps.js').computeMetadataconst computeVersionSpec = require('./install/deps.js').computeVersionSpecconst moduleName = require('./utils/module-name.js')const output = require('./utils/output.js')const ansiTrim = require('./utils/ansi-trim')const OutdatedConfig = figgyPudding({  also: {},  color: {},  depth: {},  dev: 'development',  development: {},  global: {},  json: {},  only: {},  parseable: {},  prod: 'production',  production: {},  save: {},  'save-dev': {},  'save-optional': {}})function uniq (list) {  // we maintain the array because we need an array, not iterator, return  // value.  var uniqed = []  var seen = new Set()  list.forEach(function (item) {    if (seen.has(item)) return    seen.add(item)    uniqed.push(item)  })  return uniqed}function andComputeMetadata (next) {  return function (er, tree) {    if (er) return next(er)    next(null, computeMetadata(tree))  }}function outdated (args, silent, cb) {  if (typeof cb !== 'function') {    cb = silent    silent = false  }  let opts = OutdatedConfig(npmConfig())  var dir = path.resolve(npm.dir, '..')  // default depth for `outdated` is 0 (cf. `ls`)  if (opts.depth === Infinity) opts = opts.concat({depth: 0})  readPackageTree(dir, andComputeMetadata(function (er, tree) {    if (!tree) return cb(er)    mutateIntoLogicalTree(tree)    outdated_(args, '', tree, {}, 0, opts, function (er, list) {      list = uniq(list || []).sort(function (aa, bb) {        return aa[0].path.localeCompare(bb[0].path) ||          aa[1].localeCompare(bb[1])      })      if (er || silent ||          (list.length === 0 && !opts.json)) {        return cb(er, list)      }      if (opts.json) {        output(makeJSON(list, opts))      } else if (opts.parseable) {        output(makeParseable(list, opts))      } else {        var outList = list.map(x => makePretty(x, opts))        var outHead = [ 'Package',          'Current',          'Wanted',          'Latest',          'Location'        ]        if (long) outHead.push('Package Type', 'Homepage')        var outTable = [outHead].concat(outList)        if (opts.color) {          outTable[0] = outTable[0].map(function (heading) {            return styles.underline(heading)          })        }        var tableOpts = {          align: ['l', 'r', 'r', 'r', 'l'],          stringLength: function (s) { return ansiTrim(s).length }        }        output(table(outTable, tableOpts))      }      process.exitCode = list.length ? 1 : 0      cb(null, list.map(function (item) { return [item[0].parent.path].concat(item.slice(1, 7)) }))    })  }))}// [[ dir, dep, has, want, latest, type ]]function makePretty (p, opts) {  var depname = p[1]  var has = p[2]  var want = p[3]  var latest = p[4]  var type = p[6]  var deppath = p[7]  var homepage = p[0].package.homepage || ''  var columns = [ depname,    has || 'MISSING',    want,    latest,    deppath || 'global'  ]  if (long) {    columns[5] = type    columns[6] = homepage  }  if (opts.color) {    columns[0] = color[has === want ? 'yellow' : 'red'](columns[0]) // dep    columns[2] = color.green(columns[2]) // want    columns[3] = color.magenta(columns[3]) // latest  }  return columns}function makeParseable (list) {  return list.map(function (p) {    var dep = p[0]    var depname = p[1]    var dir = dep.path    var has = p[2]    var want = p[3]    var latest = p[4]    var type = p[6]    var out = [      dir,      depname + '@' + want,      (has ? (depname + '@' + has) : 'MISSING'),      depname + '@' + latest    ]    if (long) out.push(type, dep.package.homepage)    return out.join(':')  }).join(os.EOL)}function makeJSON (list, opts) {  var out = {}  list.forEach(function (p) {    var dep = p[0]    var depname = p[1]    var dir = dep.path    var has = p[2]    var want = p[3]    var latest = p[4]    var type = p[6]    if (!opts.global) {      dir = path.relative(process.cwd(), dir)    }    out[depname] = { current: has,      wanted: want,      latest: latest,      location: dir    }    if (long) {      out[depname].type = type      out[depname].homepage = dep.package.homepage    }  })  return JSON.stringify(out, null, 2)}function outdated_ (args, path, tree, parentHas, depth, opts, cb) {  if (!tree.package) tree.package = {}  if (path && moduleName(tree)) path += ' > ' + tree.package.name  if (!path && moduleName(tree)) path = tree.package.name  if (depth > opts.depth) {    return cb(null, [])  }  var types = {}  var pkg = tree.package  if (!tree.children) tree.children = []  var deps = tree.error ? tree.children : tree.children.filter((child) => !isExtraneous(child))  deps.forEach(function (dep) {    types[moduleName(dep)] = 'dependencies'  })  Object.keys(tree.missingDeps || {}).forEach(function (name) {    deps.push({      package: { name: name },      path: tree.path,      parent: tree,      isMissing: true    })    types[name] = 'dependencies'  })  // If we explicitly asked for dev deps OR we didn't ask for production deps  // AND we asked to save dev-deps OR we didn't ask to save anything that's NOT  // dev deps then…  // (All the save checking here is because this gets called from npm-update currently  // and that requires this logic around dev deps.)  // FIXME: Refactor npm update to not be in terms of outdated.  var dev = opts.dev || /^dev(elopment)?$/.test(opts.also)  var prod = opts.production || /^prod(uction)?$/.test(opts.only)  if (    (dev || !prod) &&    (      opts['save-dev'] || (!opts.save && !opts['save-optional'])    )  ) {    Object.keys(tree.missingDevDeps).forEach(function (name) {      deps.push({        package: { name: name },        path: tree.path,        parent: tree,        isMissing: true      })      if (!types[name]) {        types[name] = 'devDependencies'      }    })  }  if (opts['save-dev']) {    deps = deps.filter(function (dep) { return pkg.devDependencies[moduleName(dep)] })    deps.forEach(function (dep) {      types[moduleName(dep)] = 'devDependencies'    })  } else if (opts.save) {    // remove optional dependencies from dependencies during --save.    deps = deps.filter(function (dep) { return !pkg.optionalDependencies[moduleName(dep)] })  } else if (opts['save-optional']) {    deps = deps.filter(function (dep) { return pkg.optionalDependencies[moduleName(dep)] })    deps.forEach(function (dep) {      types[moduleName(dep)] = 'optionalDependencies'    })  }  var doUpdate = dev || (    !prod &&    !Object.keys(parentHas).length &&    !opts.global  )  if (doUpdate) {    Object.keys(pkg.devDependencies || {}).forEach(function (k) {      if (!(k in parentHas)) {        deps[k] = pkg.devDependencies[k]        types[k] = 'devDependencies'      }    })  }  var has = Object.create(parentHas)  tree.children.forEach(function (child) {    if (moduleName(child) && child.package.private) {      deps = deps.filter(function (dep) { return dep !== child })    }    has[moduleName(child)] = {      version: child.isLink ? 'linked' : child.package.version,      from: child.isLink ? 'file:' + child.path : child.package._from    }  })  // now get what we should have, based on the dep.  // if has[dep] !== shouldHave[dep], then cb with the data  // otherwise dive into the folder  asyncMap(deps, function (dep, cb) {    var name = moduleName(dep)    var required    if (tree.package.dependencies && name in tree.package.dependencies) {      required = tree.package.dependencies[name]    } else if (tree.package.optionalDependencies && name in tree.package.optionalDependencies) {      required = tree.package.optionalDependencies[name]    } else if (tree.package.devDependencies && name in tree.package.devDependencies) {      required = tree.package.devDependencies[name]    } else if (has[name]) {      required = computeVersionSpec(tree, dep)    }    if (!long) return shouldUpdate(args, dep, name, has, required, depth, path, opts, cb)    shouldUpdate(args, dep, name, has, required, depth, path, opts, cb, types[name])  }, cb)}function shouldUpdate (args, tree, dep, has, req, depth, pkgpath, opts, cb, type) {  // look up the most recent version.  // if that's what we already have, or if it's not on the args list,  // then dive into it.  Otherwise, cb() with the data.  // { version: , from: }  var curr = has[dep]  function skip (er) {    // show user that no viable version can be found    if (er) return cb(er)    outdated_(args,      pkgpath,      tree,      has,      depth + 1,      opts,      cb)  }  if (args.length && args.indexOf(dep) === -1) return skip()  if (tree.isLink && req == null) return skip()  if (req == null || req === '') req = '*'  var parsed = npa.resolve(dep, req)  if (parsed.type === 'directory') {    if (tree.isLink) {      return skip()    } else {      return doIt('linked', 'linked')    }  } else if (parsed.type === 'git') {    return doIt('git', 'git')  } else if (parsed.type === 'file') {    return updateLocalDeps()  } else if (parsed.type === 'remote') {    return doIt('remote', 'remote')  } else {    return packument(parsed, opts.concat({      'prefer-online': true    })).nodeify(updateDeps)  }  function doIt (wanted, latest) {    let c = curr && curr.version    if (parsed.type === 'alias') {      c = `npm:${parsed.subSpec.name}@${c}`    }    if (!long) {      return cb(null, [[tree, dep, c, wanted, latest, req, null, pkgpath]])    }    cb(null, [[tree, dep, c, wanted, latest, req, type, pkgpath]])  }  function updateLocalDeps (latestRegistryVersion) {    fetchPackageMetadata('file:' + parsed.fetchSpec, '.', (er, localDependency) => {      if (er) return cb()      var wanted = localDependency.version      var latest = localDependency.version      if (latestRegistryVersion) {        latest = latestRegistryVersion        if (semver.lt(wanted, latestRegistryVersion)) {          wanted = latestRegistryVersion          req = dep + '@' + latest        }      }      if (!curr || curr.version !== wanted) {        doIt(wanted, latest)      } else {        skip()      }    })  }  function updateDeps (er, d) {    if (er) return cb(er)    if (parsed.type === 'alias') {      req = parsed.subSpec.rawSpec    }    try {      var l = pickManifest(d, 'latest')      var m = pickManifest(d, req)    } catch (er) {      if (er.code === 'ETARGET' || er.code === 'E403') {        return skip(er)      } else {        return skip()      }    }    // check that the url origin hasn't changed (#1727) and that    // there is no newer version available    var dFromUrl = m._from && url.parse(m._from).protocol    var cFromUrl = curr && curr.from && url.parse(curr.from).protocol    if (      !curr ||      (dFromUrl && cFromUrl && m._from !== curr.from) ||      m.version !== curr.version ||      m.version !== l.version    ) {      if (parsed.type === 'alias') {        doIt(          `npm:${parsed.subSpec.name}@${m.version}`,          `npm:${parsed.subSpec.name}@${l.version}`        )      } else {        doIt(m.version, l.version)      }    } else {      skip()    }  }}
 |