codekingpro/portable-devtools
114k
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 