-
-
Notifications
You must be signed in to change notification settings - Fork 105
Add --shell-auto-fallback #7
Changes from all commits
a7402dc
3c142b4
2a1d85a
d170146
81fe5a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
'use strict' | ||
|
||
const POSIX = ` | ||
command_not_found_handler() { | ||
# Do not run within a pipe | ||
if test ! -t 1; then | ||
echo "command not found: $1" | ||
return 127 | ||
fi | ||
|
||
echo "Trying with npx..." | ||
npx $* | ||
return $? | ||
}` | ||
|
||
const FISH = ` | ||
function __fish_command_not_found_on_interactive --on-event fish_prompt | ||
functions --erase __fish_command_not_found_handler | ||
functions --erase __fish_command_not_found_setup | ||
|
||
function __fish_command_not_found_handler --on-event fish_command_not_found | ||
echo "Trying with npx..." | ||
npx $argv | ||
end | ||
|
||
functions --erase __fish_command_not_found_on_interactive | ||
end` | ||
|
||
module.exports = function autoFallback (shell) { | ||
const SHELL = process.env.SHELL || '' | ||
|
||
if (shell === 'bash' || SHELL.includes('bash')) { | ||
return POSIX.replace('handler()', 'handle()') | ||
} | ||
|
||
if (shell === 'zsh' || SHELL.includes('zsh')) { | ||
return POSIX | ||
} | ||
|
||
if (shell === 'fish' || SHELL.includes('fish')) { | ||
return FISH | ||
} | ||
|
||
console.error('Only Bash, Zsh, and Fish shells are supported :(') | ||
process.exit(1) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
const npa = require('npm-package-arg') | ||
const yargs = require('yargs') | ||
|
||
const usage = `$0 [--package|-p <package>] [--cache <path>] [--save-dev|-D] [--save-prod|-P] [--save-optional|-O] [--save-bundle|-B] [--save-exact|-E] [--global|-g] [--prefix|-C] [--userconfig <path>] [-c <string>] [--version|-v] [--] <command>[@version] [command-arg]...` | ||
const usage = `$0 [--package|-p <package>] [--cache <path>] [--save-dev|-D] [--save-prod|-P] [--save-optional|-O] [--save-bundle|-B] [--save-exact|-E] [--global|-g] [--prefix|-C] [--userconfig <path>] [-c <string>] [--shell-auto-fallback [shell]] [--version|-v] [--] <command>[@version] [command-arg]...` | ||
|
||
module.exports = parseArgs | ||
function parseArgs () { | ||
|
@@ -62,6 +62,12 @@ function parseArgs () { | |
type: 'string', | ||
describe: 'execute string as if inside `npm run-script`' | ||
}) | ||
.option('shell-auto-fallback', { | ||
choices: ['', 'bash', 'fish', 'zsh'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll re-test, but I think yargs refused to parse the option if the default wasn't in the choices. Although. Given the difficulty of figuring out the shell from a subprocess, maybe I should just remove both the autodetecting code and the default value here, and just do it based on the passed value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just don't give it a default? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As to why I made the default |
||
describe: 'generate shell code to use npx as the "command not found" fallback', | ||
requireArg: false, | ||
type: 'string' | ||
}) | ||
.version() | ||
.alias('version', 'v') | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
'use strict' | ||
|
||
const exec = require('child_process').exec | ||
const test = require('tap').test | ||
|
||
test('not called with option', (t) => | ||
exec('node .', (err, stdout, stderr) => { | ||
t.equal(err.code, 1) | ||
t.notOk(stdout) | ||
t.match(stderr, /--shell-auto-fallback/) | ||
t.end() | ||
}) | ||
) | ||
|
||
test('detect: SHELL ~= fish', (t) => | ||
exec('node . --shell-auto-fallback', { | ||
env: { | ||
SHELL: '/usr/bin/fish' | ||
} | ||
}, (err, stdout, stderr) => { | ||
if (err) { throw err } | ||
t.match(stdout, /function __fish_command_not_found/) | ||
t.notOk(stderr) | ||
t.end() | ||
}) | ||
) | ||
|
||
test('detect: SHELL ~= bash', (t) => | ||
exec('node . --shell-auto-fallback', { | ||
env: { | ||
SHELL: '/bin/bash' | ||
} | ||
}, (err, stdout, stderr) => { | ||
if (err) { throw err } | ||
t.match(stdout, /command_not_found_handle\(/) | ||
t.notOk(stderr) | ||
t.end() | ||
}) | ||
) | ||
|
||
test('detect: SHELL ~= zsh', (t) => | ||
exec('node . --shell-auto-fallback', { | ||
env: { | ||
SHELL: '/usr/local/bin/zsh' | ||
} | ||
}, (err, stdout, stderr) => { | ||
if (err) { throw err } | ||
t.match(stdout, /command_not_found_handler\(/) | ||
t.notOk(stderr) | ||
t.end() | ||
}) | ||
) | ||
|
||
test('detect: no SHELL', (t) => | ||
exec('node . --shell-auto-fallback', { | ||
env: {} | ||
}, (err, stdout, stderr) => { | ||
t.equal(err.code, 1) | ||
t.notOk(stdout) | ||
t.match(stderr, /Only .+ shells are supported :\(/) | ||
t.end() | ||
}) | ||
) | ||
|
||
test('detect: SHELL ~= unsupported', (t) => | ||
exec('node . --shell-auto-fallback', { | ||
env: { | ||
SHELL: '/sbin/nope' | ||
} | ||
}, (err, stdout, stderr) => { | ||
t.equal(err.code, 1) | ||
t.notOk(stdout) | ||
t.match(stderr, /Only .+ shells are supported :\(/) | ||
t.end() | ||
}) | ||
) | ||
|
||
test('given: fish', (t) => | ||
exec('node . --shell-auto-fallback fish', (err, stdout, stderr) => { | ||
if (err) { throw err } | ||
t.match(stdout, /function __fish_command_not_found/) | ||
t.notOk(stderr) | ||
t.end() | ||
}) | ||
) | ||
|
||
test('given: bash', (t) => | ||
exec('node . --shell-auto-fallback bash', (err, stdout, stderr) => { | ||
if (err) { throw err } | ||
t.match(stdout, /command_not_found_handle\(/) | ||
t.notOk(stderr) | ||
t.end() | ||
}) | ||
) | ||
|
||
test('given: zsh', (t) => | ||
exec('node . --shell-auto-fallback zsh', (err, stdout, stderr) => { | ||
if (err) { throw err } | ||
t.match(stdout, /command_not_found_handler\(/) | ||
t.notOk(stderr) | ||
t.end() | ||
}) | ||
) | ||
|
||
test('given: unsupported', (t) => | ||
exec('node . --shell-auto-fallback nope', (err, stdout, stderr) => { | ||
t.equal(err.code, 1) | ||
t.notOk(stdout) | ||
t.match(stderr, /Invalid values:\s+Argument: shell-auto-fallback/) | ||
t.end() | ||
}) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
won't this always be true?
parseArgs()
defaults to''
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requireArg
isfalse
! So sometimes the option will return null/undefined/false/whatever yargs returns for a non-provided option. But''
is falsy, so I do need to check.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't having a default mean that you always get
''
if you don't provide the option, though? I'm so confused.