UNPKG

npm-link-up

Version:

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

245 lines (244 loc) 12.4 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const assert = require("assert"); const path = require("path"); const fs = require("fs"); const async = require("async"); const chalk_1 = require("chalk"); const logging_1 = require("./logging"); const search_queue_1 = require("./search-queue"); const map_paths_1 = require("./map-paths"); const utils_1 = require("./utils"); const searchedPaths = {}; exports.rootPaths = []; const searchQueue = async.queue((task, cb) => task(cb), 8); exports.makeFindProject = function (mainProjectName, totalList, map, ignore, opts, status, conf) { const isPathSearchableBasic = (item) => { item = path.normalize(item); if (!path.isAbsolute(item)) { throw new Error('Path to be searched is not absolute:' + item); } if (searchedPaths[item]) { opts.verbosity > 2 && logging_1.default.info('already searched this path, not searching again:', chalk_1.default.bold(item)); return false; } return true; }; const isPathSearchable = function (item) { item = path.normalize(item); if (!path.isAbsolute(item)) { throw new Error('Path to be searched is not absolute:' + item); } if (searchedPaths[item]) { opts.verbosity > 2 && logging_1.default.info('already searched this path, not searching again:', chalk_1.default.bold(item)); return false; } let goodPth = ''; const keys = Object.keys(searchedPaths); const match = keys.some(pth => { if (item.startsWith(pth)) { goodPth = pth; return true; } }); if (match && opts.verbosity > 1) { logging_1.default.info(chalk_1.default.blue('path has already been covered:')); logging_1.default.info('potential new path:', chalk_1.default.bold(item)); logging_1.default.info('already searched path:', chalk_1.default.bold(goodPth)); } return match; }; const isIgnored = (pth) => { return ignore.some(r => { if (r.test(pth)) { if (opts.verbosity > 3) { logging_1.default.warning(`Path with value "${pth}" was ignored because it matched the following regex:`); logging_1.default.warning(`${r}`); } return true; } }); }; return function findProject(item, cb) { item = path.normalize(item); if (!isPathSearchableBasic(item)) { return process.nextTick(cb); } searchedPaths[item] = true; exports.rootPaths.push(item); logging_1.default.info('New path being searched:', chalk_1.default.blue(item)); (function getMarkers(dir, cb) { if (isIgnored(String(dir + '/'))) { if (opts.verbosity > 2) { logging_1.default.warning('path ignored => ', dir); } return process.nextTick(cb); } if (status.searching === false) { opts.verbosity > 2 && logging_1.default.error('There was an error so we short-circuited search.'); return process.nextTick(cb); } searchedPaths[dir] = true; searchQueue.push(callback => { fs.readdir(dir, (err, items) => { callback(); if (err) { logging_1.default.error(err.message || err); if (String(err.message || err).match(/permission denied/)) { return cb(null); } return cb(err); } if (status.searching === false) { opts.verbosity > 2 && logging_1.default.error('There was an error so we short-circuited search.'); return process.nextTick(cb); } items = items.map(function (item) { return path.resolve(dir, item); }); let deps, npmlinkup, hasNLUJSONFile = false; try { npmlinkup = require(path.resolve(dir + '/.nlu.json')); hasNLUJSONFile = true; if (npmlinkup && npmlinkup.searchable === false) { logging_1.default.warn('The following dir is not searchable:', dir); return cb(null); } } catch (e) { npmlinkup = {}; } async.eachLimit(items, 7, (item, cb) => { if (isIgnored(String(item))) { if (opts.verbosity > 2) { logging_1.default.warning('path ignored => ', item); } return process.nextTick(cb); } fs.lstat(item, function (err, stats) { if (err) { logging_1.default.warning('warning => maybe a symlink? => ', item); return cb(); } if (status.searching === false) { opts.verbosity > 1 && logging_1.default.error('There was an error so we short-circuited search.'); return process.nextTick(cb); } if (stats.isSymbolicLink()) { opts.verbosity > 2 && logging_1.default.warning('warning => looks like a symlink => ', item); return cb(); } if (stats.isDirectory()) { if (!isPathSearchableBasic(item)) { return cb(null); } if (isIgnored(String(item + '/'))) { if (opts.verbosity > 2) { logging_1.default.warning('path ignored by settings/regex => ', item); } cb(null); } else { getMarkers(item, cb); } return; } if (!stats.isFile()) { if (opts.verbosity > 2) { logging_1.default.warning('Not a directory or file (maybe a symlink?) => ', item); } return cb(null); } let dirname = path.dirname(item); let filename = path.basename(item); if (String(filename) !== 'package.json') { return cb(null); } let pkg, linkable = null; try { pkg = require(item); } catch (err) { return cb(err); } try { linkable = pkg.nlu.linkable; } catch (err) { } if (linkable === false) { return cb(null); } if (pkg.name === mainProjectName && linkable !== true) { if (opts.verbosity > 1) { logging_1.default.info('Another project on your fs has your main projects package.json name, at path:', chalk_1.default.yellow.bold(dirname)); } return cb(null); } if (npmlinkup.linkable === false) { logging_1.default.warn(`Skipping project at dir "${dirname}" because 'linkable' was set to false.`); return cb(null); } const pkgFromConf = conf.packages[pkg.name] || {}; npmlinkup = Object.assign({}, pkgFromConf, npmlinkup); try { deps = utils_1.getDepsListFromNluJSON(npmlinkup); assert(Array.isArray(deps), `the 'list' property in an .nlu.json file is not an Array instance for '${filename}'.`); } catch (err) { logging_1.default.error(chalk_1.default.redBright('Could not parse list/packages/deps properties from .nlu.json file at this path:')); logging_1.default.error(chalk_1.default.redBright.bold(dirname)); return cb(err); } deps.forEach(item => { totalList.set(item, true); }); if (map[dirname]) { logging_1.default.warn('Map already has key: ' + dirname); return process.nextTick(cb); } const m = map[dirname] = { name: pkg.name, bin: pkg.bin || null, hasNLUJSONFile, isMainProject: false, linkToItself: Boolean(npmlinkup.linkToItself), runInstall: Boolean(npmlinkup.alwaysReinstall), path: dirname, deps: deps, package: pkg, searchRoots: null, installedSet: new Set(), linkedSet: {} }; const nm = path.resolve(dirname + '/node_modules'); const keys = opts.production ? utils_1.getProdKeys(pkg) : utils_1.getDevKeys(pkg); async.autoInject({ addToSearchRoots(cb) { const searchRoots = utils_1.getSearchRootsFromNluConf(npmlinkup); if (searchRoots.length < 1) { return process.nextTick(cb, null); } map_paths_1.mapPaths(searchRoots, dirname, (err, roots) => { if (err) { return cb(err); } m.searchRoots = roots.slice(0); roots.forEach(r => { if (isPathSearchable(r)) { logging_1.default.info(chalk_1.default.cyan('Given the .nlu.json file at this path:'), chalk_1.default.bold(dirname)); logging_1.default.info(chalk_1.default.cyan('We are adding this to the search queue:'), chalk_1.default.bold(r)); search_queue_1.q.push(cb => findProject(r, cb)); } }); cb(null); }); } }, cb); }); }, cb); }); }); })(item, cb); }; };