diff --git a/README.md b/README.md index 2606a5f0..0e4a62b3 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,10 @@ Methods Copy a file or directory. The directory can have contents. Like `cp -r`. + +Options: +clobber (boolean): overwrite existing file or directory +preserveTimestamps (boolean): will set last modification and access times to the ones of the original source files, default is `false`. Sync: `copySync()` diff --git a/lib/copy/copy-file-sync.js b/lib/copy/copy-file-sync.js index c59e608e..04962158 100644 --- a/lib/copy/copy-file-sync.js +++ b/lib/copy/copy-file-sync.js @@ -3,7 +3,10 @@ var fs = require('graceful-fs') var BUF_LENGTH = 64 * 1024 var _buff = new Buffer(BUF_LENGTH) -function copyFileSync (srcFile, destFile, clobber) { +function copyFileSync (srcFile, destFile, options) { + var clobber = options.clobber + var preserveTimestamps = options.preserveTimestamps + if (fs.existsSync(destFile) && !clobber) { throw Error('EEXIST') } @@ -22,6 +25,10 @@ function copyFileSync (srcFile, destFile, clobber) { fs.closeSync(fdr) fs.closeSync(fdw) + + if (preserveTimestamps) { + fs.utimesSync(destFile, stat.atime, stat.mtime) + } } module.exports = copyFileSync diff --git a/lib/copy/copy-sync.js b/lib/copy/copy-sync.js index 0f3adcd1..a6869443 100644 --- a/lib/copy/copy-sync.js +++ b/lib/copy/copy-sync.js @@ -13,6 +13,7 @@ function copySync (src, dest, options) { // default to true for now options.clobber = 'clobber' in options ? !!options.clobber : true + options.preserveTimestamps = 'preserveTimestamps' in options ? !!options.preserveTimestamps : true options.filter = options.filter || function () { return true } @@ -27,13 +28,15 @@ function copySync (src, dest, options) { if (performCopy) { if (!destFolderExists) mkdir.mkdirsSync(destFolder) - copyFileSync(src, dest, options.clobber) + copyFileSync(src, dest, {clobber: options.clobber, preserveTimestamps: options.preserveTimestamps}) } } else if (stats.isDirectory()) { if (!fs.existsSync(dest)) mkdir.mkdirsSync(dest) var contents = fs.readdirSync(src) contents.forEach(function (content) { - copySync(path.join(src, content), path.join(dest, content), {filter: options.filter, recursive: true}) + var opts = options + opts.recursive = true + copySync(path.join(src, content), path.join(dest, content), opts) }) } else if (options.recursive && stats.isSymbolicLink()) { var srcPath = fs.readlinkSync(src) diff --git a/lib/copy/ncp.js b/lib/copy/ncp.js index 65451113..2985d575 100644 --- a/lib/copy/ncp.js +++ b/lib/copy/ncp.js @@ -17,6 +17,7 @@ function ncp (source, dest, options, callback) { var transform = options.transform var clobber = options.clobber !== false var dereference = options.dereference + var preserveTimestamps = options.preserveTimestamps === true var errs = null @@ -106,7 +107,14 @@ function ncp (source, dest, options, callback) { } writeStream.once('finish', function () { - doneOne() + if (preserveTimestamps) { + fs.utimes(target, file.atime, file.mtime, function (err) { + if (err) return onError(err) + return doneOne() + }) + } else { + doneOne() + } }) } diff --git a/test/copy/copy.test.js b/test/copy/copy.test.js new file mode 100644 index 00000000..455cd209 --- /dev/null +++ b/test/copy/copy.test.js @@ -0,0 +1,91 @@ +var assert = require('assert') +var fs = require('fs') +var path = require('path') +var os = require('os') +var fse = require(process.cwd()) + +/* global afterEach, beforeEach, describe, it */ + +describe('fs-extra', function () { + var TEST_DIR + var SRC_FIXTURES_DIR = path.join(__dirname, './fixtures') + var FILES = ['a-file', path.join('a-folder', 'another-file'), path.join('a-folder', 'another-folder', 'file3')] + + beforeEach(function (done) { + TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy') + fse.emptyDir(TEST_DIR, done) + }) + + afterEach(function (done) { + fse.remove(TEST_DIR, done) + }) + + describe('+ copySync', function () { + describe('> when modified option is turned off', function () { + it('should have different timestamps on copy', function (done) { + var from = path.join(SRC_FIXTURES_DIR) + + fse.copySync(from, TEST_DIR, {preserveTimestamps: false}) + + FILES.forEach(testFile({preserveTimestamps: false})) + done() + }) + }) + + describe('> when modified option is turned on', function () { + it('should have the same timestamps on copy', function (done) { + var from = path.join(SRC_FIXTURES_DIR) + + fse.copySync(from, TEST_DIR, {preserveTimestamps: true}) + + FILES.forEach(testFile({preserveTimestamps: true})) + + done() + }) + }) + + }) + + describe('+ copy', function () { + describe('> when modified option is turned off', function () { + it('should have different timestamps on copy', function (done) { + var from = path.join(SRC_FIXTURES_DIR) + var to = path.join(TEST_DIR) + + fse.copy(from, to, {preserveTimestamps: false}, function () { + FILES.forEach(testFile({preserveTimestamps: false})) + done() + }) + }) + }) + + describe('> when modified option is turned on', function () { + it('should have the same timestamps on copy', function (done) { + var from = path.join(SRC_FIXTURES_DIR) + var to = path.join(TEST_DIR) + + fse.copy(from, to, {preserveTimestamps: true}, function () { + FILES.forEach(testFile({preserveTimestamps: true})) + done() + }) + }) + }) + + }) + + function testFile (options) { + return function (file) { + var a = path.join(SRC_FIXTURES_DIR, file) + var b = path.join(TEST_DIR, file) + var fromStat = fs.statSync(a) + var toStat = fs.statSync(b) + if (options.preserveTimestamps) { + assert.deepEqual(toStat.mtime, fromStat.mtime) + assert.deepEqual(toStat.atime, fromStat.atime) + } else { + assert.notEqual(toStat.mtime, fromStat.mtime) + assert.notEqual(toStat.atime, fromStat.atime) + } + } + } +}) diff --git a/test/copy/fixtures/a-file b/test/copy/fixtures/a-file new file mode 100644 index 00000000..94a709d0 --- /dev/null +++ b/test/copy/fixtures/a-file @@ -0,0 +1 @@ +sonic the hedgehog diff --git a/test/copy/fixtures/a-folder/another-file b/test/copy/fixtures/a-folder/another-file new file mode 100644 index 00000000..31340c77 --- /dev/null +++ b/test/copy/fixtures/a-folder/another-file @@ -0,0 +1 @@ +tails diff --git a/test/copy/fixtures/a-folder/another-folder/file3 b/test/copy/fixtures/a-folder/another-folder/file3 new file mode 100644 index 00000000..73a394da --- /dev/null +++ b/test/copy/fixtures/a-folder/another-folder/file3 @@ -0,0 +1 @@ +knuckles