11cb0ef41Sopenharmony_ci// npm explore <pkg>[@<version>] 21cb0ef41Sopenharmony_ci// open a subshell to the package folder. 31cb0ef41Sopenharmony_ci 41cb0ef41Sopenharmony_ciconst pkgJson = require('@npmcli/package-json') 51cb0ef41Sopenharmony_ciconst runScript = require('@npmcli/run-script') 61cb0ef41Sopenharmony_ciconst { join, relative } = require('path') 71cb0ef41Sopenharmony_ciconst log = require('../utils/log-shim.js') 81cb0ef41Sopenharmony_ciconst completion = require('../utils/completion/installed-shallow.js') 91cb0ef41Sopenharmony_ciconst BaseCommand = require('../base-command.js') 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ciclass Explore extends BaseCommand { 121cb0ef41Sopenharmony_ci static description = 'Browse an installed package' 131cb0ef41Sopenharmony_ci static name = 'explore' 141cb0ef41Sopenharmony_ci static usage = ['<pkg> [ -- <command>]'] 151cb0ef41Sopenharmony_ci static params = ['shell'] 161cb0ef41Sopenharmony_ci static ignoreImplicitWorkspace = false 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ci // TODO 191cb0ef41Sopenharmony_ci /* istanbul ignore next */ 201cb0ef41Sopenharmony_ci static async completion (opts, npm) { 211cb0ef41Sopenharmony_ci return completion(npm, opts) 221cb0ef41Sopenharmony_ci } 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ci async exec (args) { 251cb0ef41Sopenharmony_ci if (args.length < 1 || !args[0]) { 261cb0ef41Sopenharmony_ci throw this.usageError() 271cb0ef41Sopenharmony_ci } 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ci const pkgname = args.shift() 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ci // detect and prevent any .. shenanigans 321cb0ef41Sopenharmony_ci const path = join(this.npm.dir, join('/', pkgname)) 331cb0ef41Sopenharmony_ci if (relative(path, this.npm.dir) === '') { 341cb0ef41Sopenharmony_ci throw this.usageError() 351cb0ef41Sopenharmony_ci } 361cb0ef41Sopenharmony_ci 371cb0ef41Sopenharmony_ci // run as if running a script named '_explore', which we set to either 381cb0ef41Sopenharmony_ci // the set of arguments, or the shell config, and let @npmcli/run-script 391cb0ef41Sopenharmony_ci // handle all the escaping and PATH setup stuff. 401cb0ef41Sopenharmony_ci 411cb0ef41Sopenharmony_ci const { content: pkg } = await pkgJson.normalize(path).catch(er => { 421cb0ef41Sopenharmony_ci log.error('explore', `It doesn't look like ${pkgname} is installed.`) 431cb0ef41Sopenharmony_ci throw er 441cb0ef41Sopenharmony_ci }) 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci const { shell } = this.npm.flatOptions 471cb0ef41Sopenharmony_ci pkg.scripts = { 481cb0ef41Sopenharmony_ci ...(pkg.scripts || {}), 491cb0ef41Sopenharmony_ci _explore: args.join(' ').trim() || shell, 501cb0ef41Sopenharmony_ci } 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci if (!args.length) { 531cb0ef41Sopenharmony_ci this.npm.output(`\nExploring ${path}\nType 'exit' or ^D when finished\n`) 541cb0ef41Sopenharmony_ci } 551cb0ef41Sopenharmony_ci log.disableProgress() 561cb0ef41Sopenharmony_ci try { 571cb0ef41Sopenharmony_ci return await runScript({ 581cb0ef41Sopenharmony_ci ...this.npm.flatOptions, 591cb0ef41Sopenharmony_ci pkg, 601cb0ef41Sopenharmony_ci banner: false, 611cb0ef41Sopenharmony_ci path, 621cb0ef41Sopenharmony_ci event: '_explore', 631cb0ef41Sopenharmony_ci stdio: 'inherit', 641cb0ef41Sopenharmony_ci }).catch(er => { 651cb0ef41Sopenharmony_ci process.exitCode = typeof er.code === 'number' && er.code !== 0 ? er.code 661cb0ef41Sopenharmony_ci : 1 671cb0ef41Sopenharmony_ci // if it's not an exit error, or non-interactive, throw it 681cb0ef41Sopenharmony_ci const isProcExit = er.message === 'command failed' && 691cb0ef41Sopenharmony_ci (typeof er.code === 'number' || /^SIG/.test(er.signal || '')) 701cb0ef41Sopenharmony_ci if (args.length || !isProcExit) { 711cb0ef41Sopenharmony_ci throw er 721cb0ef41Sopenharmony_ci } 731cb0ef41Sopenharmony_ci }) 741cb0ef41Sopenharmony_ci } finally { 751cb0ef41Sopenharmony_ci log.enableProgress() 761cb0ef41Sopenharmony_ci } 771cb0ef41Sopenharmony_ci } 781cb0ef41Sopenharmony_ci} 791cb0ef41Sopenharmony_cimodule.exports = Explore 80