Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Isaacs/unit test run script commands #1551

Closed
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"ArrayExpression": 1,
"ObjectExpression": 1,
"ImportDeclaration": 1,
"flatTernaryExpressions": false,
"flatTernaryExpressions": true,
"ignoreComments": false,
"ignoredNodes": ["TemplateLiteral *"]
}],
Expand Down
8 changes: 6 additions & 2 deletions lib/completion.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ completion.usage = 'source <(npm completion)'
var npm = require('./npm.js')
var npmconf = require('./config/core.js')
var configDefs = npmconf.defs
const { aliases, cmdList, plumbing } = require('./config/cmd-list.js')
const aliasNames = Object.keys(aliases)
const fullList = cmdList.concat(aliasNames).filter(c => !plumbing.includes(c))
var configTypes = configDefs.types
var shorthands = configDefs.shorthands
var nopt = require('nopt')
Expand Down Expand Up @@ -121,8 +124,9 @@ function completion (args, cb) {
var parsed = opts.conf =
nopt(configTypes, shorthands, partialWords.slice(0, -1), 0)
// check if there's a command already.
console.error(parsed)
console.error('PARSED', parsed)
var cmd = parsed.argv.remain[1]
console.error('CMD', cmd)
if (!cmd) return cmdCompl(opts, cb)

Object.keys(parsed).forEach(function (k) {
Expand Down Expand Up @@ -244,5 +248,5 @@ function isFlag (word) {

// complete against the npm commands
function cmdCompl (opts, cb) {
return cb(null, npm.fullList)
return cb(null, fullList)
}
2 changes: 1 addition & 1 deletion lib/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ Object.defineProperty(exports, 'defaults', {
globalconfig: path.resolve(globalPrefix, 'etc', 'npmrc'),
'global-style': false,
group: process.platform === 'win32' ? 0
: process.env.SUDO_GID || (process.getgid && process.getgid()),
: process.env.SUDO_GID || (process.getgid && process.getgid()),
'ham-it-up': false,
heading: 'npm',
'if-present': false,
Expand Down
11 changes: 6 additions & 5 deletions lib/config/flat-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ const flatOptions = npm => npm.flatOptions || Object.freeze({
globalStyle: npm.config.get('global-style'),
legacyBundling: npm.config.get('legacy-bundling'),
scriptShell: npm.config.get('script-shell') || undefined,
shell: npm.config.get('shell'),
omit: buildOmitList(npm),
legacyPeerDeps: npm.config.get('legacy-peer-deps'),

Expand All @@ -174,12 +175,12 @@ const flatOptions = npm => npm.flatOptions || Object.freeze({
saveType: npm.config.get('save-optional') && npm.config.get('save-peer')
? 'peerOptional'
: npm.config.get('save-optional') ? 'optional'
: npm.config.get('save-dev') ? 'dev'
: npm.config.get('save-peer') ? 'peer'
: npm.config.get('save-prod') ? 'prod'
: null,
: npm.config.get('save-dev') ? 'dev'
: npm.config.get('save-peer') ? 'peer'
: npm.config.get('save-prod') ? 'prod'
: null,
savePrefix: npm.config.get('save-exact') ? ''
: npm.config.get('save-prefix'),
: npm.config.get('save-prefix'),

// configs for npm-registry-fetch
otp: npm.config.get('otp'),
Expand Down
2 changes: 1 addition & 1 deletion lib/config/set-envs.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const envVal = val => Array.isArray(val) ? val.join('\n\n') : val

const sameConfigValue = (def, val) =>
!Array.isArray(val) || !Array.isArray(def) ? def === val
: sameArrayValue(def, val)
: sameArrayValue(def, val)

const sameArrayValue = (def, val) => {
if (def.length !== val.length) {
Expand Down
103 changes: 54 additions & 49 deletions lib/explore.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,66 @@
// npm explore <pkg>[@<version>]
// open a subshell to the package folder.

module.exports = explore
explore.usage = 'npm explore <pkg> [ -- <command>]'
explore.completion = require('./utils/completion/installed-shallow.js')

var npm = require('./npm.js')
var spawn = require('./utils/spawn')
var path = require('path')
var fs = require('graceful-fs')
var isWindows = require('./utils/is-windows.js')
var escapeExecPath = require('./utils/escape-exec-path.js')
var escapeArg = require('./utils/escape-arg.js')
var output = require('./utils/output.js')
var log = require('npmlog')

function explore (args, cb) {
if (args.length < 1 || !args[0]) return cb(explore.usage)
var p = args.shift()

var cwd = path.resolve(npm.dir, p)
var opts = { cwd: cwd, stdio: 'inherit' }

var shellArgs = []
if (args) {
const usageUtil = require('./utils/usage.js')
const completion = require('./utils/completion/installed-shallow.js')
const usage = usageUtil('explore', 'npm explore <pkg> [ -- <command>]')

const cmd = (args, cb) => explore(args).then(() => cb()).catch(cb)

const output = require('./utils/output.js')
const npm = require('./npm.js')
const isWindows = require('./utils/is-windows.js')
const escapeArg = require('./utils/escape-arg.js')
const escapeExecPath = require('./utils/escape-exec-path.js')
const log = require('npmlog')

const spawn = require('@npmcli/promise-spawn')

const { resolve } = require('path')
const { promisify } = require('util')
const stat = promisify(require('fs').stat)

const explore = async args => {
if (args.length < 1 || !args[0]) {
throw usage
}

const pkg = args.shift()
const cwd = resolve(npm.dir, pkg)
const opts = { cwd, stdio: 'inherit', stdioString: true }

const shellArgs = []
if (args.length) {
if (isWindows) {
var execCmd = escapeExecPath(args.shift())
var execArgs = [execCmd].concat(args.map(escapeArg))
const execCmd = escapeExecPath(args.shift())
opts.windowsVerbatimArguments = true
shellArgs = ['/d', '/s', '/c'].concat(execArgs)
shellArgs.push('/d', '/s', '/c', execCmd, ...args.map(escapeArg))
} else {
shellArgs.unshift('-c')
shellArgs = ['-c', args.map(escapeArg).join(' ').trim()]
shellArgs.push('-c', args.map(escapeArg).join(' ').trim())
}
}

var sh = npm.config.get('shell')
fs.stat(cwd, function (er, s) {
if (er || !s.isDirectory()) {
return cb(new Error(
"It doesn't look like " + p + ' is installed.'
))
}
await stat(cwd).catch(er => {
throw new Error(`It doesn't look like ${pkg} is installed.`)
})

if (!shellArgs.length) {
output(
'\nExploring ' + cwd + '\n' +
"Type 'exit' or ^D when finished\n"
)
}
const sh = npm.flatOptions.shell
log.disableProgress()

log.silly('explore', { sh, shellArgs, opts })
var shell = spawn(sh, shellArgs, opts)
shell.on('close', function (er) {
// only fail if non-interactive.
if (!shellArgs.length) return cb()
cb(er)
})
})
if (!shellArgs.length) {
output(`\nExploring ${cwd}\nType 'exit' or ^D when finished\n`)
}

log.silly('explore', { sh, shellArgs, opts })

// only noisily fail if non-interactive, but still keep exit code intact
const proc = spawn(sh, shellArgs, opts)
try {
const res = await (shellArgs.length ? proc : proc.catch(er => er))
process.exitCode = res.code
} finally {
log.enableProgress()
}
}

module.exports = Object.assign(cmd, { completion, usage })
10 changes: 5 additions & 5 deletions lib/help-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,11 @@ function searchFiles (args, files, cb) {
results = results.sort(function (a, b) {
return a.found.length > b.found.length ? -1
: a.found.length < b.found.length ? 1
: a.totalHits > b.totalHits ? -1
: a.totalHits < b.totalHits ? 1
: a.lines.length > b.lines.length ? -1
: a.lines.length < b.lines.length ? 1
: 0
: a.totalHits > b.totalHits ? -1
: a.totalHits < b.totalHits ? 1
: a.lines.length > b.lines.length ? -1
: a.lines.length < b.lines.length ? 1
: 0
})

cb(null, results)
Expand Down
2 changes: 1 addition & 1 deletion lib/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function pickMan (mans, pref_) {
var bn = b.match(nre)[1]
return an === bn ? (a > b ? -1 : 1)
: pref[an] < pref[bn] ? -1
: 1
: 1
})
return mans[0]
}
Expand Down
4 changes: 2 additions & 2 deletions lib/outdated.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ async function outdated_ (tree, deps, opts) {

const type = edge.optional ? 'optionalDependencies'
: edge.peer ? 'peerDependencies'
: edge.dev ? 'devDependencies'
: 'dependencies'
: edge.dev ? 'devDependencies'
: 'dependencies'

// deps different from prod not currently
// on disk are not included in the output
Expand Down
4 changes: 2 additions & 2 deletions lib/repo.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const getRepo = async pkg => {
const r = mani.repository
const rurl = !r ? null
: typeof r === 'string' ? r
: typeof r === 'object' && typeof r.url === 'string' ? r.url
: null
: typeof r === 'object' && typeof r.url === 'string' ? r.url
: null

if (!rurl) {
throw Object.assign(new Error('no repository'), {
Expand Down
64 changes: 21 additions & 43 deletions lib/run-script.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,33 @@
module.exports = runScriptCmd

const run = require('@npmcli/run-script')
const npm = require('./npm.js')
const readJson = require('read-package-json-fast')
const { resolve, join } = require('path')
const { resolve } = require('path')
const output = require('./utils/output.js')
const log = require('npmlog')
const usage = require('./utils/usage')
const usageUtil = require('./utils/usage')
const didYouMean = require('./utils/did-you-mean')
const isWindowsShell = require('./utils/is-windows-shell.js')

runScriptCmd.usage = usage(
const usage = usageUtil(
'run-script',
'npm run-script <command> [-- <args>...]'
'npm run-script <command> [-- <args>]'
)

runScriptCmd.completion = function (opts, cb) {
// see if there's already a package specified.
var argv = opts.conf.argv.remain

if (argv.length >= 4) return cb()

if (argv.length === 3) {
// either specified a script locally, in which case, done,
// or a package, in which case, complete against its scripts
var json = join(npm.localPrefix, 'package.json')
return readJson(json, function (er, d) {
if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
if (er) d = {}
var scripts = Object.keys(d.scripts || {})
if (scripts.indexOf(argv[2]) !== -1) return cb()
// ok, try to find out which package it was, then
var pref = npm.config.get('global') ? npm.config.get('prefix')
: npm.localPrefix
var pkgDir = resolve(pref, 'node_modules', argv[2], 'package.json')
readJson(pkgDir, function (er, d) {
if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
if (er) d = {}
var scripts = Object.keys(d.scripts || {})
return cb(null, scripts)
})
})
const completion = async (opts, cb) => {
const argv = opts.conf.argv.remain
if (argv.length === 2) {
// find the script name
const json = resolve(npm.localPrefix, 'package.json')
const { scripts = {} } = await readJson(json).catch(er => ({}))
return cb(null, Object.keys(scripts))
}

readJson(join(npm.localPrefix, 'package.json'), function (er, d) {
if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
d = d || {}
cb(null, Object.keys(d.scripts || {}))
})
// otherwise nothing to do, just let the system handle it
return cb()
}

function runScriptCmd (args, cb) {
const cmd = (args, cb) => {
const fn = args.length ? runScript : list
fn(args).then(() => cb()).catch(cb)
return fn(args).then(() => cb()).catch(cb)
}

const runScript = async (args) => {
Expand All @@ -64,10 +39,11 @@ const runScript = async (args) => {
const { scripts = {} } = pkg

if (event === 'restart' && !scripts.restart) {
scripts.restart = 'npm stop && npm start'
scripts.restart = 'npm stop --if-present && npm start'
} else if (event === 'env') {
scripts.env = isWindowsShell ? 'SET' : 'env'
}
pkg.scripts = scripts

if (!scripts[event]) {
if (npm.config.get('if-present')) {
Expand Down Expand Up @@ -117,13 +93,13 @@ const list = async () => {
'start',
'restart',
'version'
].reduce((l, p) => l.concat(['pre' + p, p, 'post' + p]))
].reduce((l, p) => l.concat(['pre' + p, p, 'post' + p]), [])

if (!scripts) {
return []
}

const allScripts = scripts ? Object.keys(scripts) : []
const allScripts = Object.keys(scripts)
if (log.level === 'silent') {
return allScripts
}
Expand Down Expand Up @@ -165,3 +141,5 @@ const list = async () => {
}
return allScripts
}

module.exports = Object.assign(cmd, { completion, usage })
24 changes: 10 additions & 14 deletions lib/test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
/* eslint-disable standard/no-callback-literal */
module.exports = test

const testCmd = require('./utils/lifecycle-cmd.js')('test')
const { completion, usage } = testCmd
const cmd = (args, cb) => testCmd(args, er => {
if (er && er.code === 'ELIFECYCLE') {
/* eslint-disable standard/no-callback-literal */
cb('Test failed. See above for more details.')
} else {
cb(er)
}
})

test.usage = testCmd.usage

function test (args, cb) {
testCmd(args, function (er) {
if (!er) return cb()
if (er.code === 'ELIFECYCLE') {
return cb('Test failed. See above for more details.')
}
return cb(er)
})
}
module.exports = Object.assign(cmd, { completion, usage })
Loading