UNPKG

2.63 kBJavaScriptView Raw
1'use strict'
2var fs = require('graceful-fs')
3var chain = require('slide').chain
4var MurmurHash3 = require('imurmurhash')
5var extend = Object.assign || require('util')._extend
6
7function murmurhex () {
8 var hash = new MurmurHash3()
9 for (var ii = 0; ii < arguments.length; ++ii) hash.hash('' + arguments[ii])
10 return hash.result()
11}
12var invocations = 0
13var getTmpname = function (filename) {
14 return filename + '.' + murmurhex(__filename, process.pid, ++invocations)
15}
16
17module.exports = function writeFile (filename, data, options, callback) {
18 if (options instanceof Function) {
19 callback = options
20 options = null
21 }
22 if (!options) options = {}
23 var tmpfile = getTmpname(filename)
24
25 if (options.mode && options.chmod) {
26 return thenWriteFile()
27 } else {
28 // Either mode or chown is not explicitly set
29 // Default behavior is to copy it from original file
30 return fs.stat(filename, function (err, stats) {
31 options = extend({}, options)
32 if (!err && stats && !options.mode) {
33 options.mode = stats.mode
34 }
35 if (!err && stats && !options.chown && process.getuid) {
36 options.chown = { uid: stats.uid, gid: stats.gid }
37 }
38 return thenWriteFile()
39 })
40 }
41
42 function thenWriteFile () {
43 chain([
44 [fs, fs.writeFile, tmpfile, data, options.encoding || 'utf8'],
45 options.mode && [fs, fs.chmod, tmpfile, options.mode],
46 options.chown && [fs, fs.chown, tmpfile, options.chown.uid, options.chown.gid],
47 [fs, fs.rename, tmpfile, filename]
48 ], function (err) {
49 err ? fs.unlink(tmpfile, function () { callback(err) })
50 : callback()
51 })
52 }
53}
54
55module.exports.sync = function writeFileSync (filename, data, options) {
56 if (!options) options = {}
57 var tmpfile = getTmpname(filename)
58
59 try {
60 if (!options.mode || !options.chmod) {
61 // Either mode or chown is not explicitly set
62 // Default behavior is to copy it from original file
63 try {
64 var stats = fs.statSync(filename)
65
66 options = extend({}, options)
67 if (!options.mode) {
68 options.mode = stats.mode
69 }
70 if (!options.chown && process.getuid) {
71 options.chown = { uid: stats.uid, gid: stats.gid }
72 }
73 } catch (ex) {
74 // ignore stat errors
75 }
76 }
77
78 fs.writeFileSync(tmpfile, data, options.encoding || 'utf8')
79 if (options.chown) fs.chownSync(tmpfile, options.chown.uid, options.chown.gid)
80 if (options.mode) fs.chmodSync(tmpfile, options.mode)
81 fs.renameSync(tmpfile, filename)
82 } catch (err) {
83 try { fs.unlinkSync(tmpfile) } catch (e) {}
84 throw err
85 }
86}