You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tmp.js 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. /*!
  2. * Tmp
  3. *
  4. * Copyright (c) 2011-2017 KARASZI Istvan <github@spam.raszi.hu>
  5. *
  6. * MIT Licensed
  7. */
  8. /*
  9. * Module dependencies.
  10. */
  11. const fs = require('fs');
  12. const os = require('os');
  13. const path = require('path');
  14. const crypto = require('crypto');
  15. const _c = { fs: fs.constants, os: os.constants };
  16. /*
  17. * The working inner variables.
  18. */
  19. const
  20. // the random characters to choose from
  21. RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
  22. TEMPLATE_PATTERN = /XXXXXX/,
  23. DEFAULT_TRIES = 3,
  24. CREATE_FLAGS = (_c.O_CREAT || _c.fs.O_CREAT) | (_c.O_EXCL || _c.fs.O_EXCL) | (_c.O_RDWR || _c.fs.O_RDWR),
  25. // constants are off on the windows platform and will not match the actual errno codes
  26. IS_WIN32 = os.platform() === 'win32',
  27. EBADF = _c.EBADF || _c.os.errno.EBADF,
  28. ENOENT = _c.ENOENT || _c.os.errno.ENOENT,
  29. DIR_MODE = 0o700 /* 448 */,
  30. FILE_MODE = 0o600 /* 384 */,
  31. EXIT = 'exit',
  32. // this will hold the objects need to be removed on exit
  33. _removeObjects = [],
  34. // API change in fs.rmdirSync leads to error when passing in a second parameter, e.g. the callback
  35. FN_RMDIR_SYNC = fs.rmdirSync.bind(fs);
  36. let
  37. _gracefulCleanup = false;
  38. /**
  39. * Recursively remove a directory and its contents.
  40. *
  41. * @param {string} dirPath path of directory to remove
  42. * @param {Function} callback
  43. * @private
  44. */
  45. function rimraf(dirPath, callback) {
  46. return fs.rm(dirPath, { recursive: true }, callback);
  47. }
  48. /**
  49. * Recursively remove a directory and its contents, synchronously.
  50. *
  51. * @param {string} dirPath path of directory to remove
  52. * @private
  53. */
  54. function FN_RIMRAF_SYNC(dirPath) {
  55. return fs.rmSync(dirPath, { recursive: true });
  56. }
  57. /**
  58. * Gets a temporary file name.
  59. *
  60. * @param {(Options|tmpNameCallback)} options options or callback
  61. * @param {?tmpNameCallback} callback the callback function
  62. */
  63. function tmpName(options, callback) {
  64. const
  65. args = _parseArguments(options, callback),
  66. opts = args[0],
  67. cb = args[1];
  68. try {
  69. _assertAndSanitizeOptions(opts);
  70. } catch (err) {
  71. return cb(err);
  72. }
  73. let tries = opts.tries;
  74. (function _getUniqueName() {
  75. try {
  76. const name = _generateTmpName(opts);
  77. // check whether the path exists then retry if needed
  78. fs.stat(name, function (err) {
  79. /* istanbul ignore else */
  80. if (!err) {
  81. /* istanbul ignore else */
  82. if (tries-- > 0) return _getUniqueName();
  83. return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name));
  84. }
  85. cb(null, name);
  86. });
  87. } catch (err) {
  88. cb(err);
  89. }
  90. }());
  91. }
  92. /**
  93. * Synchronous version of tmpName.
  94. *
  95. * @param {Object} options
  96. * @returns {string} the generated random name
  97. * @throws {Error} if the options are invalid or could not generate a filename
  98. */
  99. function tmpNameSync(options) {
  100. const
  101. args = _parseArguments(options),
  102. opts = args[0];
  103. _assertAndSanitizeOptions(opts);
  104. let tries = opts.tries;
  105. do {
  106. const name = _generateTmpName(opts);
  107. try {
  108. fs.statSync(name);
  109. } catch (e) {
  110. return name;
  111. }
  112. } while (tries-- > 0);
  113. throw new Error('Could not get a unique tmp filename, max tries reached');
  114. }
  115. /**
  116. * Creates and opens a temporary file.
  117. *
  118. * @param {(Options|null|undefined|fileCallback)} options the config options or the callback function or null or undefined
  119. * @param {?fileCallback} callback
  120. */
  121. function file(options, callback) {
  122. const
  123. args = _parseArguments(options, callback),
  124. opts = args[0],
  125. cb = args[1];
  126. // gets a temporary filename
  127. tmpName(opts, function _tmpNameCreated(err, name) {
  128. /* istanbul ignore else */
  129. if (err) return cb(err);
  130. // create and open the file
  131. fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) {
  132. /* istanbu ignore else */
  133. if (err) return cb(err);
  134. if (opts.discardDescriptor) {
  135. return fs.close(fd, function _discardCallback(possibleErr) {
  136. // the chance of getting an error on close here is rather low and might occur in the most edgiest cases only
  137. return cb(possibleErr, name, undefined, _prepareTmpFileRemoveCallback(name, -1, opts, false));
  138. });
  139. } else {
  140. // detachDescriptor passes the descriptor whereas discardDescriptor closes it, either way, we no longer care
  141. // about the descriptor
  142. const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor;
  143. cb(null, name, fd, _prepareTmpFileRemoveCallback(name, discardOrDetachDescriptor ? -1 : fd, opts, false));
  144. }
  145. });
  146. });
  147. }
  148. /**
  149. * Synchronous version of file.
  150. *
  151. * @param {Options} options
  152. * @returns {FileSyncObject} object consists of name, fd and removeCallback
  153. * @throws {Error} if cannot create a file
  154. */
  155. function fileSync(options) {
  156. const
  157. args = _parseArguments(options),
  158. opts = args[0];
  159. const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor;
  160. const name = tmpNameSync(opts);
  161. var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);
  162. /* istanbul ignore else */
  163. if (opts.discardDescriptor) {
  164. fs.closeSync(fd);
  165. fd = undefined;
  166. }
  167. return {
  168. name: name,
  169. fd: fd,
  170. removeCallback: _prepareTmpFileRemoveCallback(name, discardOrDetachDescriptor ? -1 : fd, opts, true)
  171. };
  172. }
  173. /**
  174. * Creates a temporary directory.
  175. *
  176. * @param {(Options|dirCallback)} options the options or the callback function
  177. * @param {?dirCallback} callback
  178. */
  179. function dir(options, callback) {
  180. const
  181. args = _parseArguments(options, callback),
  182. opts = args[0],
  183. cb = args[1];
  184. // gets a temporary filename
  185. tmpName(opts, function _tmpNameCreated(err, name) {
  186. /* istanbul ignore else */
  187. if (err) return cb(err);
  188. // create the directory
  189. fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) {
  190. /* istanbul ignore else */
  191. if (err) return cb(err);
  192. cb(null, name, _prepareTmpDirRemoveCallback(name, opts, false));
  193. });
  194. });
  195. }
  196. /**
  197. * Synchronous version of dir.
  198. *
  199. * @param {Options} options
  200. * @returns {DirSyncObject} object consists of name and removeCallback
  201. * @throws {Error} if it cannot create a directory
  202. */
  203. function dirSync(options) {
  204. const
  205. args = _parseArguments(options),
  206. opts = args[0];
  207. const name = tmpNameSync(opts);
  208. fs.mkdirSync(name, opts.mode || DIR_MODE);
  209. return {
  210. name: name,
  211. removeCallback: _prepareTmpDirRemoveCallback(name, opts, true)
  212. };
  213. }
  214. /**
  215. * Removes files asynchronously.
  216. *
  217. * @param {Object} fdPath
  218. * @param {Function} next
  219. * @private
  220. */
  221. function _removeFileAsync(fdPath, next) {
  222. const _handler = function (err) {
  223. if (err && !_isENOENT(err)) {
  224. // reraise any unanticipated error
  225. return next(err);
  226. }
  227. next();
  228. };
  229. if (0 <= fdPath[0])
  230. fs.close(fdPath[0], function () {
  231. fs.unlink(fdPath[1], _handler);
  232. });
  233. else fs.unlink(fdPath[1], _handler);
  234. }
  235. /**
  236. * Removes files synchronously.
  237. *
  238. * @param {Object} fdPath
  239. * @private
  240. */
  241. function _removeFileSync(fdPath) {
  242. let rethrownException = null;
  243. try {
  244. if (0 <= fdPath[0]) fs.closeSync(fdPath[0]);
  245. } catch (e) {
  246. // reraise any unanticipated error
  247. if (!_isEBADF(e) && !_isENOENT(e)) throw e;
  248. } finally {
  249. try {
  250. fs.unlinkSync(fdPath[1]);
  251. }
  252. catch (e) {
  253. // reraise any unanticipated error
  254. if (!_isENOENT(e)) rethrownException = e;
  255. }
  256. }
  257. if (rethrownException !== null) {
  258. throw rethrownException;
  259. }
  260. }
  261. /**
  262. * Prepares the callback for removal of the temporary file.
  263. *
  264. * Returns either a sync callback or a async callback depending on whether
  265. * fileSync or file was called, which is expressed by the sync parameter.
  266. *
  267. * @param {string} name the path of the file
  268. * @param {number} fd file descriptor
  269. * @param {Object} opts
  270. * @param {boolean} sync
  271. * @returns {fileCallback | fileCallbackSync}
  272. * @private
  273. */
  274. function _prepareTmpFileRemoveCallback(name, fd, opts, sync) {
  275. const removeCallbackSync = _prepareRemoveCallback(_removeFileSync, [fd, name], sync);
  276. const removeCallback = _prepareRemoveCallback(_removeFileAsync, [fd, name], sync, removeCallbackSync);
  277. if (!opts.keep) _removeObjects.unshift(removeCallbackSync);
  278. return sync ? removeCallbackSync : removeCallback;
  279. }
  280. /**
  281. * Prepares the callback for removal of the temporary directory.
  282. *
  283. * Returns either a sync callback or a async callback depending on whether
  284. * tmpFileSync or tmpFile was called, which is expressed by the sync parameter.
  285. *
  286. * @param {string} name
  287. * @param {Object} opts
  288. * @param {boolean} sync
  289. * @returns {Function} the callback
  290. * @private
  291. */
  292. function _prepareTmpDirRemoveCallback(name, opts, sync) {
  293. const removeFunction = opts.unsafeCleanup ? rimraf : fs.rmdir.bind(fs);
  294. const removeFunctionSync = opts.unsafeCleanup ? FN_RIMRAF_SYNC : FN_RMDIR_SYNC;
  295. const removeCallbackSync = _prepareRemoveCallback(removeFunctionSync, name, sync);
  296. const removeCallback = _prepareRemoveCallback(removeFunction, name, sync, removeCallbackSync);
  297. if (!opts.keep) _removeObjects.unshift(removeCallbackSync);
  298. return sync ? removeCallbackSync : removeCallback;
  299. }
  300. /**
  301. * Creates a guarded function wrapping the removeFunction call.
  302. *
  303. * The cleanup callback is save to be called multiple times.
  304. * Subsequent invocations will be ignored.
  305. *
  306. * @param {Function} removeFunction
  307. * @param {string} fileOrDirName
  308. * @param {boolean} sync
  309. * @param {cleanupCallbackSync?} cleanupCallbackSync
  310. * @returns {cleanupCallback | cleanupCallbackSync}
  311. * @private
  312. */
  313. function _prepareRemoveCallback(removeFunction, fileOrDirName, sync, cleanupCallbackSync) {
  314. let called = false;
  315. // if sync is true, the next parameter will be ignored
  316. return function _cleanupCallback(next) {
  317. /* istanbul ignore else */
  318. if (!called) {
  319. // remove cleanupCallback from cache
  320. const toRemove = cleanupCallbackSync || _cleanupCallback;
  321. const index = _removeObjects.indexOf(toRemove);
  322. /* istanbul ignore else */
  323. if (index >= 0) _removeObjects.splice(index, 1);
  324. called = true;
  325. if (sync || removeFunction === FN_RMDIR_SYNC || removeFunction === FN_RIMRAF_SYNC) {
  326. return removeFunction(fileOrDirName);
  327. } else {
  328. return removeFunction(fileOrDirName, next || function() {});
  329. }
  330. }
  331. };
  332. }
  333. /**
  334. * The garbage collector.
  335. *
  336. * @private
  337. */
  338. function _garbageCollector() {
  339. /* istanbul ignore else */
  340. if (!_gracefulCleanup) return;
  341. // the function being called removes itself from _removeObjects,
  342. // loop until _removeObjects is empty
  343. while (_removeObjects.length) {
  344. try {
  345. _removeObjects[0]();
  346. } catch (e) {
  347. // already removed?
  348. }
  349. }
  350. }
  351. /**
  352. * Random name generator based on crypto.
  353. * Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript
  354. *
  355. * @param {number} howMany
  356. * @returns {string} the generated random name
  357. * @private
  358. */
  359. function _randomChars(howMany) {
  360. let
  361. value = [],
  362. rnd = null;
  363. // make sure that we do not fail because we ran out of entropy
  364. try {
  365. rnd = crypto.randomBytes(howMany);
  366. } catch (e) {
  367. rnd = crypto.pseudoRandomBytes(howMany);
  368. }
  369. for (var i = 0; i < howMany; i++) {
  370. value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]);
  371. }
  372. return value.join('');
  373. }
  374. /**
  375. * Helper which determines whether a string s is blank, that is undefined, or empty or null.
  376. *
  377. * @private
  378. * @param {string} s
  379. * @returns {Boolean} true whether the string s is blank, false otherwise
  380. */
  381. function _isBlank(s) {
  382. return s === null || _isUndefined(s) || !s.trim();
  383. }
  384. /**
  385. * Checks whether the `obj` parameter is defined or not.
  386. *
  387. * @param {Object} obj
  388. * @returns {boolean} true if the object is undefined
  389. * @private
  390. */
  391. function _isUndefined(obj) {
  392. return typeof obj === 'undefined';
  393. }
  394. /**
  395. * Parses the function arguments.
  396. *
  397. * This function helps to have optional arguments.
  398. *
  399. * @param {(Options|null|undefined|Function)} options
  400. * @param {?Function} callback
  401. * @returns {Array} parsed arguments
  402. * @private
  403. */
  404. function _parseArguments(options, callback) {
  405. /* istanbul ignore else */
  406. if (typeof options === 'function') {
  407. return [{}, options];
  408. }
  409. /* istanbul ignore else */
  410. if (_isUndefined(options)) {
  411. return [{}, callback];
  412. }
  413. // copy options so we do not leak the changes we make internally
  414. const actualOptions = {};
  415. for (const key of Object.getOwnPropertyNames(options)) {
  416. actualOptions[key] = options[key];
  417. }
  418. return [actualOptions, callback];
  419. }
  420. /**
  421. * Generates a new temporary name.
  422. *
  423. * @param {Object} opts
  424. * @returns {string} the new random name according to opts
  425. * @private
  426. */
  427. function _generateTmpName(opts) {
  428. const tmpDir = opts.tmpdir;
  429. /* istanbul ignore else */
  430. if (!_isUndefined(opts.name))
  431. return path.join(tmpDir, opts.dir, opts.name);
  432. /* istanbul ignore else */
  433. if (!_isUndefined(opts.template))
  434. return path.join(tmpDir, opts.dir, opts.template).replace(TEMPLATE_PATTERN, _randomChars(6));
  435. // prefix and postfix
  436. const name = [
  437. opts.prefix ? opts.prefix : 'tmp',
  438. '-',
  439. process.pid,
  440. '-',
  441. _randomChars(12),
  442. opts.postfix ? '-' + opts.postfix : ''
  443. ].join('');
  444. return path.join(tmpDir, opts.dir, name);
  445. }
  446. /**
  447. * Asserts whether the specified options are valid, also sanitizes options and provides sane defaults for missing
  448. * options.
  449. *
  450. * @param {Options} options
  451. * @private
  452. */
  453. function _assertAndSanitizeOptions(options) {
  454. options.tmpdir = _getTmpDir(options);
  455. const tmpDir = options.tmpdir;
  456. /* istanbul ignore else */
  457. if (!_isUndefined(options.name))
  458. _assertIsRelative(options.name, 'name', tmpDir);
  459. /* istanbul ignore else */
  460. if (!_isUndefined(options.dir))
  461. _assertIsRelative(options.dir, 'dir', tmpDir);
  462. /* istanbul ignore else */
  463. if (!_isUndefined(options.template)) {
  464. _assertIsRelative(options.template, 'template', tmpDir);
  465. if (!options.template.match(TEMPLATE_PATTERN))
  466. throw new Error(`Invalid template, found "${options.template}".`);
  467. }
  468. /* istanbul ignore else */
  469. if (!_isUndefined(options.tries) && isNaN(options.tries) || options.tries < 0)
  470. throw new Error(`Invalid tries, found "${options.tries}".`);
  471. // if a name was specified we will try once
  472. options.tries = _isUndefined(options.name) ? options.tries || DEFAULT_TRIES : 1;
  473. options.keep = !!options.keep;
  474. options.detachDescriptor = !!options.detachDescriptor;
  475. options.discardDescriptor = !!options.discardDescriptor;
  476. options.unsafeCleanup = !!options.unsafeCleanup;
  477. // sanitize dir, also keep (multiple) blanks if the user, purportedly sane, requests us to
  478. options.dir = _isUndefined(options.dir) ? '' : path.relative(tmpDir, _resolvePath(options.dir, tmpDir));
  479. options.template = _isUndefined(options.template) ? undefined : path.relative(tmpDir, _resolvePath(options.template, tmpDir));
  480. // sanitize further if template is relative to options.dir
  481. options.template = _isBlank(options.template) ? undefined : path.relative(options.dir, options.template);
  482. // for completeness' sake only, also keep (multiple) blanks if the user, purportedly sane, requests us to
  483. options.name = _isUndefined(options.name) ? undefined : options.name;
  484. options.prefix = _isUndefined(options.prefix) ? '' : options.prefix;
  485. options.postfix = _isUndefined(options.postfix) ? '' : options.postfix;
  486. }
  487. /**
  488. * Resolve the specified path name in respect to tmpDir.
  489. *
  490. * The specified name might include relative path components, e.g. ../
  491. * so we need to resolve in order to be sure that is is located inside tmpDir
  492. *
  493. * @param name
  494. * @param tmpDir
  495. * @returns {string}
  496. * @private
  497. */
  498. function _resolvePath(name, tmpDir) {
  499. if (name.startsWith(tmpDir)) {
  500. return path.resolve(name);
  501. } else {
  502. return path.resolve(path.join(tmpDir, name));
  503. }
  504. }
  505. /**
  506. * Asserts whether specified name is relative to the specified tmpDir.
  507. *
  508. * @param {string} name
  509. * @param {string} option
  510. * @param {string} tmpDir
  511. * @throws {Error}
  512. * @private
  513. */
  514. function _assertIsRelative(name, option, tmpDir) {
  515. if (option === 'name') {
  516. // assert that name is not absolute and does not contain a path
  517. if (path.isAbsolute(name))
  518. throw new Error(`${option} option must not contain an absolute path, found "${name}".`);
  519. // must not fail on valid .<name> or ..<name> or similar such constructs
  520. let basename = path.basename(name);
  521. if (basename === '..' || basename === '.' || basename !== name)
  522. throw new Error(`${option} option must not contain a path, found "${name}".`);
  523. }
  524. else { // if (option === 'dir' || option === 'template') {
  525. // assert that dir or template are relative to tmpDir
  526. if (path.isAbsolute(name) && !name.startsWith(tmpDir)) {
  527. throw new Error(`${option} option must be relative to "${tmpDir}", found "${name}".`);
  528. }
  529. let resolvedPath = _resolvePath(name, tmpDir);
  530. if (!resolvedPath.startsWith(tmpDir))
  531. throw new Error(`${option} option must be relative to "${tmpDir}", found "${resolvedPath}".`);
  532. }
  533. }
  534. /**
  535. * Helper for testing against EBADF to compensate changes made to Node 7.x under Windows.
  536. *
  537. * @private
  538. */
  539. function _isEBADF(error) {
  540. return _isExpectedError(error, -EBADF, 'EBADF');
  541. }
  542. /**
  543. * Helper for testing against ENOENT to compensate changes made to Node 7.x under Windows.
  544. *
  545. * @private
  546. */
  547. function _isENOENT(error) {
  548. return _isExpectedError(error, -ENOENT, 'ENOENT');
  549. }
  550. /**
  551. * Helper to determine whether the expected error code matches the actual code and errno,
  552. * which will differ between the supported node versions.
  553. *
  554. * - Node >= 7.0:
  555. * error.code {string}
  556. * error.errno {number} any numerical value will be negated
  557. *
  558. * CAVEAT
  559. *
  560. * On windows, the errno for EBADF is -4083 but os.constants.errno.EBADF is different and we must assume that ENOENT
  561. * is no different here.
  562. *
  563. * @param {SystemError} error
  564. * @param {number} errno
  565. * @param {string} code
  566. * @private
  567. */
  568. function _isExpectedError(error, errno, code) {
  569. return IS_WIN32 ? error.code === code : error.code === code && error.errno === errno;
  570. }
  571. /**
  572. * Sets the graceful cleanup.
  573. *
  574. * If graceful cleanup is set, tmp will remove all controlled temporary objects on process exit, otherwise the
  575. * temporary objects will remain in place, waiting to be cleaned up on system restart or otherwise scheduled temporary
  576. * object removals.
  577. */
  578. function setGracefulCleanup() {
  579. _gracefulCleanup = true;
  580. }
  581. /**
  582. * Returns the currently configured tmp dir from os.tmpdir().
  583. *
  584. * @private
  585. * @param {?Options} options
  586. * @returns {string} the currently configured tmp dir
  587. */
  588. function _getTmpDir(options) {
  589. return path.resolve(options && options.tmpdir || os.tmpdir());
  590. }
  591. // Install process exit listener
  592. process.addListener(EXIT, _garbageCollector);
  593. /**
  594. * Configuration options.
  595. *
  596. * @typedef {Object} Options
  597. * @property {?boolean} keep the temporary object (file or dir) will not be garbage collected
  598. * @property {?number} tries the number of tries before give up the name generation
  599. * @property (?int) mode the access mode, defaults are 0o700 for directories and 0o600 for files
  600. * @property {?string} template the "mkstemp" like filename template
  601. * @property {?string} name fixed name relative to tmpdir or the specified dir option
  602. * @property {?string} dir tmp directory relative to the root tmp directory in use
  603. * @property {?string} prefix prefix for the generated name
  604. * @property {?string} postfix postfix for the generated name
  605. * @property {?string} tmpdir the root tmp directory which overrides the os tmpdir
  606. * @property {?boolean} unsafeCleanup recursively removes the created temporary directory, even when it's not empty
  607. * @property {?boolean} detachDescriptor detaches the file descriptor, caller is responsible for closing the file, tmp will no longer try closing the file during garbage collection
  608. * @property {?boolean} discardDescriptor discards the file descriptor (closes file, fd is -1), tmp will no longer try closing the file during garbage collection
  609. */
  610. /**
  611. * @typedef {Object} FileSyncObject
  612. * @property {string} name the name of the file
  613. * @property {string} fd the file descriptor or -1 if the fd has been discarded
  614. * @property {fileCallback} removeCallback the callback function to remove the file
  615. */
  616. /**
  617. * @typedef {Object} DirSyncObject
  618. * @property {string} name the name of the directory
  619. * @property {fileCallback} removeCallback the callback function to remove the directory
  620. */
  621. /**
  622. * @callback tmpNameCallback
  623. * @param {?Error} err the error object if anything goes wrong
  624. * @param {string} name the temporary file name
  625. */
  626. /**
  627. * @callback fileCallback
  628. * @param {?Error} err the error object if anything goes wrong
  629. * @param {string} name the temporary file name
  630. * @param {number} fd the file descriptor or -1 if the fd had been discarded
  631. * @param {cleanupCallback} fn the cleanup callback function
  632. */
  633. /**
  634. * @callback fileCallbackSync
  635. * @param {?Error} err the error object if anything goes wrong
  636. * @param {string} name the temporary file name
  637. * @param {number} fd the file descriptor or -1 if the fd had been discarded
  638. * @param {cleanupCallbackSync} fn the cleanup callback function
  639. */
  640. /**
  641. * @callback dirCallback
  642. * @param {?Error} err the error object if anything goes wrong
  643. * @param {string} name the temporary file name
  644. * @param {cleanupCallback} fn the cleanup callback function
  645. */
  646. /**
  647. * @callback dirCallbackSync
  648. * @param {?Error} err the error object if anything goes wrong
  649. * @param {string} name the temporary file name
  650. * @param {cleanupCallbackSync} fn the cleanup callback function
  651. */
  652. /**
  653. * Removes the temporary created file or directory.
  654. *
  655. * @callback cleanupCallback
  656. * @param {simpleCallback} [next] function to call whenever the tmp object needs to be removed
  657. */
  658. /**
  659. * Removes the temporary created file or directory.
  660. *
  661. * @callback cleanupCallbackSync
  662. */
  663. /**
  664. * Callback function for function composition.
  665. * @see {@link https://github.com/raszi/node-tmp/issues/57|raszi/node-tmp#57}
  666. *
  667. * @callback simpleCallback
  668. */
  669. // exporting all the needed methods
  670. // evaluate _getTmpDir() lazily, mainly for simplifying testing but it also will
  671. // allow users to reconfigure the temporary directory
  672. Object.defineProperty(module.exports, 'tmpdir', {
  673. enumerable: true,
  674. configurable: false,
  675. get: function () {
  676. return _getTmpDir();
  677. }
  678. });
  679. module.exports.dir = dir;
  680. module.exports.dirSync = dirSync;
  681. module.exports.file = file;
  682. module.exports.fileSync = fileSync;
  683. module.exports.tmpName = tmpName;
  684. module.exports.tmpNameSync = tmpNameSync;
  685. module.exports.setGracefulCleanup = setGracefulCleanup;