|
| 1 | +#compdef pnpm |
| 2 | + |
| 3 | +if command -v pnpm-shell-completion &> /dev/null; then |
| 4 | + pnpm_comp_bin="$(which pnpm-shell-completion)" |
| 5 | +else |
| 6 | + pnpm_comp_bin="$(dirname $0)/pnpm-shell-completion" |
| 7 | +fi |
| 8 | + |
| 9 | +# Function to check if a command has Tab-powered completions |
| 10 | +_has_tab_completion() { |
| 11 | + local cmd="$1" |
| 12 | + |
| 13 | + # Try different methods to detect if the command has Tab completions |
| 14 | + |
| 15 | + # Method 1: Try running the command with the 'complete' subcommand |
| 16 | + # This is the standard Tab completion method |
| 17 | + if pnpm exec $cmd complete zsh &>/dev/null; then |
| 18 | + return 0 # Success - command has Tab completions |
| 19 | + fi |
| 20 | + |
| 21 | + # Method 2: Check if running the command's completion outputs a directive |
| 22 | + # Tab completions end with a line like ":4" to indicate the completion directive |
| 23 | + if pnpm exec $cmd complete -- "" 2>/dev/null | grep -q ':[0-9]\+$'; then |
| 24 | + return 0 # Success - command has Tab completions |
| 25 | + fi |
| 26 | + |
| 27 | + # Method 3: Check if the command's help mentions completions |
| 28 | + if pnpm exec $cmd --help 2>/dev/null | grep -q -i 'complet'; then |
| 29 | + return 0 # Success - command likely has completions |
| 30 | + fi |
| 31 | + |
| 32 | + # None of the detection methods worked |
| 33 | + return 1 # Failure - command doesn't have Tab completions |
| 34 | +} |
| 35 | + |
| 36 | +# Function to get completions from a Tab-powered command |
| 37 | +_get_tab_completions() { |
| 38 | + local cmd="$1" |
| 39 | + shift |
| 40 | + local args=("$@") |
| 41 | + local result |
| 42 | + |
| 43 | + # Try different methods to get completions |
| 44 | + |
| 45 | + # Method 1: Standard completion method |
| 46 | + result=$(pnpm exec $cmd complete -- "${args[@]}" 2>/dev/null) |
| 47 | + if [[ -n "$result" && $(echo "$result" | grep -c ':[0-9]\+$') -gt 0 ]]; then |
| 48 | + echo "$result" |
| 49 | + return 0 |
| 50 | + fi |
| 51 | + |
| 52 | + # Method 2: Try with the 'complete' subcommand, which some tools use |
| 53 | + result=$(pnpm exec $cmd complete "${args[@]}" 2>/dev/null) |
| 54 | + if [[ -n "$result" ]]; then |
| 55 | + echo "$result" |
| 56 | + return 0 |
| 57 | + fi |
| 58 | + |
| 59 | + # If we couldn't get any completions, return an empty result |
| 60 | + # This will make the shell fall back to its default completion behavior |
| 61 | + return 1 |
| 62 | +} |
| 63 | + |
| 64 | +_pnpm() { |
| 65 | + typeset -A opt_args |
| 66 | + local cmd_index=1 |
| 67 | + local has_custom_completion=0 |
| 68 | + local custom_cmd="" |
| 69 | + |
| 70 | + # Check if we have command arguments beyond "pnpm" |
| 71 | + if (( CURRENT > 1 )); then |
| 72 | + # The first argument after pnpm might be a command with its own completion |
| 73 | + custom_cmd="${words[2]}" |
| 74 | + |
| 75 | + # Check for workspace-specific flags that would shift the command position |
| 76 | + if [[ "${words[2]}" == "--filter" || "${words[2]}" == "-F" ]]; then |
| 77 | + # The command comes after the filter and value |
| 78 | + if (( CURRENT > 3 )); then |
| 79 | + custom_cmd="${words[4]}" |
| 80 | + cmd_index=4 |
| 81 | + else |
| 82 | + custom_cmd="" |
| 83 | + fi |
| 84 | + fi |
| 85 | + |
| 86 | + # Check if the command has Tab completions |
| 87 | + if [[ -n "$custom_cmd" ]] && _has_tab_completion "$custom_cmd"; then |
| 88 | + has_custom_completion=1 |
| 89 | + fi |
| 90 | + fi |
| 91 | + |
| 92 | + # If we found a command with Tab completions and we're trying to complete its arguments |
| 93 | + if [[ $has_custom_completion -eq 1 ]] && (( CURRENT > cmd_index )); then |
| 94 | + # Extract the arguments for the custom command |
| 95 | + local cmd_args=("${words[@]:cmd_index}") |
| 96 | + |
| 97 | + # Get Tab completions for this command |
| 98 | + _get_tab_completions "$custom_cmd" "${cmd_args[@]}" |
| 99 | + return 0 |
| 100 | + fi |
| 101 | + |
| 102 | + # Original pnpm completion logic |
| 103 | + _arguments \ |
| 104 | + '(--filter -F)'{--filter,-F}'=:flag:->filter' \ |
| 105 | + ':command:->scripts' \ |
| 106 | + '*:: :->command_args' |
| 107 | + |
| 108 | + local target_pkg=${opt_args[--filter]:-$opt_args[-F]} |
| 109 | + |
| 110 | + case $state in |
| 111 | + filter) |
| 112 | + if [[ -f ./pnpm-workspace.yaml ]]; then |
| 113 | + _values 'filter packages' $(FEATURE=filter $pnpm_comp_bin) |
| 114 | + fi |
| 115 | + ;; |
| 116 | + scripts) |
| 117 | + _values 'scripts' $(FEATURE=scripts TARGET_PKG=$target_pkg ZSH=true $pnpm_comp_bin) \ |
| 118 | + add remove install update publish |
| 119 | + ;; |
| 120 | + command_args) |
| 121 | + local cmd=$(FEATURE=pnpm_cmd $pnpm_comp_bin $words) |
| 122 | + case $cmd in |
| 123 | + add) |
| 124 | + _arguments \ |
| 125 | + '(--global -g)'{--global,-g}'[Install as a global package]' \ |
| 126 | + '(--save-dev -D)'{--save-dev,-D}'[Save package to your `devDependencies`]' \ |
| 127 | + '--save-peer[Save package to your `peerDependencies` and `devDependencies`]' |
| 128 | + ;; |
| 129 | + install|i) |
| 130 | + _arguments \ |
| 131 | + '(--dev -D)'{--dev,-D}'[Only `devDependencies` are installed regardless of the `NODE_ENV`]' \ |
| 132 | + '--fix-lockfile[Fix broken lockfile entries automatically]' \ |
| 133 | + '--force[Force reinstall dependencies]' \ |
| 134 | + "--ignore-scripts[Don't run lifecycle scripts]" \ |
| 135 | + '--lockfile-only[Dependencies are not downloaded. Only `pnpm-lock.yaml` is updated]' \ |
| 136 | + '--no-optional[`optionalDependencies` are not installed]' \ |
| 137 | + '--offline[Trigger an error if any required dependencies are not available in local store]' \ |
| 138 | + '--prefer-offline[Skip staleness checks for cached data, but request missing data from the server]' \ |
| 139 | + '(--prod -P)'{--prod,-P}"[Packages in \`devDependencies\` won't be installed]" |
| 140 | + ;; |
| 141 | + remove|rm|why) |
| 142 | + if [[ -f ./package.json ]]; then |
| 143 | + _values 'deps' $(FEATURE=deps TARGET_PKG=$target_pkg $pnpm_comp_bin) |
| 144 | + fi |
| 145 | + ;; |
| 146 | + update|upgrade|up) |
| 147 | + _arguments \ |
| 148 | + '(--dev -D)'{--dev,-D}'[Update packages only in "devDependencies"]' \ |
| 149 | + '(--global -g)'{--global,-g}'[Update globally installed packages]' \ |
| 150 | + '(--interactive -i)'{--interactive,-i}'[Show outdated dependencies and select which ones to update]' \ |
| 151 | + '(--latest -L)'{--latest,-L}'[Ignore version ranges in package.json]' \ |
| 152 | + "--no-optional[Don't update packages in \`optionalDependencies\`]" \ |
| 153 | + '(--prod -P)'{--prod,-P}'[Update packages only in "dependencies" and "optionalDependencies"]' \ |
| 154 | + '(--recursive -r)'{--recursive,-r}'[Update in every package found in subdirectories or every workspace package]' |
| 155 | + if [[ -f ./package.json ]]; then |
| 156 | + _values 'deps' $(FEATURE=deps TARGET_PKG=$target_pkg $pnpm_comp_bin) |
| 157 | + fi |
| 158 | + ;; |
| 159 | + publish) |
| 160 | + _arguments \ |
| 161 | + '--access=[Tells the registry whether this package should be published as public or restricted]: :(public restricted)' \ |
| 162 | + '--dry-run[Does everything a publish would do except actually publishing to the registry]' \ |
| 163 | + '--force[Packages are proceeded to be published even if their current version is already in the registry]' \ |
| 164 | + '--ignore-scripts[Ignores any publish related lifecycle scripts (prepublishOnly, postpublish, and the like)]' \ |
| 165 | + "--no-git-checks[Don't check if current branch is your publish branch, clean, and up to date]" \ |
| 166 | + '--otp[Specify a one-time password]' \ |
| 167 | + '--publish-branch[Sets branch name to publish]' \ |
| 168 | + '(--recursive -r)'{--recursive,-r}'[Publish all packages from the workspace]' \ |
| 169 | + '--tag=[Registers the published package with the given tag]' |
| 170 | + ;; |
| 171 | + run) |
| 172 | + if [[ -f ./package.json ]]; then |
| 173 | + _values 'scripts' $(FEATURE=scripts TARGET_PKG=$target_pkg ZSH=true $pnpm_comp_bin) |
| 174 | + fi |
| 175 | + ;; |
| 176 | + *) |
| 177 | + _files |
| 178 | + esac |
| 179 | + esac |
| 180 | +} |
| 181 | + |
| 182 | +compdef _pnpm pnpm |
0 commit comments