Repository: gulpjs/vinyl-fs
Pull request #110 (2015-10-14 16:03:51)

Set utimes of files if they are provided
This fixes issue #96 


Patch:
diff --git a/lib/dest/writeContents/writeBuffer.js b/lib/dest/writeContents/writeBuffer.js
index fd21ca5..bed14e2 100644
--- a/lib/dest/writeContents/writeBuffer.js
+++ b/lib/dest/writeContents/writeBuffer.js
@@ -2,13 +2,23 @@
 
 var fs = require('graceful-fs');
 
+var utimes = require('../../utimes');
+
 function writeBuffer(writePath, file, cb) {
+  var stat = file.stat;
+
   var opt = {
-    mode: file.stat.mode,
+    mode: stat.mode,
     flag: file.flag
   };
 
-  fs.writeFile(writePath, file.contents, opt, cb);
+  fs.writeFile(writePath, file.contents, opt, function(error) {
+    if (error) {
+      return cb(error);
+    }
+
+    utimes(writePath, stat, cb);
+  });
 }
 
 module.exports = writeBuffer;
diff --git a/lib/utimes.js b/lib/utimes.js
new file mode 100644
index 0000000..a2c8f26
--- /dev/null
+++ b/lib/utimes.js
@@ -0,0 +1,15 @@
+'use strict';
+
+var fs = require('graceful-fs');
+
+function utimes(writePath, stat, cb) {
+  if (stat.mtime) {
+    var atime = stat.atime || new Date();
+
+    return fs.utimes(writePath, atime, stat.mtime, cb);
+  }
+
+  cb();
+}
+
+module.exports = utimes;


Test patch:
diff --git a/lib/dest/writeContents/writeStream.js b/lib/dest/writeContents/writeStream.js
index 7e124c3..d22baad 100644
--- a/lib/dest/writeContents/writeStream.js
+++ b/lib/dest/writeContents/writeStream.js
@@ -1,11 +1,15 @@
 'use strict';
 
-var streamFile = require('../../src/getContents/streamFile');
 var fs = require('graceful-fs');
 
+var streamFile = require('../../src/getContents/streamFile');
+var utimes     = require('../../utimes');
+
 function writeStream(writePath, file, cb) {
+  var stat = file.stat;
+
   var opt = {
-    mode: file.stat.mode,
+    mode: stat.mode,
     flag: file.flag
   };
 
@@ -18,7 +22,13 @@ function writeStream(writePath, file, cb) {
   file.contents.pipe(outStream);
 
   function success() {
-    streamFile(file, {}, complete);
+    streamFile(file, {}, function(error) {
+      if (error) {
+        return complete(error);
+      }
+
+      utimes(writePath, stat, complete);
+    });
   }
 
   // cleanup
diff --git a/test/dest.js b/test/dest.js
index da66f67..1fa99c3 100644
--- a/test/dest.js
+++ b/test/dest.js
@@ -574,6 +574,133 @@ describe('dest stream', function() {
     stream.end();
   });
 
+  it('should not modify file mtime and atime when not provided on the vinyl stat', function(done) {
+    var inputPath = path.join(__dirname, './fixtures/test.coffee');
+    var inputBase = path.join(__dirname, './fixtures/');
+    var expectedPath = path.join(__dirname, './out-fixtures/test.coffee');
+    var expectedContents = fs.readFileSync(inputPath);
+    var expectedCwd = __dirname;
+    var expectedBase = path.join(__dirname, './out-fixtures');
+    var expectedAtime = new Date();
+    var expectedMtime = new Date();
+
+    var expectedFile = new File({
+      base: inputBase,
+      cwd: __dirname,
+      path: inputPath,
+      contents: expectedContents,
+      stat: {}
+    });
+
+    var onEnd = function(){
+      buffered.length.should.equal(1);
+      buffered[0].should.equal(expectedFile);
+      fs.existsSync(expectedPath).should.equal(true);
+      fs.lstatSync(expectedPath).atime.setMilliseconds(0).should.equal(expectedAtime.setMilliseconds(0));
+      fs.lstatSync(expectedPath).mtime.setMilliseconds(0).should.equal(expectedMtime.setMilliseconds(0));
+      expectedFile.stat.should.equal({});
+      done();
+    };
+
+    var stream = vfs.dest('./out-fixtures/', {cwd: __dirname});
+
+    var buffered = [];
+    var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd);
+
+    stream.pipe(bufferStream);
+    stream.write(expectedFile);
+    stream.end();
+  });
+
+  it('should write file mtime using the vinyl mtime', function(done) {
+    var inputPath = path.join(__dirname, './fixtures/test.coffee');
+    var inputBase = path.join(__dirname, './fixtures/');
+    var expectedPath = path.join(__dirname, './out-fixtures/test.coffee');
+    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({
+      base: inputBase,
+      cwd: __dirname,
+      path: inputPath,
+      contents: expectedContents,
+      stat: {
+        mtime: expectedMtime
+      }
+    });
+
+    // 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.equal({mtime: expectedMtime});
+      done();
+    };
+
+    var stream = vfs.dest('./out-fixtures/', {cwd: __dirname});
+
+    var buffered = [];
+    var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd);
+
+    stream.pipe(bufferStream);
+    stream.write(expectedFile);
+    stream.end();
+  });
+
+  it('should write file atime and mtime using the vinyl stat', function(done) {
+    var inputPath = path.join(__dirname, './fixtures/test.coffee');
+    var inputBase = path.join(__dirname, './fixtures/');
+    var expectedPath = path.join(__dirname, './out-fixtures/test.coffee');
+    var expectedContents = fs.readFileSync(inputPath);
+    var expectedCwd = __dirname;
+    var expectedBase = path.join(__dirname, './out-fixtures');
+    var expectedAtime = fs.lstatSync(inputPath).atime;
+    var expectedMtime = fs.lstatSync(inputPath).mtime;
+
+    var expectedFile = new File({
+      base: inputBase,
+      cwd: __dirname,
+      path: inputPath,
+      contents: expectedContents,
+      stat: {
+        atime: expectedAtime,
+        mtime: expectedMtime
+      }
+    });
+
+    // 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.equal({atime: expectedAtime, mtime: expectedMtime});
+      done();
+    };
+
+    var stream = vfs.dest('./out-fixtures/', {cwd: __dirname});
+
+    var buffered = [];
+    var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd);
+
+    stream.pipe(bufferStream);
+    stream.write(expectedFile);
+    stream.end();
+  });
+
   it('should change to the specified base as string', function(done) {
     var inputBase = path.join(__dirname, './fixtures');
     var inputPath = path.join(__dirname, './fixtures/wow/suchempty');