163 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const { dirname, join, resolve, relative, isAbsolute } = require('path')
 | |
| const rimraf_ = require('rimraf')
 | |
| const { promisify } = require('util')
 | |
| const {
 | |
|   access: access_,
 | |
|   accessSync,
 | |
|   copyFile: copyFile_,
 | |
|   copyFileSync,
 | |
|   unlink: unlink_,
 | |
|   unlinkSync,
 | |
|   readdir: readdir_,
 | |
|   readdirSync,
 | |
|   rename: rename_,
 | |
|   renameSync,
 | |
|   stat: stat_,
 | |
|   statSync,
 | |
|   lstat: lstat_,
 | |
|   lstatSync,
 | |
|   symlink: symlink_,
 | |
|   symlinkSync,
 | |
|   readlink: readlink_,
 | |
|   readlinkSync
 | |
| } = require('fs')
 | |
| 
 | |
| const access = promisify(access_)
 | |
| const copyFile = promisify(copyFile_)
 | |
| const unlink = promisify(unlink_)
 | |
| const readdir = promisify(readdir_)
 | |
| const rename = promisify(rename_)
 | |
| const stat = promisify(stat_)
 | |
| const lstat = promisify(lstat_)
 | |
| const symlink = promisify(symlink_)
 | |
| const readlink = promisify(readlink_)
 | |
| const rimraf = promisify(rimraf_)
 | |
| const rimrafSync = rimraf_.sync
 | |
| 
 | |
| const mkdirp = require('mkdirp')
 | |
| 
 | |
| const pathExists = async path => {
 | |
|   try {
 | |
|     await access(path)
 | |
|     return true
 | |
|   } catch (er) {
 | |
|     return er.code !== 'ENOENT'
 | |
|   }
 | |
| }
 | |
| 
 | |
| const pathExistsSync = path => {
 | |
|   try {
 | |
|     accessSync(path)
 | |
|     return true
 | |
|   } catch (er) {
 | |
|     return er.code !== 'ENOENT'
 | |
|   }
 | |
| }
 | |
| 
 | |
| const moveFile = async (source, destination, options = {}, root = true, symlinks = []) => {
 | |
|   if (!source || !destination) {
 | |
|     throw new TypeError('`source` and `destination` file required')
 | |
|   }
 | |
| 
 | |
|   options = {
 | |
|     overwrite: true,
 | |
|     ...options
 | |
|   }
 | |
| 
 | |
|   if (!options.overwrite && await pathExists(destination)) {
 | |
|     throw new Error(`The destination file exists: ${destination}`)
 | |
|   }
 | |
| 
 | |
|   await mkdirp(dirname(destination))
 | |
| 
 | |
|   try {
 | |
|     await rename(source, destination)
 | |
|   } catch (error) {
 | |
|     if (error.code === 'EXDEV' || error.code === 'EPERM') {
 | |
|       const sourceStat = await lstat(source)
 | |
|       if (sourceStat.isDirectory()) {
 | |
|         const files = await readdir(source)
 | |
|         await Promise.all(files.map((file) => moveFile(join(source, file), join(destination, file), options, false, symlinks)))
 | |
|       } else if (sourceStat.isSymbolicLink()) {
 | |
|         symlinks.push({ source, destination })
 | |
|       } else {
 | |
|         await copyFile(source, destination)
 | |
|       }
 | |
|     } else {
 | |
|       throw error
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (root) {
 | |
|     await Promise.all(symlinks.map(async ({ source, destination }) => {
 | |
|       let target = await readlink(source)
 | |
|       // junction symlinks in windows will be absolute paths, so we need to make sure they point to the destination
 | |
|       if (isAbsolute(target))
 | |
|         target = resolve(destination, relative(source, target))
 | |
|       // try to determine what the actual file is so we can create the correct type of symlink in windows
 | |
|       let targetStat
 | |
|       try {
 | |
|         targetStat = await stat(resolve(dirname(source), target))
 | |
|       } catch (err) {}
 | |
|       await symlink(target, destination, targetStat && targetStat.isDirectory() ? 'junction' : 'file')
 | |
|     }))
 | |
|     await rimraf(source)
 | |
|   }
 | |
| }
 | |
| 
 | |
| const moveFileSync = (source, destination, options = {}, root = true, symlinks = []) => {
 | |
|   if (!source || !destination) {
 | |
|     throw new TypeError('`source` and `destination` file required')
 | |
|   }
 | |
| 
 | |
|   options = {
 | |
|     overwrite: true,
 | |
|     ...options
 | |
|   }
 | |
| 
 | |
|   if (!options.overwrite && pathExistsSync(destination)) {
 | |
|     throw new Error(`The destination file exists: ${destination}`)
 | |
|   }
 | |
| 
 | |
|   mkdirp.sync(dirname(destination))
 | |
| 
 | |
|   try {
 | |
|     renameSync(source, destination)
 | |
|   } catch (error) {
 | |
|     if (error.code === 'EXDEV' || error.code === 'EPERM') {
 | |
|       const sourceStat = lstatSync(source)
 | |
|       if (sourceStat.isDirectory()) {
 | |
|         const files = readdirSync(source)
 | |
|         for (const file of files) {
 | |
|           moveFileSync(join(source, file), join(destination, file), options, false, symlinks)
 | |
|         }
 | |
|       } else if (sourceStat.isSymbolicLink()) {
 | |
|         symlinks.push({ source, destination })
 | |
|       } else {
 | |
|         copyFileSync(source, destination)
 | |
|       }
 | |
|     } else {
 | |
|       throw error
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (root) {
 | |
|     for (const { source, destination } of symlinks) {
 | |
|       let target = readlinkSync(source)
 | |
|       // junction symlinks in windows will be absolute paths, so we need to make sure they point to the destination
 | |
|       if (isAbsolute(target))
 | |
|         target = resolve(destination, relative(source, target))
 | |
|       // try to determine what the actual file is so we can create the correct type of symlink in windows
 | |
|       let targetStat
 | |
|       try {
 | |
|         targetStat = statSync(resolve(dirname(source), target))
 | |
|       } catch (err) {}
 | |
|       symlinkSync(target, destination, targetStat && targetStat.isDirectory() ? 'junction' : 'file')
 | |
|     }
 | |
|     rimrafSync(source)
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = moveFile
 | |
| module.exports.sync = moveFileSync
 | 
