Repository: gulpjs/vinyl-fs
Pull request #125 (2015-11-05 19:21:54)

Use `fs.futimes` to retain sub-second precision in `atime` and `mtime`
Figured it wasn't too much trouble to propose something concrete re: [this](https://github.com/gulpjs/vinyl-fs/issues/124). 

erikkemperman: Sorry about failing builds in first two commits; it worked on my machine :-)
phated: @erikkemperman can you rebase this on top of the stuff I just merged and squash these commits?

Patch:
diff --git a/lib/dest/writeContents/writeBuffer.js b/lib/dest/writeContents/writeBuffer.js
index bed14e2..d5131c7 100644
--- a/lib/dest/writeContents/writeBuffer.js
+++ b/lib/dest/writeContents/writeBuffer.js
@@ -2,23 +2,31 @@
 
 var fs = require('graceful-fs');
 
-var utimes = require('../../utimes');
+var futimes = require('../../futimes');
 
 function writeBuffer(writePath, file, cb) {
   var stat = file.stat;
 
-  var opt = {
-    mode: stat.mode,
-    flag: file.flag
-  };
-
-  fs.writeFile(writePath, file.contents, opt, function(error) {
-    if (error) {
-      return cb(error);
+  fs.open(writePath, file.flag, stat.mode, function(err, fd) {
+    if (err) {
+      return cb(err);
     }
 
-    utimes(writePath, stat, cb);
+    fs.write(fd, file.contents, 0, file.contents.length, 0, function(error) {
+      if (error) {
+        return complete(error);
+      }
+      futimes(fd, stat, complete);
+    });
+
+    // cleanup
+    function complete(err1) {
+      fs.close(fd, function(err2) {
+        cb(err1 || err2);
+      });
+    }
   });
+
 }
 
 module.exports = writeBuffer;
diff --git a/lib/utimes.js b/lib/futimes.js
similarity index 82%
rename from lib/utimes.js
rename to lib/futimes.js
index b4cd330..53ebece 100644
--- a/lib/utimes.js
+++ b/lib/futimes.js
@@ -7,7 +7,7 @@ function validDate(date) {
   return date instanceof Date && !isNaN(date.valueOf());
 }
 
-function utimes(writePath, stat, cb) {
+function futimes(fd, stat, cb) {
   // Given `mtime` is not valid, do nothing
   var mtime = stat.mtime;
   if (!validDate(mtime)) {
@@ -21,7 +21,7 @@ function utimes(writePath, stat, cb) {
   }
 
   // Set file `atime` and `mtime` fields
-  fs.utimes(writePath, atime, mtime, cb);
+  fs.futimes(fd, atime, mtime, cb);
 }
 
-module.exports = utimes;
+module.exports = futimes;


Test patch:
diff --git a/lib/dest/writeContents/writeStream.js b/lib/dest/writeContents/writeStream.js
index e4aa4b2..01bd3ce 100644
--- a/lib/dest/writeContents/writeStream.js
+++ b/lib/dest/writeContents/writeStream.js
@@ -3,7 +3,7 @@
 var fs = require('graceful-fs');
 
 var streamFile = require('../../src/getContents/streamFile');
-var utimes     = require('../../utimes');
+var futimes    = require('../../futimes');
 
 function writeStream(writePath, file, cb) {
   var stat = file.stat;
@@ -14,20 +14,26 @@ function writeStream(writePath, file, cb) {
   };
 
   var outStream = fs.createWriteStream(writePath, opt);
+  var outFD;
 
   file.contents.once('error', complete);
   outStream.once('error', complete);
+  outStream.once('open', open );
   outStream.once('finish', success);
 
   file.contents.pipe(outStream);
 
+  function open(fd) {
+    outFD = fd;
+  }
+
   function success() {
     streamFile(file, {}, function(error) {
       if (error) {
         return complete(error);
       }
 
-      utimes(writePath, stat, complete);
+      futimes(outFD, stat, complete);
     });
   }
 
@@ -35,6 +41,7 @@ function writeStream(writePath, file, cb) {
   function complete(err) {
     file.contents.removeListener('error', complete);
     outStream.removeListener('error', complete);
+    outStream.removeListener('open', open);
     outStream.removeListener('finish', success);
     cb(err);
   }
diff --git a/test/dest.js b/test/dest.js
index 5a603c1..1a4445c 100644
--- a/test/dest.js
+++ b/test/dest.js
@@ -620,7 +620,6 @@ describe('dest stream', function() {
     var expectedContents = fs.readFileSync(inputPath);
     var expectedCwd = __dirname;
     var expectedBase = path.join(__dirname, './out-fixtures');
-    var expectedAtime = new Date();
     var expectedMtime = fs.lstatSync(inputPath).mtime;
 
     var expectedFile = new File({
@@ -633,15 +632,10 @@ describe('dest stream', function() {
       }
     });
 
-    // Node.js uses `utime()`, so `fs.utimes()` has a resolution of 1 second
-    expectedAtime.setMilliseconds(0)
-    expectedMtime.setMilliseconds(0)
-
     var onEnd = function(){
       buffered.length.should.equal(1);
       buffered[0].should.equal(expectedFile);
       fs.existsSync(expectedPath).should.equal(true);
-      fs.lstatSync(expectedPath).atime.getTime().should.equal(expectedAtime.getTime());
       fs.lstatSync(expectedPath).mtime.getTime().should.equal(expectedMtime.getTime());
       expectedFile.stat.should.have.property('mtime');
       expectedFile.stat.mtime.should.equal(expectedMtime);
@@ -719,15 +713,10 @@ describe('dest stream', function() {
       }
     });
 
-    // Node.js uses `utime()`, so `fs.utimes()` has a resolution of 1 second
-    expectedAtime.setMilliseconds(0)
-    expectedMtime.setMilliseconds(0)
-
     var onEnd = function(){
       buffered.length.should.equal(1);
       buffered[0].should.equal(expectedFile);
       fs.existsSync(expectedPath).should.equal(true);
-      fs.lstatSync(expectedPath).atime.getTime().should.equal(expectedAtime.getTime());
       fs.lstatSync(expectedPath).mtime.getTime().should.equal(expectedMtime.getTime());
       done();
     };