1'use strict' 2// calls linkIfExists on unix, or cmdShimIfExists on Windows 3// reads the cmd shim to ensure it's where we need it to be in the case of 4// top level global packages 5 6const readCmdShim = require('read-cmd-shim') 7const cmdShim = require('cmd-shim') 8const {linkIfExists} = require('./link.js') 9 10const binLink = (from, to, opts, cb) => { 11 // just for testing 12 const platform = opts._FAKE_PLATFORM_ || process.platform 13 if (platform !== 'win32') { 14 return linkIfExists(from, to, opts, cb) 15 } 16 17 if (!opts.clobberLinkGently || 18 opts.force === true || 19 !opts.gently || 20 typeof opts.gently !== 'string') { 21 // easy, just go ahead and delete anything in the way 22 return cmdShim.ifExists(from, to, cb) 23 } 24 25 // read all three shim targets 26 // if any exist, and are not a shim to our gently folder, then 27 // exit with a simulated EEXIST error. 28 29 const shimFiles = [ 30 to, 31 to + '.cmd', 32 to + '.ps1' 33 ] 34 35 // call this once we've checked all three, if we're good 36 const done = () => cmdShim.ifExists(from, to, cb) 37 const then = times(3, done, cb) 38 shimFiles.forEach(to => isClobberable(from, to, opts, then)) 39} 40 41const times = (n, ok, cb) => { 42 let errState = null 43 return er => { 44 if (!errState) { 45 if (er) { 46 cb(errState = er) 47 } else if (--n === 0) { 48 ok() 49 } 50 } 51 } 52} 53 54const isClobberable = (from, to, opts, cb) => { 55 readCmdShim(to, (er, target) => { 56 // either going to get an error, or the target of where this 57 // cmd shim points. 58 // shim, not in opts.gently: simulate EEXIST 59 // not a shim: simulate EEXIST 60 // ENOENT: fine, move forward 61 // shim in opts.gently: fine 62 if (er) { 63 switch (er.code) { 64 case 'ENOENT': 65 // totally fine, nothing there to clobber 66 return cb() 67 case 'ENOTASHIM': 68 // something is there, and it's not one of ours 69 return cb(simulateEEXIST(from, to)) 70 default: 71 // would probably fail this way later anyway 72 // can't read the file, likely can't write it either 73 return cb(er) 74 } 75 } 76 // no error, check the target 77 if (target.indexOf(opts.gently) !== 0) { 78 return cb(simulateEEXIST(from, to)) 79 } 80 // ok! it's one of ours. 81 return cb() 82 }) 83} 84 85const simulateEEXIST = (from, to) => { 86 // simulate the EEXIST we'd get from fs.symlink to the file 87 const err = new Error('EEXIST: file already exists, cmd shim \'' + 88 from + '\' -> \'' + to + '\'') 89 90 err.code = 'EEXIST' 91 err.path = from 92 err.dest = to 93 return err 94} 95 96module.exports = binLink 97