| // this file is a modified version of the code in node 17.2.0 | 
| // which is, in turn, a modified version of the fs-extra module on npm | 
| // node core changes: | 
| // - Use of the assert module has been replaced with core's error system. | 
| // - All code related to the glob dependency has been removed. | 
| // - Bring your own custom fs module is not currently supported. | 
| // - Some basic code cleanup. | 
| // changes here: | 
| // - remove all callback related code | 
| // - drop sync support | 
| // - change assertions back to non-internal methods (see options.js) | 
| // - throws ENOTDIR when rmdir gets an ENOENT for a path that exists in Windows | 
| 'use strict' | 
|   | 
| const { | 
|   ERR_FS_CP_DIR_TO_NON_DIR, | 
|   ERR_FS_CP_EEXIST, | 
|   ERR_FS_CP_EINVAL, | 
|   ERR_FS_CP_FIFO_PIPE, | 
|   ERR_FS_CP_NON_DIR_TO_DIR, | 
|   ERR_FS_CP_SOCKET, | 
|   ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY, | 
|   ERR_FS_CP_UNKNOWN, | 
|   ERR_FS_EISDIR, | 
|   ERR_INVALID_ARG_TYPE, | 
| } = require('../errors.js') | 
| const { | 
|   constants: { | 
|     errno: { | 
|       EEXIST, | 
|       EISDIR, | 
|       EINVAL, | 
|       ENOTDIR, | 
|     }, | 
|   }, | 
| } = require('os') | 
| const { | 
|   chmod, | 
|   copyFile, | 
|   lstat, | 
|   mkdir, | 
|   readdir, | 
|   readlink, | 
|   stat, | 
|   symlink, | 
|   unlink, | 
|   utimes, | 
| } = require('../fs.js') | 
| const { | 
|   dirname, | 
|   isAbsolute, | 
|   join, | 
|   parse, | 
|   resolve, | 
|   sep, | 
|   toNamespacedPath, | 
| } = require('path') | 
| const { fileURLToPath } = require('url') | 
|   | 
| const defaultOptions = { | 
|   dereference: false, | 
|   errorOnExist: false, | 
|   filter: undefined, | 
|   force: true, | 
|   preserveTimestamps: false, | 
|   recursive: false, | 
| } | 
|   | 
| async function cp (src, dest, opts) { | 
|   if (opts != null && typeof opts !== 'object') { | 
|     throw new ERR_INVALID_ARG_TYPE('options', ['Object'], opts) | 
|   } | 
|   return cpFn( | 
|     toNamespacedPath(getValidatedPath(src)), | 
|     toNamespacedPath(getValidatedPath(dest)), | 
|     { ...defaultOptions, ...opts }) | 
| } | 
|   | 
| function getValidatedPath (fileURLOrPath) { | 
|   const path = fileURLOrPath != null && fileURLOrPath.href | 
|       && fileURLOrPath.origin | 
|     ? fileURLToPath(fileURLOrPath) | 
|     : fileURLOrPath | 
|   return path | 
| } | 
|   | 
| async function cpFn (src, dest, opts) { | 
|   // Warn about using preserveTimestamps on 32-bit node | 
|   // istanbul ignore next | 
|   if (opts.preserveTimestamps && process.arch === 'ia32') { | 
|     const warning = 'Using the preserveTimestamps option in 32-bit ' + | 
|       'node is not recommended' | 
|     process.emitWarning(warning, 'TimestampPrecisionWarning') | 
|   } | 
|   const stats = await checkPaths(src, dest, opts) | 
|   const { srcStat, destStat } = stats | 
|   await checkParentPaths(src, srcStat, dest) | 
|   if (opts.filter) { | 
|     return handleFilter(checkParentDir, destStat, src, dest, opts) | 
|   } | 
|   return checkParentDir(destStat, src, dest, opts) | 
| } | 
|   | 
| async function checkPaths (src, dest, opts) { | 
|   const { 0: srcStat, 1: destStat } = await getStats(src, dest, opts) | 
|   if (destStat) { | 
|     if (areIdentical(srcStat, destStat)) { | 
|       throw new ERR_FS_CP_EINVAL({ | 
|         message: 'src and dest cannot be the same', | 
|         path: dest, | 
|         syscall: 'cp', | 
|         errno: EINVAL, | 
|       }) | 
|     } | 
|     if (srcStat.isDirectory() && !destStat.isDirectory()) { | 
|       throw new ERR_FS_CP_DIR_TO_NON_DIR({ | 
|         message: `cannot overwrite directory ${src} ` + | 
|             `with non-directory ${dest}`, | 
|         path: dest, | 
|         syscall: 'cp', | 
|         errno: EISDIR, | 
|       }) | 
|     } | 
|     if (!srcStat.isDirectory() && destStat.isDirectory()) { | 
|       throw new ERR_FS_CP_NON_DIR_TO_DIR({ | 
|         message: `cannot overwrite non-directory ${src} ` + | 
|             `with directory ${dest}`, | 
|         path: dest, | 
|         syscall: 'cp', | 
|         errno: ENOTDIR, | 
|       }) | 
|     } | 
|   } | 
|   | 
|   if (srcStat.isDirectory() && isSrcSubdir(src, dest)) { | 
|     throw new ERR_FS_CP_EINVAL({ | 
|       message: `cannot copy ${src} to a subdirectory of self ${dest}`, | 
|       path: dest, | 
|       syscall: 'cp', | 
|       errno: EINVAL, | 
|     }) | 
|   } | 
|   return { srcStat, destStat } | 
| } | 
|   | 
| function areIdentical (srcStat, destStat) { | 
|   return destStat.ino && destStat.dev && destStat.ino === srcStat.ino && | 
|     destStat.dev === srcStat.dev | 
| } | 
|   | 
| function getStats (src, dest, opts) { | 
|   const statFunc = opts.dereference ? | 
|     (file) => stat(file, { bigint: true }) : | 
|     (file) => lstat(file, { bigint: true }) | 
|   return Promise.all([ | 
|     statFunc(src), | 
|     statFunc(dest).catch((err) => { | 
|       // istanbul ignore next: unsure how to cover. | 
|       if (err.code === 'ENOENT') { | 
|         return null | 
|       } | 
|       // istanbul ignore next: unsure how to cover. | 
|       throw err | 
|     }), | 
|   ]) | 
| } | 
|   | 
| async function checkParentDir (destStat, src, dest, opts) { | 
|   const destParent = dirname(dest) | 
|   const dirExists = await pathExists(destParent) | 
|   if (dirExists) { | 
|     return getStatsForCopy(destStat, src, dest, opts) | 
|   } | 
|   await mkdir(destParent, { recursive: true }) | 
|   return getStatsForCopy(destStat, src, dest, opts) | 
| } | 
|   | 
| function pathExists (dest) { | 
|   return stat(dest).then( | 
|     () => true, | 
|     // istanbul ignore next: not sure when this would occur | 
|     (err) => (err.code === 'ENOENT' ? false : Promise.reject(err))) | 
| } | 
|   | 
| // Recursively check if dest parent is a subdirectory of src. | 
| // It works for all file types including symlinks since it | 
| // checks the src and dest inodes. It starts from the deepest | 
| // parent and stops once it reaches the src parent or the root path. | 
| async function checkParentPaths (src, srcStat, dest) { | 
|   const srcParent = resolve(dirname(src)) | 
|   const destParent = resolve(dirname(dest)) | 
|   if (destParent === srcParent || destParent === parse(destParent).root) { | 
|     return | 
|   } | 
|   let destStat | 
|   try { | 
|     destStat = await stat(destParent, { bigint: true }) | 
|   } catch (err) { | 
|     // istanbul ignore else: not sure when this would occur | 
|     if (err.code === 'ENOENT') { | 
|       return | 
|     } | 
|     // istanbul ignore next: not sure when this would occur | 
|     throw err | 
|   } | 
|   if (areIdentical(srcStat, destStat)) { | 
|     throw new ERR_FS_CP_EINVAL({ | 
|       message: `cannot copy ${src} to a subdirectory of self ${dest}`, | 
|       path: dest, | 
|       syscall: 'cp', | 
|       errno: EINVAL, | 
|     }) | 
|   } | 
|   return checkParentPaths(src, srcStat, destParent) | 
| } | 
|   | 
| const normalizePathToArray = (path) => | 
|   resolve(path).split(sep).filter(Boolean) | 
|   | 
| // Return true if dest is a subdir of src, otherwise false. | 
| // It only checks the path strings. | 
| function isSrcSubdir (src, dest) { | 
|   const srcArr = normalizePathToArray(src) | 
|   const destArr = normalizePathToArray(dest) | 
|   return srcArr.every((cur, i) => destArr[i] === cur) | 
| } | 
|   | 
| async function handleFilter (onInclude, destStat, src, dest, opts, cb) { | 
|   const include = await opts.filter(src, dest) | 
|   if (include) { | 
|     return onInclude(destStat, src, dest, opts, cb) | 
|   } | 
| } | 
|   | 
| function startCopy (destStat, src, dest, opts) { | 
|   if (opts.filter) { | 
|     return handleFilter(getStatsForCopy, destStat, src, dest, opts) | 
|   } | 
|   return getStatsForCopy(destStat, src, dest, opts) | 
| } | 
|   | 
| async function getStatsForCopy (destStat, src, dest, opts) { | 
|   const statFn = opts.dereference ? stat : lstat | 
|   const srcStat = await statFn(src) | 
|   // istanbul ignore else: can't portably test FIFO | 
|   if (srcStat.isDirectory() && opts.recursive) { | 
|     return onDir(srcStat, destStat, src, dest, opts) | 
|   } else if (srcStat.isDirectory()) { | 
|     throw new ERR_FS_EISDIR({ | 
|       message: `${src} is a directory (not copied)`, | 
|       path: src, | 
|       syscall: 'cp', | 
|       errno: EINVAL, | 
|     }) | 
|   } else if (srcStat.isFile() || | 
|             srcStat.isCharacterDevice() || | 
|             srcStat.isBlockDevice()) { | 
|     return onFile(srcStat, destStat, src, dest, opts) | 
|   } else if (srcStat.isSymbolicLink()) { | 
|     return onLink(destStat, src, dest) | 
|   } else if (srcStat.isSocket()) { | 
|     throw new ERR_FS_CP_SOCKET({ | 
|       message: `cannot copy a socket file: ${dest}`, | 
|       path: dest, | 
|       syscall: 'cp', | 
|       errno: EINVAL, | 
|     }) | 
|   } else if (srcStat.isFIFO()) { | 
|     throw new ERR_FS_CP_FIFO_PIPE({ | 
|       message: `cannot copy a FIFO pipe: ${dest}`, | 
|       path: dest, | 
|       syscall: 'cp', | 
|       errno: EINVAL, | 
|     }) | 
|   } | 
|   // istanbul ignore next: should be unreachable | 
|   throw new ERR_FS_CP_UNKNOWN({ | 
|     message: `cannot copy an unknown file type: ${dest}`, | 
|     path: dest, | 
|     syscall: 'cp', | 
|     errno: EINVAL, | 
|   }) | 
| } | 
|   | 
| function onFile (srcStat, destStat, src, dest, opts) { | 
|   if (!destStat) { | 
|     return _copyFile(srcStat, src, dest, opts) | 
|   } | 
|   return mayCopyFile(srcStat, src, dest, opts) | 
| } | 
|   | 
| async function mayCopyFile (srcStat, src, dest, opts) { | 
|   if (opts.force) { | 
|     await unlink(dest) | 
|     return _copyFile(srcStat, src, dest, opts) | 
|   } else if (opts.errorOnExist) { | 
|     throw new ERR_FS_CP_EEXIST({ | 
|       message: `${dest} already exists`, | 
|       path: dest, | 
|       syscall: 'cp', | 
|       errno: EEXIST, | 
|     }) | 
|   } | 
| } | 
|   | 
| async function _copyFile (srcStat, src, dest, opts) { | 
|   await copyFile(src, dest) | 
|   if (opts.preserveTimestamps) { | 
|     return handleTimestampsAndMode(srcStat.mode, src, dest) | 
|   } | 
|   return setDestMode(dest, srcStat.mode) | 
| } | 
|   | 
| async function handleTimestampsAndMode (srcMode, src, dest) { | 
|   // Make sure the file is writable before setting the timestamp | 
|   // otherwise open fails with EPERM when invoked with 'r+' | 
|   // (through utimes call) | 
|   if (fileIsNotWritable(srcMode)) { | 
|     await makeFileWritable(dest, srcMode) | 
|     return setDestTimestampsAndMode(srcMode, src, dest) | 
|   } | 
|   return setDestTimestampsAndMode(srcMode, src, dest) | 
| } | 
|   | 
| function fileIsNotWritable (srcMode) { | 
|   return (srcMode & 0o200) === 0 | 
| } | 
|   | 
| function makeFileWritable (dest, srcMode) { | 
|   return setDestMode(dest, srcMode | 0o200) | 
| } | 
|   | 
| async function setDestTimestampsAndMode (srcMode, src, dest) { | 
|   await setDestTimestamps(src, dest) | 
|   return setDestMode(dest, srcMode) | 
| } | 
|   | 
| function setDestMode (dest, srcMode) { | 
|   return chmod(dest, srcMode) | 
| } | 
|   | 
| async function setDestTimestamps (src, dest) { | 
|   // The initial srcStat.atime cannot be trusted | 
|   // because it is modified by the read(2) system call | 
|   // (See https://nodejs.org/api/fs.html#fs_stat_time_values) | 
|   const updatedSrcStat = await stat(src) | 
|   return utimes(dest, updatedSrcStat.atime, updatedSrcStat.mtime) | 
| } | 
|   | 
| function onDir (srcStat, destStat, src, dest, opts) { | 
|   if (!destStat) { | 
|     return mkDirAndCopy(srcStat.mode, src, dest, opts) | 
|   } | 
|   return copyDir(src, dest, opts) | 
| } | 
|   | 
| async function mkDirAndCopy (srcMode, src, dest, opts) { | 
|   await mkdir(dest) | 
|   await copyDir(src, dest, opts) | 
|   return setDestMode(dest, srcMode) | 
| } | 
|   | 
| async function copyDir (src, dest, opts) { | 
|   const dir = await readdir(src) | 
|   for (let i = 0; i < dir.length; i++) { | 
|     const item = dir[i] | 
|     const srcItem = join(src, item) | 
|     const destItem = join(dest, item) | 
|     const { destStat } = await checkPaths(srcItem, destItem, opts) | 
|     await startCopy(destStat, srcItem, destItem, opts) | 
|   } | 
| } | 
|   | 
| async function onLink (destStat, src, dest) { | 
|   let resolvedSrc = await readlink(src) | 
|   if (!isAbsolute(resolvedSrc)) { | 
|     resolvedSrc = resolve(dirname(src), resolvedSrc) | 
|   } | 
|   if (!destStat) { | 
|     return symlink(resolvedSrc, dest) | 
|   } | 
|   let resolvedDest | 
|   try { | 
|     resolvedDest = await readlink(dest) | 
|   } catch (err) { | 
|     // Dest exists and is a regular file or directory, | 
|     // Windows may throw UNKNOWN error. If dest already exists, | 
|     // fs throws error anyway, so no need to guard against it here. | 
|     // istanbul ignore next: can only test on windows | 
|     if (err.code === 'EINVAL' || err.code === 'UNKNOWN') { | 
|       return symlink(resolvedSrc, dest) | 
|     } | 
|     // istanbul ignore next: should not be possible | 
|     throw err | 
|   } | 
|   if (!isAbsolute(resolvedDest)) { | 
|     resolvedDest = resolve(dirname(dest), resolvedDest) | 
|   } | 
|   if (isSrcSubdir(resolvedSrc, resolvedDest)) { | 
|     throw new ERR_FS_CP_EINVAL({ | 
|       message: `cannot copy ${resolvedSrc} to a subdirectory of self ` + | 
|             `${resolvedDest}`, | 
|       path: dest, | 
|       syscall: 'cp', | 
|       errno: EINVAL, | 
|     }) | 
|   } | 
|   // Do not 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. | 
|   const srcStat = await stat(src) | 
|   if (srcStat.isDirectory() && isSrcSubdir(resolvedDest, resolvedSrc)) { | 
|     throw new ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY({ | 
|       message: `cannot overwrite ${resolvedDest} with ${resolvedSrc}`, | 
|       path: dest, | 
|       syscall: 'cp', | 
|       errno: EINVAL, | 
|     }) | 
|   } | 
|   return copyLink(resolvedSrc, dest) | 
| } | 
|   | 
| async function copyLink (resolvedSrc, dest) { | 
|   await unlink(dest) | 
|   return symlink(resolvedSrc, dest) | 
| } | 
|   | 
| module.exports = cp |