npm-link-up
Version:
Use this package to link your projects together for local development.
245 lines (244 loc) • 12.4 kB
JavaScript
;
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);
};
};