Skip to content

Commit

Permalink
Meta tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Jun 11, 2019
1 parent d3ed7b9 commit edcc055
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 122 deletions.
15 changes: 11 additions & 4 deletions index.d.ts
@@ -1,7 +1,7 @@
declare namespace cpFile {
interface Options {
/**
Overwrite existing file.
Overwrite existing destination file.
@default true
*/
Expand Down Expand Up @@ -37,7 +37,7 @@ declare namespace cpFile {

interface ProgressEmitter {
/**
For empty files, the `progress` event is emitted only once.
Note: For empty files, the `progress` event is emitted only once.
*/
on(event: 'progress', handler: (data: ProgressData) => void): Promise<void>;
}
Expand All @@ -47,7 +47,7 @@ declare const cpFile: {
/**
Copy a file.
@param source - File you want to copy.
@param source - The file you want to copy.
@param destination - Where you want the file copied.
@returns A `Promise` that resolves when the file is copied.
Expand All @@ -66,8 +66,15 @@ declare const cpFile: {
/**
Copy a file synchronously.
@param source - File you want to copy.
@param source - The file you want to copy.
@param destination - Where you want the file copied.
@example
```
import cpFile = require('cp-file');
cpFile.sync('source/unicorn.png', 'destination/unicorn.png');
```
*/
sync(source: string, destination: string, options?: cpFile.Options): void;
};
Expand Down
17 changes: 5 additions & 12 deletions package.json
Expand Up @@ -9,13 +9,6 @@
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"maintainers": [
{
"name": "Michael Mayer",
"email": "michael@schnittstabil.de",
"url": "schnittstabil.de"
}
],
"engines": {
"node": ">=8"
},
Expand Down Expand Up @@ -51,14 +44,14 @@
"p-event": "^4.1.0"
},
"devDependencies": {
"ava": "^1.4.1",
"ava": "^2.1.0",
"clear-module": "^3.1.0",
"coveralls": "^3.0.3",
"del": "^4.1.0",
"coveralls": "^3.0.4",
"del": "^4.1.1",
"import-fresh": "^3.0.0",
"nyc": "^13.3.0",
"nyc": "^14.1.1",
"sinon": "^7.3.1",
"tsd": "^0.7.2",
"tsd": "^0.7.3",
"uuid": "^3.3.2",
"xo": "^0.24.0"
}
Expand Down
17 changes: 7 additions & 10 deletions readme.md
Expand Up @@ -33,17 +33,17 @@ const cpFile = require('cp-file');

## API

### cpFile(source, destination, [options])
### cpFile(source, destination, options?)

Returns a `Promise` that resolves when the file is copied.

### cpFile.sync(source, destination, [options])
### cpFile.sync(source, destination, options?)

#### source

Type: `string`

File you want to copy.
The file you want to copy.

#### destination

Expand All @@ -53,14 +53,14 @@ Where you want the file copied.

#### options

Type: `Object`
Type: `object`

##### overwrite

Type: `boolean`<br>
Default: `true`

Overwrite existing file.
Overwrite existing destination file.

### cpFile.on('progress', handler)

Expand Down Expand Up @@ -93,6 +93,8 @@ Type: `Function`
you add a `handler` before `.then()`:

```js
const cpFile = require('cp-file');

(async () => {
await cpFile(source, destination).on('progress', data => {
//
Expand All @@ -107,8 +109,3 @@ you add a `handler` before `.then()`:
- [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)
108 changes: 55 additions & 53 deletions test/async.js
Expand Up @@ -8,8 +8,8 @@ import test from 'ava';
import uuid from 'uuid';
import sinon from 'sinon';
import cpFile from '..';
import assertDateEqual from './helpers/assert';
import {buildEACCES, buildEIO, buildENOSPC, buildENOENT, buildEPERM} from './helpers/fs-errors';
import assertDateEqual from './helpers/_assert';
import {buildEACCES, buildEIO, buildENOSPC, buildENOENT, buildEPERM} from './helpers/_fs-errors';

const THREE_HUNDRED_KILO = (100 * 3 * 1024) + 1;

Expand All @@ -24,7 +24,9 @@ test.beforeEach(t => {
});

test.afterEach.always(t => {
t.context.creates.forEach(path => del.sync(path));
for (const path_ of t.context.creates) {
del.sync(path_);
}
});

test('reject an Error on missing `source`', async t => {
Expand All @@ -47,10 +49,10 @@ test('copy an empty file', async t => {
});

test('copy big files', async t => {
const buf = crypto.randomBytes(THREE_HUNDRED_KILO);
fs.writeFileSync(t.context.source, buf);
const buffer = crypto.randomBytes(THREE_HUNDRED_KILO);
fs.writeFileSync(t.context.source, buffer);
await cpFile(t.context.source, t.context.destination);
t.true(buf.equals(fs.readFileSync(t.context.destination)));
t.true(buffer.equals(fs.readFileSync(t.context.destination)));
});

test('do not alter overwrite option', async t => {
Expand Down Expand Up @@ -78,18 +80,18 @@ test('do not overwrite when disabled', async t => {
});

test('do not create `destination` on unreadable `source`', async t => {
const error = await t.throwsAsync(() => cpFile('node_modules', t.context.destination));
t.is(error.name, 'CpFileError', error);
t.is(error.code, 'EISDIR', error);
const error = await t.throwsAsync(cpFile('node_modules', t.context.destination));
t.is(error.name, 'CpFileError', error.message);
t.is(error.code, 'EISDIR', error.message);
t.throws(() => {
fs.statSync(t.context.destination);
}, /ENOENT/);
});

test('do not create `destination` directory on unreadable `source`', async t => {
const error = await t.throwsAsync(() => cpFile('node_modules', 'subdir/' + uuid.v4()));
t.is(error.name, 'CpFileError', error);
t.is(error.code, 'EISDIR', error);
const error = await t.throwsAsync(cpFile('node_modules', path.join('subdir', uuid.v4())));
t.is(error.name, 'CpFileError', error.message);
t.is(error.code, 'EISDIR', error.message);
t.throws(() => {
fs.statSync('subdir');
}, /ENOENT/);
Expand All @@ -98,47 +100,47 @@ test('do not create `destination` directory on unreadable `source`', async t =>
test('preserve timestamps', async t => {
await cpFile('license', t.context.destination);
const licenseStats = fs.lstatSync('license');
const tmpStats = fs.lstatSync(t.context.destination);
assertDateEqual(t, licenseStats.atime, tmpStats.atime);
assertDateEqual(t, licenseStats.mtime, tmpStats.mtime);
const tempStats = fs.lstatSync(t.context.destination);
assertDateEqual(t, licenseStats.atime, tempStats.atime);
assertDateEqual(t, licenseStats.mtime, tempStats.mtime);
});

test('preserve mode', async t => {
await cpFile('license', t.context.destination);
const licenseStats = fs.lstatSync('license');
const tmpStats = fs.lstatSync(t.context.destination);
t.is(licenseStats.mode, tmpStats.mode);
const tempStats = fs.lstatSync(t.context.destination);
t.is(licenseStats.mode, tempStats.mode);
});

test('preserve ownership', async t => {
await cpFile('license', t.context.destination);
const licenseStats = fs.lstatSync('license');
const tmpStats = fs.lstatSync(t.context.destination);
t.is(licenseStats.gid, tmpStats.gid);
t.is(licenseStats.uid, tmpStats.uid);
const tempStats = fs.lstatSync(t.context.destination);
t.is(licenseStats.gid, tempStats.gid);
t.is(licenseStats.uid, tempStats.uid);
});

test('throw an Error if `source` does not exists', async t => {
const error = await t.throwsAsync(cpFile('NO_ENTRY', t.context.destination));
t.is(error.name, 'CpFileError', error);
t.is(error.code, 'ENOENT', error);
t.regex(error.message, /`NO_ENTRY`/, error);
t.regex(error.stack, /`NO_ENTRY`/, error);
t.is(error.name, 'CpFileError', error.message);
t.is(error.code, 'ENOENT', error.message);
t.regex(error.message, /`NO_ENTRY`/, error.message);
t.regex(error.stack, /`NO_ENTRY`/, error.message);
});

test.serial('rethrow mkdir EACCES errors', async t => {
const dirPath = '/root/NO_ACCESS_' + uuid.v4();
const dest = dirPath + '/' + uuid.v4();
const mkdirError = buildEACCES(dirPath);
const directoryPath = `/root/NO_ACCESS_${uuid.v4()}`;
const destination = path.join(directoryPath, uuid.v4());
const mkdirError = buildEACCES(directoryPath);

fs.stat = sinon.stub(fs, 'stat').throws(mkdirError);
fs.mkdir = sinon.stub(fs, 'mkdir').throws(mkdirError);

const error = await t.throwsAsync(cpFile('license', dest));
t.is(error.name, 'CpFileError', error);
t.is(error.errno, mkdirError.errno, error);
t.is(error.code, mkdirError.code, error);
t.is(error.path, mkdirError.path, error);
const error = await t.throwsAsync(cpFile('license', destination));
t.is(error.name, 'CpFileError', error.message);
t.is(error.errno, mkdirError.errno, error.message);
t.is(error.code, mkdirError.code, error.message);
t.is(error.path, mkdirError.path, error.message);
t.true(fs.mkdir.called || fs.stat.called);

fs.mkdir.restore();
Expand All @@ -148,14 +150,14 @@ test.serial('rethrow mkdir EACCES errors', async t => {
test.serial('rethrow ENOSPC errors', async t => {
const {createWriteStream} = fs;
const noSpaceError = buildENOSPC();
let called = false;
let isCalled = false;

fs.createWriteStream = (path, options) => {
const stream = createWriteStream(path, options);
if (path === t.context.destination) {
stream.on('pipe', () => {
if (!called) {
called = true;
if (!isCalled) {
isCalled = true;
stream.emit('error', noSpaceError);
}
});
Expand All @@ -167,10 +169,10 @@ test.serial('rethrow ENOSPC errors', async t => {
clearModule('../fs');
const uncached = importFresh('..');
const error = await t.throwsAsync(uncached('license', t.context.destination));
t.is(error.name, 'CpFileError', error);
t.is(error.errno, noSpaceError.errno, error);
t.is(error.code, noSpaceError.code, error);
t.true(called);
t.is(error.name, 'CpFileError', error.message);
t.is(error.errno, noSpaceError.errno, error.message);
t.is(error.code, noSpaceError.code, error.message);
t.true(isCalled);

fs.createWriteStream = createWriteStream;
});
Expand All @@ -184,9 +186,9 @@ test.serial('rethrow stat errors', async t => {
clearModule('../fs');
const uncached = importFresh('..');
const error = await t.throwsAsync(uncached(t.context.source, t.context.destination));
t.is(error.name, 'CpFileError', error);
t.is(error.errno, fstatError.errno, error);
t.is(error.code, fstatError.code, error);
t.is(error.name, 'CpFileError', error.message);
t.is(error.errno, fstatError.errno, error.message);
t.is(error.code, fstatError.code, error.message);
t.true(fs.lstat.called);

fs.lstat.restore();
Expand All @@ -200,8 +202,8 @@ test.serial('rethrow utimes errors', async t => {
clearModule('../fs');
const uncached = importFresh('..');
const error = await t.throwsAsync(uncached('license', t.context.destination));
t.is(error.name, 'CpFileError', error);
t.is(error.code, 'ENOENT', error);
t.is(error.name, 'CpFileError', error.message);
t.is(error.code, 'ENOENT', error.message);
t.true(fs.utimes.called);

fs.utimes.restore();
Expand All @@ -215,9 +217,9 @@ test.serial('rethrow chmod errors', async t => {
clearModule('../fs');
const uncached = importFresh('..');
const error = await t.throwsAsync(uncached('license', t.context.destination));
t.is(error.name, 'CpFileError', error);
t.is(error.code, chmodError.code, error);
t.is(error.path, chmodError.path, error);
t.is(error.name, 'CpFileError', error.message);
t.is(error.code, chmodError.code, error.message);
t.is(error.path, chmodError.path, error.message);
t.true(fs.chmod.called);

fs.chmod.restore();
Expand All @@ -231,9 +233,9 @@ test.serial('rethrow chown errors', async t => {
clearModule('../fs');
const uncached = importFresh('..');
const error = await t.throwsAsync(uncached('license', t.context.destination));
t.is(error.name, 'CpFileError', error);
t.is(error.code, chownError.code, error);
t.is(error.path, chownError.path, error);
t.is(error.name, 'CpFileError', error.message);
t.is(error.code, chownError.code, error.message);
t.is(error.path, chownError.path, error.message);
t.true(fs.chown.called);

fs.chown.restore();
Expand Down Expand Up @@ -272,9 +274,9 @@ test.serial('rethrow read after open errors', async t => {
clearModule('../fs');
const uncached = importFresh('..');
const error = await t.throwsAsync(uncached('license', t.context.destination));
t.is(error.name, 'CpFileError', error);
t.is(error.errno, readError.errno, error);
t.is(error.code, readError.code, error);
t.is(error.name, 'CpFileError', error.message);
t.is(error.errno, readError.errno, error.message);
t.is(error.code, readError.code, error.message);
t.is(calledWriteEnd, 1);

Object.assign(fs, {createWriteStream, createReadStream});
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit edcc055

Please sign in to comment.