1// check to see if a bin is allowed to be overwritten 2// either rejects or resolves to nothing. return value not relevant. 3const isWindows = require('./is-windows.js') 4const binTarget = require('./bin-target.js') 5const { resolve, dirname } = require('path') 6const readCmdShim = require('read-cmd-shim') 7const { readlink } = require('fs/promises') 8 9const checkBin = async ({ bin, path, top, global, force }) => { 10 // always ok to clobber when forced 11 // always ok to clobber local bins, or when forced 12 if (force || !global || !top) { 13 return 14 } 15 16 // ok, need to make sure, then 17 const target = resolve(binTarget({ path, top }), bin) 18 path = resolve(path) 19 return isWindows ? checkShim({ target, path }) : checkLink({ target, path }) 20} 21 22// only enoent is allowed. anything else is a problem. 23const handleReadLinkError = async ({ er, target }) => 24 er.code === 'ENOENT' ? null 25 : failEEXIST({ target }) 26 27const checkLink = async ({ target, path }) => { 28 const current = await readlink(target) 29 .catch(er => handleReadLinkError({ er, target })) 30 31 if (!current) { 32 return 33 } 34 35 const resolved = resolve(dirname(target), current) 36 37 if (resolved.toLowerCase().indexOf(path.toLowerCase()) !== 0) { 38 return failEEXIST({ target }) 39 } 40} 41 42const handleReadCmdShimError = ({ er, target }) => 43 er.code === 'ENOENT' ? null 44 : failEEXIST({ target }) 45 46const failEEXIST = ({ target }) => 47 Promise.reject(Object.assign(new Error('EEXIST: file already exists'), { 48 path: target, 49 code: 'EEXIST', 50 })) 51 52const checkShim = async ({ target, path }) => { 53 const shims = [ 54 target, 55 target + '.cmd', 56 target + '.ps1', 57 ] 58 await Promise.all(shims.map(async shim => { 59 const current = await readCmdShim(shim) 60 .catch(er => handleReadCmdShimError({ er, target: shim })) 61 62 if (!current) { 63 return 64 } 65 66 const resolved = resolve(dirname(shim), current.replace(/\\/g, '/')) 67 68 if (resolved.toLowerCase().indexOf(path.toLowerCase()) !== 0) { 69 return failEEXIST({ target: shim }) 70 } 71 })) 72} 73 74module.exports = checkBin 75