1
-
const cacache = require('cacache')
2
-
const pacote = require('pacote')
3
1
const fs = require('node:fs/promises')
4
2
const { join } = require('node:path')
3
+
const cacache = require('cacache')
4
+
const pacote = require('pacote')
5
5
const semver = require('semver')
6
-
const BaseCommand = require('../base-cmd.js')
7
6
const npa = require('npm-package-arg')
8
7
const jsonParse = require('json-parse-even-better-errors')
9
8
const localeCompare = require('@isaacs/string-locale-compare')('en')
10
9
const { log, output } = require('proc-log')
10
+
const PkgJson = require('@npmcli/package-json')
11
+
const BaseCommand = require('../base-cmd.js')
12
+
const abbrev = require('abbrev')
11
13
12
14
const searchCachePackage = async (path, parsed, cacheKeys) => {
13
15
const searchMFH = new RegExp(`^make-fetch-happen:request-cache:.*(?<!/[@a-zA-Z]+)/${parsed.name}/-/(${parsed.name}[^/]+.tgz)$`)
@@ -62,20 +64,23 @@ const searchCachePackage = async (path, parsed, cacheKeys) => {
62
64
}
63
65
64
66
class Cache extends BaseCommand {
65
-
static description = 'Manipulates packages cache'
67
+
static description = 'Manipulates packages and npx cache'
66
68
static name = 'cache'
67
69
static params = ['cache']
68
70
static usage = [
69
71
'add <package-spec>',
70
72
'clean [<key>]',
71
73
'ls [<name>@<version>]',
72
74
'verify',
75
+
'npx ls',
76
+
'npx rm [<key>...]',
77
+
'npx info <key>...',
73
78
]
74
79
75
80
static async completion (opts) {
76
81
const argv = opts.conf.argv.remain
77
82
if (argv.length === 2) {
78
-
return ['add', 'clean', 'verify', 'ls']
83
+
return ['add', 'clean', 'verify', 'ls', 'npx']
79
84
}
80
85
81
86
// TODO - eventually...
@@ -99,14 +104,31 @@ class Cache extends BaseCommand {
99
104
return await this.verify()
100
105
case 'ls':
101
106
return await this.ls(args)
107
+
case 'npx':
108
+
return await this.npx(args)
109
+
default:
110
+
throw this.usageError()
111
+
}
112
+
}
113
+
114
+
// npm cache npx
115
+
async npx ([cmd, ...keys]) {
116
+
switch (cmd) {
117
+
case 'ls':
118
+
return await this.npxLs(keys)
119
+
case 'rm':
120
+
return await this.npxRm(keys)
121
+
case 'info':
122
+
return await this.npxInfo(keys)
102
123
default:
103
124
throw this.usageError()
104
125
}
105
126
}
106
127
107
-
// npm cache clean [pkg]*
128
+
// npm cache clean [spec]*
108
129
async clean (args) {
109
-
const cachePath = join(this.npm.cache, '_cacache')
130
+
// this is a derived value
131
+
const cachePath = this.npm.flatOptions.cache
110
132
if (args.length === 0) {
111
133
if (!this.npm.config.get('force')) {
112
134
throw new Error(`As of npm@5, the npm cache self-heals from corruption issues
@@ -169,11 +191,12 @@ class Cache extends BaseCommand {
169
191
}
170
192
171
193
async verify () {
172
-
const cache = join(this.npm.cache, '_cacache')
173
-
const prefix = cache.indexOf(process.env.HOME) === 0
174
-
? `~${cache.slice(process.env.HOME.length)}`
175
-
: cache
176
-
const stats = await cacache.verify(cache)
194
+
// this is a derived value
195
+
const cachePath = this.npm.flatOptions.cache
196
+
const prefix = cachePath.indexOf(process.env.HOME) === 0
197
+
? `~${cachePath.slice(process.env.HOME.length)}`
198
+
: cachePath
199
+
const stats = await cacache.verify(cachePath)
177
200
output.standard(`Cache verified and compressed (${prefix})`)
178
201
output.standard(`Content verified: ${stats.verifiedContent} (${stats.keptSize} bytes)`)
179
202
if (stats.badContentCount) {
@@ -189,9 +212,10 @@ class Cache extends BaseCommand {
189
212
output.standard(`Finished in ${stats.runTime.total / 1000}s`)
190
213
}
191
214
192
-
// npm cache ls [--package <spec> ...]
215
+
// npm cache ls [<spec> ...]
193
216
async ls (specs) {
194
-
const cachePath = join(this.npm.cache, '_cacache')
217
+
// This is a derived value
218
+
const { cache: cachePath } = this.npm.flatOptions
195
219
const cacheKeys = Object.keys(await cacache.ls(cachePath))
196
220
if (specs.length > 0) {
197
221
// get results for each package spec specified
@@ -211,6 +235,136 @@ class Cache extends BaseCommand {
211
235
}
212
236
cacheKeys.sort(localeCompare).forEach(key => output.standard(key))
213
237
}
238
+
239
+
async #npxCache (keys = []) {
240
+
// This is a derived value
241
+
const { npxCache } = this.npm.flatOptions
242
+
let dirs
243
+
try {
244
+
dirs = await fs.readdir(npxCache, { encoding: 'utf-8' })
245
+
} catch {
246
+
output.standard('npx cache does not exist')
247
+
return
248
+
}
249
+
const cache = {}
250
+
const { default: pMap } = await import('p-map')
251
+
await pMap(dirs, async e => {
252
+
const pkgPath = join(npxCache, e)
253
+
cache[e] = {
254
+
hash: e,
255
+
path: pkgPath,
256
+
valid: false,
257
+
}
258
+
try {
259
+
const pkgJson = await PkgJson.load(pkgPath)
260
+
cache[e].package = pkgJson.content
261
+
cache[e].valid = true
262
+
} catch {
263
+
// Defaults to not valid already
264
+
}
265
+
}, { concurrency: 20 })
266
+
if (!keys.length) {
267
+
return cache
268
+
}
269
+
const result = {}
270
+
const abbrevs = abbrev(Object.keys(cache))
271
+
for (const key of keys) {
272
+
if (!abbrevs[key]) {
273
+
throw this.usageError(`Invalid npx key ${key}`)
274
+
}
275
+
result[abbrevs[key]] = cache[abbrevs[key]]
276
+
}
277
+
return result
278
+
}
279
+
280
+
async npxLs () {
281
+
const cache = await this.#npxCache()
282
+
for (const key in cache) {
283
+
const { hash, valid, package: pkg } = cache[key]
284
+
let result = `${hash}:`
285
+
if (!valid) {
286
+
result = `${result} (empty/invalid)`
287
+
} else if (pkg?._npx) {
288
+
result = `${result} ${pkg._npx.packages.join(', ')}`
289
+
} else {
290
+
result = `${result} (unknown)`
291
+
}
292
+
output.standard(result)
293
+
}
294
+
}
295
+
296
+
async npxRm (keys) {
297
+
if (!keys.length) {
298
+
if (!this.npm.config.get('force')) {
299
+
throw this.usageError('Please use --force to remove entire npx cache')
300
+
}
301
+
const { npxCache } = this.npm.flatOptions
302
+
if (!this.npm.config.get('dry-run')) {
303
+
return fs.rm(npxCache, { recursive: true, force: true })
304
+
}
305
+
}
306
+
307
+
const cache = await this.#npxCache(keys)
308
+
for (const key in cache) {
309
+
const { path: cachePath } = cache[key]
310
+
output.standard(`Removing npx key at ${cachePath}`)
311
+
if (!this.npm.config.get('dry-run')) {
312
+
await fs.rm(cachePath, { recursive: true })
313
+
}
314
+
}
315
+
}
316
+
317
+
async npxInfo (keys) {
318
+
const chalk = this.npm.chalk
319
+
if (!keys.length) {
320
+
throw this.usageError()
321
+
}
322
+
const cache = await this.#npxCache(keys)
323
+
const Arborist = require('@npmcli/arborist')
324
+
for (const key in cache) {
325
+
const { hash, path, package: pkg } = cache[key]
326
+
let valid = cache[key].valid
327
+
const results = []
328
+
try {
329
+
if (valid) {
330
+
const arb = new Arborist({ path })
331
+
const tree = await arb.loadVirtual()
332
+
if (pkg._npx) {
333
+
results.push('packages:')
334
+
for (const p of pkg._npx.packages) {
335
+
const parsed = npa(p)
336
+
if (parsed.type === 'directory') {
337
+
// in the tree the spec is relative, even if the dependency spec is absolute, so we can't find it by name or spec.
338
+
results.push(`- ${chalk.cyan(p)}`)
339
+
} else {
340
+
results.push(`- ${chalk.cyan(p)} (${chalk.blue(tree.children.get(parsed.name).pkgid)})`)
341
+
}
342
+
}
343
+
} else {
344
+
results.push('packages: (unknown)')
345
+
results.push(`dependencies:`)
346
+
for (const dep in pkg.dependencies) {
347
+
const child = tree.children.get(dep)
348
+
if (child.isLink) {
349
+
results.push(`- ${chalk.cyan(child.realpath)}`)
350
+
} else {
351
+
results.push(`- ${chalk.cyan(child.pkgid)}`)
352
+
}
353
+
}
354
+
}
355
+
}
356
+
} catch (ex) {
357
+
valid = false
358
+
}
359
+
const v = valid ? chalk.green('valid') : chalk.red('invalid')
360
+
output.standard(`${v} npx cache entry with key ${chalk.blue(hash)}`)
361
+
output.standard(`location: ${chalk.blue(path)}`)
362
+
if (valid) {
363
+
output.standard(results.join('\n'))
364
+
}
365
+
output.standard('')
366
+
}
367
+
}
214
368
}
215
369
216
370
module.exports = Cache
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4