-
Notifications
You must be signed in to change notification settings - Fork 97
fix #155 #161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix #155 #161
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ node_modules/ | |
coverage/ | ||
.idea/ | ||
.*.swp | ||
package-lock.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ const crypto = require('crypto'); | |
const _c = fs.constants && os.constants ? | ||
{ fs: fs.constants, os: os.constants } : | ||
process.binding('constants'); | ||
const rimraf = require('rimraf'); | ||
|
||
/* | ||
* The working inner variables. | ||
|
@@ -302,45 +303,6 @@ function fileSync(options) { | |
}; | ||
} | ||
|
||
/** | ||
* Removes files and folders in a directory recursively. | ||
* | ||
* @param {string} root | ||
* @private | ||
*/ | ||
function _rmdirRecursiveSync(root) { | ||
const dirs = [root]; | ||
|
||
do { | ||
var | ||
dir = dirs.pop(), | ||
deferred = false, | ||
files = fs.readdirSync(dir); | ||
|
||
for (var i = 0, length = files.length; i < length; i++) { | ||
var | ||
file = path.join(dir, files[i]), | ||
stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories | ||
|
||
if (stat.isDirectory()) { | ||
/* istanbul ignore else */ | ||
if (!deferred) { | ||
deferred = true; | ||
dirs.push(dir); | ||
} | ||
dirs.push(file); | ||
} else { | ||
fs.unlinkSync(file); | ||
} | ||
} | ||
|
||
/* istanbul ignore else */ | ||
if (!deferred) { | ||
fs.rmdirSync(dir); | ||
} | ||
} while (dirs.length !== 0); | ||
} | ||
|
||
/** | ||
* Creates a temporary directory. | ||
* | ||
|
@@ -390,52 +352,94 @@ function dirSync(options) { | |
} | ||
|
||
/** | ||
* Prepares the callback for removal of the temporary file. | ||
* Removes files asynchronously. | ||
* | ||
* @param {string} name the path of the file | ||
* @param {number} fd file descriptor | ||
* @param {Object} opts | ||
* @returns {fileCallback} | ||
* @param {Object} fdPath | ||
* @param {Function} next | ||
* @private | ||
*/ | ||
function _prepareTmpFileRemoveCallback(name, fd, opts) { | ||
const removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) { | ||
try { | ||
/* istanbul ignore else */ | ||
if (0 <= fdPath[0]) { | ||
fs.closeSync(fdPath[0]); | ||
} | ||
} | ||
catch (e) { | ||
// under some node/windows related circumstances, a temporary file | ||
// may have not be created as expected or the file was already closed | ||
// by the user, in which case we will simply ignore the error | ||
/* istanbul ignore else */ | ||
if (!isEBADF(e) && !isENOENT(e)) { | ||
// reraise any unanticipated error | ||
throw e; | ||
} | ||
function _removeFileAsync(fdPath, next) { | ||
const _handler = function (err) { | ||
if (err && !isENOENT(err)) { | ||
// reraise any unanticipated error | ||
return next(err); | ||
} | ||
next(); | ||
} | ||
|
||
if (0 <= fdPath[0]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is too dense and error prone. Please put parentheses around the branches. |
||
fs.close(fdPath[0], function (err) { | ||
fs.unlink(fdPath[1], _handler); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe we should extract this to a local function because it is also used in the const _removeFile = function () { fs.unlink(fdPath[1], _handler); } |
||
}); | ||
else fs.unlink(fdPath[1], _handler); | ||
} | ||
|
||
/** | ||
* Removes files synchronously. | ||
* | ||
* @param {Object} fdPath | ||
* @private | ||
*/ | ||
function _removeFileSync(fdPath) { | ||
try { | ||
if (0 <= fdPath[0]) fs.closeSync(fdPath[0]); | ||
} catch (e) { | ||
// reraise any unanticipated error | ||
if (!isEBADF(e) && !isENOENT(e)) throw e; | ||
} finally { | ||
try { | ||
fs.unlinkSync(fdPath[1]); | ||
} | ||
catch (e) { | ||
/* istanbul ignore else */ | ||
if (!isENOENT(e)) { | ||
// reraise any unanticipated error | ||
throw e; | ||
} | ||
// reraise any unanticipated error | ||
if (!isENOENT(e)) throw e; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will cause a linter error because of |
||
} | ||
}, [fd, name]); | ||
|
||
/* istanbul ignore else */ | ||
if (!opts.keep) { | ||
_removeObjects.unshift(removeCallback); | ||
} | ||
} | ||
|
||
/** | ||
* Prepares the callback for removal of the temporary file. | ||
* | ||
* @param {string} name the path of the file | ||
* @param {number} fd file descriptor | ||
* @param {Object} opts | ||
* @returns {fileCallback} | ||
* @private | ||
*/ | ||
function _prepareTmpFileRemoveCallback(name, fd, opts) { | ||
const removeCallbackSync = _prepareRemoveCallback(_removeFileSync, [fd, name]); | ||
const removeCallback = _prepareRemoveCallback(_removeFileAsync, [fd, name], removeCallbackSync); | ||
|
||
if (!opts.keep) _removeObjects.unshift(removeCallbackSync); | ||
|
||
return removeCallback; | ||
} | ||
|
||
/** | ||
* Simple wrapper for rimraf. | ||
* | ||
* @param {string} dirPath | ||
* @param {Function} next | ||
* @private | ||
*/ | ||
function _rimrafRemoveDirWrapper(dirPath, next) { | ||
rimraf(dirPath, next); | ||
} | ||
|
||
/** | ||
* Simple wrapper for rimraf.sync. | ||
* | ||
* @param {string} dirPath | ||
* @private | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
*/ | ||
function _rimrafRemoveDirSyncWrapper(dirPath, next) { | ||
try { | ||
return next(null, rimraf.sync(dirPath)); | ||
} catch (err) { | ||
return next(err); | ||
} | ||
} | ||
|
||
/** | ||
* Prepares the callback for removal of the temporary directory. | ||
* | ||
|
@@ -445,13 +449,11 @@ function _prepareTmpFileRemoveCallback(name, fd, opts) { | |
* @private | ||
*/ | ||
function _prepareTmpDirRemoveCallback(name, opts) { | ||
const removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs.rmdirSync.bind(fs); | ||
const removeCallback = _prepareRemoveCallback(removeFunction, name); | ||
|
||
/* istanbul ignore else */ | ||
if (!opts.keep) { | ||
_removeObjects.unshift(removeCallback); | ||
} | ||
const removeFunction = opts.unsafeCleanup ? _rimrafRemoveDirWrapper : fs.rmdir.bind(fs); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is technically calling |
||
const removeFunctionSync = opts.unsafeCleanup ? _rimrafRemoveDirSyncWrapper : fs.rmdirSync.bind(fs); | ||
const removeCallbackSync = _prepareRemoveCallback(removeFunctionSync, name); | ||
const removeCallback = _prepareRemoveCallback(removeFunction, name, removeCallbackSync); | ||
if (!opts.keep) _removeObjects.unshift(removeCallbackSync); | ||
|
||
return removeCallback; | ||
} | ||
|
@@ -464,24 +466,32 @@ function _prepareTmpDirRemoveCallback(name, opts) { | |
* @returns {Function} | ||
* @private | ||
*/ | ||
function _prepareRemoveCallback(removeFunction, arg) { | ||
function _prepareRemoveCallback(removeFunction, arg, cleanupCallbackSync) { | ||
var called = false; | ||
|
||
return function _cleanupCallback(next) { | ||
/* istanbul ignore else */ | ||
next = next || function () {}; | ||
if (!called) { | ||
const index = _removeObjects.indexOf(_cleanupCallback); | ||
const toRemove = cleanupCallbackSync || _cleanupCallback; | ||
const index = _removeObjects.indexOf(toRemove); | ||
/* istanbul ignore else */ | ||
if (index >= 0) { | ||
_removeObjects.splice(index, 1); | ||
} | ||
if (index >= 0) _removeObjects.splice(index, 1); | ||
|
||
called = true; | ||
removeFunction(arg); | ||
} | ||
|
||
/* istanbul ignore else */ | ||
if (next) next(null); | ||
// sync? | ||
if (removeFunction.length == 1) { | ||
try { | ||
removeFunction(arg); | ||
return next(null); | ||
} | ||
catch (err) { | ||
// if no next is provided and since we are | ||
// in silent cleanup mode on process exit, | ||
// we will ignore the error | ||
return next(err); | ||
} | ||
} else return removeFunction(arg, next); | ||
} else return next(new Error('cleanup callback has already been called')); | ||
}; | ||
} | ||
|
||
|
@@ -492,15 +502,13 @@ function _prepareRemoveCallback(removeFunction, arg) { | |
*/ | ||
function _garbageCollector() { | ||
/* istanbul ignore else */ | ||
if (!_gracefulCleanup) { | ||
return; | ||
} | ||
if (!_gracefulCleanup) return; | ||
|
||
// the function being called removes itself from _removeObjects, | ||
// loop until _removeObjects is empty | ||
while (_removeObjects.length) { | ||
try { | ||
_removeObjects[0].call(null); | ||
_removeObjects[0](); | ||
} catch (e) { | ||
// already removed? | ||
} | ||
|
@@ -555,12 +563,21 @@ function setGracefulCleanup() { | |
/** | ||
* If there are multiple different versions of tmp in place, make sure that | ||
* we recognize the old listeners. | ||
* | ||
* @param {Function} listener | ||
* @private | ||
* @returns {Boolean} true whether listener is a legacy listener | ||
*/ | ||
function _is_legacy_listener(listener) { | ||
return (listener.name == '_exit' || listener.name == '_uncaughtExceptionThrown') | ||
&& listener.toString().indexOf('_garbageCollector();') > -1; | ||
} | ||
|
||
/** | ||
* Safely install process exit listeners. | ||
* | ||
* @private | ||
*/ | ||
function _safely_install_listener() { | ||
var listeners = process.listeners(EVENT); | ||
|
||
|
@@ -570,7 +587,7 @@ function _safely_install_listener() { | |
var lstnr = listeners[i]; | ||
/* istanbul ignore else */ | ||
if (lstnr.name == '_tmp$safe_listener' || _is_legacy_listener(lstnr)) { | ||
/* istanbul ignore else */ | ||
// we must forget about the uncaughtException listener | ||
if (lstnr.name != '_uncaughtExceptionThrown') existingListeners.push(lstnr); | ||
process.removeListener(EVENT, lstnr); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, how did I miss this?