UNPKG

npm-link-up

Version:

Use this package to link your projects together for local development.

311 lines (310 loc) 11.9 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); 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'); exports.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.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.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.getUniqueList = (a) => { return Array.from(new Set(a)); }; exports.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 exports.getUniqueList(exports.flattenDeep([list, packages, deps]).filter(Boolean)); }; exports.getSearchRootsFromNluConf = (nluJSON) => { let searchRoots = [nluJSON.searchRoots, nluJSON.searchRoot]; const searchRootsReduced = []; exports.getUniqueList(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.flattenDeep = function (a) { return a.reduce((acc, val) => Array.isArray(val) ? acc.concat(exports.flattenDeep(val)) : acc.concat(val), []); }; exports.getProdKeys = (pkg) => { return Object.keys(pkg.dependencies || {}); }; exports.getDevKeys = (pkg) => { return Object.keys(pkg.dependencies || {}) .concat(Object.keys(pkg.devDependencies || {})) .concat(Object.keys(pkg.optionalDependencies || {})); }; exports.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.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); }, {}); }; 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; } }); }; exports.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); }); }); };