Socket
Socket
Sign inDemoInstall

bin-links

Package Overview
Dependencies
Maintainers
6
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bin-links - npm Package Compare versions

Comparing version 2.3.0 to 3.0.0

lib/index.js

8

lib/bin-target.js
const isWindows = require('./is-windows.js')
const getPrefix = require('./get-prefix.js')
const getNodeModules = require('./get-node-modules.js')
const {dirname} = require('path')
const { dirname } = require('path')
module.exports = ({top, path}) =>
module.exports = ({ top, path }) =>
!top ? getNodeModules(path) + '/.bin'
: isWindows ? getPrefix(path)
: dirname(getPrefix(path)) + '/bin'
: isWindows ? getPrefix(path)
: dirname(getPrefix(path)) + '/bin'

@@ -5,43 +5,46 @@ // check to see if a bin is allowed to be overwritten

const binTarget = require('./bin-target.js')
const {resolve, dirname} = require('path')
const { resolve, dirname } = require('path')
const readCmdShim = require('read-cmd-shim')
const fs = require('fs')
const {promisify} = require('util')
const { promisify } = require('util')
const readlink = promisify(fs.readlink)
const checkBin = async ({bin, path, top, global, force}) => {
const checkBin = async ({ bin, path, top, global, force }) => {
// always ok to clobber when forced
// always ok to clobber local bins, or when forced
if (force || !global || !top)
if (force || !global || !top) {
return
}
// ok, need to make sure, then
const target = resolve(binTarget({path, top}), bin)
const target = resolve(binTarget({ path, top }), bin)
path = resolve(path)
return isWindows ? checkShim({target, path}) : checkLink({target, path})
return isWindows ? checkShim({ target, path }) : checkLink({ target, path })
}
// only enoent is allowed. anything else is a problem.
const handleReadLinkError = async ({er, target}) =>
const handleReadLinkError = async ({ er, target }) =>
er.code === 'ENOENT' ? null
: failEEXIST({target})
: failEEXIST({ target })
const checkLink = async ({target, path}) => {
const checkLink = async ({ target, path }) => {
const current = await readlink(target)
.catch(er => handleReadLinkError({er, target}))
.catch(er => handleReadLinkError({ er, target }))
if (!current)
if (!current) {
return
}
const resolved = resolve(dirname(target), current)
if (resolved.toLowerCase().indexOf(path.toLowerCase()) !== 0)
return failEEXIST({target})
if (resolved.toLowerCase().indexOf(path.toLowerCase()) !== 0) {
return failEEXIST({ target })
}
}
const handleReadCmdShimError = ({er, target}) =>
const handleReadCmdShimError = ({ er, target }) =>
er.code === 'ENOENT' ? null
: failEEXIST({target})
: failEEXIST({ target })
const failEEXIST = ({target}) =>
const failEEXIST = ({ target }) =>
Promise.reject(Object.assign(new Error('EEXIST: file already exists'), {

@@ -52,3 +55,3 @@ path: target,

const checkShim = async ({target, path}) => {
const checkShim = async ({ target, path }) => {
const shims = [

@@ -61,11 +64,13 @@ target,

const current = await readCmdShim(target)
.catch(er => handleReadCmdShimError({er, target}))
.catch(er => handleReadCmdShimError({ er, target }))
if (!current)
if (!current) {
return
}
const resolved = resolve(dirname(target), current.replace(/\\/g, '/'))
if (resolved.toLowerCase().indexOf(path.toLowerCase()) !== 0)
return failEEXIST({target})
if (resolved.toLowerCase().indexOf(path.toLowerCase()) !== 0) {
return failEEXIST({ target })
}
}))

@@ -72,0 +77,0 @@ }

@@ -6,12 +6,14 @@ const checkBin = require('./check-bin.js')

// always ok to clobber local bins, or when forced
if (force || !global || !top)
if (force || !global || !top) {
return
}
pkg = normalize(pkg)
if (!pkg.bin)
if (!pkg.bin) {
return
}
await Promise.all(Object.keys(pkg.bin)
.map(bin => checkBin({bin, path, top, global, force})))
.map(bin => checkBin({ bin, path, top, global, force })))
}
module.exports = checkBins

@@ -5,8 +5,9 @@ // we know it's global and/or not top, so the path has to be

const {dirname, basename} = require('path')
const { dirname, basename } = require('path')
// this gets called a lot and can't change, so memoize it
const memo = new Map()
module.exports = path => {
if (memo.has(path))
if (memo.has(path)) {
return memo.get(path)
}

@@ -13,0 +14,0 @@ const scopeOrNm = dirname(path)

@@ -6,10 +6,11 @@ // get all the paths that are (or might be) installed for a given pkg

const manTarget = require('./man-target.js')
const {resolve, basename} = require('path')
const { resolve, basename } = require('path')
const isWindows = require('./is-windows.js')
module.exports = ({path, pkg, global, top}) => {
if (top && !global)
module.exports = ({ path, pkg, global, top }) => {
if (top && !global) {
return []
}
const binSet = []
const binTarg = binTarget({path, top})
const binTarg = binTarget({ path, top })
if (pkg.bin) {

@@ -26,3 +27,3 @@ for (const bin of Object.keys(pkg.bin)) {

const manTarg = manTarget({path, top})
const manTarg = manTarget({ path, top })
const manSet = []

@@ -33,4 +34,5 @@ if (manTarg && pkg.man && Array.isArray(pkg.man) && pkg.man.length) {

// invalid entries invalidate the entire man set
if (!parseMan)
if (!parseMan) {
return binSet
}

@@ -43,4 +45,5 @@ const stem = parseMan[1]

/* istanbul ignore if - should be impossible */
if (absFrom.indexOf(path) !== 0)
if (absFrom.indexOf(path) !== 0) {
return binSet
}

@@ -47,0 +50,0 @@ manSet.push(resolve(manTarg, 'man' + sxn, base))

@@ -1,3 +0,3 @@

const {dirname} = require('path')
const { dirname } = require('path')
const getNodeModules = require('./get-node-modules.js')
module.exports = path => dirname(getNodeModules(path))

@@ -5,6 +5,6 @@ const linkGently = require('./link-gently.js')

// linking bins is simple. just symlink, and if we linked it, fix the bin up
const linkBin = ({path, to, from, absFrom, force}) =>
linkGently({path, to, from, absFrom, force})
const linkBin = ({ path, to, from, absFrom, force }) =>
linkGently({ path, to, from, absFrom, force })
.then(linked => linked && fixBin(absFrom))
module.exports = linkBin

@@ -7,8 +7,9 @@ const isWindows = require('./is-windows.js')

const linkBins = ({path, pkg, top, force}) => {
const linkBins = ({ path, pkg, top, force }) => {
pkg = normalize(pkg)
if (!pkg.bin)
if (!pkg.bin) {
return Promise.resolve([])
}
const promises = []
const target = binTarget({path, top})
const target = binTarget({ path, top })
for (const [key, val] of Object.entries(pkg.bin)) {

@@ -18,3 +19,3 @@ const to = resolve(target, key)

const from = relative(dirname(to), absFrom)
promises.push(linkBin({path, from, to, absFrom, force}))
promises.push(linkBin({ path, from, to, absFrom, force }))
}

@@ -21,0 +22,0 @@ return Promise.all(promises)

@@ -14,3 +14,7 @@ // if the thing isn't there, skip it

const lstat = promisify(fs.lstat)
const throwNonEnoent = er => { if (er.code !== 'ENOENT') throw er }
const throwNonEnoent = er => {
if (er.code !== 'ENOENT') {
throw er
}
}

@@ -28,7 +32,8 @@ // even in --force mode, we never create a link over a link we've

const SKIP = Symbol('skip - missing or already installed')
const CLOBBER = Symbol('clobber - ours or in forceful mode')
const CLOBBER = Symbol('clobber - ours or in forceful mode')
const linkGently = async ({path, to, from, absFrom, force}) => {
if (seen.has(to))
const linkGently = async ({ path, to, from, absFrom, force }) => {
if (seen.has(to)) {
return true
}
seen.add(to)

@@ -45,17 +50,21 @@

// not present in package, skip it
if (!stFrom)
if (!stFrom) {
return SKIP
}
// exists! maybe clobber if we can
if (stTo) {
if (!stTo.isSymbolicLink())
if (!stTo.isSymbolicLink()) {
return force && rm(to).then(() => CLOBBER)
}
return readlink(to).then(target => {
if (target === from)
return SKIP // skip it, already set up like we want it.
if (target === from) {
return SKIP
} // skip it, already set up like we want it.
target = resolve(dirname(to), target)
if (target.indexOf(path) === 0 || force)
if (target.indexOf(path) === 0 || force) {
return rm(to).then(() => CLOBBER)
}
})

@@ -67,11 +76,13 @@ } else {

})
.then(skipOrClobber => {
if (skipOrClobber === SKIP)
return false
return symlink(from, to, 'file').catch(er => {
if (skipOrClobber === CLOBBER || force)
return rm(to).then(() => symlink(from, to, 'file'))
throw er
}).then(() => true)
})
.then(skipOrClobber => {
if (skipOrClobber === SKIP) {
return false
}
return symlink(from, to, 'file').catch(er => {
if (skipOrClobber === CLOBBER || force) {
return rm(to).then(() => symlink(from, to, 'file'))
}
throw er
}).then(() => true)
})
}

@@ -78,0 +89,0 @@

@@ -5,6 +5,7 @@ const { dirname, relative, join, resolve, basename } = require('path')

const linkMans = ({path, pkg, top, force}) => {
const target = manTarget({path, top})
if (!target || !pkg.man || !Array.isArray(pkg.man) || !pkg.man.length)
const linkMans = ({ path, pkg, top, force }) => {
const target = manTarget({ path, top })
if (!target || !pkg.man || !Array.isArray(pkg.man) || !pkg.man.length) {
return Promise.resolve([])
}

@@ -48,3 +49,3 @@ // break any links to c:\\blah or /foo/blah or ../blah

return linkGently({from, to, path, absFrom, force})
return linkGently({ from, to, path, absFrom, force })
}))

@@ -51,0 +52,0 @@ }

const isWindows = require('./is-windows.js')
const getPrefix = require('./get-prefix.js')
const {dirname} = require('path')
const { dirname } = require('path')
module.exports = ({top, path}) => !top || isWindows ? null
module.exports = ({ top, path }) => !top || isWindows ? null
: dirname(getPrefix(path)) + '/share/man'

@@ -5,3 +5,7 @@ const { promisify } = require('util')

const lstat = promisify(fs.lstat)
const throwNonEnoent = er => { if (er.code !== 'ENOENT') throw er }
const throwNonEnoent = er => {
if (er.code !== 'ENOENT') {
throw er
}
}

@@ -19,3 +23,3 @@ const cmdShim = require('cmd-shim')

const failEEXIST = ({path, to, from}) =>
const failEEXIST = ({ path, to, from }) =>
Promise.reject(Object.assign(new Error('EEXIST: file already exists'), {

@@ -27,9 +31,9 @@ path: to,

const handleReadCmdShimError = ({er, from, to}) =>
const handleReadCmdShimError = ({ er, from, to }) =>
er.code === 'ENOENT' ? null
: er.code === 'ENOTASHIM' ? failEEXIST({from, to})
: er.code === 'ENOTASHIM' ? failEEXIST({ from, to })
: Promise.reject(er)
const SKIP = Symbol('skip - missing or already installed')
const shimBin = ({path, to, from, absFrom, force}) => {
const shimBin = ({ path, to, from, absFrom, force }) => {
const shims = [

@@ -42,4 +46,5 @@ to,

for (const shim of shims) {
if (seen.has(shim))
if (seen.has(shim)) {
return true
}
seen.add(shim)

@@ -52,26 +57,25 @@ }

].map(f => lstat(f).catch(throwNonEnoent))).then((stats) => {
const [
stToBase,
stToCmd,
stToPs1,
stFrom,
] = stats
if (!stFrom)
const [, , , stFrom] = stats
if (!stFrom) {
return SKIP
}
if (force)
if (force) {
return
}
return Promise.all(shims.map((s, i) => [s, stats[i]]).map(([s, st]) => {
if (!st)
if (!st) {
return
}
return readCmdShim(s)
.then(target => {
target = resolve(dirname(to), target)
if (target.indexOf(resolve(path)) !== 0)
return failEEXIST({from, to, path})
}, er => handleReadCmdShimError({er, from, to}))
if (target.indexOf(resolve(path)) !== 0) {
return failEEXIST({ from, to, path })
}
}, er => handleReadCmdShimError({ er, from, to }))
}))
})
.then(skip => skip !== SKIP && doShim(absFrom, to))
.then(skip => skip !== SKIP && doShim(absFrom, to))
}

@@ -78,0 +82,0 @@

{
"name": "bin-links",
"version": "2.3.0",
"version": "3.0.0",
"description": "JavaScript package binary linker",
"main": "index.js",
"main": "./lib/index.js",
"scripts": {
"preversion": "npm t",
"preversion": "npm test",
"postversion": "npm publish",
"prepublishOnly": "git push --follow-tags",
"prepublishOnly": "git push origin --follow-tags",
"snap": "tap",
"test": "tap"
"test": "tap",
"lint": "eslint '**/*.js'",
"postlint": "npm-template-check",
"lintfix": "npm run lint -- --fix",
"posttest": "npm run lint"
},

@@ -29,5 +33,6 @@ "repository": {

"rimraf": "^3.0.0",
"write-file-atomic": "^3.0.3"
"write-file-atomic": "^4.0.0"
},
"devDependencies": {
"@npmcli/template-oss": "^2.5.0",
"mkdirp": "^1.0.3",

@@ -42,8 +47,13 @@ "require-inject": "^1.4.4",

"files": [
"index.js",
"lib/*.js"
"bin",
"lib"
],
"engines": {
"node": ">=10"
"node": "^12.13.0 || ^14.15.0 || >=16"
},
"author": "GitHub Inc.",
"templateOSS": {
"windowsCI": false,
"version": "2.5.0"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc