mirror of
https://github.com/jprichardson/node-fs-extra.git
synced 2026-01-18 16:13:55 +00:00
Rewrite copy to use recursive pattern for dirs, add more tests
This commit is contained in:
parent
2599b67848
commit
fe0bfe2931
192
lib/copy/__tests__/copy-prevent-copying-identical.test.js
Normal file
192
lib/copy/__tests__/copy-prevent-copying-identical.test.js
Normal file
@ -0,0 +1,192 @@
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const fs = require(process.cwd())
|
||||
const klawSync = require('klaw-sync')
|
||||
|
||||
/* global beforeEach, afterEach, describe, it */
|
||||
|
||||
describe('+ copySync() - prevent copying identical files and dirs', () => {
|
||||
let TEST_DIR = ''
|
||||
let src = ''
|
||||
let dest = ''
|
||||
|
||||
beforeEach(done => {
|
||||
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-sync-prevent-copying-identical')
|
||||
fs.emptyDir(TEST_DIR, done)
|
||||
})
|
||||
|
||||
afterEach(done => fs.remove(TEST_DIR, done))
|
||||
|
||||
it('should return an error if src and dest are the same', done => {
|
||||
const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_copy_sync')
|
||||
const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy_sync')
|
||||
|
||||
fs.copy(fileSrc, fileDest, err => {
|
||||
assert.equal(err.message, 'Source and destination must not be the same.')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
// src is directory:
|
||||
// src is regular, dest is symlink
|
||||
// src is symlink, dest is regular
|
||||
// src is symlink, dest is symlink
|
||||
|
||||
describe('> when the source is a directory', () => {
|
||||
describe(`>> when src is regular and dest is a symlink that points to src`, () => {
|
||||
it('should not copy and return', done => {
|
||||
src = path.join(TEST_DIR, 'src')
|
||||
fs.mkdirsSync(src)
|
||||
const subdir = path.join(TEST_DIR, 'src', 'subdir')
|
||||
fs.mkdirsSync(subdir)
|
||||
fs.writeFileSync(path.join(subdir, 'file.txt'), 'some data')
|
||||
|
||||
const destLink = path.join(TEST_DIR, 'dest-symlink')
|
||||
fs.symlinkSync(src, destLink, 'dir')
|
||||
|
||||
const oldlen = klawSync(src).length
|
||||
|
||||
fs.copy(src, destLink, err => {
|
||||
assert.ifError(err)
|
||||
|
||||
const newlen = klawSync(src).length
|
||||
assert.strictEqual(newlen, oldlen)
|
||||
const link = fs.readlinkSync(destLink)
|
||||
assert.strictEqual(link, src)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe(`>> when src is a symlink that points to a regular dest`, () => {
|
||||
it('should throw error', done => {
|
||||
dest = path.join(TEST_DIR, 'dest')
|
||||
fs.mkdirsSync(dest)
|
||||
const subdir = path.join(TEST_DIR, 'dest', 'subdir')
|
||||
fs.mkdirsSync(subdir)
|
||||
fs.writeFileSync(path.join(subdir, 'file.txt'), 'some data')
|
||||
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
fs.symlinkSync(dest, srcLink, 'dir')
|
||||
|
||||
const oldlen = klawSync(dest).length
|
||||
|
||||
fs.copy(srcLink, dest, err => {
|
||||
assert.ok(err)
|
||||
|
||||
// assert nothing copied
|
||||
const newlen = klawSync(dest).length
|
||||
assert.strictEqual(newlen, oldlen)
|
||||
const link = fs.readlinkSync(srcLink)
|
||||
assert.strictEqual(link, dest)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('>> when src and dest are symlinks that point to the exact same path', () => {
|
||||
it('should not copy and return', done => {
|
||||
src = path.join(TEST_DIR, 'src')
|
||||
fs.mkdirsSync(src)
|
||||
const srcLink = path.join(TEST_DIR, 'src_symlink')
|
||||
fs.symlinkSync(src, srcLink, 'dir')
|
||||
const destLink = path.join(TEST_DIR, 'dest_symlink')
|
||||
fs.symlinkSync(src, destLink, 'dir')
|
||||
|
||||
const srclenBefore = klawSync(srcLink).length
|
||||
const destlenBefore = klawSync(destLink).length
|
||||
|
||||
fs.copy(srcLink, destLink, err => {
|
||||
assert.ifError(err)
|
||||
|
||||
const srclenAfter = klawSync(srcLink).length
|
||||
assert.strictEqual(srclenAfter, srclenBefore, 'src length should not change')
|
||||
const destlenAfter = klawSync(destLink).length
|
||||
assert.strictEqual(destlenAfter, destlenBefore, 'dest length should not change')
|
||||
|
||||
const srcln = fs.readlinkSync(srcLink)
|
||||
assert.strictEqual(srcln, src)
|
||||
const destln = fs.readlinkSync(destLink)
|
||||
assert.strictEqual(destln, src)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// src is file:
|
||||
// src is regular, dest is symlink
|
||||
// src is symlink, dest is regular
|
||||
// src is symlink, dest is symlink
|
||||
|
||||
describe('> when the source is a file', () => {
|
||||
describe(`>> when src is regular and dest is a symlink that points to src`, () => {
|
||||
it('should not copy and return', done => {
|
||||
src = path.join(TEST_DIR, 'src', 'somefile.txt')
|
||||
fs.ensureFileSync(src)
|
||||
fs.writeFileSync(src, 'some data')
|
||||
|
||||
const destLink = path.join(TEST_DIR, 'dest-symlink')
|
||||
fs.symlinkSync(src, destLink, 'file')
|
||||
|
||||
fs.copy(src, destLink, err => {
|
||||
assert.ifError(err)
|
||||
|
||||
const link = fs.readlinkSync(destLink)
|
||||
assert.strictEqual(link, src)
|
||||
assert(fs.readFileSync(link, 'utf8'), 'some data')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe(`>> when src is a symlink that points to a regular dest`, () => {
|
||||
it('should throw error', done => {
|
||||
dest = path.join(TEST_DIR, 'dest', 'somefile.txt')
|
||||
fs.ensureFileSync(dest)
|
||||
fs.writeFileSync(dest, 'some data')
|
||||
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
fs.symlinkSync(dest, srcLink, 'file')
|
||||
|
||||
fs.copy(srcLink, dest, err => {
|
||||
assert.ok(err)
|
||||
|
||||
const link = fs.readlinkSync(srcLink)
|
||||
assert.strictEqual(link, dest)
|
||||
assert(fs.readFileSync(link, 'utf8'), 'some data')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('>> when src and dest are symlinks that point to the exact same path', () => {
|
||||
it('should not copy and return', done => {
|
||||
src = path.join(TEST_DIR, 'src', 'srcfile.txt')
|
||||
fs.ensureFileSync(src)
|
||||
fs.writeFileSync(src, 'src data')
|
||||
|
||||
const srcLink = path.join(TEST_DIR, 'src_symlink')
|
||||
fs.symlinkSync(src, srcLink, 'file')
|
||||
|
||||
const destLink = path.join(TEST_DIR, 'dest_symlink')
|
||||
fs.symlinkSync(src, destLink, 'file')
|
||||
|
||||
fs.copy(srcLink, destLink, err => {
|
||||
assert.ifError(err)
|
||||
|
||||
const srcln = fs.readlinkSync(srcLink)
|
||||
assert.strictEqual(srcln, src)
|
||||
const destln = fs.readlinkSync(destLink)
|
||||
assert.strictEqual(destln, src)
|
||||
assert(fs.readFileSync(srcln, 'utf8'), 'src data')
|
||||
assert(fs.readFileSync(destln, 'utf8'), 'src data')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
372
lib/copy/__tests__/copy-prevent-copying-into-itself.test.js
Normal file
372
lib/copy/__tests__/copy-prevent-copying-into-itself.test.js
Normal file
@ -0,0 +1,372 @@
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const fs = require(process.cwd())
|
||||
const klawSync = require('klaw-sync')
|
||||
|
||||
/* global beforeEach, afterEach, describe, it */
|
||||
|
||||
// these files are used for all tests
|
||||
const FILES = [
|
||||
'file0.txt',
|
||||
path.join('dir1', 'file1.txt'),
|
||||
path.join('dir1', 'dir2', 'file2.txt'),
|
||||
path.join('dir1', 'dir2', 'dir3', 'file3.txt')
|
||||
]
|
||||
|
||||
const dat0 = 'file0'
|
||||
const dat1 = 'file1'
|
||||
const dat2 = 'file2'
|
||||
const dat3 = 'file3'
|
||||
|
||||
function testSuccess (src, dest, done) {
|
||||
const srclen = klawSync(src).length
|
||||
assert(srclen > 2)
|
||||
fs.copy(src, dest, err => {
|
||||
assert.ifError(err)
|
||||
|
||||
const destlen = klawSync(dest).length
|
||||
|
||||
assert.strictEqual(destlen, srclen)
|
||||
|
||||
FILES.forEach(f => assert(fs.existsSync(path.join(dest, f)), 'file copied'))
|
||||
|
||||
const o0 = fs.readFileSync(path.join(dest, FILES[0]), 'utf8')
|
||||
const o1 = fs.readFileSync(path.join(dest, FILES[1]), 'utf8')
|
||||
const o2 = fs.readFileSync(path.join(dest, FILES[2]), 'utf8')
|
||||
const o3 = fs.readFileSync(path.join(dest, FILES[3]), 'utf8')
|
||||
|
||||
assert.strictEqual(o0, dat0, 'file contents matched')
|
||||
assert.strictEqual(o1, dat1, 'file contents matched')
|
||||
assert.strictEqual(o2, dat2, 'file contents matched')
|
||||
assert.strictEqual(o3, dat3, 'file contents matched')
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
function testError (src, dest, done) {
|
||||
fs.copy(src, dest, err => {
|
||||
assert.strictEqual(err.message, `Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`)
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
describe('+ copy() - prevent copying into itself', () => {
|
||||
let TEST_DIR, src
|
||||
|
||||
beforeEach(done => {
|
||||
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-prevent-copying-into-itself-4')
|
||||
src = path.join(TEST_DIR, 'src')
|
||||
fs.mkdirpSync(src)
|
||||
|
||||
fs.outputFileSync(path.join(src, FILES[0]), dat0)
|
||||
fs.outputFileSync(path.join(src, FILES[1]), dat1)
|
||||
fs.outputFileSync(path.join(src, FILES[2]), dat2)
|
||||
fs.outputFileSync(path.join(src, FILES[3]), dat3)
|
||||
done()
|
||||
})
|
||||
|
||||
afterEach(done => fs.remove(TEST_DIR, done))
|
||||
|
||||
describe('> when source is a file', () => {
|
||||
it('should copy the file successfully even when dest parent is a subdir of src', done => {
|
||||
const srcFile = path.join(TEST_DIR, 'src', 'srcfile.txt')
|
||||
const destFile = path.join(TEST_DIR, 'src', 'dest', 'destfile.txt')
|
||||
fs.writeFileSync(srcFile, dat0)
|
||||
|
||||
fs.copy(srcFile, destFile, err => {
|
||||
assert.ifError(err)
|
||||
|
||||
assert(fs.existsSync(destFile, 'file copied'))
|
||||
const out = fs.readFileSync(destFile, 'utf8')
|
||||
assert.strictEqual(out, dat0, 'file contents matched')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// test for these cases:
|
||||
// - src is directory, dest is directory
|
||||
// - src is directory, dest is symlink
|
||||
// - src is symlink, dest is directory
|
||||
// - src is symlink, dest is symlink
|
||||
|
||||
describe('> when source is a directory', () => {
|
||||
describe('>> when dest is a directory', () => {
|
||||
it(`should copy the directory successfully when dest is 'src_dest'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src_dest')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
it(`should copy the directory successfully when dest is 'src-dest'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src-dest')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'dest_src'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'dest_src')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'src_dest/src'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src_dest', 'src')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'src-dest/src'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src-dest', 'src')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'dest_src/src'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'dest_src', 'src')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'src_src/dest'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src_src', 'dest')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'src-src/dest'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src-src', 'dest')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'srcsrc/dest'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'srcsrc', 'dest')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'dest/src'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'dest', 'src')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it('should copy the directory successfully when dest is very nested that all its parents need to be created', done => {
|
||||
const dest = path.join(TEST_DIR, 'dest', 'src', 'foo', 'bar', 'baz', 'qux', 'quux', 'waldo',
|
||||
'grault', 'garply', 'fred', 'plugh', 'thud', 'some', 'highly', 'deeply',
|
||||
'badly', 'nasty', 'crazy', 'mad', 'nested', 'dest')
|
||||
return testSuccess(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should error when dest is 'src/dest'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src', 'dest')
|
||||
return testError(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should error when dest is 'src/src_dest'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src', 'src_dest')
|
||||
return testError(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should error when dest is 'src/dest_src'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src', 'dest_src')
|
||||
return testError(src, dest, done)
|
||||
})
|
||||
|
||||
it(`should error when dest is 'src/dest/src'`, done => {
|
||||
const dest = path.join(TEST_DIR, 'src', 'dest', 'src')
|
||||
return testError(src, dest, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('>> when dest is a symlink', () => {
|
||||
it('should not copy and return when dest points exactly to src', done => {
|
||||
const destLink = path.join(TEST_DIR, 'dest-symlink')
|
||||
fs.symlinkSync(src, destLink, 'dir')
|
||||
|
||||
const srclenBefore = klawSync(src).length
|
||||
assert(srclenBefore > 2)
|
||||
|
||||
fs.copy(src, destLink, err => {
|
||||
assert.ifError(err)
|
||||
|
||||
const srclenAfter = klawSync(src).length
|
||||
assert.strictEqual(srclenAfter, srclenBefore, 'src length should not change')
|
||||
|
||||
const link = fs.readlinkSync(destLink)
|
||||
assert.strictEqual(link, src)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should copy the directory successfully when src is a subdir of resolved dest path', done => {
|
||||
const srcInDest = path.join(TEST_DIR, 'dest', 'some', 'nested', 'src')
|
||||
const destLink = path.join(TEST_DIR, 'dest-symlink')
|
||||
fs.copySync(src, srcInDest) // put some stuff in srcInDest
|
||||
|
||||
const dest = path.join(TEST_DIR, 'dest')
|
||||
fs.symlinkSync(dest, destLink, 'dir')
|
||||
|
||||
const srclen = klawSync(srcInDest).length
|
||||
const destlenBefore = klawSync(dest).length
|
||||
|
||||
assert(srclen > 2)
|
||||
fs.copy(srcInDest, destLink, err => {
|
||||
assert.ifError(err)
|
||||
|
||||
const destlenAfter = klawSync(dest).length
|
||||
|
||||
// assert dest length is oldlen + length of stuff copied from src
|
||||
assert.strictEqual(destlenAfter, destlenBefore + srclen, 'dest length should be equal to old length + copied legnth')
|
||||
|
||||
FILES.forEach(f => assert(fs.existsSync(path.join(dest, f)), 'file copied'))
|
||||
|
||||
const o0 = fs.readFileSync(path.join(dest, FILES[0]), 'utf8')
|
||||
const o1 = fs.readFileSync(path.join(dest, FILES[1]), 'utf8')
|
||||
const o2 = fs.readFileSync(path.join(dest, FILES[2]), 'utf8')
|
||||
const o3 = fs.readFileSync(path.join(dest, FILES[3]), 'utf8')
|
||||
|
||||
assert.strictEqual(o0, dat0, 'files contents matched')
|
||||
assert.strictEqual(o1, dat1, 'files contents matched')
|
||||
assert.strictEqual(o2, dat2, 'files contents matched')
|
||||
assert.strictEqual(o3, dat3, 'files contents matched')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('> when source is a symlink', () => {
|
||||
describe('>> when dest is a directory', () => {
|
||||
it('should error when resolved src path points to dest', done => {
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
fs.symlinkSync(src, srcLink, 'dir')
|
||||
|
||||
const dest = path.join(TEST_DIR, 'src')
|
||||
|
||||
fs.copy(srcLink, dest, err => {
|
||||
assert(err)
|
||||
// assert source not affected
|
||||
const link = fs.readlinkSync(srcLink)
|
||||
assert.strictEqual(link, src)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should error when dest is a subdir of resolved src path', done => {
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
fs.symlinkSync(src, srcLink, 'dir')
|
||||
|
||||
const dest = path.join(TEST_DIR, 'src', 'some', 'nested', 'dest')
|
||||
fs.mkdirsSync(dest)
|
||||
|
||||
fs.copy(srcLink, dest, err => {
|
||||
assert(err)
|
||||
const link = fs.readlinkSync(srcLink)
|
||||
assert.strictEqual(link, src)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should error when resolved src path is a subdir of dest', done => {
|
||||
const dest = path.join(TEST_DIR, 'dest')
|
||||
|
||||
const resolvedSrcPath = path.join(dest, 'contains', 'src')
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
fs.copySync(src, resolvedSrcPath)
|
||||
|
||||
// make symlink that points to a subdir in dest
|
||||
fs.symlinkSync(resolvedSrcPath, srcLink, 'dir')
|
||||
|
||||
fs.copy(srcLink, dest, err => {
|
||||
assert(err)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'src_src/dest'`, done => {
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
fs.symlinkSync(src, srcLink, 'dir')
|
||||
|
||||
const dest = path.join(TEST_DIR, 'src_src', 'dest')
|
||||
testSuccess(srcLink, dest, () => {
|
||||
const link = fs.readlinkSync(dest)
|
||||
assert.strictEqual(link, src)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`should copy the directory successfully when dest is 'srcsrc/dest'`, done => {
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
fs.symlinkSync(src, srcLink, 'dir')
|
||||
|
||||
const dest = path.join(TEST_DIR, 'srcsrc', 'dest')
|
||||
testSuccess(srcLink, dest, () => {
|
||||
const link = fs.readlinkSync(dest)
|
||||
assert.strictEqual(link, src)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('>> when dest is a symlink', () => {
|
||||
it('should silently return when resolved dest path is exactly the same as resolved src path', done => {
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
fs.symlinkSync(src, srcLink, 'dir')
|
||||
const destLink = path.join(TEST_DIR, 'dest-symlink')
|
||||
fs.symlinkSync(src, destLink, 'dir')
|
||||
|
||||
const srclenBefore = klawSync(srcLink).length
|
||||
const destlenBefore = klawSync(destLink).length
|
||||
assert(srclenBefore > 2)
|
||||
assert(destlenBefore > 2)
|
||||
|
||||
fs.copy(srcLink, destLink, err => {
|
||||
assert.ifError(err)
|
||||
|
||||
const srclenAfter = klawSync(srcLink).length
|
||||
assert.strictEqual(srclenAfter, srclenBefore, 'src length should not change')
|
||||
const destlenAfter = klawSync(destLink).length
|
||||
assert.strictEqual(destlenAfter, destlenBefore, 'dest length should not change')
|
||||
|
||||
const srcln = fs.readlinkSync(srcLink)
|
||||
assert.strictEqual(srcln, src)
|
||||
const destln = fs.readlinkSync(destLink)
|
||||
assert.strictEqual(destln, src)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should error when resolved dest path is a subdir of resolved src path', done => {
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
fs.symlinkSync(src, srcLink, 'dir')
|
||||
|
||||
const destLink = path.join(TEST_DIR, 'dest-symlink')
|
||||
const resolvedDestPath = path.join(TEST_DIR, 'src', 'some', 'nested', 'dest')
|
||||
fs.ensureFileSync(path.join(resolvedDestPath, 'subdir', 'somefile.txt'))
|
||||
|
||||
fs.symlinkSync(resolvedDestPath, destLink, 'dir')
|
||||
|
||||
fs.copy(srcLink, destLink, err => {
|
||||
assert.ifError(err)
|
||||
const destln = fs.readlinkSync(destLink)
|
||||
assert.strictEqual(destln, src)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should error when resolved src path is a subdir of resolved dest path', done => {
|
||||
const srcInDest = path.join(TEST_DIR, 'dest', 'some', 'nested', 'src')
|
||||
const srcLink = path.join(TEST_DIR, 'src-symlink')
|
||||
const destLink = path.join(TEST_DIR, 'dest-symlink')
|
||||
|
||||
const dest = path.join(TEST_DIR, 'dest')
|
||||
|
||||
fs.mkdirSync(dest)
|
||||
|
||||
fs.symlinkSync(srcInDest, srcLink, 'dir')
|
||||
fs.symlinkSync(dest, destLink, 'dir')
|
||||
|
||||
fs.copy(srcLink, destLink, err => {
|
||||
assert.strictEqual(err.message, `Cannot overwrite '${dest}' with '${srcInDest}'.`)
|
||||
const destln = fs.readlinkSync(destLink)
|
||||
assert.strictEqual(destln, dest)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -30,6 +30,30 @@ describe('fs-extra', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should error when overwrite=false and file exists', done => {
|
||||
const src = path.join(TEST_DIR, 'src.txt')
|
||||
const dest = path.join(TEST_DIR, 'dest.txt')
|
||||
|
||||
fse.ensureFileSync(src)
|
||||
fse.ensureFileSync(dest)
|
||||
fse.copy(src, dest, {overwrite: false, errorOnExist: true}, err => {
|
||||
assert(err)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should error when overwrite=false and file exists in a dir', done => {
|
||||
const src = path.join(TEST_DIR, 'src', 'sfile.txt')
|
||||
const dest = path.join(TEST_DIR, 'dest', 'dfile.txt')
|
||||
|
||||
fse.ensureFileSync(src)
|
||||
fse.ensureFileSync(dest)
|
||||
fse.copy(src, dest, {overwrite: false, errorOnExist: true}, err => {
|
||||
assert(err)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('> when the source is a file', () => {
|
||||
it('should copy the file asynchronously', done => {
|
||||
const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
|
||||
@ -98,6 +122,20 @@ describe('fs-extra', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('> when dest exists and is a file', () => {
|
||||
it('should return an error', done => {
|
||||
const src = path.join(TEST_DIR, 'src')
|
||||
const dest = path.join(TEST_DIR, 'file.txt')
|
||||
fs.mkdirSync(src)
|
||||
fse.ensureFileSync(dest)
|
||||
|
||||
fse.copy(src, dest, err => {
|
||||
assert.strictEqual(err.message, `Cannot overwrite non-directory '${dest}' with directory '${src}'.`)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should copy the directory asynchronously', done => {
|
||||
const FILES = 2
|
||||
const src = path.join(TEST_DIR, 'src')
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const fse = require(process.cwd())
|
||||
const ncp = require('../../ncp')
|
||||
const ncp = require('../../copy')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const fse = require(process.cwd())
|
||||
const ncp = require('../../ncp')
|
||||
const ncp = require('../../copy')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const ncp = require('../../ncp')
|
||||
const ncp = require('../../copy')
|
||||
const path = require('path')
|
||||
const rimraf = require('rimraf')
|
||||
const assert = require('assert')
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const fse = require(process.cwd())
|
||||
const ncp = require('../../ncp')
|
||||
const ncp = require('../../copy')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
|
||||
263
lib/copy/copy.js
263
lib/copy/copy.js
@ -2,53 +2,246 @@
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const path = require('path')
|
||||
const ncp = require('./ncp')
|
||||
const mkdir = require('../mkdirs')
|
||||
const mkdirp = require('../mkdirs').mkdirs
|
||||
const pathExists = require('../path-exists').pathExists
|
||||
const utimes = require('../util/utimes').utimesMillis
|
||||
|
||||
function copy (src, dest, options, callback) {
|
||||
if (typeof options === 'function' && !callback) {
|
||||
callback = options
|
||||
options = {}
|
||||
} else if (typeof options === 'function' || options instanceof RegExp) {
|
||||
options = {filter: options}
|
||||
const notExist = Symbol('notExist')
|
||||
const existsReg = Symbol('existsReg')
|
||||
|
||||
function copy (src, dest, opts, cb) {
|
||||
if (typeof opts === 'function' && !cb) {
|
||||
cb = opts
|
||||
opts = {}
|
||||
} else if (typeof opts === 'function' || opts instanceof RegExp) {
|
||||
opts = {filter: opts}
|
||||
}
|
||||
callback = callback || function () {}
|
||||
options = options || {}
|
||||
|
||||
// Warn about using preserveTimestamps on 32-bit node:
|
||||
if (options.preserveTimestamps && process.arch === 'ia32') {
|
||||
cb = cb || function () {}
|
||||
opts = opts || {}
|
||||
|
||||
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now
|
||||
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber
|
||||
|
||||
// Warn about using preserveTimestamps on 32-bit node
|
||||
if (opts.preserveTimestamps && process.arch === 'ia32') {
|
||||
console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
|
||||
see https://github.com/jprichardson/node-fs-extra/issues/269`)
|
||||
}
|
||||
|
||||
src = path.resolve(src)
|
||||
dest = path.resolve(dest)
|
||||
|
||||
// don't allow src and dest to be the same
|
||||
const basePath = process.cwd()
|
||||
const currentPath = path.resolve(basePath, src)
|
||||
const targetPath = path.resolve(basePath, dest)
|
||||
if (currentPath === targetPath) return callback(new Error('Source and destination must not be the same.'))
|
||||
if (src === dest) return cb(new Error('Source and destination must not be the same.'))
|
||||
|
||||
fs.lstat(src, (err, stats) => {
|
||||
if (err) return callback(err)
|
||||
|
||||
let dir = null
|
||||
if (stats.isDirectory()) {
|
||||
const parts = dest.split(path.sep)
|
||||
parts.pop()
|
||||
dir = parts.join(path.sep)
|
||||
} else {
|
||||
dir = path.dirname(dest)
|
||||
}
|
||||
|
||||
pathExists(dir, (err, dirExists) => {
|
||||
if (err) return callback(err)
|
||||
if (dirExists) return ncp(src, dest, options, callback)
|
||||
mkdir.mkdirs(dir, err => {
|
||||
if (err) return callback(err)
|
||||
ncp(src, dest, options, callback)
|
||||
})
|
||||
const destParent = path.dirname(dest)
|
||||
pathExists(destParent, (err, dirExists) => {
|
||||
if (err) return cb(err)
|
||||
if (dirExists) return startCopy(src, dest, opts, cb)
|
||||
mkdirp(destParent, err => {
|
||||
if (err) return cb(err)
|
||||
return startCopy(src, dest, opts, cb)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function startCopy (src, dest, opts, cb) {
|
||||
if (opts.filter) {
|
||||
if (opts.filter instanceof RegExp) {
|
||||
console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function')
|
||||
if (!opts.filter.test(src)) return cb()
|
||||
} else if (typeof opts.filter === 'function') {
|
||||
if (!opts.filter(src, dest)) return cb()
|
||||
}
|
||||
}
|
||||
return getStats(src, dest, opts, cb)
|
||||
}
|
||||
|
||||
function getStats (src, dest, opts, cb) {
|
||||
const stat = opts.dereference ? fs.stat : fs.lstat
|
||||
stat(src, (err, st) => {
|
||||
if (err) return cb(err)
|
||||
|
||||
if (st.isDirectory()) return onDir(st, src, dest, opts, cb)
|
||||
else if (st.isFile() ||
|
||||
st.isCharacterDevice() ||
|
||||
st.isBlockDevice()) return onFile(st, src, dest, opts, cb)
|
||||
else if (st.isSymbolicLink()) return onLink(src, dest, opts, cb)
|
||||
})
|
||||
}
|
||||
|
||||
function onFile (srcStat, src, dest, opts, cb) {
|
||||
checkDest(dest, (err, resolvedPath) => {
|
||||
if (err) return cb(err)
|
||||
if (resolvedPath === notExist) {
|
||||
return cpFile(srcStat, src, dest, opts, cb)
|
||||
} else if (resolvedPath === existsReg) {
|
||||
return mayCopyFile(srcStat, src, dest, opts, cb)
|
||||
} else {
|
||||
if (src === resolvedPath) return cb()
|
||||
return mayCopyFile(srcStat, src, dest, opts, cb)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function mayCopyFile (srcStat, src, dest, opts, cb) {
|
||||
if (opts.overwrite) {
|
||||
fs.unlink(dest, err => {
|
||||
if (err) return cb(err)
|
||||
return cpFile(srcStat, src, dest, opts, cb)
|
||||
})
|
||||
} else if (opts.errorOnExist) {
|
||||
return cb(new Error(`'${dest}' already exists`))
|
||||
} else return cb()
|
||||
}
|
||||
|
||||
function cpFile (srcStat, src, dest, opts, cb) {
|
||||
const rs = fs.createReadStream(src)
|
||||
const ws = fs.createWriteStream(dest, { mode: srcStat.mode })
|
||||
|
||||
rs.on('error', err => cb(err))
|
||||
ws.on('error', err => cb(err))
|
||||
|
||||
ws.on('open', () => {
|
||||
rs.pipe(ws)
|
||||
}).once('close', () => {
|
||||
fs.chmod(dest, srcStat.mode, err => {
|
||||
if (err) return cb(err)
|
||||
if (opts.preserveTimestamps) {
|
||||
return utimes(dest, srcStat.atime, srcStat.mtime, cb)
|
||||
}
|
||||
return cb()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function onDir (srcStat, src, dest, opts, cb) {
|
||||
checkDest(dest, (err, resolvedPath) => {
|
||||
if (err) return cb(err)
|
||||
if (resolvedPath === notExist) {
|
||||
if (isSrcSubdir(src, dest)) {
|
||||
return cb(new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`))
|
||||
}
|
||||
return mkDirAndCopy(srcStat, src, dest, opts, cb)
|
||||
} else if (resolvedPath === existsReg) {
|
||||
if (isSrcSubdir(src, dest)) {
|
||||
return cb(new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`))
|
||||
}
|
||||
return mayCopyDir(src, dest, opts, cb)
|
||||
} else {
|
||||
if (src === resolvedPath) return cb()
|
||||
return cpDir(src, dest, opts, cb)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function mayCopyDir (src, dest, opts, cb) {
|
||||
fs.stat(dest, (err, st) => {
|
||||
if (err) return cb(err)
|
||||
if (!st.isDirectory()) {
|
||||
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
|
||||
}
|
||||
return cpDir(src, dest, opts, cb)
|
||||
})
|
||||
}
|
||||
|
||||
function mkDirAndCopy (srcStat, src, dest, opts, cb) {
|
||||
fs.mkdir(dest, srcStat.mode, err => {
|
||||
if (err) return cb(err)
|
||||
fs.chmod(dest, srcStat.mode, err => {
|
||||
if (err) return cb(err)
|
||||
return cpDir(src, dest, opts, cb)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function cpDir (src, dest, opts, cb) {
|
||||
fs.readdir(src, (err, items) => {
|
||||
if (err) return cb(err)
|
||||
return cpDirItems(items, src, dest, opts, cb)
|
||||
})
|
||||
}
|
||||
|
||||
function cpDirItems (items, src, dest, opts, cb) {
|
||||
const item = items.pop()
|
||||
if (!item) return cb()
|
||||
startCopy(path.join(src, item), path.join(dest, item), opts, err => {
|
||||
if (err) return cb(err)
|
||||
return cpDirItems(items, src, dest, opts, cb)
|
||||
})
|
||||
}
|
||||
|
||||
function onLink (src, dest, opts, cb) {
|
||||
fs.readlink(src, (err, resolvedSrcPath) => {
|
||||
if (err) return cb(err)
|
||||
|
||||
if (opts.dereference) {
|
||||
resolvedSrcPath = path.resolve(process.cwd(), resolvedSrcPath)
|
||||
}
|
||||
|
||||
checkDest(dest, (err, resolvedDestPath) => {
|
||||
if (err) return cb(err)
|
||||
|
||||
if (resolvedDestPath === notExist || resolvedDestPath === existsReg) {
|
||||
// if dest already exists, fs throws error anyway,
|
||||
// so no need to guard against it here.
|
||||
return fs.symlink(resolvedSrcPath, dest, cb)
|
||||
} else {
|
||||
if (opts.dereference) {
|
||||
resolvedDestPath = path.resolve(process.cwd(), resolvedDestPath)
|
||||
}
|
||||
if (resolvedDestPath === resolvedSrcPath) return cb()
|
||||
|
||||
// prevent copy if src is a subdir of dest since unlinking
|
||||
// dest in this case results in removing src contents
|
||||
// and therefore a broken symlink will be created.
|
||||
fs.stat(dest, (err, st) => {
|
||||
if (err) return cb(err)
|
||||
if (st.isDirectory() && isSrcSubdir(resolvedDestPath, resolvedSrcPath)) {
|
||||
return cb(new Error(`Cannot overwrite '${resolvedDestPath}' with '${resolvedSrcPath}'.`))
|
||||
}
|
||||
return cpLink(resolvedSrcPath, dest, cb)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function cpLink (resolvedSrcPath, dest, cb) {
|
||||
fs.unlink(dest, err => {
|
||||
if (err) return cb(err)
|
||||
return fs.symlink(resolvedSrcPath, dest, cb)
|
||||
})
|
||||
}
|
||||
|
||||
// check dest to see if it exists and/or is a symlink
|
||||
function checkDest (dest, cb) {
|
||||
fs.readlink(dest, (err, resolvedPath) => {
|
||||
if (err) {
|
||||
if (err.code === 'ENOENT') return cb(null, notExist)
|
||||
|
||||
// dest exists and is a regular file or directory, Windows throws UNKNOWN error.
|
||||
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return cb(null, existsReg)
|
||||
|
||||
return cb(err)
|
||||
}
|
||||
return cb(null, resolvedPath) // dest exists and is a symlink
|
||||
})
|
||||
}
|
||||
|
||||
// return true if dest is a subdir of src, otherwise false.
|
||||
// extract dest base dir and check if that is the same as src basename
|
||||
function isSrcSubdir (src, dest) {
|
||||
const baseDir = dest.split(path.dirname(src) + path.sep)[1]
|
||||
if (baseDir) {
|
||||
const destBasename = baseDir.split(path.sep)[0]
|
||||
if (destBasename) {
|
||||
return src !== dest && dest.indexOf(src) > -1 && destBasename === path.basename(src)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
module.exports = copy
|
||||
|
||||
234
lib/copy/ncp.js
234
lib/copy/ncp.js
@ -1,234 +0,0 @@
|
||||
// imported from ncp (this is temporary, will rewrite)
|
||||
|
||||
var fs = require('graceful-fs')
|
||||
var path = require('path')
|
||||
var utimes = require('../util/utimes')
|
||||
|
||||
function ncp (source, dest, options, callback) {
|
||||
if (!callback) {
|
||||
callback = options
|
||||
options = {}
|
||||
}
|
||||
|
||||
var basePath = process.cwd()
|
||||
var currentPath = path.resolve(basePath, source)
|
||||
var targetPath = path.resolve(basePath, dest)
|
||||
|
||||
var filter = options.filter
|
||||
var transform = options.transform
|
||||
var overwrite = options.overwrite
|
||||
// If overwrite is undefined, use clobber, otherwise default to true:
|
||||
if (overwrite === undefined) overwrite = options.clobber
|
||||
if (overwrite === undefined) overwrite = true
|
||||
var errorOnExist = options.errorOnExist
|
||||
var dereference = options.dereference
|
||||
var preserveTimestamps = options.preserveTimestamps === true
|
||||
|
||||
var started = 0
|
||||
var finished = 0
|
||||
var running = 0
|
||||
|
||||
var errored = false
|
||||
|
||||
startCopy(currentPath)
|
||||
|
||||
function startCopy (source) {
|
||||
started++
|
||||
if (filter) {
|
||||
if (filter instanceof RegExp) {
|
||||
console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function')
|
||||
if (!filter.test(source)) {
|
||||
return doneOne(true)
|
||||
}
|
||||
} else if (typeof filter === 'function') {
|
||||
if (!filter(source, dest)) {
|
||||
return doneOne(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
return getStats(source)
|
||||
}
|
||||
|
||||
function getStats (source) {
|
||||
var stat = dereference ? fs.stat : fs.lstat
|
||||
running++
|
||||
stat(source, function (err, stats) {
|
||||
if (err) return onError(err)
|
||||
|
||||
// We need to get the mode from the stats object and preserve it.
|
||||
var item = {
|
||||
name: source,
|
||||
mode: stats.mode,
|
||||
mtime: stats.mtime, // modified time
|
||||
atime: stats.atime, // access time
|
||||
stats: stats // temporary
|
||||
}
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return onDir(item)
|
||||
} else if (stats.isFile() || stats.isCharacterDevice() || stats.isBlockDevice()) {
|
||||
return onFile(item)
|
||||
} else if (stats.isSymbolicLink()) {
|
||||
// Symlinks don't really need to know about the mode.
|
||||
return onLink(source)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function onFile (file) {
|
||||
var target = file.name.replace(currentPath, targetPath.replace('$', '$$$$')) // escapes '$' with '$$'
|
||||
isWritable(target, function (writable) {
|
||||
if (writable) {
|
||||
copyFile(file, target)
|
||||
} else {
|
||||
if (overwrite) {
|
||||
rmFile(target, function () {
|
||||
copyFile(file, target)
|
||||
})
|
||||
} else if (errorOnExist) {
|
||||
onError(new Error(target + ' already exists'))
|
||||
} else {
|
||||
doneOne()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function copyFile (file, target) {
|
||||
var readStream = fs.createReadStream(file.name)
|
||||
var writeStream = fs.createWriteStream(target, { mode: file.mode })
|
||||
|
||||
readStream.on('error', onError)
|
||||
writeStream.on('error', onError)
|
||||
|
||||
if (transform) {
|
||||
transform(readStream, writeStream, file)
|
||||
} else {
|
||||
writeStream.on('open', function () {
|
||||
readStream.pipe(writeStream)
|
||||
})
|
||||
}
|
||||
|
||||
writeStream.once('close', function () {
|
||||
fs.chmod(target, file.mode, function (err) {
|
||||
if (err) return onError(err)
|
||||
if (preserveTimestamps) {
|
||||
utimes.utimesMillis(target, file.atime, file.mtime, function (err) {
|
||||
if (err) return onError(err)
|
||||
return doneOne()
|
||||
})
|
||||
} else {
|
||||
doneOne()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function rmFile (file, done) {
|
||||
fs.unlink(file, function (err) {
|
||||
if (err) return onError(err)
|
||||
return done()
|
||||
})
|
||||
}
|
||||
|
||||
function onDir (dir) {
|
||||
var target = dir.name.replace(currentPath, targetPath.replace('$', '$$$$')) // escapes '$' with '$$'
|
||||
isWritable(target, function (writable) {
|
||||
if (writable) {
|
||||
return mkDir(dir, target)
|
||||
}
|
||||
copyDir(dir.name)
|
||||
})
|
||||
}
|
||||
|
||||
function mkDir (dir, target) {
|
||||
fs.mkdir(target, dir.mode, function (err) {
|
||||
if (err) return onError(err)
|
||||
// despite setting mode in fs.mkdir, doesn't seem to work
|
||||
// so we set it here.
|
||||
fs.chmod(target, dir.mode, function (err) {
|
||||
if (err) return onError(err)
|
||||
copyDir(dir.name)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function copyDir (dir) {
|
||||
fs.readdir(dir, function (err, items) {
|
||||
if (err) return onError(err)
|
||||
items.forEach(function (item) {
|
||||
startCopy(path.join(dir, item))
|
||||
})
|
||||
return doneOne()
|
||||
})
|
||||
}
|
||||
|
||||
function onLink (link) {
|
||||
var target = link.replace(currentPath, targetPath)
|
||||
fs.readlink(link, function (err, resolvedPath) {
|
||||
if (err) return onError(err)
|
||||
checkLink(resolvedPath, target)
|
||||
})
|
||||
}
|
||||
|
||||
function checkLink (resolvedPath, target) {
|
||||
if (dereference) {
|
||||
resolvedPath = path.resolve(basePath, resolvedPath)
|
||||
}
|
||||
isWritable(target, function (writable) {
|
||||
if (writable) {
|
||||
return makeLink(resolvedPath, target)
|
||||
}
|
||||
fs.readlink(target, function (err, targetDest) {
|
||||
if (err) return onError(err)
|
||||
|
||||
if (dereference) {
|
||||
targetDest = path.resolve(basePath, targetDest)
|
||||
}
|
||||
if (targetDest === resolvedPath) {
|
||||
return doneOne()
|
||||
}
|
||||
return rmFile(target, function () {
|
||||
makeLink(resolvedPath, target)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function makeLink (linkPath, target) {
|
||||
fs.symlink(linkPath, target, function (err) {
|
||||
if (err) return onError(err)
|
||||
return doneOne()
|
||||
})
|
||||
}
|
||||
|
||||
function isWritable (path, done) {
|
||||
fs.lstat(path, function (err) {
|
||||
if (err) {
|
||||
if (err.code === 'ENOENT') return done(true)
|
||||
return done(false)
|
||||
}
|
||||
return done(false)
|
||||
})
|
||||
}
|
||||
|
||||
function onError (err) {
|
||||
// ensure callback is defined & called only once:
|
||||
if (!errored && callback !== undefined) {
|
||||
errored = true
|
||||
return callback(err)
|
||||
}
|
||||
}
|
||||
|
||||
function doneOne (skipped) {
|
||||
if (!skipped) running--
|
||||
finished++
|
||||
if ((started === finished) && (running === 0)) {
|
||||
if (callback !== undefined) {
|
||||
return callback(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ncp
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
const u = require('universalify').fromCallback
|
||||
const fs = require('graceful-fs')
|
||||
const ncp = require('../copy/ncp')
|
||||
const copy = require('../copy/copy')
|
||||
const path = require('path')
|
||||
const remove = require('../remove').remove
|
||||
const mkdirp = require('../mkdirs').mkdirs
|
||||
@ -133,14 +133,14 @@ function moveDirAcrossDevice (src, dest, overwrite, callback) {
|
||||
if (overwrite) {
|
||||
remove(dest, err => {
|
||||
if (err) return callback(err)
|
||||
startNcp()
|
||||
startCopy()
|
||||
})
|
||||
} else {
|
||||
startNcp()
|
||||
startCopy()
|
||||
}
|
||||
|
||||
function startNcp () {
|
||||
ncp(src, dest, options, err => {
|
||||
function startCopy () {
|
||||
copy(src, dest, options, err => {
|
||||
if (err) return callback(err)
|
||||
remove(src, callback)
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user