This commit is contained in:
Tutur33
2023-11-24 22:35:41 +01:00
parent 3c0b507a93
commit 7644b2a0f7
45165 changed files with 4803356 additions and 3 deletions
+12
View File
@@ -0,0 +1,12 @@
'use strict';
const NestedError = require('nested-error-stacks');
class CpyError extends NestedError {
constructor(message, nested) {
super(message, nested);
Object.assign(this, nested);
this.name = 'CpyError';
}
}
module.exports = CpyError;
+161
View File
@@ -0,0 +1,161 @@
import {GlobbyOptions} from 'globby';
import {Options as CpFileOptions} from 'cp-file';
declare namespace cpy {
interface SourceFile {
/**
Resolved path to the file.
@example '/tmp/dir/foo.js'
*/
readonly path: string;
/**
Relative path to the file from `cwd`.
@example 'dir/foo.js' if `cwd` was '/tmp'
*/
readonly relativePath: string;
/**
Filename with extension.
@example 'foo.js'
*/
readonly name: string;
/**
Filename without extension.
@example 'foo'
*/
readonly nameWithoutExtension: string;
/**
File extension.
@example 'js'
*/
readonly extension: string;
}
interface Options extends Readonly<GlobbyOptions>, CpFileOptions {
/**
Working directory to find source files.
@default process.cwd()
*/
readonly cwd?: string;
/**
Preserve path structure.
@default false
*/
readonly parents?: boolean;
/**
Filename or function returning a filename used to rename every file in `source`.
@example
```
import cpy = require('cpy');
(async () => {
await cpy('foo.js', 'destination', {
rename: basename => `prefix-${basename}`
});
})();
```
*/
readonly rename?: string | ((basename: string) => string);
/**
Number of files being copied concurrently.
@default (os.cpus().length || 1) * 2
*/
readonly concurrency?: number;
/**
Ignore junk files.
@default true
*/
readonly ignoreJunk?: boolean;
/**
Function to filter files to copy.
Receives a source file object as the first argument.
Return true to include, false to exclude. You can also return a Promise that resolves to true or false.
@example
```
import cpy = require('cpy');
(async () => {
await cpy('foo', 'destination', {
filter: file => file.extension !== 'nocopy'
});
})();
```
*/
readonly filter?: (file: SourceFile) => (boolean | Promise<boolean>);
}
interface ProgressData {
/**
Copied file count.
*/
completedFiles: number;
/**
Overall file count.
*/
totalFiles: number;
/**
Completed size in bytes.
*/
completedSize: number;
/**
Completed percentage. A value between `0` and `1`.
*/
percent: number;
}
interface ProgressEmitter {
on(
event: 'progress',
handler: (progress: ProgressData) => void
): Promise<string[]>;
}
}
/**
Copy files.
@param source - Files to copy. If any of the files do not exist, an error will be thrown (does not apply to globs).
@param destination - Destination directory.
@param options - In addition to the options defined here, options are passed to [globby](https://github.com/sindresorhus/globby#options).
@example
```
import cpy = require('cpy');
(async () => {
await cpy(['source/*.png', '!source/goat.png'], 'destination');
console.log('Files copied!');
})();
```
*/
declare function cpy(
source: string | ReadonlyArray<string>,
destination: string,
options?: cpy.Options
): Promise<string[]> & cpy.ProgressEmitter;
export = cpy;
+159
View File
@@ -0,0 +1,159 @@
'use strict';
const EventEmitter = require('events');
const path = require('path');
const os = require('os');
const pMap = require('p-map');
const arrify = require('arrify');
const globby = require('globby');
const hasGlob = require('has-glob');
const cpFile = require('cp-file');
const junk = require('junk');
const pFilter = require('p-filter');
const CpyError = require('./cpy-error');
const defaultOptions = {
ignoreJunk: true
};
class SourceFile {
constructor(relativePath, path) {
this.path = path;
this.relativePath = relativePath;
Object.freeze(this);
}
get name() {
return path.basename(this.relativePath);
}
get nameWithoutExtension() {
return path.basename(this.relativePath, path.extname(this.relativePath));
}
get extension() {
return path.extname(this.relativePath).slice(1);
}
}
const preprocessSourcePath = (source, options) => path.resolve(options.cwd ? options.cwd : process.cwd(), source);
const preprocessDestinationPath = (source, destination, options) => {
let basename = path.basename(source);
if (typeof options.rename === 'string') {
basename = options.rename;
} else if (typeof options.rename === 'function') {
basename = options.rename(basename);
}
if (options.cwd) {
destination = path.resolve(options.cwd, destination);
}
if (options.parents) {
const dirname = path.dirname(source);
const parsedDirectory = path.parse(dirname);
return path.join(destination, dirname.replace(parsedDirectory.root, path.sep), basename);
}
return path.join(destination, basename);
};
module.exports = (source, destination, {
concurrency = (os.cpus().length || 1) * 2,
...options
} = {}) => {
const progressEmitter = new EventEmitter();
options = {
...defaultOptions,
...options
};
const promise = (async () => {
source = arrify(source);
if (source.length === 0 || !destination) {
throw new CpyError('`source` and `destination` required');
}
const copyStatus = new Map();
let completedFiles = 0;
let completedSize = 0;
let files;
try {
files = await globby(source, options);
if (options.ignoreJunk) {
files = files.filter(file => junk.not(path.basename(file)));
}
} catch (error) {
throw new CpyError(`Cannot glob \`${source}\`: ${error.message}`, error);
}
if (files.length === 0 && !hasGlob(source)) {
throw new CpyError(`Cannot copy \`${source}\`: the file doesn't exist`);
}
let sources = files.map(sourcePath => new SourceFile(sourcePath, preprocessSourcePath(sourcePath, options)));
if (options.filter !== undefined) {
const filteredSources = await pFilter(sources, options.filter, {concurrency: 1024});
sources = filteredSources;
}
if (sources.length === 0) {
progressEmitter.emit('progress', {
totalFiles: 0,
percent: 1,
completedFiles: 0,
completedSize: 0
});
}
const fileProgressHandler = event => {
const fileStatus = copyStatus.get(event.src) || {written: 0, percent: 0};
if (fileStatus.written !== event.written || fileStatus.percent !== event.percent) {
completedSize -= fileStatus.written;
completedSize += event.written;
if (event.percent === 1 && fileStatus.percent !== 1) {
completedFiles++;
}
copyStatus.set(event.src, {
written: event.written,
percent: event.percent
});
progressEmitter.emit('progress', {
totalFiles: files.length,
percent: completedFiles / files.length,
completedFiles,
completedSize
});
}
};
return pMap(sources, async source => {
const to = preprocessDestinationPath(source.relativePath, destination, options);
try {
await cpFile(source.path, to, options).on('progress', fileProgressHandler);
} catch (error) {
throw new CpyError(`Cannot copy from \`${source.relativePath}\` to \`${to}\`: ${error.message}`, error);
}
return to;
}, {concurrency});
})();
promise.on = (...arguments_) => {
progressEmitter.on(...arguments_);
return promise;
};
return promise;
};
+9
View File
@@ -0,0 +1,9 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+12
View File
@@ -0,0 +1,12 @@
'use strict';
const NestedError = require('nested-error-stacks');
class CpFileError extends NestedError {
constructor(message, nested) {
super(message, nested);
Object.assign(this, nested);
this.name = 'CpFileError';
}
}
module.exports = CpFileError;
+99
View File
@@ -0,0 +1,99 @@
'use strict';
const {promisify} = require('util');
const fs = require('graceful-fs');
const makeDir = require('make-dir');
const pEvent = require('p-event');
const CpFileError = require('./cp-file-error');
const stat = promisify(fs.stat);
const lstat = promisify(fs.lstat);
const utimes = promisify(fs.utimes);
const chmod = promisify(fs.chmod);
const chown = promisify(fs.chown);
exports.closeSync = fs.closeSync.bind(fs);
exports.createWriteStream = fs.createWriteStream.bind(fs);
exports.createReadStream = async (path, options) => {
const read = fs.createReadStream(path, options);
try {
await pEvent(read, ['readable', 'end']);
} catch (error) {
throw new CpFileError(`Cannot read from \`${path}\`: ${error.message}`, error);
}
return read;
};
exports.stat = path => stat(path).catch(error => {
throw new CpFileError(`Cannot stat path \`${path}\`: ${error.message}`, error);
});
exports.lstat = path => lstat(path).catch(error => {
throw new CpFileError(`lstat \`${path}\` failed: ${error.message}`, error);
});
exports.utimes = (path, atime, mtime) => utimes(path, atime, mtime).catch(error => {
throw new CpFileError(`utimes \`${path}\` failed: ${error.message}`, error);
});
exports.chmod = (path, mode) => chmod(path, mode).catch(error => {
throw new CpFileError(`chmod \`${path}\` failed: ${error.message}`, error);
});
exports.chown = (path, uid, gid) => chown(path, uid, gid).catch(error => {
throw new CpFileError(`chown \`${path}\` failed: ${error.message}`, error);
});
exports.statSync = path => {
try {
return fs.statSync(path);
} catch (error) {
throw new CpFileError(`stat \`${path}\` failed: ${error.message}`, error);
}
};
exports.utimesSync = (path, atime, mtime) => {
try {
return fs.utimesSync(path, atime, mtime);
} catch (error) {
throw new CpFileError(`utimes \`${path}\` failed: ${error.message}`, error);
}
};
exports.chmodSync = (path, mode) => {
try {
return fs.chmodSync(path, mode);
} catch (error) {
throw new CpFileError(`chmod \`${path}\` failed: ${error.message}`, error);
}
};
exports.chownSync = (path, uid, gid) => {
try {
return fs.chownSync(path, uid, gid);
} catch (error) {
throw new CpFileError(`chown \`${path}\` failed: ${error.message}`, error);
}
};
exports.makeDir = path => makeDir(path, {fs}).catch(error => {
throw new CpFileError(`Cannot create directory \`${path}\`: ${error.message}`, error);
});
exports.makeDirSync = path => {
try {
makeDir.sync(path, {fs});
} catch (error) {
throw new CpFileError(`Cannot create directory \`${path}\`: ${error.message}`, error);
}
};
exports.copyFileSync = (source, destination, flags) => {
try {
fs.copyFileSync(source, destination, flags);
} catch (error) {
throw new CpFileError(`Cannot copy from \`${source}\` to \`${destination}\`: ${error.message}`, error);
}
};
+75
View File
@@ -0,0 +1,75 @@
declare namespace cpFile {
interface Options {
/**
Overwrite existing file.
@default true
*/
readonly overwrite?: boolean;
}
interface ProgressData {
/**
Absolute path to source.
*/
src: string;
/**
Absolute path to destination.
*/
dest: string;
/**
File size in bytes.
*/
size: number;
/**
Copied size in bytes.
*/
written: number;
/**
Copied percentage, a value between `0` and `1`.
*/
percent: number;
}
interface ProgressEmitter {
/**
For empty files, the `progress` event is emitted only once.
*/
on(event: 'progress', handler: (data: ProgressData) => void): Promise<void>;
}
}
declare const cpFile: {
/**
Copy a file.
@param source - File you want to copy.
@param destination - Where you want the file copied.
@returns A `Promise` that resolves when the file is copied.
@example
```
import cpFile = require('cp-file');
(async () => {
await cpFile('source/unicorn.png', 'destination/unicorn.png');
console.log('File copied');
})();
```
*/
(source: string, destination: string, options?: cpFile.Options): Promise<void> & cpFile.ProgressEmitter;
/**
Copy a file synchronously.
@param source - File you want to copy.
@param destination - Where you want the file copied.
*/
sync(source: string, destination: string, options?: cpFile.Options): void;
};
export = cpFile;
+117
View File
@@ -0,0 +1,117 @@
'use strict';
const path = require('path');
const {constants: fsConstants} = require('fs');
const pEvent = require('p-event');
const CpFileError = require('./cp-file-error');
const fs = require('./fs');
const ProgressEmitter = require('./progress-emitter');
const cpFileAsync = async (source, destination, options, progressEmitter) => {
let readError;
const stat = await fs.stat(source);
progressEmitter.size = stat.size;
const read = await fs.createReadStream(source);
await fs.makeDir(path.dirname(destination));
const write = fs.createWriteStream(destination, {flags: options.overwrite ? 'w' : 'wx'});
read.on('data', () => {
progressEmitter.written = write.bytesWritten;
});
read.once('error', error => {
readError = new CpFileError(`Cannot read from \`${source}\`: ${error.message}`, error);
write.end();
});
let updateStats = false;
try {
const writePromise = pEvent(write, 'close');
read.pipe(write);
await writePromise;
progressEmitter.written = progressEmitter.size;
updateStats = true;
} catch (error) {
if (options.overwrite || error.code !== 'EEXIST') {
throw new CpFileError(`Cannot write to \`${destination}\`: ${error.message}`, error);
}
}
if (readError) {
throw readError;
}
if (updateStats) {
const stats = await fs.lstat(source);
return Promise.all([
fs.utimes(destination, stats.atime, stats.mtime),
fs.chmod(destination, stats.mode),
fs.chown(destination, stats.uid, stats.gid)
]);
}
};
const cpFile = (source, destination, options) => {
if (!source || !destination) {
return Promise.reject(new CpFileError('`source` and `destination` required'));
}
options = {
overwrite: true,
...options
};
const progressEmitter = new ProgressEmitter(path.resolve(source), path.resolve(destination));
const promise = cpFileAsync(source, destination, options, progressEmitter);
promise.on = (...args) => {
progressEmitter.on(...args);
return promise;
};
return promise;
};
module.exports = cpFile;
const checkSourceIsFile = (stat, source) => {
if (stat.isDirectory()) {
throw Object.assign(new CpFileError(`EISDIR: illegal operation on a directory '${source}'`), {
errno: -21,
code: 'EISDIR',
source
});
}
};
const fixupAttributes = (destination, stat) => {
fs.chmodSync(destination, stat.mode);
fs.chownSync(destination, stat.uid, stat.gid);
};
module.exports.sync = (source, destination, options) => {
if (!source || !destination) {
throw new CpFileError('`source` and `destination` required');
}
options = {
overwrite: true,
...options
};
const stat = fs.statSync(source);
checkSourceIsFile(stat, source);
fs.makeDirSync(path.dirname(destination));
const flags = options.overwrite ? null : fsConstants.COPYFILE_EXCL;
try {
fs.copyFileSync(source, destination, flags);
} catch (error) {
if (!options.overwrite && error.code === 'EEXIST') {
return;
}
throw error;
}
fs.utimesSync(destination, stat.atime, stat.mtime);
fixupAttributes(destination, stat);
};
+9
View File
@@ -0,0 +1,9 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+65
View File
@@ -0,0 +1,65 @@
{
"name": "cp-file",
"version": "7.0.0",
"description": "Copy a file",
"license": "MIT",
"repository": "sindresorhus/cp-file",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"maintainers": [
{
"name": "Michael Mayer",
"email": "michael@schnittstabil.de",
"url": "schnittstabil.de"
}
],
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && nyc ava && tsd"
},
"files": [
"cp-file-error.js",
"fs.js",
"index.js",
"index.d.ts",
"progress-emitter.js"
],
"keywords": [
"copy",
"cp",
"file",
"clone",
"fs",
"stream",
"file-system",
"ncp",
"fast",
"quick",
"data",
"content",
"contents"
],
"dependencies": {
"graceful-fs": "^4.1.2",
"make-dir": "^3.0.0",
"nested-error-stacks": "^2.0.0",
"p-event": "^4.1.0"
},
"devDependencies": {
"ava": "^1.4.1",
"clear-module": "^3.1.0",
"coveralls": "^3.0.3",
"del": "^4.1.0",
"import-fresh": "^3.0.0",
"nyc": "^13.3.0",
"sinon": "^7.3.1",
"tsd": "^0.7.2",
"uuid": "^3.3.2",
"xo": "^0.24.0"
}
}
+34
View File
@@ -0,0 +1,34 @@
'use strict';
const EventEmitter = require('events');
const written = new WeakMap();
class ProgressEmitter extends EventEmitter {
constructor(source, destination) {
super();
this._source = source;
this._destination = destination;
}
set written(value) {
written.set(this, value);
this.emitProgress();
}
get written() {
return written.get(this);
}
emitProgress() {
const {size, written} = this;
this.emit('progress', {
src: this._source,
dest: this._destination,
size,
written,
percent: written === size ? 1 : written / size
});
}
}
module.exports = ProgressEmitter;
+114
View File
@@ -0,0 +1,114 @@
# cp-file [![Build Status](https://travis-ci.org/sindresorhus/cp-file.svg?branch=master)](https://travis-ci.org/sindresorhus/cp-file) [![Coverage Status](https://coveralls.io/repos/github/sindresorhus/cp-file/badge.svg?branch=master)](https://coveralls.io/github/sindresorhus/cp-file?branch=master)
> Copy a file
## Highlights
- Fast by using streams in the async version and [`fs.copyFileSync()`](https://nodejs.org/api/fs.html#fs_fs_copyfilesync_src_dest_flags) in the synchronous version.
- Resilient by using [graceful-fs](https://github.com/isaacs/node-graceful-fs).
- User-friendly by creating non-existent destination directories for you.
- Can be safe by turning off [overwriting](#optionsoverwrite).
- User-friendly errors.
## Install
```
$ npm install cp-file
```
## Usage
```js
const cpFile = require('cp-file');
(async () => {
await cpFile('source/unicorn.png', 'destination/unicorn.png');
console.log('File copied');
})();
```
## API
### cpFile(source, destination, [options])
Returns a `Promise` that resolves when the file is copied.
### cpFile.sync(source, destination, [options])
#### source
Type: `string`
File you want to copy.
#### destination
Type: `string`
Where you want the file copied.
#### options
Type: `Object`
##### overwrite
Type: `boolean`<br>
Default: `true`
Overwrite existing file.
### cpFile.on('progress', handler)
Progress reporting. Only available when using the async method.
#### handler(data)
Type: `Function`
##### data
```js
{
src: string,
dest: string,
size: number,
written: number,
percent: number
}
```
- `src` and `dest` are absolute paths.
- `size` and `written` are in bytes.
- `percent` is a value between `0` and `1`.
###### Notes
- For empty files, the `progress` event is emitted only once.
- The `.on()` method is available only right after the initial `cpFile()` call. So make sure
you add a `handler` before `.then()`:
```js
(async () => {
await cpFile(source, destination).on('progress', data => {
// …
});
})();
```
## Related
- [cpy](https://github.com/sindresorhus/cpy) - Copy files
- [cpy-cli](https://github.com/sindresorhus/cpy-cli) - Copy files on the command-line
- [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
## License
MIT © [Sindre Sorhus](https://sindresorhus.com)
+64
View File
@@ -0,0 +1,64 @@
{
"name": "cpy",
"version": "8.1.2",
"description": "Copy files",
"license": "MIT",
"repository": "sindresorhus/cpy",
"funding": "https://github.com/sponsors/sindresorhus",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && ava && tsd"
},
"files": [
"cpy-error.js",
"index.js",
"index.d.ts"
],
"keywords": [
"copy",
"cp",
"cpy",
"file",
"files",
"clone",
"fs",
"stream",
"glob",
"file-system",
"ncp",
"fast",
"quick",
"data",
"content",
"contents",
"cpx",
"directory",
"directories"
],
"dependencies": {
"arrify": "^2.0.1",
"cp-file": "^7.0.0",
"globby": "^9.2.0",
"has-glob": "^1.0.0",
"junk": "^3.1.0",
"nested-error-stacks": "^2.1.0",
"p-all": "^2.1.0",
"p-filter": "^2.1.0",
"p-map": "^3.0.0"
},
"devDependencies": {
"ava": "^3.12.1",
"proxyquire": "^2.1.3",
"rimraf": "^3.0.0",
"tempy": "^0.6.0",
"tsd": "^0.11.0",
"xo": "^0.25.3"
}
}
+203
View File
@@ -0,0 +1,203 @@
# cpy
> Copy files
## Why
- Fast by using streams.
- 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
```
$ npm install cpy
```
## Usage
```js
const cpy = require('cpy');
(async () => {
await cpy(['source/*.png', '!source/goat.png'], 'destination');
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).
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.
##### parents
Type: `boolean`\
Default: `false`
Preserve path structure.
##### rename
Type: `string | Function`
Filename or function returning a filename used to rename every file in `source`.
```js
const cpy = require('cpy');
(async () => {
await cpy('foo.js', 'destination', {
rename: basename => `prefix-${basename}`
});
})();
```
##### concurrency
Type: `number`\
Default: `(os.cpus().length || 1) * 2`
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 as the first argument.
Return true to include, false to exclude. You can also return a Promise that resolves to true or false.
```js
const cpy = require('cpy');
(async () => {
await cpy('foo', 'destination', {
filter: file => file.extension !== 'nocopy'
});
})();
```
##### 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.
## Progress reporting
### cpy.on('progress', handler)
#### handler(progress)
Type: `Function`
##### progress
```js
{
completedFiles: number,
totalFiles: number,
completedSize: number,
percent: number
}
```
- `completedSize` is in bytes
- `percent` is a value between `0` and `1`
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
(async () => {
await cpy(source, destination).on('progress', progress => {
// …
});
})();
```
## Related
- [cpy-cli](https://github.com/sindresorhus/cpy-cli) - CLI for this module
- [cp-file](https://github.com/sindresorhus/cp-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