diff options
Diffstat (limited to 'deps/npm/lib/cache.js')
-rw-r--r-- | deps/npm/lib/cache.js | 128 |
1 files changed, 107 insertions, 21 deletions
diff --git a/deps/npm/lib/cache.js b/deps/npm/lib/cache.js index 55fb3e8636..aed2cce31e 100644 --- a/deps/npm/lib/cache.js +++ b/deps/npm/lib/cache.js @@ -4,7 +4,59 @@ const log = require('npmlog') const pacote = require('pacote') const path = require('path') const rimraf = promisify(require('rimraf')) +const semver = require('semver') const BaseCommand = require('./base-command.js') +const npa = require('npm-package-arg') +const jsonParse = require('json-parse-even-better-errors') + +const searchCachePackage = async (path, spec, cacheKeys) => { + const parsed = npa(spec) + if (parsed.rawSpec !== '' && parsed.type === 'tag') + throw new Error(`Cannot list cache keys for a tagged package.`) + const searchMFH = new RegExp(`^make-fetch-happen:request-cache:.*(?<!/[@a-zA-Z]+)/${parsed.name}/-/(${parsed.name}[^/]+.tgz)$`) + const searchPack = new RegExp(`^make-fetch-happen:request-cache:.*/${parsed.escapedName}$`) + const results = new Set() + cacheKeys = new Set(cacheKeys) + for (const key of cacheKeys) { + // match on the public key registry url format + if (searchMFH.test(key)) { + // extract the version from the filename + const filename = key.match(searchMFH)[1] + const noExt = filename.slice(0, -4) + const noScope = `${parsed.name.split('/').pop()}-` + const ver = noExt.slice(noScope.length) + if (semver.satisfies(ver, parsed.rawSpec)) + results.add(key) + continue + } + // is this key a packument? + if (!searchPack.test(key)) + continue + + results.add(key) + let packument, details + try { + details = await cacache.get(path, key) + packument = jsonParse(details.data) + } catch (_) { + // if we couldn't parse the packument, abort + continue + } + if (!packument.versions || typeof packument.versions !== 'object') + continue + // assuming this is a packument + for (const ver of Object.keys(packument.versions)) { + if (semver.satisfies(ver, parsed.rawSpec)) { + if (packument.versions[ver].dist + && typeof packument.versions[ver].dist === 'object' + && packument.versions[ver].dist.tarball !== undefined + && cacheKeys.has(`make-fetch-happen:request-cache:${packument.versions[ver].dist.tarball}`)) + results.add(`make-fetch-happen:request-cache:${packument.versions[ver].dist.tarball}`) + } + } + } + return results +} class Cache extends BaseCommand { static get description () { @@ -29,7 +81,8 @@ class Cache extends BaseCommand { 'add <tarball url>', 'add <git url>', 'add <name>@<version>', - 'clean', + 'clean [<key>]', + 'ls [<name>@<version>]', 'verify', ] } @@ -37,13 +90,15 @@ class Cache extends BaseCommand { async completion (opts) { const argv = opts.conf.argv.remain if (argv.length === 2) - return ['add', 'clean', 'verify'] + return ['add', 'clean', 'verify', 'ls', 'delete'] // TODO - eventually... switch (argv[2]) { case 'verify': case 'clean': case 'add': + case 'ls': + case 'delete': return [] } } @@ -61,6 +116,8 @@ class Cache extends BaseCommand { return await this.add(args) case 'verify': case 'check': return await this.verify() + case 'ls': + return await this.ls(args) default: throw Object.assign(new Error(this.usage), { code: 'EUSAGE' }) } @@ -68,27 +125,38 @@ class Cache extends BaseCommand { // npm cache clean [pkg]* async clean (args) { - if (args.length) - throw new Error('npm cache clear does not accept arguments') - const cachePath = path.join(this.npm.cache, '_cacache') - if (!this.npm.config.get('force')) { - throw new Error(`As of npm@5, the npm cache self-heals from corruption issues -by treating integrity mismatches as cache misses. As a result, -data extracted from the cache is guaranteed to be valid. If you -want to make sure everything is consistent, use \`npm cache verify\` -instead. Deleting the cache can only make npm go slower, and is -not likely to correct any problems you may be encountering! - -On the other hand, if you're debugging an issue with the installer, -or race conditions that depend on the timing of writing to an empty -cache, you can use \`npm install --cache /tmp/empty-cache\` to use a -temporary cache instead of nuking the actual one. - -If you're sure you want to delete the entire cache, rerun this command -with --force.`) + if (args.length === 0) { + if (!this.npm.config.get('force')) { + throw new Error(`As of npm@5, the npm cache self-heals from corruption issues + by treating integrity mismatches as cache misses. As a result, + data extracted from the cache is guaranteed to be valid. If you + want to make sure everything is consistent, use \`npm cache verify\` + instead. Deleting the cache can only make npm go slower, and is + not likely to correct any problems you may be encountering! + + On the other hand, if you're debugging an issue with the installer, + or race conditions that depend on the timing of writing to an empty + cache, you can use \`npm install --cache /tmp/empty-cache\` to use a + temporary cache instead of nuking the actual one. + + If you're sure you want to delete the entire cache, rerun this command + with --force.`) + } + return rimraf(cachePath) + } + for (const key of args) { + let entry + try { + entry = await cacache.get(cachePath, key) + } catch (err) { + this.npm.log.warn(`Not Found: ${key}`) + break + } + this.npm.output(`Deleted: ${key}`) + await cacache.rm.entry(cachePath, key) + await cacache.rm.content(cachePath, entry.integrity) } - return rimraf(cachePath) } // npm cache add <tarball-url>... @@ -131,6 +199,24 @@ with --force.`) this.npm.output(`Index entries: ${stats.totalEntries}`) this.npm.output(`Finished in ${stats.runTime.total / 1000}s`) } + + // npm cache ls [--package <spec> ...] + async ls (specs) { + const cachePath = path.join(this.npm.cache, '_cacache') + const cacheKeys = Object.keys(await cacache.ls(cachePath)) + if (specs.length > 0) { + // get results for each package spec specified + const results = new Set() + for (const spec of specs) { + const keySet = await searchCachePackage(cachePath, spec, cacheKeys) + for (const key of keySet) + results.add(key) + } + [...results].sort((a, b) => a.localeCompare(b, 'en')).forEach(key => this.npm.output(key)) + return + } + cacheKeys.sort((a, b) => a.localeCompare(b, 'en')).forEach(key => this.npm.output(key)) + } } module.exports = Cache |