write-file-atomic
Advanced tools
Comparing version 1.1.3 to 1.1.4
71
index.js
'use strict' | ||
var fs = require('graceful-fs'); | ||
var chain = require('slide').chain; | ||
var crypto = require('crypto'); | ||
var fs = require('graceful-fs') | ||
var chain = require('slide').chain | ||
var MurmurHash3 = require('imurmurhash') | ||
var md5hex = function () { | ||
var hash = crypto.createHash('md5'); | ||
for (var ii=0; ii<arguments.length; ++ii) hash.update(''+arguments[ii]) | ||
return hash.digest('hex') | ||
function murmurhex () { | ||
var hash = new MurmurHash3() | ||
for (var ii = 0; ii < arguments.length; ++ii) hash.hash('' + arguments[ii]) | ||
return hash.result() | ||
} | ||
var invocations = 0; | ||
var invocations = 0 | ||
var getTmpname = function (filename) { | ||
return filename + "." + md5hex(__filename, process.pid, ++invocations) | ||
return filename + '.' + murmurhex(__filename, process.pid, ++invocations) | ||
} | ||
module.exports = function writeFile(filename, data, options, callback) { | ||
if (options instanceof Function) { | ||
callback = options; | ||
options = null; | ||
} | ||
if (!options) options = {}; | ||
var tmpfile = getTmpname(filename); | ||
chain([ | ||
[fs, fs.writeFile, tmpfile, data, options], | ||
options.chown && [fs, fs.chown, tmpfile, options.chown.uid, options.chown.gid], | ||
[fs, fs.rename, tmpfile, filename] | ||
], function (err) { | ||
err ? fs.unlink(tmpfile, function () { callback(err) }) | ||
: callback() | ||
}) | ||
module.exports = function writeFile (filename, data, options, callback) { | ||
if (options instanceof Function) { | ||
callback = options | ||
options = null | ||
} | ||
if (!options) options = {} | ||
var tmpfile = getTmpname(filename) | ||
chain([ | ||
[fs, fs.writeFile, tmpfile, data, options], | ||
options.chown && [fs, fs.chown, tmpfile, options.chown.uid, options.chown.gid], | ||
[fs, fs.rename, tmpfile, filename] | ||
], function (err) { | ||
err ? fs.unlink(tmpfile, function () { callback(err) }) | ||
: callback() | ||
}) | ||
} | ||
module.exports.sync = function writeFileSync(filename, data, options) { | ||
if (!options) options = {}; | ||
var tmpfile = getTmpname(filename); | ||
try { | ||
fs.writeFileSync(tmpfile, data, options); | ||
if (options.chown) fs.chownSync(tmpfile, options.chown.uid, options.chown.gid); | ||
fs.renameSync(tmpfile, filename); | ||
} | ||
catch (err) { | ||
try { fs.unlinkSync(tmpfile) } catch(e) {} | ||
throw err; | ||
} | ||
module.exports.sync = function writeFileSync (filename, data, options) { | ||
if (!options) options = {} | ||
var tmpfile = getTmpname(filename) | ||
try { | ||
fs.writeFileSync(tmpfile, data, options) | ||
if (options.chown) fs.chownSync(tmpfile, options.chown.uid, options.chown.gid) | ||
fs.renameSync(tmpfile, filename) | ||
} catch (err) { | ||
try { fs.unlinkSync(tmpfile) } catch (e) {} | ||
throw err | ||
} | ||
} |
{ | ||
"name": "write-file-atomic", | ||
"version": "1.1.3", | ||
"version": "1.1.4", | ||
"description": "Write files in an atomic fashion w/configurable ownership", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "tap test/*.js" | ||
"test": "standard && tap --coverage test/*.js" | ||
}, | ||
@@ -25,2 +25,3 @@ "repository": { | ||
"graceful-fs": "^4.1.2", | ||
"imurmurhash": "^0.1.4", | ||
"slide": "^1.1.5" | ||
@@ -30,4 +31,5 @@ }, | ||
"require-inject": "^1.1.0", | ||
"tap": "^0.4.12" | ||
"standard": "^5.4.1", | ||
"tap": "^2.3.1" | ||
} | ||
} |
@@ -1,97 +0,97 @@ | ||
"use strict"; | ||
var test = require('tap').test; | ||
var requireInject = require('require-inject'); | ||
'use strict' | ||
var test = require('tap').test | ||
var requireInject = require('require-inject') | ||
var writeFileAtomic = requireInject('../index', { | ||
'graceful-fs': { | ||
writeFile: function (tmpfile, data, options, cb) { | ||
if (/nowrite/.test(tmpfile)) return cb('ENOWRITE'); | ||
cb(); | ||
}, | ||
chown: function (tmpfile, uid, gid, cb) { | ||
if (/nochown/.test(tmpfile)) return cb('ENOCHOWN'); | ||
cb(); | ||
}, | ||
rename: function (tmpfile, filename, cb) { | ||
if (/norename/.test(tmpfile)) return cb('ENORENAME'); | ||
cb(); | ||
}, | ||
unlink: function (tmpfile, cb) { | ||
if (/nounlink/.test(tmpfile)) return cb('ENOUNLINK'); | ||
cb(); | ||
}, | ||
writeFileSync: function (tmpfile, data, options) { | ||
if (/nowrite/.test(tmpfile)) throw 'ENOWRITE'; | ||
}, | ||
chownSync: function (tmpfile, uid, gid) { | ||
if (/nochown/.test(tmpfile)) throw 'ENOCHOWN'; | ||
}, | ||
renameSync: function (tmpfile, filename) { | ||
if (/norename/.test(tmpfile)) throw 'ENORENAME'; | ||
}, | ||
unlinkSync: function (tmpfile) { | ||
if (/nounlink/.test(tmpfile)) throw 'ENOUNLINK'; | ||
}, | ||
'graceful-fs': { | ||
writeFile: function (tmpfile, data, options, cb) { | ||
if (/nowrite/.test(tmpfile)) return cb(new Error('ENOWRITE')) | ||
cb() | ||
}, | ||
chown: function (tmpfile, uid, gid, cb) { | ||
if (/nochown/.test(tmpfile)) return cb(new Error('ENOCHOWN')) | ||
cb() | ||
}, | ||
rename: function (tmpfile, filename, cb) { | ||
if (/norename/.test(tmpfile)) return cb(new Error('ENORENAME')) | ||
cb() | ||
}, | ||
unlink: function (tmpfile, cb) { | ||
if (/nounlink/.test(tmpfile)) return cb(new Error('ENOUNLINK')) | ||
cb() | ||
}, | ||
writeFileSync: function (tmpfile, data, options) { | ||
if (/nowrite/.test(tmpfile)) throw new Error('ENOWRITE') | ||
}, | ||
chownSync: function (tmpfile, uid, gid) { | ||
if (/nochown/.test(tmpfile)) throw new Error('ENOCHOWN') | ||
}, | ||
renameSync: function (tmpfile, filename) { | ||
if (/norename/.test(tmpfile)) throw new Error('ENORENAME') | ||
}, | ||
unlinkSync: function (tmpfile) { | ||
if (/nounlink/.test(tmpfile)) throw new Error('ENOUNLINK') | ||
} | ||
}); | ||
var writeFileAtomicSync = writeFileAtomic.sync; | ||
} | ||
}) | ||
var writeFileAtomicSync = writeFileAtomic.sync | ||
test('async tests', function (t) { | ||
t.plan(7); | ||
writeFileAtomic('good', 'test', {mode: '0777'}, function (err) { | ||
t.notOk(err, 'No errors occur when passing in options'); | ||
}); | ||
writeFileAtomic('good', 'test', function (err) { | ||
t.notOk(err, 'No errors occur when NOT passing in options'); | ||
}); | ||
writeFileAtomic('nowrite', 'test', function (err) { | ||
t.is(err, 'ENOWRITE', 'writeFile failures propagate'); | ||
}); | ||
writeFileAtomic('nochown', 'test', {chown: {uid:100,gid:100}}, function (err) { | ||
t.is(err, 'ENOCHOWN', 'Chown failures propagate'); | ||
}); | ||
writeFileAtomic('nochown', 'test', function (err) { | ||
t.notOk(err, 'No attempt to chown when no uid/gid passed in'); | ||
}); | ||
writeFileAtomic('norename', 'test', function (err) { | ||
t.is(err, 'ENORENAME', 'Rename errors propagate'); | ||
}); | ||
writeFileAtomic('norename nounlink', 'test', function (err) { | ||
t.is(err, 'ENORENAME', 'Failure to unlink the temp file does not clobber the original error'); | ||
}); | ||
}); | ||
t.plan(7) | ||
writeFileAtomic('good', 'test', {mode: '0777'}, function (err) { | ||
t.notOk(err, 'No errors occur when passing in options') | ||
}) | ||
writeFileAtomic('good', 'test', function (err) { | ||
t.notOk(err, 'No errors occur when NOT passing in options') | ||
}) | ||
writeFileAtomic('nowrite', 'test', function (err) { | ||
t.is(err.message, 'ENOWRITE', 'writeFile failures propagate') | ||
}) | ||
writeFileAtomic('nochown', 'test', {chown: {uid: 100, gid: 100}}, function (err) { | ||
t.is(err.message, 'ENOCHOWN', 'Chown failures propagate') | ||
}) | ||
writeFileAtomic('nochown', 'test', function (err) { | ||
t.notOk(err, 'No attempt to chown when no uid/gid passed in') | ||
}) | ||
writeFileAtomic('norename', 'test', function (err) { | ||
t.is(err.message, 'ENORENAME', 'Rename errors propagate') | ||
}) | ||
writeFileAtomic('norename nounlink', 'test', function (err) { | ||
t.is(err.message, 'ENORENAME', 'Failure to unlink the temp file does not clobber the original error') | ||
}) | ||
}) | ||
test('sync tests', function (t) { | ||
t.plan(7); | ||
var throws = function (shouldthrow, msg, todo) { | ||
var err; | ||
try { todo() } catch (e) { err = e } | ||
t.is(shouldthrow,err,msg); | ||
} | ||
var noexception = function (msg, todo) { | ||
var err; | ||
try { todo() } catch (e) { err = e } | ||
t.notOk(err,msg); | ||
} | ||
t.plan(7) | ||
var throws = function (shouldthrow, msg, todo) { | ||
var err | ||
try { todo() } catch (e) { err = e } | ||
t.is(shouldthrow, err.message, msg) | ||
} | ||
var noexception = function (msg, todo) { | ||
var err | ||
try { todo() } catch (e) { err = e } | ||
t.notOk(err, msg) | ||
} | ||
noexception('No errors occur when passing in options',function (){ | ||
writeFileAtomicSync('good', 'test', {mode: '0777'}); | ||
}) | ||
noexception('No errors occur when NOT passing in options',function (){ | ||
writeFileAtomicSync('good', 'test'); | ||
}); | ||
throws('ENOWRITE', 'writeFile failures propagate', function () { | ||
writeFileAtomicSync('nowrite', 'test'); | ||
}); | ||
throws('ENOCHOWN', 'Chown failures propagate', function () { | ||
writeFileAtomicSync('nochown', 'test', {chown: {uid:100,gid:100}}); | ||
}); | ||
noexception('No attempt to chown when no uid/gid passed in', function (){ | ||
writeFileAtomicSync('nochown', 'test'); | ||
}); | ||
throws('ENORENAME', 'Rename errors propagate', function (){ | ||
writeFileAtomicSync('norename', 'test'); | ||
}); | ||
throws('ENORENAME', 'Failure to unlink the temp file does not clobber the original error', function (){ | ||
writeFileAtomicSync('norename nounlink', 'test'); | ||
}); | ||
}); | ||
noexception('No errors occur when passing in options', function () { | ||
writeFileAtomicSync('good', 'test', {mode: '0777'}) | ||
}) | ||
noexception('No errors occur when NOT passing in options', function () { | ||
writeFileAtomicSync('good', 'test') | ||
}) | ||
throws('ENOWRITE', 'writeFile failures propagate', function () { | ||
writeFileAtomicSync('nowrite', 'test') | ||
}) | ||
throws('ENOCHOWN', 'Chown failures propagate', function () { | ||
writeFileAtomicSync('nochown', 'test', {chown: {uid: 100, gid: 100}}) | ||
}) | ||
noexception('No attempt to chown when no uid/gid passed in', function () { | ||
writeFileAtomicSync('nochown', 'test') | ||
}) | ||
throws('ENORENAME', 'Rename errors propagate', function () { | ||
writeFileAtomicSync('norename', 'test') | ||
}) | ||
throws('ENORENAME', 'Failure to unlink the temp file does not clobber the original error', function () { | ||
writeFileAtomicSync('norename nounlink', 'test') | ||
}) | ||
}) |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
12411
9
3
3
135
1
+ Addedimurmurhash@^0.1.4
+ Addedimurmurhash@0.1.4(transitive)