|
|
var fs = require('fs'), |
|
|
path = require('path'), |
|
|
cnst = require('constants'); |
|
|
|
|
|
var rimraf = require('rimraf'), |
|
|
os = require('os'), |
|
|
rimrafSync = rimraf.sync; |
|
|
|
|
|
|
|
|
|
|
|
var dir = path.resolve(os.tmpdir()); |
|
|
|
|
|
var RDWR_EXCL = cnst.O_CREAT | cnst.O_TRUNC | cnst.O_RDWR | cnst.O_EXCL; |
|
|
|
|
|
var promisify = function(callback) { |
|
|
if (typeof callback === 'function') { |
|
|
return [undefined, callback]; |
|
|
} |
|
|
|
|
|
var promiseCallback; |
|
|
var promise = new Promise(function(resolve, reject) { |
|
|
promiseCallback = function() { |
|
|
var args = Array.from(arguments); |
|
|
var err = args.shift(); |
|
|
|
|
|
process.nextTick(function() { |
|
|
if (err) { |
|
|
reject(err); |
|
|
} else if (args.length === 1) { |
|
|
resolve(args[0]); |
|
|
} else { |
|
|
resolve(args); |
|
|
} |
|
|
}); |
|
|
}; |
|
|
}); |
|
|
|
|
|
return [promise, promiseCallback]; |
|
|
}; |
|
|
|
|
|
var generateName = function(rawAffixes, defaultPrefix) { |
|
|
var affixes = parseAffixes(rawAffixes, defaultPrefix); |
|
|
var now = new Date(); |
|
|
var name = [affixes.prefix, |
|
|
now.getFullYear(), now.getMonth(), now.getDate(), |
|
|
'-', |
|
|
process.pid, |
|
|
'-', |
|
|
(Math.random() * 0x100000000 + 1).toString(36), |
|
|
affixes.suffix].join(''); |
|
|
return path.join(affixes.dir || 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; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var tracking = false; |
|
|
var track = function(value) { |
|
|
tracking = (value !== false); |
|
|
return module.exports; |
|
|
}; |
|
|
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) { |
|
|
var p = promisify(callback); |
|
|
var promise = p[0]; |
|
|
callback = p[1]; |
|
|
|
|
|
if (!tracking) { |
|
|
callback(new Error("not tracking")); |
|
|
return promise; |
|
|
} |
|
|
var count = 0; |
|
|
var left = filesToDelete.length; |
|
|
if (!left) { |
|
|
callback(null, count); |
|
|
return promise; |
|
|
} |
|
|
var toDelete; |
|
|
var rimrafCallback = function(err) { |
|
|
if (!left) { |
|
|
|
|
|
return; |
|
|
} |
|
|
if (err) { |
|
|
|
|
|
|
|
|
callback(err); |
|
|
left = 0; |
|
|
return; |
|
|
} else { |
|
|
count++; |
|
|
} |
|
|
left--; |
|
|
if (!left) { |
|
|
callback(null, count); |
|
|
} |
|
|
}; |
|
|
while ((toDelete = filesToDelete.shift()) !== undefined) { |
|
|
rimraf(toDelete, rimrafCallback); |
|
|
} |
|
|
return promise; |
|
|
} |
|
|
|
|
|
function cleanupDirsSync() { |
|
|
if (!tracking) { |
|
|
return false; |
|
|
} |
|
|
var count = 0; |
|
|
var toDelete; |
|
|
while ((toDelete = dirsToDelete.shift()) !== undefined) { |
|
|
rimrafSync(toDelete); |
|
|
count++; |
|
|
} |
|
|
return count; |
|
|
} |
|
|
|
|
|
function cleanupDirs(callback) { |
|
|
var p = promisify(callback); |
|
|
var promise = p[0]; |
|
|
callback = p[1]; |
|
|
|
|
|
if (!tracking) { |
|
|
callback(new Error("not tracking")); |
|
|
return promise; |
|
|
} |
|
|
var count = 0; |
|
|
var left = dirsToDelete.length; |
|
|
if (!left) { |
|
|
callback(null, count); |
|
|
return promise; |
|
|
} |
|
|
var toDelete; |
|
|
var rimrafCallback = function (err) { |
|
|
if (!left) { |
|
|
|
|
|
return; |
|
|
} |
|
|
if (err) { |
|
|
|
|
|
|
|
|
callback(err, count); |
|
|
left = 0; |
|
|
return; |
|
|
} else { |
|
|
count++; |
|
|
} |
|
|
left--; |
|
|
if (!left) { |
|
|
callback(null, count); |
|
|
} |
|
|
}; |
|
|
while ((toDelete = dirsToDelete.shift()) !== undefined) { |
|
|
rimraf(toDelete, rimrafCallback); |
|
|
} |
|
|
return promise; |
|
|
} |
|
|
|
|
|
function cleanupSync() { |
|
|
if (!tracking) { |
|
|
return false; |
|
|
} |
|
|
var fileCount = cleanupFilesSync(); |
|
|
var dirCount = cleanupDirsSync(); |
|
|
return {files: fileCount, dirs: dirCount}; |
|
|
} |
|
|
|
|
|
function cleanup(callback) { |
|
|
var p = promisify(callback); |
|
|
var promise = p[0]; |
|
|
callback = p[1]; |
|
|
|
|
|
if (!tracking) { |
|
|
callback(new Error("not tracking")); |
|
|
return promise; |
|
|
} |
|
|
cleanupFiles(function(fileErr, fileCount) { |
|
|
if (fileErr) { |
|
|
callback(fileErr, {files: fileCount}); |
|
|
} else { |
|
|
cleanupDirs(function(dirErr, dirCount) { |
|
|
callback(dirErr, {files: fileCount, dirs: dirCount}); |
|
|
}); |
|
|
} |
|
|
}); |
|
|
return promise; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function mkdir(affixes, callback) { |
|
|
var p = promisify(callback); |
|
|
var promise = p[0]; |
|
|
callback = p[1]; |
|
|
|
|
|
var dirPath = generateName(affixes, 'd-'); |
|
|
fs.mkdir(dirPath, parseInt('0700', 8), function(err) { |
|
|
if (!err) { |
|
|
deleteDirOnExit(dirPath); |
|
|
} |
|
|
callback(err, dirPath); |
|
|
}); |
|
|
return promise; |
|
|
} |
|
|
|
|
|
function mkdirSync(affixes) { |
|
|
var dirPath = generateName(affixes, 'd-'); |
|
|
fs.mkdirSync(dirPath, parseInt('0700', 8)); |
|
|
deleteDirOnExit(dirPath); |
|
|
return dirPath; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function open(affixes, callback) { |
|
|
var p = promisify(callback); |
|
|
var promise = p[0]; |
|
|
callback = p[1]; |
|
|
|
|
|
var filePath = generateName(affixes, 'f-'); |
|
|
fs.open(filePath, RDWR_EXCL, parseInt('0600', 8), function(err, fd) { |
|
|
if (!err) { |
|
|
deleteFileOnExit(filePath); |
|
|
} |
|
|
callback(err, {path: filePath, fd: fd}); |
|
|
}); |
|
|
return promise; |
|
|
} |
|
|
|
|
|
function openSync(affixes) { |
|
|
var filePath = generateName(affixes, 'f-'); |
|
|
var fd = fs.openSync(filePath, RDWR_EXCL, parseInt('0600', 8)); |
|
|
deleteFileOnExit(filePath); |
|
|
return {path: filePath, fd: fd}; |
|
|
} |
|
|
|
|
|
function createWriteStream(affixes) { |
|
|
var filePath = generateName(affixes, 's-'); |
|
|
var stream = fs.createWriteStream(filePath, {flags: RDWR_EXCL, mode: parseInt('0600', 8)}); |
|
|
deleteFileOnExit(filePath); |
|
|
return stream; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
exports.dir = dir; |
|
|
exports.track = track; |
|
|
|
|
|
exports.mkdir = mkdir; |
|
|
exports.mkdirSync = mkdirSync; |
|
|
exports.open = open; |
|
|
exports.openSync = openSync; |
|
|
exports.path = generateName; |
|
|
exports.cleanup = cleanup; |
|
|
exports.cleanupSync = cleanupSync; |
|
|
exports.createWriteStream = createWriteStream; |
|
|
|