1 var fs = require('fs'),
2 path = require('path'),
3 cnst = require('constants');
5 var rimraf = require('rimraf'),
6 osTmpdir = require('os-tmpdir'),
7 rimrafSync = rimraf.sync;
11 var RDWR_EXCL = cnst.O_CREAT | cnst.O_TRUNC | cnst.O_RDWR | cnst.O_EXCL;
13 var generateName = function(rawAffixes, defaultPrefix) {
14 var affixes = parseAffixes(rawAffixes, defaultPrefix);
16 var name = [affixes.prefix,
17 now.getYear(), now.getMonth(), now.getDate(),
21 (Math.random() * 0x100000000 + 1).toString(36),
22 affixes.suffix].join('');
23 return path.join(affixes.dir || exports.dir, name);
26 var parseAffixes = function(rawAffixes, defaultPrefix) {
27 var affixes = {prefix: null, suffix: null};
29 switch (typeof(rawAffixes)) {
31 affixes.prefix = rawAffixes;
37 throw new Error("Unknown affix declaration: " + affixes);
40 affixes.prefix = defaultPrefix;
45 /* -------------------------------------------------------------------------
46 * Don't forget to call track() if you want file tracking and exit handlers!
47 * -------------------------------------------------------------------------
48 * When any temp file or directory is created, it is added to filesToDelete
49 * or dirsToDelete. The first time any temp file is created, a listener is
50 * added to remove all temp files and directories at exit.
53 var track = function(value) {
54 tracking = (value !== false);
55 return module.exports; // chainable
57 var exitListenerAttached = false;
58 var filesToDelete = [];
59 var dirsToDelete = [];
61 function deleteFileOnExit(filePath) {
62 if (!tracking) return false;
64 filesToDelete.push(filePath);
67 function deleteDirOnExit(dirPath) {
68 if (!tracking) return false;
70 dirsToDelete.push(dirPath);
73 function attachExitListener() {
74 if (!tracking) return false;
75 if (!exitListenerAttached) {
76 process.addListener('exit', cleanupSync);
77 exitListenerAttached = true;
81 function cleanupFilesSync() {
87 while ((toDelete = filesToDelete.shift()) !== undefined) {
94 function cleanupFiles(callback) {
97 callback(new Error("not tracking"));
102 var left = filesToDelete.length;
105 callback(null, count);
110 var rimrafCallback = function(err) {
112 // Prevent processing if aborted
116 // This shouldn't happen; pass error to callback and abort
127 if (!left && callback) {
128 callback(null, count);
131 while ((toDelete = filesToDelete.shift()) !== undefined) {
132 rimraf(toDelete, rimrafCallback);
136 function cleanupDirsSync() {
142 while ((toDelete = dirsToDelete.shift()) !== undefined) {
143 rimrafSync(toDelete);
149 function cleanupDirs(callback) {
152 callback(new Error("not tracking"));
157 var left = dirsToDelete.length;
160 callback(null, count);
165 var rimrafCallback = function (err) {
167 // Prevent processing if aborted
171 // rimraf handles most "normal" errors; pass the error to the
172 // callback and abort processing
174 callback(err, count);
182 if (!left && callback) {
183 callback(null, count);
186 while ((toDelete = dirsToDelete.shift()) !== undefined) {
187 rimraf(toDelete, rimrafCallback);
191 function cleanupSync() {
195 var fileCount = cleanupFilesSync();
196 var dirCount = cleanupDirsSync();
197 return {files: fileCount, dirs: dirCount};
200 function cleanup(callback) {
203 callback(new Error("not tracking"));
207 cleanupFiles(function(fileErr, fileCount) {
210 callback(fileErr, {files: fileCount})
213 cleanupDirs(function(dirErr, dirCount) {
215 callback(dirErr, {files: fileCount, dirs: dirCount});
224 function mkdir(affixes, callback) {
225 var dirPath = generateName(affixes, 'd-');
226 fs.mkdir(dirPath, 0700, function(err) {
228 deleteDirOnExit(dirPath);
231 callback(err, dirPath);
236 function mkdirSync(affixes) {
237 var dirPath = generateName(affixes, 'd-');
238 fs.mkdirSync(dirPath, 0700);
239 deleteDirOnExit(dirPath);
245 function open(affixes, callback) {
246 var filePath = generateName(affixes, 'f-');
247 fs.open(filePath, RDWR_EXCL, 0600, function(err, fd) {
249 deleteFileOnExit(filePath);
252 callback(err, {path: filePath, fd: fd});
257 function openSync(affixes) {
258 var filePath = generateName(affixes, 'f-');
259 var fd = fs.openSync(filePath, RDWR_EXCL, 0600);
260 deleteFileOnExit(filePath);
261 return {path: filePath, fd: fd};
264 function createWriteStream(affixes) {
265 var filePath = generateName(affixes, 's-');
266 var stream = fs.createWriteStream(filePath, {flags: RDWR_EXCL, mode: 0600});
267 deleteFileOnExit(filePath);
273 exports.dir = path.resolve(osTmpdir());
274 exports.track = track;
276 exports.mkdir = mkdir;
277 exports.mkdirSync = mkdirSync;
279 exports.openSync = openSync;
280 exports.path = generateName;
281 exports.cleanup = cleanup;
282 exports.cleanupSync = cleanupSync;
283 exports.createWriteStream = createWriteStream;