| 52 | static usage = ['[<package-spec> ...]'] |
| 53 | |
| 54 | static async completion (opts) { |
| 55 | const { partialWord } = opts |
| 56 | // install can complete to a folder with a package.json, or any package. |
| 57 | // if it has a slash, then it's gotta be a folder |
| 58 | // if it starts with https?://, then just give up, because it's a url |
| 59 | if (/^https?:\/\//.test(partialWord)) { |
| 60 | // do not complete to URLs |
| 61 | return [] |
| 62 | } |
| 63 | |
| 64 | if (/\//.test(partialWord)) { |
| 65 | // Complete fully to folder if there is exactly one match and it is a folder containing a package.json file. |
| 66 | // If that is not the case we return 0 matches, which will trigger the default bash complete. |
| 67 | const lastSlashIdx = partialWord.lastIndexOf('/') |
| 68 | const partialName = partialWord.slice(lastSlashIdx + 1) |
| 69 | const partialPath = partialWord.slice(0, lastSlashIdx) || '/' |
| 70 | |
| 71 | const isDirMatch = async sibling => { |
| 72 | if (sibling.slice(0, partialName.length) !== partialName) { |
| 73 | return false |
| 74 | } |
| 75 | |
| 76 | try { |
| 77 | const contents = await readdir(join(partialPath, sibling)) |
| 78 | const result = (contents.indexOf('package.json') !== -1) |
| 79 | return result |
| 80 | } catch { |
| 81 | return false |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | try { |
| 86 | const siblings = await readdir(partialPath) |
| 87 | const matches = [] |
| 88 | for (const sibling of siblings) { |
| 89 | if (await isDirMatch(sibling)) { |
| 90 | matches.push(sibling) |
| 91 | } |
| 92 | } |
| 93 | if (matches.length === 1) { |
| 94 | return [join(partialPath, matches[0])] |
| 95 | } |
| 96 | // no matches |
| 97 | return [] |
| 98 | } catch { |
| 99 | return [] // invalid dir: no matching |
| 100 | } |
| 101 | } |
| 102 | // Note: there used to be registry completion here, but it stopped making sense somewhere around 50,000 packages on the registry |
| 103 | } |
| 104 | |
| 105 | async exec (args) { |
| 106 | // the /path/to/node_modules/.. |