(item: string, lstats?: Stats | Dirent)
| 27 | |
| 28 | // deep copy the file/directory |
| 29 | async function _copy(item: string, lstats?: Stats | Dirent): Promise<void> { |
| 30 | const target = item.replace(from, to) |
| 31 | |
| 32 | await sema.acquire() |
| 33 | |
| 34 | if (!lstats) { |
| 35 | // after lock on first run |
| 36 | lstats = await promises.lstat(from) |
| 37 | } |
| 38 | |
| 39 | // readdir & lstat do not follow symbolic links |
| 40 | // if part is a symbolic link, follow it with stat |
| 41 | let isFile = lstats.isFile() |
| 42 | let isDirectory = lstats.isDirectory() |
| 43 | if (lstats.isSymbolicLink()) { |
| 44 | const stats = await promises.stat(item) |
| 45 | isFile = stats.isFile() |
| 46 | isDirectory = stats.isDirectory() |
| 47 | } |
| 48 | |
| 49 | if (isDirectory) { |
| 50 | try { |
| 51 | await promises.mkdir(target, { recursive: true }) |
| 52 | } catch (err) { |
| 53 | // do not throw `folder already exists` errors |
| 54 | if (isError(err) && err.code !== 'EEXIST') { |
| 55 | throw err |
| 56 | } |
| 57 | } |
| 58 | sema.release() |
| 59 | const files = await promises.readdir(item, { withFileTypes: true }) |
| 60 | await Promise.all( |
| 61 | files.map((file) => _copy(path.join(item, file.name), file)) |
| 62 | ) |
| 63 | } else if ( |
| 64 | isFile && |
| 65 | // before we send the path to filter |
| 66 | // we remove the base path (from) and replace \ by / (windows) |
| 67 | filter(item.replace(from, '').replace(/\\/g, '/')) |
| 68 | ) { |
| 69 | await promises |
| 70 | .copyFile(item, target, overwrite ? undefined : COPYFILE_EXCL) |
| 71 | .catch((err) => { |
| 72 | // if overwrite is false we shouldn't fail on EEXIST |
| 73 | if (err.code !== 'EEXIST') { |
| 74 | throw err |
| 75 | } |
| 76 | }) |
| 77 | sema.release() |
| 78 | } else { |
| 79 | sema.release() |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | await _copy(from) |
| 84 | } |
no test coverage detected