284 lines
6.4 KiB
JavaScript
284 lines
6.4 KiB
JavaScript
|
var fs = require('fs'),
|
||
|
path = require('path'),
|
||
|
cnst = require('constants');
|
||
|
|
||
|
var rimraf = require('rimraf'),
|
||
|
osTmpdir = require('os-tmpdir'),
|
||
|
rimrafSync = rimraf.sync;
|
||
|
|
||
|
/* HELPERS */
|
||
|
|
||
|
var RDWR_EXCL = cnst.O_CREAT | cnst.O_TRUNC | cnst.O_RDWR | cnst.O_EXCL;
|
||
|
|
||
|
var generateName = function(rawAffixes, defaultPrefix) {
|
||
|
var affixes = parseAffixes(rawAffixes, defaultPrefix);
|
||
|
var now = new Date();
|
||
|
var name = [affixes.prefix,
|
||
|
now.getYear(), now.getMonth(), now.getDate(),
|
||
|
'-',
|
||
|
process.pid,
|
||
|
'-',
|
||
|
(Math.random() * 0x100000000 + 1).toString(36),
|
||
|
affixes.suffix].join('');
|
||
|
return path.join(affixes.dir || exports.dir, name);
|
||
|
};
|
||
|
|
||
|
var parseAffixes = function(rawAffixes, defaultPrefix) {
|
||
|
var affixes = {prefix: null, suffix: null};
|
||
|
if(rawAffixes) {
|
||
|
switch (typeof(rawAffixes)) {
|
||
|
case 'string':
|
||
|
affixes.prefix = rawAffixes;
|
||
|
break;
|
||
|
case 'object':
|
||
|
affixes = rawAffixes;
|
||
|
break;
|
||
|
default:
|
||
|
throw new Error("Unknown affix declaration: " + affixes);
|
||
|
}
|
||
|
} else {
|
||
|
affixes.prefix = defaultPrefix;
|
||
|
}
|
||
|
return affixes;
|
||
|
};
|
||
|
|
||
|
/* -------------------------------------------------------------------------
|
||
|
* Don't forget to call track() if you want file tracking and exit handlers!
|
||
|
* -------------------------------------------------------------------------
|
||
|
* When any temp file or directory is created, it is added to filesToDelete
|
||
|
* or dirsToDelete. The first time any temp file is created, a listener is
|
||
|
* added to remove all temp files and directories at exit.
|
||
|
*/
|
||
|
var tracking = false;
|
||
|
var track = function(value) {
|
||
|
tracking = (value !== false);
|
||
|
return module.exports; // chainable
|
||
|
};
|
||
|
var exitListenerAttached = false;
|
||
|
var filesToDelete = [];
|
||
|
var dirsToDelete = [];
|
||
|
|
||
|
function deleteFileOnExit(filePath) {
|
||
|
if (!tracking) return false;
|
||
|
attachExitListener();
|
||
|
filesToDelete.push(filePath);
|
||
|
}
|
||
|
|
||
|
function deleteDirOnExit(dirPath) {
|
||
|
if (!tracking) return false;
|
||
|
attachExitListener();
|
||
|
dirsToDelete.push(dirPath);
|
||
|
}
|
||
|
|
||
|
function attachExitListener() {
|
||
|
if (!tracking) return false;
|
||
|
if (!exitListenerAttached) {
|
||
|
process.addListener('exit', cleanupSync);
|
||
|
exitListenerAttached = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function cleanupFilesSync() {
|
||
|
if (!tracking) {
|
||
|
return false;
|
||
|
}
|
||
|
var count = 0;
|
||
|
var toDelete;
|
||
|
while ((toDelete = filesToDelete.shift()) !== undefined) {
|
||
|
rimrafSync(toDelete);
|
||
|
count++;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
function cleanupFiles(callback) {
|
||
|
if (!tracking) {
|
||
|
if (callback) {
|
||
|
callback(new Error("not tracking"));
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
var count = 0;
|
||
|
var left = filesToDelete.length;
|
||
|
if (!left) {
|
||
|
if (callback) {
|
||
|
callback(null, count);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
var toDelete;
|
||
|
var rimrafCallback = function(err) {
|
||
|
if (!left) {
|
||
|
// Prevent processing if aborted
|
||
|
return;
|
||
|
}
|
||
|
if (err) {
|
||
|
// This shouldn't happen; pass error to callback and abort
|
||
|
// processing
|
||
|
if (callback) {
|
||
|
callback(err);
|
||
|
}
|
||
|
left = 0;
|
||
|
return;
|
||
|
} else {
|
||
|
count++;
|
||
|
}
|
||
|
left--;
|
||
|
if (!left && callback) {
|
||
|
callback(null, count);
|
||
|
}
|
||
|
};
|
||
|
while ((toDelete = filesToDelete.shift()) !== undefined) {
|
||
|
rimraf(toDelete, rimrafCallback);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function cleanupDirsSync() {
|
||
|
if (!tracking) {
|
||
|
return false;
|
||
|
}
|
||
|
var count = 0;
|
||
|
var toDelete;
|
||
|
while ((toDelete = dirsToDelete.shift()) !== undefined) {
|
||
|
rimrafSync(toDelete);
|
||
|
count++;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
function cleanupDirs(callback) {
|
||
|
if (!tracking) {
|
||
|
if (callback) {
|
||
|
callback(new Error("not tracking"));
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
var count = 0;
|
||
|
var left = dirsToDelete.length;
|
||
|
if (!left) {
|
||
|
if (callback) {
|
||
|
callback(null, count);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
var toDelete;
|
||
|
var rimrafCallback = function (err) {
|
||
|
if (!left) {
|
||
|
// Prevent processing if aborted
|
||
|
return;
|
||
|
}
|
||
|
if (err) {
|
||
|
// rimraf handles most "normal" errors; pass the error to the
|
||
|
// callback and abort processing
|
||
|
if (callback) {
|
||
|
callback(err, count);
|
||
|
}
|
||
|
left = 0;
|
||
|
return;
|
||
|
} else {
|
||
|
count;
|
||
|
}
|
||
|
left--;
|
||
|
if (!left && callback) {
|
||
|
callback(null, count);
|
||
|
}
|
||
|
};
|
||
|
while ((toDelete = dirsToDelete.shift()) !== undefined) {
|
||
|
rimraf(toDelete, rimrafCallback);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function cleanupSync() {
|
||
|
if (!tracking) {
|
||
|
return false;
|
||
|
}
|
||
|
var fileCount = cleanupFilesSync();
|
||
|
var dirCount = cleanupDirsSync();
|
||
|
return {files: fileCount, dirs: dirCount};
|
||
|
}
|
||
|
|
||
|
function cleanup(callback) {
|
||
|
if (!tracking) {
|
||
|
if (callback) {
|
||
|
callback(new Error("not tracking"));
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
cleanupFiles(function(fileErr, fileCount) {
|
||
|
if (fileErr) {
|
||
|
if (callback) {
|
||
|
callback(fileErr, {files: fileCount})
|
||
|
}
|
||
|
} else {
|
||
|
cleanupDirs(function(dirErr, dirCount) {
|
||
|
if (callback) {
|
||
|
callback(dirErr, {files: fileCount, dirs: dirCount});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/* DIRECTORIES */
|
||
|
|
||
|
function mkdir(affixes, callback) {
|
||
|
var dirPath = generateName(affixes, 'd-');
|
||
|
fs.mkdir(dirPath, 0700, function(err) {
|
||
|
if (!err) {
|
||
|
deleteDirOnExit(dirPath);
|
||
|
}
|
||
|
if (callback) {
|
||
|
callback(err, dirPath);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function mkdirSync(affixes) {
|
||
|
var dirPath = generateName(affixes, 'd-');
|
||
|
fs.mkdirSync(dirPath, 0700);
|
||
|
deleteDirOnExit(dirPath);
|
||
|
return dirPath;
|
||
|
}
|
||
|
|
||
|
/* FILES */
|
||
|
|
||
|
function open(affixes, callback) {
|
||
|
var filePath = generateName(affixes, 'f-');
|
||
|
fs.open(filePath, RDWR_EXCL, 0600, function(err, fd) {
|
||
|
if (!err) {
|
||
|
deleteFileOnExit(filePath);
|
||
|
}
|
||
|
if (callback) {
|
||
|
callback(err, {path: filePath, fd: fd});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function openSync(affixes) {
|
||
|
var filePath = generateName(affixes, 'f-');
|
||
|
var fd = fs.openSync(filePath, RDWR_EXCL, 0600);
|
||
|
deleteFileOnExit(filePath);
|
||
|
return {path: filePath, fd: fd};
|
||
|
}
|
||
|
|
||
|
function createWriteStream(affixes) {
|
||
|
var filePath = generateName(affixes, 's-');
|
||
|
var stream = fs.createWriteStream(filePath, {flags: RDWR_EXCL, mode: 0600});
|
||
|
deleteFileOnExit(filePath);
|
||
|
return stream;
|
||
|
}
|
||
|
|
||
|
/* EXPORTS */
|
||
|
// Settings
|
||
|
exports.dir = path.resolve(osTmpdir());
|
||
|
exports.track = track;
|
||
|
// Functions
|
||
|
exports.mkdir = mkdir;
|
||
|
exports.mkdirSync = mkdirSync;
|
||
|
exports.open = open;
|
||
|
exports.openSync = openSync;
|
||
|
exports.path = generateName;
|
||
|
exports.cleanup = cleanup;
|
||
|
exports.cleanupSync = cleanupSync;
|
||
|
exports.createWriteStream = createWriteStream;
|