npm-link-up
Version:
Use this package to link your projects together for local development.
352 lines (351 loc) • 14.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const util = require("util");
const assert = require("assert");
const path = require("path");
const chalk_1 = require("chalk");
const dashdash = require('dashdash');
const async = require("async");
const residence = require("residence");
const cwd = process.cwd();
let root = residence.findProjectRoot(cwd);
const treeify = require('treeify');
const mkdirp = require("mkdirp");
const find_projects_1 = require("../../find-projects");
const map_paths_1 = require("../../map-paths");
const cache_clean_1 = require("../../cache-clean");
const logging_1 = require("../../logging");
const handle_options_1 = require("../../handle-options");
const cmd_line_opts_1 = require("./cmd-line-opts");
const run_link_1 = require("../../run-link");
const create_visual_tree_1 = require("../../create-visual-tree");
const get_clean_final_map_1 = require("../../get-clean-final-map");
const search_queue_1 = require("../../search-queue");
const utils_1 = require("../../utils");
process.once('exit', function (code) {
if (code !== 0) {
logging_1.default.warn('NLU is exiting with code:', code, '\n');
}
else {
logging_1.default.info('NLU is exiting with code:', code, '\n');
}
});
const allowUnknown = process.argv.indexOf('--allow-unknown') > 0;
let opts, globalConf, parser = dashdash.createParser({ options: cmd_line_opts_1.default, allowUnknown });
try {
opts = parser.parse(process.argv);
}
catch (e) {
logging_1.default.error(chalk_1.default.magenta('CLI parsing error:'), chalk_1.default.magentaBright.bold(e.message));
process.exit(1);
}
if (opts.help) {
let help = parser.help({ includeEnv: true }).trimRight();
console.log('usage: nlu run [OPTIONS]\n'
+ 'options:\n'
+ help);
process.exit(0);
}
try {
globalConf = require(utils_1.globalConfigFilePath);
}
catch (err) {
logging_1.default.warn('Could not load global config');
globalConf = {};
}
if (!(globalConf && typeof globalConf === 'object')) {
globalConf = {};
}
if (Array.isArray(globalConf)) {
globalConf = {};
}
const { nluFilePath, nluConfigRoot } = utils_1.handleConfigCLIOpt(cwd, opts);
let pkg, conf, hasNLUJSONFile = false;
try {
conf = require(nluFilePath);
opts.umbrella = opts.umbrella || Boolean(conf.umbrella);
hasNLUJSONFile = true;
}
catch (e) {
if (!opts.umbrella) {
logging_1.default.error('Could not load your .nlu.json file at this path:', chalk_1.default.bold(nluFilePath));
logging_1.default.error('Your project root is supposedly here:', chalk_1.default.bold(root));
logging_1.default.error(chalk_1.default.magentaBright(e.message));
process.exit(1);
}
opts.all_packages = true;
conf = {
'npm-link-up': true,
linkable: false,
searchRoots: ['.'],
list: []
};
}
if (!root) {
if (!(opts.all_packages || opts.umbrella)) {
logging_1.default.warn('You do not appear to be within an NPM project (no package.json could be found).');
logging_1.default.warn(' => Your present working directory is =>', chalk_1.default.magenta.bold(cwd));
logging_1.default.warn('Perhaps you meant to use the', chalk_1.default.bold('--umbrella'), 'CLI option?');
process.exit(1);
}
root = cwd;
}
try {
pkg = require(path.resolve(root + '/package.json'));
}
catch (e) {
if (!(opts.umbrella || opts.all_packages)) {
logging_1.default.error('Bizarrely, you do not seem to have a "package.json" file in the root of your project.');
logging_1.default.error('Your project root is supposedly here:', chalk_1.default.magenta(root));
logging_1.default.error(e.message);
process.exit(1);
}
pkg = {
name: '(root)'
};
}
if (Array.isArray(conf.packages)) {
throw chalk_1.default.magenta(`"packages" property should be an object but no an array => ${util.inspect(conf)}`);
}
if ('packages' in conf) {
assert.strictEqual(typeof conf.packages, 'object', `packages" property should be an object => ${util.inspect(conf)}`);
}
conf.packages = conf.packages || {};
conf.localSettings = conf.localSettings || {};
if (!(conf.localSettings && typeof conf.localSettings === 'object')) {
conf.localSettings = {};
}
if (Array.isArray(conf.localSettings)) {
conf.localSettings = {};
}
opts = Object.assign({}, utils_1.mapConfigObject(globalConf), utils_1.mapConfigObject(conf.localSettings), opts);
if (!utils_1.validateOptions(opts)) {
logging_1.default.error(chalk_1.default.bold('Your command line arguments were invalid, try:', chalk_1.default.magentaBright('nlu run --help')));
process.exit(1);
}
if (!utils_1.validateConfigFile(conf)) {
console.error();
if (!opts.override) {
logging_1.default.error(chalk_1.default.redBright('Your .nlu.json config file appears to be invalid. To override this, use --override.'));
process.exit(1);
}
}
const mainProjectName = pkg.name;
if (!mainProjectName) {
logging_1.default.error('Ummmm, your package.json file does not have a name property. Fatal.');
process.exit(1);
}
if (opts.verbosity > 0) {
logging_1.default.info(`We are running the "npm-link-up" tool for your project named "${chalk_1.default.magenta(mainProjectName)}".`);
}
const productionDepsKeys = utils_1.getProdKeys(pkg);
const allDepsKeys = utils_1.getDevKeys(pkg);
const list = utils_1.getDepsListFromNluJSON(conf);
if (list.length < 1) {
if (!opts.all_packages) {
logging_1.default.error(chalk_1.default.magenta(' => You do not have any dependencies listed in your .nlu.json file.'));
logging_1.default.error(chalk_1.default.cyan.bold(util.inspect(conf)));
process.exit(1);
}
}
const searchRoots = handle_options_1.getSearchRoots(opts, conf);
if (searchRoots.length < 1) {
logging_1.default.error(chalk_1.default.red('No search-roots provided.'));
logging_1.default.error('You should either update your .nlu.json config to have a searchRoots array.');
logging_1.default.error('Or you can use the --search-root=X option at the command line.');
logging_1.default.error(chalk_1.default.bold('Conveniently, you may include environment variables in your search root strings.'));
logging_1.default.error('For example, to search everything starting from $HOME, you can use --search-root=$HOME option at the command line.');
logging_1.default.error('However, it is highly recommended to choose a subdirectory from $HOME, since searching that many files can take some time.');
process.exit(1);
}
const inListButNotInDeps = list.filter(item => {
return !allDepsKeys.includes(item);
});
inListButNotInDeps.forEach(item => {
if (opts.verbosity > 1) {
logging_1.default.warning('warning, the following item was listed in your .nlu.json file, ' +
'but is not listed in your package.json dependencies => "' + item + '".');
}
});
const originalList = list.slice(0);
if (!list.includes(mainProjectName)) {
if (!opts.umbrella) {
list.push(mainProjectName);
}
}
const totalList = new Map();
list.forEach(l => {
totalList.set(l, true);
});
const ignore = handle_options_1.getIgnore(conf, opts);
originalList.forEach((item) => {
if (opts.verbosity > 0) {
logging_1.default.info(`The following dep will be linked to this project => "${chalk_1.default.gray.bold(item)}".`);
}
});
const map = {};
if (opts.dry_run) {
logging_1.default.warning(chalk_1.default.bold.gray('Because --dry-run was used, we are not actually linking projects together.'));
}
const mainDep = map[root] = {
name: mainProjectName,
bin: null,
hasNLUJSONFile,
isMainProject: true,
linkToItself: conf.linkToItself,
runInstall: conf.alwaysReinstall,
path: root,
deps: list,
package: pkg,
searchRoots: null,
installedSet: new Set(),
linkedSet: {}
};
async.autoInject({
readNodeModulesFolders(cb) {
const nm = path.resolve(root + '/node_modules');
const keys = opts.production ? productionDepsKeys : allDepsKeys;
utils_1.determineIfReinstallIsNeeded(nm, mainDep, keys, opts, (err, val) => {
if (err) {
return cb(err);
}
if (val === true) {
mainDep.runInstall = true;
opts.install_main = true;
}
cb(null);
});
},
ensureNodeModules(readNodeModulesFolders, cb) {
if (!readNodeModulesFolders) {
return process.nextTick(cb);
}
opts.install_main = true;
mkdirp(path.resolve(root + '/node_modules'), cb);
},
npmCacheClean(cb) {
if (opts.dry_run) {
return process.nextTick(cb);
}
if (!opts.clear_all_caches) {
return process.nextTick(cb);
}
logging_1.default.info(`Cleaning the NPM cache.`);
cache_clean_1.cleanCache(cb);
},
mapSearchRoots(npmCacheClean, cb) {
opts.verbosity > 3 && logging_1.default.info(`Mapping original search roots from your root project's "searchRoots" property.`);
map_paths_1.mapPaths(searchRoots, nluConfigRoot, (err, roots) => {
if (err) {
return cb(err);
}
mainDep.searchRoots = roots.slice(0);
cb(err, roots);
});
},
findItems(mapSearchRoots, cb) {
let searchRoots = mapSearchRoots.slice(0);
if (opts.verbosity > 1) {
logging_1.default.info('Beginning to search for NPM projects on your filesystem.');
}
if (opts.verbosity > 3) {
logging_1.default.info('NPM-Link-Up will be searching these roots for relevant projects:');
logging_1.default.info(chalk_1.default.magenta(util.inspect(searchRoots)));
}
if (opts.verbosity > 2) {
logging_1.default.warning('Note that NPM-Link-Up may come across a project of yours that needs to search in directories');
logging_1.default.warning('not covered by your original search roots, and these new directories will be searched as well.');
}
const status = { searching: true };
const findProject = find_projects_1.makeFindProject(mainProjectName, totalList, map, ignore, opts, status, conf);
searchRoots.forEach(sr => {
search_queue_1.q.push(cb => findProject(sr, cb));
});
if (search_queue_1.q.idle()) {
return process.nextTick(cb, new Error('For some reason, no paths/items went onto the search queue.'));
}
let first = true;
search_queue_1.q.error = search_queue_1.q.drain = (err) => {
if (err) {
status.searching = false;
logging_1.default.error(chalk_1.default.magenta('There was a search queue processing error.'));
}
if (first) {
search_queue_1.q.kill();
cb(err, { actuallyRan: true });
}
first = false;
};
},
runUtility(findItems, cb) {
const unfound = Array.from(totalList.keys()).filter(v => {
return !map[v];
});
if (false && unfound.length > 0) {
logging_1.default.warn(`The following packages could ${chalk_1.default.bold('not')} be located:`);
for (let i of unfound.keys()) {
logging_1.default.warn(chalk_1.default.bold(String(i + 1), chalk_1.default.bold.green(unfound[i])));
}
if (!opts.allow_missing) {
console.error();
logging_1.default.warn('The following paths (and their subdirectories) were searched:');
find_projects_1.rootPaths.forEach((v, i) => {
console.info('\t\t', `${chalk_1.default.blueBright.bold(String(i + 1))}.`, chalk_1.default.blueBright(v));
});
console.error();
logging_1.default.error('because the --allow-missing flag was not use, we are exiting.');
process.exit(1);
}
}
let cleanMap;
try {
if (opts.all_packages) {
cleanMap = get_clean_final_map_1.getCleanMapOfOnlyPackagesWithNluJSONFiles(mainProjectName, map);
}
else {
cleanMap = get_clean_final_map_1.getCleanMap(mainDep, map, opts);
}
}
catch (err) {
return process.nextTick(cb, err);
}
if (opts.dry_run) {
return process.nextTick(() => {
cb(null, cleanMap);
});
}
logging_1.default.info('Beginning to actually link projects together...');
run_link_1.runNPMLink(cleanMap, opts, err => {
cb(err, cleanMap);
});
}
}, (err, results) => {
if (err) {
logging_1.default.error('There was an error while running nlu run/add:');
logging_1.default.error(chalk_1.default.magenta(util.inspect(err.message || err)));
return process.exit(1);
}
if (results.runUtility) {
logging_1.default.good(chalk_1.default.green.underline('NPM-Link-Up run was successful. All done.'));
}
const cleanMap = results.runUtility;
if (cleanMap && typeof cleanMap === 'object') {
const treeObj = create_visual_tree_1.createTree(cleanMap, mainDep.path, mainDep, opts);
const treeString = treeify.asTree(treeObj, true);
const formattedStr = [''].concat(String(treeString).split('\n')).map(function (line) {
return ' look here: \t' + line;
}).concat('');
if (opts.verbosity > 1) {
console.log();
logging_1.default.info(chalk_1.default.cyan.bold('NPM-Link-Up results as a visual:'), '\n');
console.log(chalk_1.default.white(formattedStr.join('\n')));
}
}
else {
logging_1.default.warn('Missing map object; could not create dependency tree visualization.');
}
setTimeout(function () {
process.exit(0);
}, 100);
});