'use strict'; var PromiseA; try { PromiseA = require('bluebird'); } catch(e) { PromiseA = global.Promise; } var util = require('util'); var promisify = util.promisify || PromiseA.promisify; if (!PromiseA || !promisify) { throw new Error("DON'T PANIC. Everything is A-OK." + " However, you're on a really old version of node. All you need to do is `npm install --save bluebird`" + " (in your project directory, which is probably '" + require('path').dirname(require.main.filename) + "')" + " and everything will work just fine."); } var fs = require('fs'); var writeFileAsync = promisify(fs.writeFile); var unlinkAsync = promisify(fs.unlink); var renameAsync = promisify(fs.rename); var crypto = require('crypto'); function noop() { } function create(options) { if (!options) { options = {}; } if (!options.tmp) { options.tmp = 'tmp'; } if (!options.bak) { options.bak = 'bak'; } if (options.tmp === options.bak) { throw new Error("'tmp' and 'bak' suffixes cannot be the same... duh"); } var tmpnamefn = options.tmpnamefn || function (pathname) { return pathname + '.' + crypto.randomBytes(8).toString('hex') + '.' + options.tmp; }; var baknamefn = options.baknamefn || function (pathname) { return pathname + '.' + options.bak; }; /* var namefn = options.namefn || function (pathname) { return pathname; }; */ var sfs = { writeFileAsync: function (filename, data, options) { return sfs.stage(filename, data, options).then(function (tmpname) { //console.log(filename); return sfs.commit(tmpname, filename); }); } , stageAsync: function (filename, data, options) { var tmpname = tmpnamefn(filename); //console.log(tmpname); return writeFileAsync(tmpname, data, options).then(function () { return tmpname; }); } , commitAsync: function (tmpname, filename) { var bakname = baknamefn(filename); // this may not exist return unlinkAsync(bakname).then(noop, noop).then(function () { // this may not exist //console.log(namefn(filename), '->', bakname); return renameAsync(filename, bakname).then(function () { //console.log('created bak'); }, noop); }).then(function () { // this must be successful //console.log(filename, '->', filename); return renameAsync(tmpname, filename).then(noop, function (err) { //console.error(err); return sfs.revert(filename).then(function () { return PromiseA.reject(err); }); }); }); } , revertAsync: function (filename) { return new PromiseA(function (resolve, reject) { var bakname = baknamefn(filename); var tmpname = tmpnamefn(filename); var reader = fs.createReadStream(bakname); var writer = fs.createWriteStream(tmpname); reader.on('error', reject); writer.on('error', reject); reader.pipe(writer); writer.on('close', function () { sfs.commit(tmpname, filename).then(resolve, reject); }); }); } , tmpnamefn: tmpnamefn , baknamefn: baknamefn , create: create }; sfs.writeFile = sfs.writeFileAsync; sfs.stage = sfs.stageAsync; sfs.commit = sfs.commitAsync; sfs.revert = sfs.revertAsync; return sfs; } module.exports = create();