Skip to content
This repository has been archived by the owner on Jul 6, 2019. It is now read-only.

Add --shell-auto-fallback #7

Merged
merged 5 commits into from
May 31, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
feat(opts): add --shell-auto-fallback
Generates shell code that hooks into the "command not found" mechanism
and attempts to run the command with npx instead of failing. The option
has an optional argument that forces generation of a particular variant,
otherwise it attempts to autodetect the shell. However, autodetecting is
actually very flaky, so it's better to always specify the argument.

To use, place this in relevant shell config file:

For Bash:

    source <(npx --shell-auto-fallback bash)

For Zsh:

    source <(npx --shell-auto-fallback zsh)

For Fish:

    source (npx --shell-auto-fallback fish | psub)

As Seen On Twitter: https://twitter.com/passcod/status/869469928474107906
  • Loading branch information
passcod committed May 30, 2017
commit a7402dcacb88e55c42c26763904835385d8c7539
46 changes: 46 additions & 0 deletions auto-fallback.js
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 { BASH_VERSION, SHELL, ZSH_VERSION } = process.env
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're restricted to node@4 or latest, so was can't do destructuring assignment 😭


if (shell === 'bash' || BASH_VERSION || /bash/.test(SHELL)) {
return POSIX.replace('handler()', 'handle()')
}

if (shell === 'zsh' || ZSH_VERSION || /zsh/.test(SHELL)) {
return POSIX
}

if (shell === 'fish' || /fish/.test(SHELL)) {
return FISH
}

console.error('Only Bash, Zsh, and Fish shells are supported :(')
process.exit(1)
}
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

const BB = require('bluebird')

const autoFallback = require('./auto-fallback.js')
const cp = require('child_process')
const getPrefix = require('./get-prefix.js')
const parseArgs = require('./parse-args.js')
Expand All @@ -18,6 +19,12 @@ updateNotifier({pkg}).notify()
main(parseArgs())

function main (argv) {
const shell = argv['shell-auto-fallback']
if (shell || shell === '') {
Copy link
Owner

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 ''

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requireArg is false! 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.

Copy link
Owner

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.

console.log(autoFallback(shell))
process.exit(0)
}

if (!argv.command || !argv.package) {
console.error('\nERROR: You must supply a command.\n')
yargs.showHelp()
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions parse-args.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ function parseArgs () {
type: 'string',
describe: 'execute string as if inside `npm run-script`'
})
.option('shell-auto-fallback', {
choices: ['', 'bash', 'fish', 'zsh'],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why ''?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just don't give it a default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As to why I made the default ''... err. I don't remember. I'll have a look at the code again in its entirety to see if I can figure it out.

describe: 'generate shell code to use npx as the "command not found" fallback',
default: '',
defaultDescription: 'detect',
requiresArg: false,
type: 'string'
})
.version()
.alias('version', 'v')

Expand Down