Skip to content

Commit

Permalink
Merge pull request #366 from jprichardson/add-dest-param-filter-copy
Browse files Browse the repository at this point in the history
Add dest parameter to filter option in copy and copySync
  • Loading branch information
jprichardson committed Feb 23, 2017
2 parents 25475c7 + e261c49 commit a37d7bb
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 45 deletions.
16 changes: 16 additions & 0 deletions docs/copy.md
Expand Up @@ -28,3 +28,19 @@ fs.copy('/tmp/mydir', '/tmp/mynewdir', err => {
console.log('success!')
}) // copies directory, even if it has subdirectories or files
```

**Using filter function**

```js
var fs = require('fs-extra')

var filterFunc = function (src, dest) {
// your logic here
// it will be copied if return true
}

fs.copy('/tmp/mydir', '/tmp/mynewdir', { filter: filterFunc }, function (err) {
if (err) return console.error(err)
console.log('success!')
})
```
97 changes: 76 additions & 21 deletions lib/copy-sync/__tests__/copy-sync-dir.test.js
Expand Up @@ -27,15 +27,15 @@ describe('+ copySync()', () => {
src = path.join(TEST_DIR, 'src')
dest = path.join(TEST_DIR, 'dest')

fs.mkdirsSync(src)
fs.mkdirSync(src)

for (let i = 0; i < FILES; ++i) {
fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
}

const subdir = path.join(src, 'subdir')

fs.mkdirsSync(subdir)
fs.mkdirSync(subdir)

for (let i = 0; i < FILES; ++i) {
fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
Expand All @@ -55,7 +55,7 @@ describe('+ copySync()', () => {
})

it('should preserve symbolic links', () => {
fs.mkdirsSync(src)
fs.mkdirSync(src)
fs.symlinkSync('destination', path.join(src, 'symlink'))

fs.copySync(src, dest)
Expand All @@ -64,19 +64,44 @@ describe('+ copySync()', () => {
assert.strictEqual(link, 'destination')
})

describe('> when the destination dir does not exist', () => {
it('should create the destination directory and copy the file', () => {
const src = path.join(TEST_DIR, 'data/')
fs.mkdirSync(src)

const d1 = 'file1'
const d2 = 'file2'

fs.writeFileSync(path.join(src, 'f1.txt'), d1)
fs.writeFileSync(path.join(src, 'f2.txt'), d2)

const dest = path.join(TEST_DIR, 'this/path/does/not/exist/outputDir')

fs.copySync(src, dest)

const o1 = fs.readFileSync(path.join(dest, 'f1.txt'), 'utf8')
const o2 = fs.readFileSync(path.join(dest, 'f2.txt'), 'utf8')

assert.strictEqual(d1, o1)
assert.strictEqual(d2, o2)
})
})
})

describe('> when filter is used', () => {
it('should should apply filter recursively', () => {
const FILES = 2
// Don't match anything that ends with a digit higher than 0:
const filter = s => /(0|\D)$/i.test(s)

fs.mkdirsSync(src)
fs.mkdirSync(src)

for (let i = 0; i < FILES; ++i) {
fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
}

const subdir = path.join(src, 'subdir')
fs.mkdirsSync(subdir)
fs.mkdirSync(subdir)

for (let i = 0; i < FILES; ++i) {
fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
Expand Down Expand Up @@ -110,10 +135,10 @@ describe('+ copySync()', () => {
const IGNORE = 'ignore'
const filter = p => !~p.indexOf(IGNORE)

fs.mkdirsSync(src)
fs.mkdirSync(src)

const ignoreDir = path.join(src, IGNORE)
fs.mkdirsSync(ignoreDir)
fs.mkdirSync(ignoreDir)

fs.writeFileSync(path.join(ignoreDir, 'file'), crypto.randomBytes(SIZE))

Expand All @@ -123,27 +148,57 @@ describe('+ copySync()', () => {
assert(!fs.existsSync(path.join(dest, IGNORE, 'file')), 'file was not ignored')
})

describe('> when the destination dir does not exist', () => {
it('should create the destination directory and copy the file', () => {
const src = path.join(TEST_DIR, 'data/')
it('should apply filter when it is applied only to dest', done => {
const timeCond = new Date().getTime()

const filter = (s, d) => fs.statSync(d).birthtime.getTime() < timeCond

const dest = path.join(TEST_DIR, 'dest')

setTimeout(() => {
fs.mkdirSync(src)
fs.writeFileSync(path.join(src, 'somefile.html'), 'some data')
fs.mkdirSync(dest)
try {
fs.copySync(src, dest, filter)
} catch (err) {
assert.ifError(err)
}
assert(!fs.existsSync(path.join(dest, 'somefile.html')))
done()
}, 1000)
})

const d1 = 'file1'
const d2 = 'file2'
it('should apply filter when it is applied to both src and dest', done => {
const timeCond = new Date().getTime()
const filter = (s, d) => s.split('.').pop() !== 'css' && fs.statSync(path.dirname(d)).birthtime.getTime() > timeCond

fs.writeFileSync(path.join(src, 'f1.txt'), d1)
fs.writeFileSync(path.join(src, 'f2.txt'), d2)
const dest = path.join(TEST_DIR, 'dest')

const dest = path.join(TEST_DIR, 'this/path/does/not/exist/outputDir')
setTimeout(() => {
const srcFile1 = path.join(TEST_DIR, '1.html')
const srcFile2 = path.join(TEST_DIR, '2.css')
const srcFile3 = path.join(TEST_DIR, '3.jade')

fs.copySync(src, dest)
fs.writeFileSync(srcFile1, '')
fs.writeFileSync(srcFile2, '')
fs.writeFileSync(srcFile3, '')

const o1 = fs.readFileSync(path.join(dest, 'f1.txt'), 'utf8')
const o2 = fs.readFileSync(path.join(dest, 'f2.txt'), 'utf8')
const destFile1 = path.join(dest, 'dest1.html')
const destFile2 = path.join(dest, 'dest2.css')
const destFile3 = path.join(dest, 'dest3.jade')

assert.strictEqual(d1, o1)
assert.strictEqual(d2, o2)
})
fs.mkdirSync(dest)

fs.copySync(srcFile1, destFile1, filter)
fs.copySync(srcFile2, destFile2, filter)
fs.copySync(srcFile3, destFile3, filter)

assert(fs.existsSync(destFile1))
assert(!fs.existsSync(destFile2))
assert(fs.existsSync(destFile3))
done()
}, 1000)
})
})
})
2 changes: 1 addition & 1 deletion lib/copy-sync/copy-sync.js
Expand Up @@ -36,7 +36,7 @@ function copySync (src, dest, options) {
if (options.filter instanceof RegExp) {
console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function')
performCopy = options.filter.test(src)
} else if (typeof options.filter === 'function') performCopy = options.filter(src)
} else if (typeof options.filter === 'function') performCopy = options.filter(src, dest)

if (stats.isFile() && performCopy) {
if (!destFolderExists) mkdir.mkdirsSync(destFolder)
Expand Down
178 changes: 156 additions & 22 deletions lib/copy/__tests__/copy.test.js
Expand Up @@ -56,28 +56,6 @@ describe('fs-extra', () => {
})
})

it('should only copy files allowed by filter fn', done => {
const srcFile1 = path.join(TEST_DIR, '1.css')
fs.writeFileSync(srcFile1, '')
const destFile1 = path.join(TEST_DIR, 'dest1.css')
const filter = s => s.split('.').pop() !== 'css'
fse.copy(srcFile1, destFile1, filter, () => {
assert(!fs.existsSync(destFile1))
done()
})
})

it('accepts options object in place of filter', done => {
const srcFile1 = path.join(TEST_DIR, '1.jade')
fs.writeFileSync(srcFile1, '')
const destFile1 = path.join(TEST_DIR, 'dest1.jade')
const options = { filter: s => /.html$|.css$/i.test(s) }
fse.copy(srcFile1, destFile1, options, () => {
assert(!fs.existsSync(destFile1))
done()
})
})

it('should copy to a destination file with two \'$\' characters in name (eg: TEST_fs-extra_$$_copy)', done => {
const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_$$_copy')
Expand Down Expand Up @@ -190,5 +168,161 @@ describe('fs-extra', () => {
})
})
})

describe('> when filter is used', () => {
it('should only copy files allowed by filter fn', done => {
const srcFile1 = path.join(TEST_DIR, '1.css')
fs.writeFileSync(srcFile1, '')
const destFile1 = path.join(TEST_DIR, 'dest1.css')
const filter = s => s.split('.').pop() !== 'css'

fse.copy(srcFile1, destFile1, filter, err => {
assert(!err)
assert(!fs.existsSync(destFile1))
done()
})
})

it('accepts options object in place of filter', done => {
const srcFile1 = path.join(TEST_DIR, '1.jade')
fs.writeFileSync(srcFile1, '')
const destFile1 = path.join(TEST_DIR, 'dest1.jade')
const options = { filter: s => /.html$|.css$/i.test(s) }

fse.copy(srcFile1, destFile1, options, (err) => {
assert(!err)
assert(!fs.existsSync(destFile1))
done()
})
})

it('should should apply filter recursively', done => {
const FILES = 2
// Don't match anything that ends with a digit higher than 0:
const filter = s => /(0|\D)$/i.test(s)

const src = path.join(TEST_DIR, 'src')
fse.mkdirsSync(src)

for (let i = 0; i < FILES; ++i) {
fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
}

const subdir = path.join(src, 'subdir')
fse.mkdirsSync(subdir)

for (let i = 0; i < FILES; ++i) {
fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
}
const dest = path.join(TEST_DIR, 'dest')
fse.copy(src, dest, filter, err => {
assert(!err)

assert(fs.existsSync(dest))
assert(FILES > 1)

for (let i = 0; i < FILES; ++i) {
if (i === 0) {
assert(fs.existsSync(path.join(dest, i.toString())))
} else {
assert(!fs.existsSync(path.join(dest, i.toString())))
}
}

const destSub = path.join(dest, 'subdir')

for (let j = 0; j < FILES; ++j) {
if (j === 0) {
assert(fs.existsSync(path.join(destSub, j.toString())))
} else {
assert(!fs.existsSync(path.join(destSub, j.toString())))
}
}
done()
})
})

it('should apply the filter to directory names', done => {
const IGNORE = 'ignore'
const filter = p => !~p.indexOf(IGNORE)

const src = path.join(TEST_DIR, 'src')
fse.mkdirsSync(src)

const ignoreDir = path.join(src, IGNORE)
fse.mkdirsSync(ignoreDir)

fse.writeFileSync(path.join(ignoreDir, 'file'), crypto.randomBytes(SIZE))

const dest = path.join(TEST_DIR, 'dest')

fse.copySync(src, dest, filter)

assert(!fs.existsSync(path.join(dest, IGNORE)), 'directory was not ignored')
assert(!fs.existsSync(path.join(dest, IGNORE, 'file')), 'file was not ignored')
done()
})

it('should apply filter when it is applied only to dest', done => {
const timeCond = new Date().getTime()

const filter = (s, d) => fs.statSync(d).birthtime.getTime() < timeCond

const src = path.join(TEST_DIR, 'src')
fse.mkdirsSync(src)
const subdir = path.join(src, 'subdir')
fse.mkdirsSync(subdir)

const dest = path.join(TEST_DIR, 'dest')

setTimeout(() => {
fse.mkdirsSync(dest)

fse.copy(src, dest, filter, err => {
assert(!err)
assert(!fs.existsSync(path.join(dest, 'subdir')))
done()
})
}, 1000)
})

it('should apply filter when it is applied to both src and dest', done => {
const timeCond = new Date().getTime()
const filter = (s, d) => s.split('.').pop() !== 'css' && fs.statSync(path.dirname(d)).birthtime.getTime() > timeCond

const dest = path.join(TEST_DIR, 'dest')
setTimeout(() => {
fse.mkdirsSync(dest)

const srcFile1 = path.join(TEST_DIR, '1.html')
const srcFile2 = path.join(TEST_DIR, '2.css')
const srcFile3 = path.join(TEST_DIR, '3.jade')

fse.writeFileSync(srcFile1, '')
fse.writeFileSync(srcFile2, '')
fse.writeFileSync(srcFile3, '')

const destFile1 = path.join(dest, 'dest1.html')
const destFile2 = path.join(dest, 'dest2.css')
const destFile3 = path.join(dest, 'dest3.jade')

fse.copy(srcFile1, destFile1, filter, err => {
assert(!err)
assert(fs.existsSync(destFile1))

fse.copy(srcFile2, destFile2, filter, err => {
assert(!err)
assert(!fs.existsSync(destFile2))

fse.copy(srcFile3, destFile3, filter, err => {
assert(!err)
assert(fs.existsSync(destFile3))
done()
})
})
})
}, 1000)
})
})
})
})
2 changes: 1 addition & 1 deletion lib/copy/ncp.js
Expand Up @@ -41,7 +41,7 @@ function ncp (source, dest, options, callback) {
return doneOne(true)
}
} else if (typeof filter === 'function') {
if (!filter(source)) {
if (!filter(source, dest)) {
return doneOne(true)
}
}
Expand Down

0 comments on commit a37d7bb

Please sign in to comment.