alm
Version:
The best IDE for TypeScript
272 lines (271 loc) • 11.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var sw = require("../../utils/simpleWorker");
var contract = require("./fileListingContract");
var fsu = require("../../utils/fsu");
var glob = require("glob");
var chokidar = require("chokidar");
var utils_1 = require("../../../common/utils");
var types = require("../../../common/types");
var maxFileCount = 100000;
/** The directory to watch */
var directoryUnderWatch;
var Worker;
(function (Worker) {
Worker.echo = function (q) {
return exports.master.increment(q).then(function (res) {
return {
text: q.text,
num: res.num
};
});
};
Worker.setupWatch = function (q) {
directoryUnderWatch = q.directory;
var completed = false;
var liveList = {};
// Effectively a list of the mutations that `liveList` is going through after initial sync
var bufferedAdded = [];
var bufferedRemoved = [];
var filterName = function (filePath) {
return (
// Remove .git we have no use for that here
!filePath.endsWith('.git') && !filePath.includes('/.git/')
// MAC
&& !filePath.endsWith('.DS_Store'));
};
// Utility to send new file list
var sendNewFileList = function () {
var filePaths = Object.keys(liveList)
.filter(filterName)
.sort(function (a, b) {
// sub dir wins!
if (b.startsWith(a)) {
return -1;
}
if (a.startsWith(b)) {
return 1;
}
// The next sorts are slow and only done after initial listing!
if (!completed) {
return a.length - b.length;
}
// sort by name
return a.toLowerCase().localeCompare(b.toLowerCase());
})
.map(function (filePath) {
var type = liveList[filePath];
return { filePath: filePath, type: type };
});
exports.master.fileListUpdated({
filePaths: filePaths,
completed: completed
});
// Send out the delta as well
// Unless of course this is the *initial* sending of file listing
if (bufferedAdded.length || bufferedRemoved.length) {
exports.master.fileListingDelta({
addedFilePaths: bufferedAdded.filter(function (x) { return filterName(x.filePath); }),
removedFilePaths: bufferedRemoved.filter(function (x) { return filterName(x.filePath); })
});
bufferedAdded = [];
bufferedRemoved = [];
}
};
/**
* Slower version for
* - initial partial serach
* - later updates which might be called a lot because of some directory of files removed
*/
var sendNewFileListThrottled = utils_1.throttle(sendNewFileList, 1500);
/**
* Utility function to get the listing from a directory
* No side effects in this function
*/
var getListing = function (dirPath) {
return new Promise(function (resolve) {
var mg = new glob.Glob('**', { cwd: dirPath, dot: true }, function (e, globResult) {
if (e) {
console.error('Globbing error:', e);
}
var list = globResult.map(function (nl) {
var p = fsu.resolve(dirPath, nl);
var type = mg.cache[p] && mg.cache[p] == 'FILE' ? types.FilePathType.File : types.FilePathType.Dir;
return {
filePath: fsu.consistentPath(p),
type: type,
};
});
resolve(list);
});
});
};
// create initial list using 10x faster glob.Glob!
(function () {
/** These things are coming on a mac for some reason */
var ignoreThisPathThatGlobGivesForUnknownReasons = function (filePath) {
return filePath.includes('0.0.0.0') || (filePath.includes('[object Object]'));
};
var cwd = q.directory;
var mg = new glob.Glob('**', { cwd: cwd, dot: true }, function (e, newList) {
if (e) {
checkGlobbingError(e);
if (abortGlobbing) {
mg.abort();
// if we don't exit then glob keeps globbing + erroring despite mg.abort()
process.exit();
return;
}
console.error('Globbing error:', e);
}
var list = newList.map(function (nl) {
var p = fsu.resolve(cwd, nl);
// NOTE: the glob cache also uses consistent path even on windows, hence `fsu.resolve` ^ :)
var type = mg.cache[p] && mg.cache[p] == 'FILE' ? types.FilePathType.File : types.FilePathType.Dir;
if (ignoreThisPathThatGlobGivesForUnknownReasons(nl)) {
// console.log(nl, mg.cache[p]); /// DEBUG
return null;
}
return {
filePath: fsu.consistentPath(p),
type: type,
};
}).filter(function (x) { return !!x; });
// Initial search complete!
completed = true;
list.forEach(function (entry) { return liveList[entry.filePath] = entry.type; });
sendNewFileList();
});
var matchLength = 0;
/** Still send the listing while globbing so user gets immediate feedback */
mg.on('match', function (match) {
matchLength++;
if (matchLength > maxFileCount) {
abortDueToTooManyFiles();
return;
}
var p = fsu.resolve(cwd, match);
if (mg.cache[p]) {
if (ignoreThisPathThatGlobGivesForUnknownReasons(match)) {
return;
}
liveList[fsu.consistentPath(p)] = mg.cache[p] == 'FILE' ? types.FilePathType.File : types.FilePathType.Dir;
sendNewFileListThrottled();
}
});
})();
function fileAdded(filePath) {
filePath = fsu.consistentPath(filePath);
// Only send if we don't know about this already (because of faster initial scan)
if (!liveList[filePath]) {
var type = types.FilePathType.File;
liveList[filePath] = type;
bufferedAdded.push({
filePath: filePath,
type: type
});
sendNewFileListThrottled();
}
}
function dirAdded(dirPath) {
dirPath = fsu.consistentPath(dirPath);
liveList[dirPath] = types.FilePathType.Dir;
bufferedAdded.push({
filePath: dirPath,
type: types.FilePathType.Dir
});
/**
* - glob the folder
* - send the folder throttled
*/
getListing(dirPath).then(function (res) {
res.forEach(function (fpDetails) {
if (!liveList[fpDetails.filePath]) {
var type = fpDetails.type;
liveList[fpDetails.filePath] = type;
bufferedAdded.push({
filePath: fpDetails.filePath,
type: type
});
}
});
sendNewFileListThrottled();
}).catch(function (res) {
console.error('[FLW] DirPath listing failed:', dirPath, res);
});
}
function fileDeleted(filePath) {
filePath = fsu.consistentPath(filePath);
delete liveList[filePath];
bufferedRemoved.push({
filePath: filePath,
type: types.FilePathType.File
});
sendNewFileListThrottled();
}
function dirDeleted(dirPath) {
dirPath = fsu.consistentPath(dirPath);
Object.keys(liveList).forEach(function (filePath) {
if (filePath.startsWith(dirPath)) {
bufferedRemoved.push({
filePath: filePath,
type: liveList[filePath]
});
delete liveList[filePath];
}
});
sendNewFileListThrottled();
}
/** Create watcher */
var watcher = chokidar.watch(directoryUnderWatch, {
/** Don't care about initial as we did that using glob as its faster */
ignoreInitial: true,
});
// Just the ones that impact file listing
// https://github.com/paulmillr/chokidar#methods--events
watcher.on('add', fileAdded);
watcher.on('addDir', dirAdded);
watcher.on('unlink', fileDeleted);
watcher.on('unlinkDir', dirDeleted);
// Just for changes
watcher.on('change', function (filePath) {
// We have no use for this right now
});
return Promise.resolve({});
};
})(Worker || (Worker = {}));
// Ensure that the namespace follows the contract
var _checkTypes = Worker;
// run worker
exports.master = sw.runWorker({
workerImplementation: Worker,
masterContract: contract.master
}).master;
function debug() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
console.error.apply(console, args);
}
/**
* If its a permission error warn the user that they should start in some project directory
*/
var abortGlobbing = false;
function checkGlobbingError(err) {
if (err.path &&
(err.code == 'EPERM' /*win*/ || err.code == 'EACCES' /*mac*/)) {
abortGlobbing = true;
var errorMessage = "Exiting: Permission error when trying to list " + err.path + ".\n- Start the IDE in a project folder (e.g. '/your/project')\n- Check access to the path";
exports.master.abort({ errorMessage: errorMessage });
}
}
function abortDueToTooManyFiles() {
abortGlobbing = true;
var errorMessage = "Exiting: Too many files in folder. Currently we limit to " + maxFileCount + ".\n- Start the IDE in a project folder (e.g. '/your/project')";
exports.master.abort({ errorMessage: errorMessage });
}
process.on('error', function () {
console.log('here');
process.exit();
});