You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

npmsearch 2.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #!/usr/bin/env bash
  2. declare -a args
  3. declare -a packages
  4. declare savedev=0
  5. declare global=0
  6. declare -A colors
  7. colors[red]=$(tput setaf 1)
  8. colors[green]=$(tput setaf 2)
  9. colors[reset]=$(tput sgr0)
  10. color() {
  11. local color="$1"; shift
  12. printf '%s' "${colors[$color]}"
  13. printf '%s\n' "$@"
  14. printf '%s' "${colors[reset]}"
  15. }
  16. err() { color red "$@" >&2; return 1; }
  17. die() {
  18. (( $# > 0 )) && err "$@"
  19. exit 1
  20. }
  21. has() {
  22. local v c
  23. if [[ $1 = '-v' ]]; then
  24. v=1
  25. shift
  26. fi
  27. for c; do c="${c%% *}"
  28. if ! command -v "$c" &> /dev/null; then
  29. (( v > 0 )) && err "$c not found"
  30. return 1
  31. fi
  32. done
  33. }
  34. select_from() {
  35. local cmd='command -v'
  36. for a; do
  37. case "$a" in
  38. -c) cmd="$2"; shift 2 ;;
  39. esac
  40. done
  41. for c; do
  42. if $cmd "${c%% *}" &> /dev/null; then
  43. echo "$c"
  44. return 0
  45. fi
  46. done
  47. return 1
  48. }
  49. has -v fzf npm || die
  50. fzf() {
  51. command fzf --inline-info --ansi --reverse +s --no-hscroll "$@"
  52. }
  53. if [[ $1 = '-r' || $1 = '--remove' ]]; then
  54. mapfile -t rm < <(
  55. jq -r '{dependencies, devDependencies} | .[] | keys | .[]' package.json |
  56. fzf -m --cycle -e --reverse)
  57. (( ${#rm} > 0 )) && $(select_from 'yarn remove' 'npm uninstall') "${rm[@]}"
  58. die
  59. fi
  60. printf '%ssearching...%s\r' "${colors[green]}" "${colors[reset]}"
  61. search=$(npm search --json "$*") || exit
  62. search=$(jq -r '.[] | "\(.name)|\(.version)|\(.description)"' <<< "$search" | column -t -s'|')
  63. mapfile -t packages < <(fzf --multi \
  64. --bind='ctrl-x:toggle-preview' \
  65. --expect='ctrl-g,ctrl-d,enter,ctrl-v,esc' \
  66. --preview-window='hidden:down' \
  67. --preview="npm -s --json view {1} | jq -C \"del(.users, .time, .versions)\"" \
  68. --header='C-d saves as devDependency, C-g installs globally, C-v for specific version, C-x for more info, ' \
  69. <<< "$search")
  70. key="${packages[0]}"
  71. case "${key,,}" in
  72. esc) die ;;
  73. ctrl-d) savedev=1 ;;
  74. ctrl-g) global=1 ;;
  75. ctrl-v)
  76. package="${packages[1]%% *}"
  77. version=$(
  78. npm -s --json view "$package" |
  79. jq -r '.versions[]' | # label with .dist-tags?
  80. fzf --header="choose version for $package | C-d saves as devDependency" --tac --expect='ctrl-d,esc')
  81. key="${version[0]}"
  82. case "${key,,}" in
  83. esc) die ;;
  84. ctrl-d) savedev=1 ;;
  85. esac
  86. packages=( "${package}@${version[1]}" )
  87. ;;
  88. esac
  89. (( ${#packages[@]} > 0 )) || die 'no packages to install'
  90. if (( ${#packages[@]} > 1 )); then
  91. packages=( "${packages[@]:1}" )
  92. packages=( "${packages[@]%% *}" )
  93. fi
  94. printf '%sinstalling...%s\r' "${colors[green]}" "${colors[reset]}"
  95. if (( global > 0 )); then
  96. npm install -g "${args[@]}" "${packages[@]}"
  97. elif has yarn; then
  98. (( savedev > 0 )) && args+=( -D )
  99. yarn add "${args[@]}" "${packages[@]}"
  100. else
  101. if (( savedev > 0 )); then
  102. args+=( -D )
  103. else
  104. args+=( -S )
  105. fi
  106. npm i "${args[@]}" "${packages[@]}"
  107. fi