123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- let fs = require('fs');
- let path = require('path');
- let cnst = require('constants');
-
- let os = require('os');
- let rimraf = require('rimraf');
- let mkdirp = require('mkdirp');
- let osTmpdir = require('os').tmpdir();
-
- const rimrafSync = rimraf.sync;
-
- //== helpers
- //
- let dir = path.resolve(os.tmpdir());
-
- let RDWR_EXCL = cnst.O_CREAT | cnst.O_TRUNC | cnst.O_RDWR | cnst.O_EXCL;
-
- let 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;
- };
-
- /* -------------------------------------------------------------------------
- * 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', function() {
- try {
- cleanupSync();
- } catch(err) {
- console.warn("Fail to clean temporary files on exit : ", err);
- throw err;
- }
- });
- exitListenerAttached = true;
- }
- }
-
- function cleanupFilesSync() {
- if (!tracking) {
- return false;
- }
- var count = 0;
- var toDelete;
- while ((toDelete = filesToDelete.shift()) !== undefined) {
- rimrafSync(toDelete, { maxBusyTries: 6 });
- 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) {
- // Prevent processing if aborted
- return;
- }
- if (err) {
- // This shouldn't happen; pass error to callback and abort
- // processing
- callback(err);
- left = 0;
- return;
- } else {
- count++;
- }
- left--;
- if (!left) {
- callback(null, count);
- }
- };
- while ((toDelete = filesToDelete.shift()) !== undefined) {
- rimraf(toDelete, { maxBusyTries: 6 }, rimrafCallback);
- }
- return promise;
- }
-
- function cleanupDirsSync() {
- if (!tracking) {
- return false;
- }
- var count = 0;
- var toDelete;
- while ((toDelete = dirsToDelete.shift()) !== undefined) {
- rimrafSync(toDelete, { maxBusyTries: 6 });
- 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) {
- // Prevent processing if aborted
- return;
- }
- if (err) {
- // rimraf handles most "normal" errors; pass the error to the
- // callback and abort processing
- callback(err, count);
- left = 0;
- return;
- } else {
- count++;
- }
- left--;
- if (!left) {
- callback(null, count);
- }
- };
- while ((toDelete = dirsToDelete.shift()) !== undefined) {
- rimraf(toDelete, { maxBusyTries: 6 }, 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;
- }
-
- //== directories
- //
- const mkdir = (affixes, callback) => {
- const p = promisify(callback);
- const promise = p[0];
- callback = p[1];
-
- let dirPath = generateName(affixes, 'd-');
- mkdirp(dirPath, 0o700, (err) => {
- if (!err) {
- deleteDirOnExit(dirPath);
- }
- callback(err, dirPath);
- });
- return promise;
- }
-
- const mkdirSync = (affixes) => {
- let dirPath = generateName(affixes, 'd-');
- mkdirp.sync(dirPath, 0o700);
- deleteDirOnExit(dirPath);
- return dirPath;
- }
-
- //== files
- //
- const open = (affixes, callback) => {
- const p = promisify(callback);
- const promise = p[0];
- callback = p[1];
-
- const path = generateName(affixes, 'f-');
- fs.open(path, RDWR_EXCL, 0o600, (err, fd) => {
- if (!err) {
- deleteFileOnExit(path);
- }
- callback(err, { path, fd });
- });
- return promise;
- }
-
- const openSync = (affixes) => {
- const path = generateName(affixes, 'f-');
- let fd = fs.openSync(path, RDWR_EXCL, 0o600);
- deleteFileOnExit(path);
- return { path, fd };
- }
-
- const createWriteStream = (affixes) => {
- const path = generateName(affixes, 's-');
- let stream = fs.createWriteStream(path, { flags: RDWR_EXCL, mode: 0o600 });
- deleteFileOnExit(path);
- return stream;
- }
-
- //== settings
- //
- exports.dir = dir;
- 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;
|