• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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