Skip to content

Commit 17b9080

Browse files
committed
fix #197: return sync callback when using the sync interface, otherwise return the async callback
1 parent 6e79e62 commit 17b9080

File tree

4 files changed

+124
-83
lines changed

4 files changed

+124
-83
lines changed

lib/tmp.js

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ const
4242
SIGINT = 'SIGINT',
4343

4444
// this will hold the objects need to be removed on exit
45-
_removeObjects = [];
45+
_removeObjects = [],
46+
47+
// API change in fs.rmdirSync leads to error when passing in a second parameter, e.g. the callback
48+
FN_RMDIR_SYNC = fs.rmdirSync.bind(fs);
4649

4750
var
4851
_gracefulCleanup = false;
@@ -251,7 +254,9 @@ function file(options, callback) {
251254
/* istanbul ignore else */
252255
if (err) return cb(err);
253256

257+
// FIXME overall handling of opts.discardDescriptor is off
254258
if (opts.discardDescriptor) {
259+
// FIXME? must not unlink as the user expects the filename to be reserved
255260
return fs.close(fd, function _discardCallback(err) {
256261
/* istanbul ignore else */
257262
if (err) {
@@ -270,12 +275,11 @@ function file(options, callback) {
270275
}
271276
cb(null, name, undefined, _prepareTmpFileRemoveCallback(name, -1, opts));
272277
});
278+
} else {
279+
// FIXME detachDescriptor passes the descriptor whereas discardDescriptor closes it
280+
const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor;
281+
cb(null, name, fd, _prepareTmpFileRemoveCallback(name, discardOrDetachDescriptor ? -1 : fd, opts, false));
273282
}
274-
/* istanbul ignore else */
275-
if (opts.detachDescriptor) {
276-
return cb(null, name, fd, _prepareTmpFileRemoveCallback(name, -1, opts));
277-
}
278-
cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
279283
});
280284
});
281285
}
@@ -304,7 +308,7 @@ function fileSync(options) {
304308
return {
305309
name: name,
306310
fd: fd,
307-
removeCallback: _prepareTmpFileRemoveCallback(name, discardOrDetachDescriptor ? -1 : fd, opts)
311+
removeCallback: _prepareTmpFileRemoveCallback(name, discardOrDetachDescriptor ? -1 : fd, opts, true)
308312
};
309313
}
310314

@@ -330,7 +334,7 @@ function dir(options, callback) {
330334
/* istanbul ignore else */
331335
if (err) return cb(err);
332336

333-
cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
337+
cb(null, name, _prepareTmpDirRemoveCallback(name, opts, false));
334338
});
335339
});
336340
}
@@ -352,7 +356,7 @@ function dirSync(options) {
352356

353357
return {
354358
name: name,
355-
removeCallback: _prepareTmpDirRemoveCallback(name, opts)
359+
removeCallback: _prepareTmpDirRemoveCallback(name, opts, true)
356360
};
357361
}
358362

@@ -370,7 +374,7 @@ function _removeFileAsync(fdPath, next) {
370374
return next(err);
371375
}
372376
next();
373-
}
377+
};
374378

375379
if (0 <= fdPath[0])
376380
fs.close(fdPath[0], function (err) {
@@ -405,19 +409,23 @@ function _removeFileSync(fdPath) {
405409
/**
406410
* Prepares the callback for removal of the temporary file.
407411
*
412+
* Returns either a sync callback or a async callback depending on whether
413+
* fileSync or file was called, which is expressed by the sync parameter.
414+
*
408415
* @param {string} name the path of the file
409416
* @param {number} fd file descriptor
410417
* @param {Object} opts
411-
* @returns {fileCallback}
418+
* @param {boolean} sync
419+
* @returns {fileCallback | fileCallbackSync}
412420
* @private
413421
*/
414-
function _prepareTmpFileRemoveCallback(name, fd, opts) {
415-
const removeCallbackSync = _prepareRemoveCallback(_removeFileSync, [fd, name]);
416-
const removeCallback = _prepareRemoveCallback(_removeFileAsync, [fd, name], removeCallbackSync);
422+
function _prepareTmpFileRemoveCallback(name, fd, opts, sync) {
423+
const removeCallbackSync = _prepareRemoveCallback(_removeFileSync, [fd, name], sync);
424+
const removeCallback = _prepareRemoveCallback(_removeFileAsync, [fd, name], sync, removeCallbackSync);
417425

418426
if (!opts.keep) _removeObjects.unshift(removeCallbackSync);
419427

420-
return removeCallback;
428+
return sync ? removeCallbackSync : removeCallback;
421429
}
422430

423431
/**
@@ -445,67 +453,62 @@ function _rimrafRemoveDirSyncWrapper(dirPath, next) {
445453
}
446454
}
447455

448-
const FN_RMDIR_SYNC = fs.rmdirSync.bind(fs);
449-
450456
/**
451457
* Prepares the callback for removal of the temporary directory.
452458
*
459+
* Returns either a sync callback or a async callback depending on whether
460+
* tmpFileSync or tmpFile was called, which is expressed by the sync parameter.
461+
*
453462
* @param {string} name
454463
* @param {Object} opts
464+
* @param {boolean} sync
455465
* @returns {Function} the callback
456466
* @private
457467
*/
458-
function _prepareTmpDirRemoveCallback(name, opts) {
468+
function _prepareTmpDirRemoveCallback(name, opts, sync) {
459469
const removeFunction = opts.unsafeCleanup ? _rimrafRemoveDirWrapper : fs.rmdir.bind(fs);
460470
const removeFunctionSync = opts.unsafeCleanup ? _rimrafRemoveDirSyncWrapper : FN_RMDIR_SYNC;
461-
const removeCallbackSync = _prepareRemoveCallback(removeFunctionSync, name);
462-
const removeCallback = _prepareRemoveCallback(removeFunction, name, removeCallbackSync);
471+
const removeCallbackSync = _prepareRemoveCallback(removeFunctionSync, name, sync);
472+
const removeCallback = _prepareRemoveCallback(removeFunction, name, sync, removeCallbackSync);
463473
if (!opts.keep) _removeObjects.unshift(removeCallbackSync);
464474

465-
return removeCallback;
475+
return sync ? removeCallbackSync : removeCallback;
466476
}
467477

468478
/**
469479
* Creates a guarded function wrapping the removeFunction call.
470480
*
481+
* The cleanup callback is save to be called multiple times.
482+
* Subsequent invocations will be ignored.
483+
*
471484
* @param {Function} removeFunction
472-
* @param {Object} arg
473-
* @returns {Function}
485+
* @param {string} fileOrDirName
486+
* @param {boolean} sync
487+
* @param {cleanupCallbackSync?} cleanupCallbackSync
488+
* @returns {cleanupCallback | cleanupCallbackSync}
474489
* @private
475490
*/
476-
function _prepareRemoveCallback(removeFunction, arg, cleanupCallbackSync) {
491+
function _prepareRemoveCallback(removeFunction, fileOrDirName, sync, cleanupCallbackSync) {
477492
var called = false;
478493

494+
// if sync is true, the next parameter will be ignored
479495
return function _cleanupCallback(next) {
480-
next = next || function () {};
496+
497+
/* istanbul ignore else */
481498
if (!called) {
499+
// remove cleanupCallback from cache
482500
const toRemove = cleanupCallbackSync || _cleanupCallback;
483501
const index = _removeObjects.indexOf(toRemove);
484502
/* istanbul ignore else */
485503
if (index >= 0) _removeObjects.splice(index, 1);
486504

487505
called = true;
488-
// sync?
489-
if (removeFunction.length === 1) {
490-
try {
491-
removeFunction(arg);
492-
return next(null);
493-
}
494-
catch (err) {
495-
// if no next is provided and since we are
496-
// in silent cleanup mode on process exit,
497-
// we will ignore the error
498-
return next(err);
499-
}
506+
if (sync || removeFunction === FN_RMDIR_SYNC) {
507+
return removeFunction(fileOrDirName);
500508
} else {
501-
// must no call rmdirSync/rmSync this way
502-
if (removeFunction == FN_RMDIR_SYNC) {
503-
return removeFunction(arg);
504-
} else {
505-
return removeFunction(arg, next);
506-
}
509+
return removeFunction(fileOrDirName, next || function() {});
507510
}
508-
} else return next(new Error('cleanup callback has already been called'));
511+
}
509512
};
510513
}
511514

@@ -726,18 +729,39 @@ _safely_install_sigint_listener();
726729
* @param {cleanupCallback} fn the cleanup callback function
727730
*/
728731

732+
/**
733+
* @callback fileCallbackSync
734+
* @param {?Error} err the error object if anything goes wrong
735+
* @param {string} name the temporary file name
736+
* @param {number} fd the file descriptor
737+
* @param {cleanupCallbackSync} fn the cleanup callback function
738+
*/
739+
729740
/**
730741
* @callback dirCallback
731742
* @param {?Error} err the error object if anything goes wrong
732743
* @param {string} name the temporary file name
733744
* @param {cleanupCallback} fn the cleanup callback function
734745
*/
735746

747+
/**
748+
* @callback dirCallbackSync
749+
* @param {?Error} err the error object if anything goes wrong
750+
* @param {string} name the temporary file name
751+
* @param {cleanupCallbackSync} fn the cleanup callback function
752+
*/
753+
736754
/**
737755
* Removes the temporary created file or directory.
738756
*
739757
* @callback cleanupCallback
740-
* @param {simpleCallback} [next] function to call after entry was removed
758+
* @param {simpleCallback} [next] function to call whenever the tmp object needs to be removed
759+
*/
760+
761+
/**
762+
* Removes the temporary created file or directory.
763+
*
764+
* @callback cleanupCallbackSync
741765
*/
742766

743767
/**

test/dir-sync-test.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
var
55
assert = require('assert'),
6-
fs = require('fs'),
76
path = require('path'),
87
inbandStandardTests = require('./inband-standard'),
98
childProcess = require('./child-process').genericChildProcess,
@@ -20,7 +19,7 @@ describe('tmp', function () {
2019
describe('when running inband standard tests', function () {
2120
inbandStandardTests(false, function before() {
2221
this.topic = tmp.dirSync(this.opts);
23-
});
22+
}, true);
2423

2524
describe('with invalid tries', function () {
2625
it('should result in an error on negative tries', function () {
@@ -68,8 +67,8 @@ describe('tmp', function () {
6867
if (!stderr) return done(new Error('stderr expected'));
6968
try {
7069
assertions.assertExists(stdout);
71-
} catch (err) {
7270
rimraf.sync(stdout);
71+
} catch (err) {
7372
return done(err);
7473
}
7574
done();
@@ -82,8 +81,8 @@ describe('tmp', function () {
8281
if (stderr) return done(new Error(stderr));
8382
try {
8483
assertions.assertExists(stdout);
85-
} catch (err) {
8684
rimraf.sync(stdout);
85+
} catch (err) {
8786
return done(err);
8887
}
8988
done();
@@ -130,8 +129,8 @@ describe('tmp', function () {
130129
} else {
131130
assertions.assertExists(path.join(stdout, 'symlinkme-target'));
132131
}
133-
} catch (err) {
134132
rimraf.sync(stdout);
133+
} catch (err) {
135134
return done(err);
136135
}
137136
done();

test/file-sync-test.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
var
55
assert = require('assert'),
6-
fs = require('fs'),
76
inbandStandardTests = require('./inband-standard'),
87
assertions = require('./assertions'),
98
childProcess = require('./child-process').genericChildProcess,
@@ -20,7 +19,7 @@ describe('tmp', function () {
2019
describe('when running inband standard tests', function () {
2120
inbandStandardTests(true, function before() {
2221
this.topic = tmp.fileSync(this.opts);
23-
});
22+
}, true);
2423

2524
describe('with invalid tries', function () {
2625
it('should result in an error on negative tries', function () {
@@ -71,8 +70,8 @@ describe('tmp', function () {
7170
if (!stderr) return done(new Error('stderr expected'));
7271
try {
7372
assertions.assertExists(stdout, true);
74-
} catch (err) {
7573
rimraf.sync(stdout);
74+
} catch (err) {
7675
return done(err);
7776
}
7877
done();
@@ -85,8 +84,8 @@ describe('tmp', function () {
8584
if (stderr) return done(new Error(stderr));
8685
try {
8786
assertions.assertExists(stdout, true);
88-
} catch (err) {
8987
rimraf.sync(stdout);
88+
} catch (err) {
9089
return done(err);
9190
}
9291
done();
@@ -122,6 +121,19 @@ describe('tmp', function () {
122121
done();
123122
});
124123
});
124+
it('on issue #115', function (done) {
125+
childProcess(this, 'issue115-sync.json', function (err, stderr, stdout) {
126+
if (err) return done(err);
127+
if (stderr) return done(new Error(stderr));
128+
try {
129+
assertions.assertDoesNotExist(stdout);
130+
} catch (err) {
131+
rimraf.sync(stdout);
132+
return done(err);
133+
}
134+
done();
135+
});
136+
});
125137
});
126138
});
127139
});

0 commit comments

Comments
 (0)