• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2/* eslint-disable standard/no-callback-literal */
3
4const BB = require('bluebird')
5
6const assert = require('assert')
7const cacache = require('cacache')
8const finished = BB.promisify(require('mississippi').finished)
9const log = require('npmlog')
10const npa = require('npm-package-arg')
11const npm = require('./npm.js')
12const npmConfig = require('./config/figgy-config.js')
13const output = require('./utils/output.js')
14const pacote = require('pacote')
15const path = require('path')
16const rm = BB.promisify(require('./utils/gently-rm.js'))
17const unbuild = BB.promisify(npm.commands.unbuild)
18
19cache.usage = 'npm cache add <tarball file>' +
20              '\nnpm cache add <folder>' +
21              '\nnpm cache add <tarball url>' +
22              '\nnpm cache add <git url>' +
23              '\nnpm cache add <name>@<version>' +
24              '\nnpm cache clean' +
25              '\nnpm cache verify'
26
27cache.completion = function (opts, cb) {
28  var argv = opts.conf.argv.remain
29  if (argv.length === 2) {
30    return cb(null, ['add', 'clean'])
31  }
32
33  // TODO - eventually...
34  switch (argv[2]) {
35    case 'clean':
36    case 'add':
37      return cb(null, [])
38  }
39}
40
41exports = module.exports = cache
42function cache (args, cb) {
43  const cmd = args.shift()
44  let result
45  switch (cmd) {
46    case 'rm': case 'clear': case 'clean':
47      result = clean(args)
48      break
49    case 'add':
50      result = add(args, npm.prefix)
51      break
52    case 'verify': case 'check':
53      result = verify()
54      break
55    default: return cb('Usage: ' + cache.usage)
56  }
57  if (!result || !result.then) {
58    throw new Error(`npm cache ${cmd} handler did not return a Promise`)
59  }
60  result.then(() => cb(), cb)
61}
62
63// npm cache clean [pkg]*
64cache.clean = clean
65function clean (args) {
66  if (!args) args = []
67  if (args.length) {
68    return BB.reject(new Error('npm cache clear does not accept arguments'))
69  }
70  const cachePath = path.join(npm.cache, '_cacache')
71  if (!npm.config.get('force')) {
72    return BB.reject(new Error("As of npm@5, the npm cache self-heals from corruption issues and data extracted from the cache is guaranteed to be valid. If you want to make sure everything is consistent, use 'npm cache verify' instead. On the other hand, if you're debugging an issue with the installer, you can use `npm install --cache /tmp/empty-cache` to use a temporary cache instead of nuking the actual one.\n\nIf you're sure you want to delete the entire cache, rerun this command with --force."))
73  }
74  // TODO - remove specific packages or package versions
75  return rm(cachePath)
76}
77
78// npm cache add <tarball-url>
79// npm cache add <pkg> <ver>
80// npm cache add <tarball>
81// npm cache add <folder>
82cache.add = function (pkg, ver, where, scrub) {
83  assert(typeof pkg === 'string', 'must include name of package to install')
84  if (scrub) {
85    return clean([]).then(() => {
86      return add([pkg, ver], where)
87    })
88  }
89  return add([pkg, ver], where)
90}
91
92function add (args, where) {
93  var usage = 'Usage:\n' +
94              '    npm cache add <tarball-url>\n' +
95              '    npm cache add <pkg>@<ver>\n' +
96              '    npm cache add <tarball>\n' +
97              '    npm cache add <folder>\n'
98  var spec
99  log.silly('cache add', 'args', args)
100  if (args[1] === undefined) args[1] = null
101  // at this point the args length must ==2
102  if (args[1] !== null) {
103    spec = args[0] + '@' + args[1]
104  } else if (args.length === 2) {
105    spec = args[0]
106  }
107  log.verbose('cache add', 'spec', spec)
108  if (!spec) return BB.reject(new Error(usage))
109  log.silly('cache add', 'parsed spec', spec)
110  return finished(pacote.tarball.stream(spec, npmConfig({where})).resume())
111}
112
113cache.verify = verify
114function verify () {
115  const cache = path.join(npm.config.get('cache'), '_cacache')
116  let prefix = cache
117  if (prefix.indexOf(process.env.HOME) === 0) {
118    prefix = '~' + prefix.substr(process.env.HOME.length)
119  }
120  return cacache.verify(cache).then((stats) => {
121    output(`Cache verified and compressed (${prefix}):`)
122    output(`Content verified: ${stats.verifiedContent} (${stats.keptSize} bytes)`)
123    stats.badContentCount && output(`Corrupted content removed: ${stats.badContentCount}`)
124    stats.reclaimedCount && output(`Content garbage-collected: ${stats.reclaimedCount} (${stats.reclaimedSize} bytes)`)
125    stats.missingContent && output(`Missing content: ${stats.missingContent}`)
126    output(`Index entries: ${stats.totalEntries}`)
127    output(`Finished in ${stats.runTime.total / 1000}s`)
128  })
129}
130
131cache.unpack = unpack
132function unpack (pkg, ver, unpackTarget, dmode, fmode, uid, gid) {
133  return unbuild([unpackTarget], true).then(() => {
134    const opts = npmConfig({dmode, fmode, uid, gid, offline: true})
135    return pacote.extract(npa.resolve(pkg, ver), unpackTarget, opts)
136  })
137}
138