Skip to content

Commit

Permalink
Add native fs.copyFile to copy()
Browse files Browse the repository at this point in the history
  • Loading branch information
manidlou committed Oct 31, 2017
1 parent 166f4a7 commit 0bd5279
Showing 1 changed file with 35 additions and 24 deletions.
59 changes: 35 additions & 24 deletions lib/copy/copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ 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)
return copyFile(srcStat, src, dest, opts, cb)
} else if (resolvedPath === existsReg) {
return mayCopyFile(srcStat, src, dest, opts, cb)
} else {
Expand All @@ -89,30 +89,41 @@ 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)
return copyFile(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) {
function copyFile (srcStat, src, dest, opts, cb) {
if (typeof fs.copyFile === 'function') {
return fs.copyFile(src, dest, err => {
if (err) return cb(err)
return handleDestModeAndTimestamps(srcStat, dest, opts, cb)
})
}
return copyFileFallback(srcStat, src, dest, opts, cb)
}

function copyFileFallback (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()
})
ws.on('open', () => rs.pipe(ws))
.once('close', () => handleDestModeAndTimestamps(srcStat, dest, opts, cb))
}

function handleDestModeAndTimestamps (srcStat, dest, opts, cb) {
fs.chmod(dest, srcStat.mode, err => {
if (err) return cb(err)
if (opts.preserveTimestamps) {
return utimes(dest, srcStat.atime, srcStat.mtime, cb)
}
return cb()
})
}

Expand All @@ -131,7 +142,7 @@ function onDir (srcStat, src, dest, opts, cb) {
return mayCopyDir(src, dest, opts, cb)
} else {
if (src === resolvedPath) return cb()
return cpDir(src, dest, opts, cb)
return copyDir(src, dest, opts, cb)
}
})
}
Expand All @@ -142,7 +153,7 @@ function mayCopyDir (src, dest, opts, cb) {
if (!st.isDirectory()) {
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
}
return cpDir(src, dest, opts, cb)
return copyDir(src, dest, opts, cb)
})
}

Expand All @@ -151,24 +162,24 @@ function mkDirAndCopy (srcStat, src, dest, opts, cb) {
if (err) return cb(err)
fs.chmod(dest, srcStat.mode, err => {
if (err) return cb(err)
return cpDir(src, dest, opts, cb)
return copyDir(src, dest, opts, cb)
})
})
}

function cpDir (src, dest, opts, cb) {
function copyDir (src, dest, opts, cb) {
fs.readdir(src, (err, items) => {
if (err) return cb(err)
return cpDirItems(items, src, dest, opts, cb)
return copyDirItems(items, src, dest, opts, cb)
})
}

function cpDirItems (items, src, dest, opts, cb) {
function copyDirItems (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)
return copyDirItems(items, src, dest, opts, cb)
})
}

Expand Down Expand Up @@ -201,27 +212,27 @@ function onLink (src, dest, opts, cb) {
if (st.isDirectory() && isSrcSubdir(resolvedDestPath, resolvedSrcPath)) {
return cb(new Error(`Cannot overwrite '${resolvedDestPath}' with '${resolvedSrcPath}'.`))
}
return cpLink(resolvedSrcPath, dest, cb)
return copyLink(resolvedSrcPath, dest, cb)
})
}
})
})
}

function cpLink (resolvedSrcPath, dest, cb) {
function copyLink (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
// check if dest 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.
// dest exists and is a regular file or directory, Windows may throw UNKNOWN error.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return cb(null, existsReg)

return cb(err)
Expand Down

0 comments on commit 0bd5279

Please sign in to comment.