rsnext/packages/next/lib/recursive-copy.ts
Luc 3b5f18495b Replace recursive-copy with own implementation (#7263)
* replace recursive-copy with own implementation

* update yarn.lock

* do not filter out not directories

* do not fail if folder already exists

* replace `\` by `/` when sending pathes to filter

* use fs-extra only in tests

* investigate and test recursive-copy npm module

* improve test by creating fixtures programmatically

* remove recursive-copy npm module test

* add recursive-copy to bench

* add bench:recursive-copy script

* fix Sema import in recursive-copy.ts

* small improvements
2019-06-06 12:33:11 +02:00

58 lines
1.4 KiB
TypeScript

import path from 'path'
import fs from 'fs'
import { promisify } from 'util'
import { Sema } from 'async-sema'
const mkdir = promisify(fs.mkdir)
const stat = promisify(fs.stat)
const readdir = promisify(fs.readdir)
const copyFile = promisify(fs.copyFile)
const COPYFILE_EXCL = fs.constants.COPYFILE_EXCL
export async function recursiveCopy(
source: string,
dest: string,
{
concurrency = 255,
filter = () => true,
}: { concurrency?: number; filter?(path: string): boolean } = {}
) {
const cwdPath = process.cwd()
const from = path.resolve(cwdPath, source)
const to = path.resolve(cwdPath, dest)
const sema = new Sema(concurrency)
async function _copy(item: string) {
const target = item.replace(from, to)
const stats = await stat(item)
await sema.acquire()
if (stats.isDirectory()) {
try {
await mkdir(target)
} catch (err) {
// do not throw `folder already exists` errors
if (err.code !== 'EEXIST') {
throw err
}
}
const files = await readdir(item)
await Promise.all(files.map(file => _copy(path.join(item, file))))
} else if (
stats.isFile() &&
// before we send the path to filter
// we remove the base path (from) and replace \ by / (windows)
filter(item.replace(from, '').replace(/\\/g, '/'))
) {
await copyFile(item, target, COPYFILE_EXCL)
}
sema.release()
return
}
await _copy(from)
}