# cpy

> Copy files

## Why

- Fast by [cloning](https://stackoverflow.com/questions/71629903/node-js-why-we-should-use-copyfile-ficlone-and-copyfile-ficlone-force-what-is) the files whenever possible.
- Resilient by using [graceful-fs](https://github.com/isaacs/node-graceful-fs).
- User-friendly by accepting [globs](https://github.com/sindresorhus/globby#globbing-patterns) and creating non-existent destination directories.
- User-friendly error messages.
- Progress reporting.

## Install

```sh
npm install cpy
```

## Usage

```js
import cpy from 'cpy';

await cpy([
	'source/*.png', // Copy all .png files
	'!source/goat.png', // Ignore goat.png
], 'destination');

// Copy node_modules to destination/node_modules
await cpy('node_modules', 'destination');

// Copy node_modules content to destination
await cpy('node_modules/**', 'destination');

// Copy node_modules structure but skip all files except .json files
await cpy('node_modules/**/*.json', 'destination');

// Copy all png files into destination without keeping directory structure
await cpy('**/*.png', 'destination', {flat: true});

// Progress reporting
await cpy('source/**', 'destination', {
	onProgress: progress => {
		console.log(`Progress: ${Math.round(progress.percent * 100)}%`);
	}
});

console.log('Files copied!');
```

## API

### cpy(source, destination, options?)

Returns a `Promise<string[]>` with the destination file paths.

#### source

Type: `string | string[]`

Files to copy.

If any of the files do not exist, an error will be thrown (does not apply to globs).

#### destination

Type: `string`

Destination directory.

#### options

Type: `object`

Options are passed to [globby](https://github.com/sindresorhus/globby#options).

Note: Dotfiles are excluded by default. Set `dot: true` to include them.

In addition, you can specify the below options.

##### cwd

Type: `string`\
Default: `process.cwd()`

Working directory to find source files.

##### overwrite

Type: `boolean`\
Default: `true`

Overwrite existing files.

##### ignoreExisting

Type: `boolean`\
Default: `false`

Skip files when the destination path already exists.

This option takes precedence over `overwrite`.

##### update

Type: `boolean`\
Default: `false`

Only overwrite when the source is newer, or when sizes differ with the same modification time. Ignored when `overwrite` is `false` or `ignoreExisting` is `true`.

##### flat

Type: `boolean`\
Default: `false`

Flatten directory structure. All copied files will be put in the same directory.

```js
import cpy from 'cpy';

await cpy('src/**/*.js', 'destination', {
	flat: true
});
```

##### base

Type: `'cwd' | 'pattern'`\
Default: `undefined`

Choose how destination paths are calculated for patterns. By default, globs are resolved relative to their parent and explicit paths are resolved relative to `cwd`. Set to `'pattern'` to make explicit paths behave like globs, or `'cwd'` to make globs behave like explicit paths.

##### rename

Type: `string | Function`

Filename or function used to rename every file in `source`. Use a two-argument function to receive a frozen `source` file object and a mutable `destination` file object. The destination path must stay within the original destination directory. The legacy single-argument form is deprecated, emits a warning, and will be removed in the next major release.

```js
import cpy from 'cpy';

await cpy('foo.js', 'destination', {
	rename: (source, destination) => {
		if (source.nameWithoutExtension === 'foo') {
			destination.nameWithoutExtension = 'bar';
		}
	}
});

await cpy('foo.js', 'destination', {
	rename: (source, destination) => {
		if (source.nameWithoutExtension === 'foo') {
			destination.extension = 'ts';
		}

		console.log(destination.name);
		//=> 'foo.ts'
	}
});

await cpy('foo.js', 'destination', {
	rename: 'new-name'
});
```

##### concurrency

Type: `number`\
Default: `os.availableParallelism()`

Number of files being copied concurrently.

##### ignoreJunk

Type: `boolean`\
Default: `true`

Ignores [junk](https://github.com/sindresorhus/junk) files.

##### filter

Type: `Function`

Function to filter files to copy.

Receives a source file object and a context object with the resolved destination path.

Return true to include, false to exclude. You can also return a Promise that resolves to true or false.

```js
import cpy from 'cpy';

await cpy('foo', 'destination', {
	filter: (file, {destinationPath}) => file.extension !== 'nocopy'
});
```

##### onProgress

Type: `Function`

The given function is called whenever there is measurable progress.

```js
import cpy from 'cpy';

await cpy('foo', 'destination', {
	onProgress: progress => {
		// …
	}
});
```

##### signal

Type: [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)

Abort signal to cancel the copy operation.

##### followSymbolicLinks

Type: `boolean`\
Default: `true`

Whether to follow symbolic links.

##### preserveTimestamps

Type: `boolean`\
Default: `false`

Preserve file access and modification timestamps when copying.

##### dryRun

Type: `boolean`\
Default: `false`

Skip copying and return the resolved destination paths.

##### Source file object

###### path

Type: `string`\
Example: `'/tmp/dir/foo.js'`

Resolved path to the file.

###### relativePath

Type: `string`\
Example: `'dir/foo.js'` if `cwd` was `'/tmp'`

Relative path to the file from `cwd`.

###### name

Type: `string`\
Example: `'foo.js'`

Filename with extension.

###### nameWithoutExtension

Type: `string`\
Example: `'foo'`

Filename without extension.

###### extension

Type: `string`\
Example: `'js'`

File extension.

##### Destination file object

###### path

Type: `string`\
Example: `'/tmp/dir/foo.js'`

Resolved destination path for the file. The directory part must stay within the original destination directory.

###### name

Type: `string`\
Example: `'foo.js'`

Filename with extension.

###### nameWithoutExtension

Type: `string`\
Example: `'foo'`

Filename without extension.

###### extension

Type: `string`\
Example: `'js'`

File extension.

## Progress reporting

The `onProgress` option provides progress information during file copying:

```js
import cpy from 'cpy';

await cpy(source, destination, {
	onProgress: progress => {
		console.log(`Progress: ${Math.round(progress.percent * 100)}%`);
	}
});
```

### Progress object

```js
{
	completedFiles: number,
	totalFiles: number,
	completedSize: number,
	percent: number,
	sourcePath: string,
	destinationPath: string,
}
```

- `completedFiles` - Number of files copied so far.
- `totalFiles` - Total number of files to copy.
- `completedSize` - Number of bytes copied so far.
- `percent` - Progress percentage as a value between `0` and `1`.
- `sourcePath` - Absolute source path of the current file being copied.
- `destinationPath` - Absolute destination path of the current file being copied.

#### handler(progress)

Type: `Function`

Note that the `.on()` method is available only right after the initial `cpy` call, so make sure you add a `handler` before awaiting the promise:

```js
import cpy from 'cpy';

await cpy(source, destination).on('progress', progress => {
	// …
});
```

## Related

- [cpy-cli](https://github.com/sindresorhus/cpy-cli) - CLI for this module
- [copy-file](https://github.com/sindresorhus/copy-file) - Copy a single file
- [move-file](https://github.com/sindresorhus/move-file) - Move a file
- [make-dir](https://github.com/sindresorhus/make-dir) - Make a directory and its parents if needed
