npm-link-up
Version:
Use this package to link your projects together for local development.
324 lines (323 loc) • 12.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.determineIfReinstallIsNeeded = exports.mapConfigObject = exports.validateOptions = exports.getDevKeys = exports.getProdKeys = exports.flattenDeep = exports.getSearchRootsFromNluConf = exports.getDepsListFromNluJSON = exports.getUniqueList = exports.validateConfigFile = exports.handleConfigCLIOpt = exports.getPath = exports.globalConfigFilePath = void 0;
const assert = require("assert");
const Ajv = require("ajv");
const schema = require('../assets/nlu.schema.json');
const logging_1 = require("./logging");
const chalk_1 = require("chalk");
const fs = require("fs");
const path = require("path");
const async = require("async");
const util = require("util");
const semver = require("semver");
const residence = require("residence");
exports.globalConfigFilePath = path.resolve(process.env.HOME + '/.nlu/global/settings.json');
const getPath = (map, dep, opts) => {
const isAccessible = (p) => {
const matched = dep.searchRoots.some(r => p.startsWith(r));
if (!matched) {
logging_1.default.error('The following dep', p, 'is not accessible for project at path:', dep.path);
}
return matched;
};
return (packageName) => {
let path = null;
for (let [key, val] of Object.entries(map)) {
if (val.name === packageName) {
if (val.path !== key) {
throw new Error('Key of map should be the name as the path in value object => ' + util.inspect(val));
}
if (!isAccessible(val.path)) {
continue;
}
if (path) {
throw new Error('Path should only be defined once.');
}
path = val.path;
}
}
if (!path && !opts.allow_missing) {
logging_1.default.error(`No package could be located on disk for package name: '${chalk_1.default.bold(packageName)}'.`);
logging_1.default.error(`To overcome this problem, either use the ${chalk_1.default.bold('--allow-missing')} flag, ` +
'or include the desired package in your searchRoots in .nlu.json.');
process.exit(1);
}
return path;
};
};
exports.getPath = getPath;
const handleConfigCLIOpt = (cwd, opts) => {
const configOpt = String(opts.config || '');
let isFile = null, nluConfigRoot = '', nluFilePath = String(opts.config || '');
if (configOpt) {
if (!path.isAbsolute(opts.config)) {
nluFilePath = path.resolve(cwd + '/' + String(configOpt || ''));
}
try {
assert(fs.statSync(opts.config).isFile(), 'config path is not a file.');
isFile = true;
}
catch (err) {
isFile = false;
nluFilePath = path.resolve(cwd + '/' + String(configOpt || '') + '/.nlu.json');
try {
assert(fs.statSync(opts.config).isFile(), 'config path is not a file.');
}
catch (err) {
logging_1.default.error('You declared a config path using the -c option, but the following path is not a file:', chalk_1.default.bold(opts.config));
throw chalk_1.default.magenta(err.message);
}
}
}
if (nluFilePath) {
nluConfigRoot = path.resolve(nluFilePath);
if (isFile) {
nluConfigRoot = path.dirname(nluConfigRoot);
}
}
else {
nluConfigRoot = residence.findRootDir(cwd, '.nlu.json');
}
if (!nluConfigRoot) {
nluConfigRoot = cwd;
}
if (!nluFilePath) {
nluFilePath = path.resolve(nluConfigRoot + '/.nlu.json');
}
return { nluFilePath, nluConfigRoot };
};
exports.handleConfigCLIOpt = handleConfigCLIOpt;
const validateConfigFile = (data) => {
try {
const ajv = new Ajv({ allErrors: false });
const validate = ajv.compile(schema);
return true;
const valid = validate(data);
if (!valid)
console.error(validate.errors);
return valid;
}
catch (err) {
logging_1.default.error(err.message);
return false;
}
};
exports.validateConfigFile = validateConfigFile;
const getUniqueList = (a) => {
return Array.from(new Set(a));
};
exports.getUniqueList = getUniqueList;
const getDepsListFromNluJSON = (nluJSON) => {
let list, deps, packages;
try {
if ('list' in nluJSON) {
assert(Array.isArray(nluJSON.list), `"list" property is not an array => '${util.inspect(nluJSON)}'`);
list = nluJSON.list;
}
if ('deps' in nluJSON) {
assert(Array.isArray(nluJSON.deps), `"deps" property is not an array => '${util.inspect(nluJSON)}'`);
deps = nluJSON.deps;
}
if ('packages' in nluJSON) {
packages = Object.keys(nluJSON.packages).filter(v => nluJSON.packages[v]);
}
}
catch (err) {
logging_1.default.error('config file was malformed:', nluJSON);
throw chalk_1.default.magenta(err.message);
}
return (0, exports.getUniqueList)((0, exports.flattenDeep)([list, packages, deps]).filter(Boolean));
};
exports.getDepsListFromNluJSON = getDepsListFromNluJSON;
const getSearchRootsFromNluConf = (nluJSON) => {
let searchRoots = [nluJSON.searchRoots, nluJSON.searchRoot];
const searchRootsReduced = [];
(0, exports.getUniqueList)((0, exports.flattenDeep)(searchRoots))
.map(d => String(d || '').trim())
.filter(Boolean)
.sort((a, b) => (a.length - b.length))
.filter((v, i, a) => {
const s = !a.some(p => {
return p.startsWith(v + '/');
});
if (s) {
searchRootsReduced.push(v);
}
});
return searchRootsReduced;
};
exports.getSearchRootsFromNluConf = getSearchRootsFromNluConf;
const flattenDeep = function (a) {
return a.reduce((acc, val) => Array.isArray(val) ? acc.concat((0, exports.flattenDeep)(val)) : acc.concat(val), []);
};
exports.flattenDeep = flattenDeep;
const getProdKeys = (pkg) => {
return Object.keys(pkg.dependencies || {});
};
exports.getProdKeys = getProdKeys;
const getDevKeys = (pkg) => {
return Object.keys(pkg.dependencies || {})
.concat(Object.keys(pkg.devDependencies || {}))
.concat(Object.keys(pkg.optionalDependencies || {}));
};
exports.getDevKeys = getDevKeys;
const validateOptions = (opts) => {
try {
assert(opts.verbosity >= 1, chalk_1.default.magenta('Verbosity must be an integer between 1 and 4, inclusive'));
assert(opts.verbosity <= 4, chalk_1.default.magenta('Verbosity must be an integer between 1 and 4, inclusive'));
}
catch (err) {
logging_1.default.error(err.message);
return false;
}
return true;
};
exports.validateOptions = validateOptions;
const mapConfigObject = (obj) => {
return Object.keys(obj).reduce((a, b) => {
const key = String(b).replace(/[^a-zA-z]+/g, '_').toLowerCase();
return (a[key] = obj[b], a);
}, {});
};
exports.mapConfigObject = mapConfigObject;
const checkPackages = (dep, m, sym) => {
const d = dep.package.dependencies || {};
return Object.keys(d).some(v => {
const desiredVersion = d[v];
if (sym.has(v)) {
return false;
}
const installedVersion = m.get(v);
try {
if (!/.*[0-9]{1,6}\.[0-9]{1,6}\.[0-9]{1,6}/.test(desiredVersion)) {
logging_1.default.warn('The following package version did not match a semverish regex:', desiredVersion, 'for package:', v);
return false;
}
}
catch (err) {
logging_1.default.warn(err.message);
return false;
}
try {
const satisfies = semver.satisfies(installedVersion, desiredVersion);
if (!satisfies) {
logging_1.default.warn('package with name', v, 'is not satisfied. Installed version:', installedVersion, 'desired version:', desiredVersion);
}
return !satisfies;
}
catch (err) {
logging_1.default.warn(err.message);
return false;
}
});
};
const determineIfReinstallIsNeeded = (nodeModulesPath, dep, depsKeys, opts, cb) => {
const map = new Map();
const sym = new Set();
const result = {
install: false
};
if (opts.no_install) {
return process.nextTick(cb);
}
fs.readdir(nodeModulesPath, (err, originalItemsInNodeModules) => {
if (err || !Array.isArray(originalItemsInNodeModules)) {
opts.verbosity > 1 && logging_1.default.warn('Reinstalling because node_modules dir does not seem to exist in dir: ', nodeModulesPath);
return cb(null, true);
}
const orgItems = originalItemsInNodeModules.filter(v => {
return String(v).startsWith('@');
});
const topLevel = originalItemsInNodeModules.filter(v => {
return !String(v).startsWith('@') && String(v) !== '.bin';
});
const totalValid = new Set(topLevel.slice(0));
const processFolder = (name, folder, cb) => {
totalValid.add(name);
async.autoInject({
stat(cb) {
fs.lstat(folder, (err, stats) => {
if (err) {
result.install = true;
return cb(err);
}
if (stats.isSymbolicLink()) {
sym.add(name);
}
cb(null);
});
},
package(cb) {
const packageJSON = path.resolve(folder + '/package.json');
fs.readFile(packageJSON, (err, data) => {
if (err) {
result.install = true;
return cb(err);
}
try {
const version = JSON.parse(String(data)).version;
assert(version && typeof version === 'string', 'version is not defined, or not a string.');
map.set(name, version);
}
catch (err) {
result.install = true;
return cb(err);
}
cb(null);
});
}
}, cb);
};
async.autoInject({
topLevel(cb) {
async.eachLimit(topLevel, 3, (item, cb) => {
const folder = path.resolve(nodeModulesPath + '/' + item);
processFolder(item, folder, cb);
}, cb);
},
orgLevel(cb) {
async.eachLimit(orgItems, 3, (item, cb) => {
const p = path.resolve(nodeModulesPath + '/' + item);
fs.readdir(p, (err, orgtems) => {
if (err) {
result.install = true;
return cb(err);
}
if (orgtems.length < 1) {
return process.nextTick(cb);
}
async.eachLimit(orgtems, 5, (v, cb) => {
const name = item + '/' + v;
const folder = path.resolve(p + '/' + v);
processFolder(name, folder, cb);
}, cb);
});
}, cb);
}
}, (err) => {
if (err && result.install === true) {
return cb(null, true);
}
if (err) {
return cb(err, false);
}
const allThere = depsKeys.every(d => {
if (!totalValid.has(d)) {
opts.verbosity > 1 && logging_1.default.info('The following dep in package.json', d, 'did not appear to be in node_modules located here:', nodeModulesPath);
return false;
}
return true;
});
if (!allThere) {
opts.verbosity > 1 && logging_1.default.warn('Reinstalling because not all package.json dependencies exist in node_modules:', nodeModulesPath);
return cb(null, true);
}
if (checkPackages(dep, map, sym)) {
return cb(null, true);
}
cb(null, false);
});
});
};
exports.determineIfReinstallIsNeeded = determineIfReinstallIsNeeded;