diff options
Diffstat (limited to 'node_modules/fs-extra/lib/copy')
-rw-r--r-- | node_modules/fs-extra/lib/copy/copy.js | 264 | ||||
-rw-r--r-- | node_modules/fs-extra/lib/copy/index.js | 4 |
2 files changed, 268 insertions, 0 deletions
diff --git a/node_modules/fs-extra/lib/copy/copy.js b/node_modules/fs-extra/lib/copy/copy.js new file mode 100644 index 0000000..fd9687f --- /dev/null +++ b/node_modules/fs-extra/lib/copy/copy.js @@ -0,0 +1,264 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') +const mkdirp = require('../mkdirs').mkdirs +const pathExists = require('../path-exists').pathExists +const utimes = require('../util/utimes').utimesMillis + +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 = {filter: opts} + } + + 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 + if (src === dest) return cb(new Error('Source and destination must not be the same.')) + + if (opts.filter) return handleFilter(checkParentDir, src, dest, opts, cb) + return checkParentDir(src, dest, opts, cb) +} + +function checkParentDir (src, dest, opts, cb) { + 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) return handleFilter(getStats, src, dest, opts, cb) + return getStats(src, dest, opts, cb) +} + +function handleFilter (onInclude, src, dest, opts, cb) { + Promise.resolve(opts.filter(src, dest)) + .then(include => { + if (include) return onInclude(src, dest, opts, cb) + return cb() + }, error => cb(error)) +} + +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 copyFile(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 copyFile(srcStat, src, dest, opts, cb) + }) + } else if (opts.errorOnExist) { + return cb(new Error(`'${dest}' already exists`)) + } else return 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 setDestModeAndTimestamps(srcStat, dest, opts, cb) + }) + } + return copyFileFallback(srcStat, src, dest, opts, cb) +} + +function copyFileFallback (srcStat, src, dest, opts, cb) { + const rs = fs.createReadStream(src) + rs.on('error', err => cb(err)) + .once('open', () => { + const ws = fs.createWriteStream(dest, { mode: srcStat.mode }) + ws.on('error', err => cb(err)) + .on('open', () => rs.pipe(ws)) + .once('close', () => setDestModeAndTimestamps(srcStat, dest, opts, cb)) + }) +} + +function setDestModeAndTimestamps (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() + }) +} + +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 copyDir(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 copyDir(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 copyDir(src, dest, opts, cb) + }) + }) +} + +function copyDir (src, dest, opts, cb) { + fs.readdir(src, (err, items) => { + if (err) return cb(err) + return copyDirItems(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 copyDirItems(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 would result in removing src contents + // and therefore a broken symlink would 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 copyLink(resolvedSrcPath, dest, cb) + }) + } + }) + }) +} + +function copyLink (resolvedSrcPath, dest, cb) { + fs.unlink(dest, err => { + if (err) return cb(err) + return fs.symlink(resolvedSrcPath, dest, cb) + }) +} + +// 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 may throw 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 diff --git a/node_modules/fs-extra/lib/copy/index.js b/node_modules/fs-extra/lib/copy/index.js new file mode 100644 index 0000000..a6a51da --- /dev/null +++ b/node_modules/fs-extra/lib/copy/index.js @@ -0,0 +1,4 @@ +const u = require('universalify').fromCallback +module.exports = { + copy: u(require('./copy')) +} |