Comparing version 2.3.0 to 3.0.0
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" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
21213
461
1
0
4
+ Addedwrite-file-atomic@4.0.2(transitive)
- Removedis-typedarray@1.0.0(transitive)
- Removedtypedarray-to-buffer@3.1.5(transitive)
- Removedwrite-file-atomic@3.0.3(transitive)
Updatedwrite-file-atomic@^4.0.0