module-link-unlink
Version:
Link and unlink a module with saving and restoring the previous install
60 lines (59 loc) • 2.29 kB
JavaScript
import fs from 'fs';
import { link } from 'link-unlink';
import { Lock } from 'lock';
import mkdirp from 'mkdirp-classic';
import path from 'path';
import Queue from 'queue-cb';
const lock = Lock();
function linkBin(src, binPath, nodeModules, binName, callback) {
const binFullPath = path.join.apply(null, [
src,
...binPath.split('/')
]);
const destBin = path.join(nodeModules, '.bin', binName);
fs.stat(binFullPath, (err)=>{
if (!err) return link(binFullPath, destBin, callback);
console.log(`bin not found: ${binFullPath}. Skipping`);
callback();
});
}
function worker(src, nodeModules, callback) {
lock([
src,
nodeModules
], (release)=>{
callback = release(callback);
try {
const pkg = JSON.parse(fs.readFileSync(path.join(src, 'package.json'), 'utf8'));
const dest = path.join.apply(null, [
nodeModules,
...pkg.name.split('/')
]);
mkdirp(path.dirname(dest), (err)=>{
if (err) return callback(err);
const queue = new Queue();
queue.defer((cb)=>link(src, dest, (err)=>cb(err)));
if (typeof pkg.bin === 'string') {
const binName = pkg.name;
const binPath = pkg.bin;
queue.defer((cb)=>linkBin(src, binPath, nodeModules, binName, (err)=>cb(err)));
} else {
for(const binName in pkg.bin){
const bn = binName;
const bp = pkg.bin[bn];
queue.defer((cb)=>linkBin(src, bp, nodeModules, bn, (err)=>cb(err)));
}
}
queue.await((err)=>{
err ? callback(err) : callback(undefined, dest);
});
});
} catch (err) {
return callback(err instanceof Error ? err : new Error(String(err)));
}
});
}
export default function linkModule(src, nodeModules, callback) {
if (typeof callback === 'function') return worker(src, nodeModules, callback);
return new Promise((resolve, reject)=>worker(src, nodeModules, (err, restore)=>err ? reject(err) : resolve(restore)));
}