Skip to content

Isaacs/unit test explore #1543

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

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
25 changes: 8 additions & 17 deletions lib/utils/escape-arg.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict'
var path = require('path')
var isWindows = require('./is-windows.js')
const { normalize } = require('path')
const isWindows = require('./is-windows.js')

/*
Escape the name of an executable suitable for passing to the system shell.
Expand All @@ -9,19 +8,11 @@ Windows is easy, wrap in double quotes and you're done, as there's no
facility to create files with quotes in their names.

Unix-likes are a little more complicated, wrap in single quotes and escape
any single quotes in the filename.
any single quotes in the filename. The '"'"' construction ends the quoted
block, creates a new " quoted string with ' in it. So, `foo'bar` becomes
`'foo'"'"'bar'`, which is the bash way of saying `'foo' + "'" + 'bar'`.
*/

module.exports = escapify

function escapify (str) {
if (isWindows) {
return '"' + path.normalize(str) + '"'
} else {
if (/[^-_.~/\w]/.test(str)) {
return "'" + str.replace(/'/g, "'\"'\"'") + "'"
} else {
return str
}
}
}
module.exports = str => isWindows ? '"' + normalize(str) + '"'
: /[^-_.~/\w]/.test(str) ? "'" + str.replace(/'/g, '\'"\'"\'') + "'"
: str
29 changes: 10 additions & 19 deletions lib/utils/escape-exec-path.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict'
var path = require('path')
var isWindows = require('./is-windows.js')
const { normalize } = require('path')
const isWindows = require('./is-windows.js')

/*
Escape the name of an executable suitable for passing to the system shell.
Expand All @@ -9,22 +8,14 @@ Windows is easy, wrap in double quotes and you're done, as there's no
facility to create files with quotes in their names.

Unix-likes are a little more complicated, wrap in single quotes and escape
any single quotes in the filename.
any single quotes in the filename. The '"'"' construction ends the quoted
block, creates a new " quoted string with ' in it. So, `foo'bar` becomes
`'foo'"'"'bar'`, which is the bash way of saying `'foo' + "'" + 'bar'`.
*/

module.exports = escapify
const winQuote = str => !/ /.test(str) ? str : '"' + str + '"'
const winEsc = str => normalize(str).split(/\\/).map(winQuote).join('\\')

function windowsQuotes (str) {
if (!/ /.test(str)) return str
return '"' + str + '"'
}

function escapify (str) {
if (isWindows) {
return path.normalize(str).split(/\\/).map(windowsQuotes).join('\\')
} else if (/[^-_.~/\w]/.test(str)) {
return "'" + str.replace(/'/g, "'\"'\"'") + "'"
} else {
return str
}
}
module.exports = str => isWindows ? winEsc(str)
: /[^-_.~/\w]/.test(str) ? "'" + str.replace(/'/g, '\'"\'"\'') + "'"
: str
4 changes: 2 additions & 2 deletions lib/utils/hosted-git-info-from-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ module.exports = mani => {
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

// hgi returns undefined sometimes, but let's always return null here
return (rurl && hostedGitInfo.fromUrl(rurl.replace(/^git\+/, ''))) || null
Expand Down
4 changes: 2 additions & 2 deletions lib/utils/setup-log.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ module.exports = (config) => {

const enableColorStderr = color === 'always' ? true
: color === false ? false
: stderrTTY
: stderrTTY

const enableColorStdout = color === 'always' ? true
: color === false ? false
: stdoutTTY
: stdoutTTY

if (enableColorStderr) {
log.enableColor()
Expand Down
4 changes: 2 additions & 2 deletions lib/utils/update-notifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ module.exports = (npm) => {
const type = notifier.update.type
const typec = !useColor ? type
: type === 'major' ? chalk.red(type)
: type === 'minor' ? chalk.yellow(type)
: chalk.green(type)
: type === 'minor' ? chalk.yellow(type)
: chalk.green(type)

const changelog = `https://github.com/npm/cli/releases/tag/v${latest}`
notifier.notify({
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@
"sudotest": "sudo npm run test --",
"sudotest:nocleanup": "sudo NO_TEST_CLEANUP=1 npm run test --",
"posttest": "npm run lint",
"eslint": "eslint --",
"eslint": "eslint",
"lint": "npm run eslint -- \"lib/**/*.js\"",
"lintfix": "npm run lint -- --fix",
"prelint": "rimraf test/npm_cache*"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/tap/flat-options.js TAP basic > flat options 1`] = `
exports[`test/lib/config/flat-options.js TAP basic > flat options 1`] = `
Object {
"@scope:registry": "@scope:registry",
"//nerf.dart:_authToken": "//nerf.dart:_authToken",
"access": "access",
"all": undefined,
"allowSameVersion": "allow-same-version",
"alwaysAuth": "always-auth",
"audit": "audit",
Expand Down Expand Up @@ -51,6 +52,7 @@ Object {
"json": undefined,
"key": "key",
"legacyBundling": "legacy-bundling",
"legacyPeerDeps": undefined,
"localPrefix": "/path/to/npm/cli",
"lockFile": Object {
"retries": "cache-lock-retries",
Expand Down Expand Up @@ -102,6 +104,7 @@ Object {
"staleness": "searchstaleness",
},
"sendMetrics": "send-metrics",
"shell": undefined,
"signGitCommit": "sign-git-commit",
"signGitTag": "sign-git-tag",
"ssoPollFrequency": undefined,
Expand All @@ -114,5 +117,6 @@ Object {
"unicode": undefined,
"userAgent": "user-agent",
"viewer": "viewer",
"which": undefined,
}
`
Loading