nlu
Version:
Use this package to link your projects together for local development.
222 lines (221 loc) • 9.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const util = require("util");
const cp = require("child_process");
const chalk_1 = require("chalk");
const async = require("async");
const logging_1 = require("./logging");
exports.runNPMLink = (map, opts, cb) => {
const keys = Object.keys(map);
if (keys.length < 1) {
return process.nextTick(cb, new Error('NLU could not find any dependencies on the filesystem;' +
' perhaps broaden your search using searchRoots.'));
}
if (opts.dry_run) {
logging_1.default.warning('given the --treeify option passed at the command line, npm-link-up will only print out the dependency tree and exit.');
logging_1.default.veryGood('the following is a complete list of recursively related dependencies:\n');
logging_1.default.veryGood(util.inspect(Object.keys(map)));
return process.nextTick(cb);
}
if (opts.verbosity > 2) {
logging_1.default.good('Dependency map:');
}
Object.keys(map).forEach(function (k) {
if (opts.verbosity > 2) {
logging_1.default.info('Info for project:', chalk_1.default.bold(k));
console.log(chalk_1.default.green.bold(util.inspect(map[k])));
console.log();
}
});
const isAllLinked = function () {
return Object.keys(map).every(function (k) {
return map[k].isLinked;
});
};
const getCountOfUnlinkedDeps = function (dep) {
return dep.deps.filter(function (d) {
if (!map[d]) {
logging_1.default.warning(`there is no dependency named ${d} in the map.`);
return false;
}
return !map[d].isLinked;
})
.length;
};
const findNextDep = function () {
let dep;
let count = null;
for (let name in map) {
if (map.hasOwnProperty(name)) {
let d = map[name];
if (!d.isLinked) {
if (!count) {
dep = d;
count = dep.deps.length;
}
else if (getCountOfUnlinkedDeps(d) < count) {
dep = d;
count = dep.deps.length;
}
}
}
}
if (!dep) {
logging_1.default.error('Internal implementation error => no dep found,\nbut there should be at least one yet-to-be-linked dep.');
return process.exit(1);
}
return dep;
};
const getNPMLinkList = (deps) => {
return deps.filter(function (d) {
if (!map[d]) {
logging_1.default.warning('Map for key => "' + d + '" is not defined.');
return false;
}
return map[d] && map[d].isLinked;
})
.map(function (d) {
const path = map[d].path;
const bin = map[d].bin;
return ` mkdir -p node_modules && rm -rf "node_modules/${d}" && mkdir -p "node_modules/${d}" && rm -rf "node_modules/${d}" ` +
` && ln -s "${path}" "node_modules/${d}" ` + ` ${getBinMap(bin, path, d)}`;
});
};
const getBinMap = function (bin, path, name) {
if (!bin) {
return '';
}
if (typeof bin === 'string') {
return ` && mkdir -p "node_modules/.bin" && ln -s "${path}/${bin}" "node_modules/.bin/${name}" `;
}
const keys = Object.keys(bin);
if (keys.length < 1) {
return '';
}
return ` && ` + keys.map(function (k) {
return ` mkdir -p node_modules/.bin && ln -sf "${path}/${bin[k]}" "node_modules/.bin/${k}" `;
})
.join(' && ');
};
const getCommandListOfLinked = function (name) {
const path = map[name] && map[name].path;
const bin = map[name] && map[name].bin;
if (!path) {
logging_1.default.error(`missing path for dependency with name "${name}"`);
return process.exit(1);
}
return Object.keys(map).filter(function (k) {
return map[k].isLinked && map[k].deps.includes(name);
})
.map(function (k) {
const p = `${map[k].path}`;
return ` cd ${p} && mkdir -p node_modules && rm -rf "node_modules/${name}" && mkdir -p "node_modules/${name}" ` +
` && rm -rf "node_modules/${name}" && ln -s "${path}" "node_modules/${name}" ` + ` ${getBinMap(bin, path, name)} `;
});
};
const getInstallCommand = function (dep) {
if (!opts.no_install && (dep.runInstall || opts.install_all || (dep.isMainProject && opts.install_main))) {
const installProd = opts.production ? ' --production ' : '';
return ` && rm -rf node_modules && npm install --cache-min 999999 --loglevel=warn ${installProd}`;
}
};
const getLinkToItselfCommand = function (dep) {
if (!opts.no_link && (opts.self_link_all || dep.linkToItself === true)) {
return ` && mkdir -p node_modules && rm -rf "node_modules/${dep.name}" && mkdir -p "node_modules/${dep.name}" ` +
` && rm -rf "node_modules/${dep.name}" ` +
` && ln -s "${dep.path}" "node_modules/${dep.name}" `;
}
};
const getGlobalLinkCommand = function (dep) {
if (!opts.no_link && (opts.link_all || (dep.isMainProject && opts.link_main))) {
const installProd = opts.production ? ' --production ' : '';
return ` && mkdir -p node_modules/.bin && npm link --cache-min 999999 -f ${installProd}`;
}
};
async.until(isAllLinked, function (cb) {
if (opts.verbosity > 2) {
logging_1.default.info(`Searching for next dep to run.`);
}
const dep = findNextDep();
if (opts.verbosity > 1) {
logging_1.default.good(`Processing dep with name => '${chalk_1.default.bold(dep.name)}'.`);
}
const deps = getNPMLinkList(dep.deps);
const links = deps.length > 0 ? '&& ' + deps.join(' && ') : '';
const script = [
`cd ${dep.path}`,
getInstallCommand(dep),
getGlobalLinkCommand(dep),
links,
getLinkToItselfCommand(dep)
]
.filter(Boolean)
.join(' ');
if (opts.verbosity > 1) {
logging_1.default.info(`First-pass script is => "${chalk_1.default.blueBright.bold(script)}"`);
}
const k = cp.spawn('bash', [], {
env: Object.assign({}, process.env, {
NPM_LINK_UP: 'yes'
})
});
k.stdin.end(script);
k.stdout.setEncoding('utf8');
k.stderr.setEncoding('utf8');
k.stderr.pipe(process.stderr);
if (opts.verbosity > 2) {
k.stdout.pipe(process.stdout);
}
let stderr = '';
k.stderr.on('data', function (d) {
stderr += d;
});
k.once('error', cb);
k.once('exit', function (code) {
if (code > 0 && /ERR/i.test(stderr)) {
logging_1.default.error(`Dep with name "${dep.name}" is done, but with an error.`);
return cb({ code, dep, error: stderr });
}
dep.isLinked = map[dep.name].isLinked = true;
const linkPreviouslyUnlinked = function (cb) {
const cmds = getCommandListOfLinked(dep.name);
if (!cmds.length) {
return process.nextTick(cb);
}
const cmd = cmds.join(' && ');
if (opts.verbosity > 1) {
logging_1.default.info(`Running this command for "${chalk_1.default.bold(dep.name)}" => '${chalk_1.default.blueBright(cmd)}'.`);
}
const k = cp.spawn('bash', [], {
env: Object.assign({}, process.env, {
NPM_LINK_UP: 'yes'
})
});
k.stdin.write(cmd);
k.stdout.setEncoding('utf8');
k.stderr.setEncoding('utf8');
k.stderr.pipe(process.stderr, { end: false });
if (opts.verbosity > 2) {
k.stdout.pipe(process.stdout, { end: false });
}
k.stdin.end();
k.once('exit', cb);
};
linkPreviouslyUnlinked(function (err) {
if (err) {
logging_1.default.error(`Dep with name "${dep.name}" is done, but with an error => `, err.message || err);
}
else {
if (opts.verbosity > 1) {
logging_1.default.veryGood(`Dep with name '${chalk_1.default.bold(dep.name)}' is done.`);
}
}
cb(err, {
code: code,
dep: dep,
error: stderr
});
});
});
}, cb);
};