Comparing version 7.3.0 to 8.0.0
@@ -25,8 +25,26 @@ import {GlobbyOptions} from 'globby'; | ||
``` | ||
cpy('foo.js', 'destination', { | ||
rename: basename => `prefix-${basename}` | ||
}); | ||
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; | ||
} | ||
@@ -64,36 +82,25 @@ | ||
declare const cpy: { | ||
/** | ||
Copy files. | ||
/** | ||
Copy files. | ||
@param source - Files to copy. | ||
@param destination - Destination directory. | ||
@param options - Options are passed to [cp-file](https://github.com/sindresorhus/cp-file#options) and [globby](https://github.com/sindresorhus/globby#options). | ||
@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'); | ||
@example | ||
``` | ||
import cpy = require('cpy'); | ||
(async () => { | ||
await cpy(['source/*.png', '!source/goat.png'], 'destination'); | ||
console.log('Files copied!'); | ||
})(); | ||
``` | ||
*/ | ||
( | ||
source: string | ReadonlyArray<string>, | ||
destination: string, | ||
options?: cpy.Options | ||
): Promise<string[]> & cpy.ProgressEmitter; | ||
(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; | ||
// TODO: Remove this for the next major release, refactor the whole definition to: | ||
// declare function cpy( | ||
// source: string | ReadonlyArray<string>, | ||
// destination: string, | ||
// options?: cpy.Options | ||
// ): Promise<void> & cpy.ProgressEmitter; | ||
// export = cpy; | ||
default: typeof cpy; | ||
}; | ||
export = cpy; |
150
index.js
'use strict'; | ||
const EventEmitter = require('events'); | ||
const path = require('path'); | ||
const os = require('os'); | ||
const pAll = require('p-all'); | ||
const arrify = require('arrify'); | ||
const globby = require('globby'); | ||
const isGlob = require('is-glob'); | ||
const cpFile = require('cp-file'); | ||
const junk = require('junk'); | ||
const CpyError = require('./cpy-error'); | ||
const preprocessSrcPath = (srcPath, options) => options.cwd ? path.resolve(options.cwd, srcPath) : srcPath; | ||
const defaultOptions = { | ||
ignoreJunk: true | ||
}; | ||
const preprocessDestPath = (srcPath, dest, options) => { | ||
let basename = path.basename(srcPath); | ||
const dirname = path.dirname(srcPath); | ||
const preprocessSourcePath = (source, options) => options.cwd ? path.resolve(options.cwd, source) : source; | ||
const preprocessDestinationPath = (source, destination, options) => { | ||
let basename = path.basename(source); | ||
const dirname = path.dirname(source); | ||
if (typeof options.rename === 'string') { | ||
@@ -22,80 +30,94 @@ basename = options.rename; | ||
if (options.cwd) { | ||
dest = path.resolve(options.cwd, dest); | ||
destination = path.resolve(options.cwd, destination); | ||
} | ||
if (options.parents) { | ||
return path.join(dest, dirname, basename); | ||
return path.join(destination, dirname, basename); | ||
} | ||
return path.join(dest, basename); | ||
return path.join(destination, basename); | ||
}; | ||
const cpy = (src, dest, options = {}) => { | ||
src = arrify(src); | ||
module.exports = (source, destination, { | ||
concurrency = (os.cpus().length || 1) * 2, | ||
...options | ||
} = {}) => { | ||
const progressEmitter = new EventEmitter(); | ||
if (src.length === 0 || !dest) { | ||
const promise = Promise.reject(new CpyError('`files` and `destination` required')); | ||
promise.on = (...args) => { | ||
progressEmitter.on(...args); | ||
return promise; | ||
}; | ||
options = { | ||
...defaultOptions, | ||
...options | ||
}; | ||
return promise; | ||
} | ||
const promise = (async () => { | ||
source = arrify(source); | ||
const copyStatus = new Map(); | ||
let completedFiles = 0; | ||
let completedSize = 0; | ||
if (source.length === 0 || !destination) { | ||
throw new CpyError('`source` and `destination` required'); | ||
} | ||
const promise = globby(src, options) | ||
.catch(error => { | ||
throw new CpyError(`Cannot glob \`${src}\`: ${error.message}`, error); | ||
}) | ||
.then(files => { | ||
if (files.length === 0) { | ||
progressEmitter.emit('progress', { | ||
totalFiles: 0, | ||
percent: 1, | ||
completedFiles: 0, | ||
completedSize: 0 | ||
}); | ||
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); | ||
} | ||
return Promise.all(files.map(srcPath => { | ||
const from = preprocessSrcPath(srcPath, options); | ||
const to = preprocessDestPath(srcPath, dest, options); | ||
const sourcePaths = source.filter(value => !isGlob(value)); | ||
return cpFile(from, to, options) | ||
.on('progress', event => { | ||
const fileStatus = copyStatus.get(event.src) || {written: 0, percent: 0}; | ||
if (files.length === 0 || (sourcePaths.length > 0 && !sourcePaths.every(value => files.includes(value)))) { | ||
throw new CpyError(`Cannot copy \`${source}\`: the file doesn't exist`); | ||
} | ||
if (fileStatus.written !== event.written || fileStatus.percent !== event.percent) { | ||
completedSize -= fileStatus.written; | ||
completedSize += event.written; | ||
const fileProgressHandler = event => { | ||
const fileStatus = copyStatus.get(event.src) || {written: 0, percent: 0}; | ||
if (event.percent === 1 && fileStatus.percent !== 1) { | ||
completedFiles++; | ||
} | ||
if (fileStatus.written !== event.written || fileStatus.percent !== event.percent) { | ||
completedSize -= fileStatus.written; | ||
completedSize += event.written; | ||
copyStatus.set(event.src, {written: event.written, percent: event.percent}); | ||
if (event.percent === 1 && fileStatus.percent !== 1) { | ||
completedFiles++; | ||
} | ||
progressEmitter.emit('progress', { | ||
totalFiles: files.length, | ||
percent: completedFiles / files.length, | ||
completedFiles, | ||
completedSize | ||
}); | ||
} | ||
}) | ||
.then(() => to) | ||
.catch(error => { | ||
throw new CpyError(`Cannot copy from \`${from}\` to \`${to}\`: ${error.message}`, error); | ||
}); | ||
})); | ||
}); | ||
copyStatus.set(event.src, { | ||
written: event.written, | ||
percent: event.percent | ||
}); | ||
promise.on = (...args) => { | ||
progressEmitter.on(...args); | ||
progressEmitter.emit('progress', { | ||
totalFiles: files.length, | ||
percent: completedFiles / files.length, | ||
completedFiles, | ||
completedSize | ||
}); | ||
} | ||
}; | ||
return pAll(files.map(sourcePath => { | ||
return async () => { | ||
const from = preprocessSourcePath(sourcePath, options); | ||
const to = preprocessDestinationPath(sourcePath, destination, options); | ||
try { | ||
await cpFile(from, to, options).on('progress', fileProgressHandler); | ||
} catch (error) { | ||
throw new CpyError(`Cannot copy from \`${from}\` to \`${to}\`: ${error.message}`, error); | ||
} | ||
return to; | ||
}; | ||
}), {concurrency}); | ||
})(); | ||
promise.on = (...arguments_) => { | ||
progressEmitter.on(...arguments_); | ||
return promise; | ||
@@ -106,5 +128,1 @@ }; | ||
}; | ||
module.exports = cpy; | ||
// TODO: Remove this for the next major release | ||
module.exports.default = cpy; |
{ | ||
"name": "cpy", | ||
"version": "7.3.0", | ||
"version": "8.0.0", | ||
"description": "Copy files", | ||
"license": "MIT", | ||
"repository": "sindresorhus/cpy", | ||
"funding": "https://github.com/sponsors/sindresorhus", | ||
"author": { | ||
@@ -13,3 +14,3 @@ "name": "Sindre Sorhus", | ||
"engines": { | ||
"node": ">=6" | ||
"node": ">=8" | ||
}, | ||
@@ -40,17 +41,23 @@ "scripts": { | ||
"content", | ||
"contents" | ||
"contents", | ||
"cpx", | ||
"directory", | ||
"directories" | ||
], | ||
"dependencies": { | ||
"arrify": "^1.0.1", | ||
"cp-file": "^6.1.0", | ||
"arrify": "^2.0.1", | ||
"cp-file": "^7.0.0", | ||
"globby": "^9.2.0", | ||
"nested-error-stacks": "^2.1.0" | ||
"is-glob": "^4.0.1", | ||
"junk": "^3.1.0", | ||
"nested-error-stacks": "^2.1.0", | ||
"p-all": "^2.1.0" | ||
}, | ||
"devDependencies": { | ||
"ava": "^1.4.1", | ||
"rimraf": "^2.6.3", | ||
"tempfile": "^2.0.0", | ||
"tsd": "^0.7.2", | ||
"xo": "^0.24.0" | ||
"ava": "^2.1.0", | ||
"rimraf": "^3.0.0", | ||
"tempfile": "^3.0.0", | ||
"tsd": "^0.11.0", | ||
"xo": "^0.25.3" | ||
} | ||
} |
@@ -5,3 +5,2 @@ # cpy [![Build Status](https://travis-ci.org/sindresorhus/cpy.svg?branch=master)](https://travis-ci.org/sindresorhus/cpy) | ||
## Why | ||
@@ -15,3 +14,2 @@ | ||
## Install | ||
@@ -23,3 +21,2 @@ | ||
## Usage | ||
@@ -36,3 +33,2 @@ | ||
## API | ||
@@ -50,2 +46,4 @@ | ||
If any of the files do not exist, an error will be thrown (does not apply to globs). | ||
#### destination | ||
@@ -67,3 +65,3 @@ | ||
Type: `string`<br> | ||
Type: `string`\ | ||
Default: `process.cwd()` | ||
@@ -75,3 +73,3 @@ | ||
Type: `boolean`<br> | ||
Type: `boolean`\ | ||
Default: `true` | ||
@@ -83,3 +81,3 @@ | ||
Type: `boolean`<br> | ||
Type: `boolean`\ | ||
Default: `false` | ||
@@ -96,8 +94,25 @@ | ||
```js | ||
cpy('foo.js', 'destination', { | ||
rename: basename => `prefix-${basename}` | ||
}); | ||
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. | ||
## Progress reporting | ||
@@ -117,3 +132,4 @@ | ||
totalFiles: number, | ||
completedSize: number | ||
completedSize: number, | ||
percent: number | ||
} | ||
@@ -135,3 +151,2 @@ ``` | ||
## Related | ||
@@ -138,0 +153,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
10321
193
147
7
+ Addedis-glob@^4.0.1
+ Addedjunk@^3.1.0
+ Addedp-all@^2.1.0
+ Addedarrify@2.0.1(transitive)
+ Addedcp-file@7.0.0(transitive)
+ Addedjunk@3.1.0(transitive)
+ Addedmake-dir@3.1.0(transitive)
+ Addedp-all@2.1.0(transitive)
+ Addedp-event@4.2.0(transitive)
+ Addedp-finally@1.0.0(transitive)
+ Addedp-map@2.1.0(transitive)
+ Addedp-timeout@3.2.0(transitive)
+ Addedsemver@6.3.1(transitive)
- Removedarrify@1.0.1(transitive)
- Removedcp-file@6.2.0(transitive)
- Removedmake-dir@2.1.0(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsemver@5.7.2(transitive)
Updatedarrify@^2.0.1
Updatedcp-file@^7.0.0