Repository: jprichardson/node-fs-extra
Pull request #518 (2017-11-09 22:04:21)

Allow copy's filter to return a Promise

coveralls: 
[![Coverage Status](https://coveralls.io/builds/14128129/badge)](https://coveralls.io/builds/14128129)

Coverage decreased (-0.06%) to 86.042% when pulling **f2c043d5213fef6388e2ce924816874ffe80171d on async-filter** into **b72ba64dbb94afb0416a25dfa5fdb2c21ca58f3d on develop**.

RyanZim: @manidlou Good point about the perf; wouldn't have much impact if we're just calling a noop function, but with the extra `Promise.resolve` layer, that will probably have a perf hit.

Patch:
diff --git a/docs/copy.md b/docs/copy.md
index df83448..f7909d0 100644
--- a/docs/copy.md
+++ b/docs/copy.md
@@ -9,7 +9,7 @@ Copy a file or directory. The directory can have contents. Like `cp -r`.
   - `errorOnExist` `<boolean>`: when `overwrite` is `false` and the destination exists, throw an error. Default is `false`.
   - `dereference` `<boolean>`: dereference symlinks, default is `false`.
   - `preserveTimestamps` `<boolean>`: will set last modification and access times to the ones of the original source files, default is `false`.
-  - `filter` `<Function>`: Function to filter copied files. Return `true` to include, `false` to exclude.
+  - `filter` `<Function>`: Function to filter copied files. Return `true` to include, `false` to exclude. Can also return a `Promise` that resolves to `true` or `false` (or pass in an `async` function).
 - `callback` `<Function>`
 
 ## Example:
diff --git a/lib/copy/copy.js b/lib/copy/copy.js
index f270c1d..421436a 100644
--- a/lib/copy/copy.js
+++ b/lib/copy/copy.js
@@ -13,7 +13,7 @@ function copy (src, dest, opts, cb) {
   if (typeof opts === 'function' && !cb) {
     cb = opts
     opts = {}
-  } else if (typeof opts === 'function' || opts instanceof RegExp) {
+  } else if (typeof opts === 'function') {
     opts = {filter: opts}
   }
 
@@ -49,8 +49,13 @@ function copy (src, dest, opts, cb) {
 }
 
 function startCopy (src, dest, opts, cb) {
-  if (opts.filter && !opts.filter(src, dest)) return cb()
-  return getStats(src, dest, opts, cb)
+  if (opts.filter) {
+    Promise.resolve(opts.filter(src, dest))
+      .then(include => {
+        if (include) getStats(src, dest, opts, cb)
+        else cb()
+      }, error => cb(error))
+  } else getStats(src, dest, opts, cb)
 }
 
 function getStats (src, dest, opts, cb) {


Test patch:
diff --git a/lib/copy/__tests__/copy.test.js b/lib/copy/__tests__/copy.test.js
index d8e38f6..98692b4 100644
--- a/lib/copy/__tests__/copy.test.js
+++ b/lib/copy/__tests__/copy.test.js
@@ -249,6 +249,19 @@ describe('fs-extra', () => {
         })
       })
 
+      it('allows filter fn to return a promise', done => {
+        const srcFile1 = path.join(TEST_DIR, '1.css')
+        fs.writeFileSync(srcFile1, '')
+        const destFile1 = path.join(TEST_DIR, 'dest1.css')
+        const filter = s => Promise.resolve(s.split('.').pop() !== 'css')
+
+        fse.copy(srcFile1, destFile1, filter, err => {
+          assert(!err)
+          assert(!fs.existsSync(destFile1))
+          done()
+        })
+      })
+
       it('should apply filter recursively', done => {
         const FILES = 2
         // Don't match anything that ends with a digit higher than 0: