codekingpro/portable-devtools
114k
1const pkgJson = require('@npmcli/package-json')
2const runScript = require('@npmcli/run-script')
3const { join, relative } = require('node:path')
4const { log, output } = require('proc-log')
5const completion = require('../utils/installed-shallow.js')
6const BaseCommand = require('../base-cmd.js')
7
8// npm explore <pkg>[@<version>]
9// open a subshell to the package folder.
10class Explore extends BaseCommand {
11 static description = 'Browse an installed package'
12 static name = 'explore'
13 static usage = ['<pkg> [ -- <command>]']
14 static params = ['shell']
15 static ignoreImplicitWorkspace = false
16
17 static async completion (opts, npm) {
18 return completion(npm, opts)
19 }
20
21 async exec (args) {
22 if (args.length < 1 || !args[0]) {
23 throw this.usageError()
24 }
25
26 const pkgname = args.shift()
27
28 // detect and prevent any .. shenanigans
29 const path = join(this.npm.dir, join('/', pkgname))
30 if (relative(path, this.npm.dir) === '') {
31 throw this.usageError()
32 }
33
34 // run as if running a script named '_explore', which we set to either the set of arguments, or the shell config, and let @npmcli/run-script handle all the escaping and PATH setup stuff.
35
36 const { content: pkg } = await pkgJson.normalize(path).catch(er => {
37 log.error('explore', `It doesn't look like ${pkgname} is installed.`)
38 throw er
39 })
40
41 const { shell } = this.npm.flatOptions
42 pkg.scripts = {
43 ...(pkg.scripts || {}),
44 _explore: args.join(' ').trim() || shell,
45 }
46
47 if (!args.length) {
48 output.standard(`\nExploring ${path}\nType 'exit' or ^D when finished\n`)
49 }
50
51 return runScript({
52 ...this.npm.flatOptions,
53 pkg,
54 path,
55 event: '_explore',
56 stdio: 'inherit',
57 }).catch(er => {
58 process.exitCode = typeof er.code === 'number' && er.code !== 0 ? er.code
59 : 1
60 // if it's not an exit error, or non-interactive, throw it
61 const isProcExit = er.message === 'command failed' &&
62 (typeof er.code === 'number' || /^SIG/.test(er.signal || ''))
63 if (args.length || !isProcExit) {
64 throw er
65 }
66 })
67 }
68}
69
70module.exports = Explore
71 