Skip to content

Commit 8b16c8e

Browse files
committed
Require Node.js 18
1 parent a98bdff commit 8b16c8e

File tree

12 files changed

+73
-71
lines changed

12 files changed

+73
-71
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
node-version:
13+
- 20
1314
- 18
14-
- 16
15-
- 14
1615
os:
1716
- ubuntu-latest
1817
- macos-latest
1918
- windows-latest
2019
steps:
21-
- uses: actions/checkout@v3
22-
- uses: actions/setup-node@v3
20+
- uses: actions/checkout@v4
21+
- uses: actions/setup-node@v4
2322
with:
2423
node-version: ${{ matrix.node-version }}
2524
- run: npm install

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ node_modules
22
yarn.lock
33
.nyc_output
44
coverage
5+
temp

copy-file-error.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import NestedError from 'nested-error-stacks';
2-
3-
// TODO: Use `Error#cause`.
4-
export default class CopyFileError extends NestedError {
5-
constructor(message, nested) {
6-
super(message, nested);
7-
Object.assign(this, nested);
1+
export default class CopyFileError extends Error {
2+
constructor(message, {cause} = {}) {
3+
super(message, {cause});
4+
Object.assign(this, cause);
85
this.name = 'CopyFileError';
96
}
107
}

fs.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,60 +18,60 @@ export async function createReadStream(path, options) {
1818
try {
1919
await pEvent(read, ['readable', 'end']);
2020
} catch (error) {
21-
throw new CopyFileError(`Cannot read from \`${path}\`: ${error.message}`, error);
21+
throw new CopyFileError(`Cannot read from \`${path}\`: ${error.message}`, {cause: error});
2222
}
2323

2424
return read;
2525
}
2626

2727
export const stat = path => statP(path).catch(error => {
28-
throw new CopyFileError(`Cannot stat path \`${path}\`: ${error.message}`, error);
28+
throw new CopyFileError(`Cannot stat path \`${path}\`: ${error.message}`, {cause: error});
2929
});
3030

3131
export const lstat = path => lstatP(path).catch(error => {
32-
throw new CopyFileError(`lstat \`${path}\` failed: ${error.message}`, error);
32+
throw new CopyFileError(`lstat \`${path}\` failed: ${error.message}`, {cause: error});
3333
});
3434

3535
export const utimes = (path, atime, mtime) => utimesP(path, atime, mtime).catch(error => {
36-
throw new CopyFileError(`utimes \`${path}\` failed: ${error.message}`, error);
36+
throw new CopyFileError(`utimes \`${path}\` failed: ${error.message}`, {cause: error});
3737
});
3838

3939
export const chmod = (path, mode) => chmodP(path, mode).catch(error => {
40-
throw new CopyFileError(`chmod \`${path}\` failed: ${error.message}`, error);
40+
throw new CopyFileError(`chmod \`${path}\` failed: ${error.message}`, {cause: error});
4141
});
4242

4343
export const statSync = path => {
4444
try {
4545
return fs.statSync(path);
4646
} catch (error) {
47-
throw new CopyFileError(`stat \`${path}\` failed: ${error.message}`, error);
47+
throw new CopyFileError(`stat \`${path}\` failed: ${error.message}`, {cause: error});
4848
}
4949
};
5050

5151
export const utimesSync = (path, atime, mtime) => {
5252
try {
5353
return fs.utimesSync(path, atime, mtime);
5454
} catch (error) {
55-
throw new CopyFileError(`utimes \`${path}\` failed: ${error.message}`, error);
55+
throw new CopyFileError(`utimes \`${path}\` failed: ${error.message}`, {cause: error});
5656
}
5757
};
5858

5959
export const makeDirectory = (path, options) => makeDirectoryP(path, {...options, recursive: true}).catch(error => {
60-
throw new CopyFileError(`Cannot create directory \`${path}\`: ${error.message}`, error);
60+
throw new CopyFileError(`Cannot create directory \`${path}\`: ${error.message}`, {cause: error});
6161
});
6262

6363
export const makeDirectorySync = (path, options) => {
6464
try {
6565
fs.mkdirSync(path, {...options, recursive: true});
6666
} catch (error) {
67-
throw new CopyFileError(`Cannot create directory \`${path}\`: ${error.message}`, error);
67+
throw new CopyFileError(`Cannot create directory \`${path}\`: ${error.message}`, {cause: error});
6868
}
6969
};
7070

7171
export const copyFileSync = (source, destination, flags) => {
7272
try {
7373
fs.copyFileSync(source, destination, flags);
7474
} catch (error) {
75-
throw new CopyFileError(`Cannot copy from \`${source}\` to \`${destination}\`: ${error.message}`, error);
75+
throw new CopyFileError(`Cannot copy from \`${source}\` to \`${destination}\`: ${error.message}`, {cause: error});
7676
}
7777
};

index.d.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export interface Options {
1+
export type Options = {
22
/**
33
Overwrite existing destination file.
44
@@ -23,9 +23,9 @@ export interface Options {
2323
@default process.cwd()
2424
*/
2525
readonly cwd?: string;
26-
}
26+
};
2727

28-
export interface AsyncOptions {
28+
export type AsyncOptions = {
2929
/**
3030
The given function is called whenever there is measurable progress.
3131
@@ -43,9 +43,9 @@ export interface AsyncOptions {
4343
```
4444
*/
4545
readonly onProgress?: (progress: ProgressData) => void;
46-
}
46+
};
4747

48-
export interface ProgressData {
48+
export type ProgressData = {
4949
/**
5050
Absolute path to source.
5151
*/
@@ -70,7 +70,7 @@ export interface ProgressData {
7070
Copied percentage, a value between `0` and `1`.
7171
*/
7272
percent: number;
73-
}
73+
};
7474

7575
/**
7676
Copy a file.

index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const copyFileAsync = async (source, destination, options) => {
3131
});
3232

3333
readStream.once('error', error => {
34-
readError = new CopyFileError(`Cannot read from \`${source}\`: ${error.message}`, error);
34+
readError = new CopyFileError(`Cannot read from \`${source}\`: ${error.message}`, {cause: error});
3535
});
3636

3737
let shouldUpdateStats = false;
@@ -42,7 +42,7 @@ const copyFileAsync = async (source, destination, options) => {
4242
emitProgress(size);
4343
shouldUpdateStats = true;
4444
} catch (error) {
45-
throw new CopyFileError(`Cannot write to \`${destination}\`: ${error.message}`, error);
45+
throw new CopyFileError(`Cannot write to \`${destination}\`: ${error.message}`, {cause: error});
4646
}
4747

4848
if (readError) {

index.test-d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {expectError, expectType} from 'tsd';
2-
import {copyFile, copyFileSync, ProgressData} from './index.js';
2+
import {copyFile, copyFileSync, type ProgressData} from './index.js';
33

44
expectType<Promise<void> >(
55
copyFile('source/unicorn.png', 'destination/unicorn.png'),

package.json

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
"url": "https://sindresorhus.com"
1212
},
1313
"type": "module",
14-
"exports": "./index.js",
15-
"types": "./index.d.ts",
14+
"exports": {
15+
"types": "./index.d.ts",
16+
"default": "./index.js"
17+
},
1618
"engines": {
17-
"node": ">=14.16"
19+
"node": ">=18"
1820
},
1921
"scripts": {
2022
"test": "xo && nyc ava && tsd"
@@ -27,42 +29,46 @@
2729
],
2830
"keywords": [
2931
"copy",
32+
"copying",
3033
"cp",
3134
"file",
3235
"clone",
3336
"fs",
3437
"stream",
3538
"file-system",
39+
"filesystem",
3640
"ncp",
3741
"fast",
3842
"quick",
3943
"data",
4044
"content",
41-
"contents"
45+
"contents",
46+
"read",
47+
"write",
48+
"io"
4249
],
4350
"dependencies": {
44-
"graceful-fs": "^4.2.10",
45-
"nested-error-stacks": "^2.1.1",
46-
"p-event": "^5.0.1"
51+
"graceful-fs": "^4.2.11",
52+
"p-event": "^6.0.0"
4753
},
4854
"devDependencies": {
49-
"ava": "^4.3.0",
55+
"ava": "^5.3.1",
5056
"clear-module": "^4.1.2",
5157
"coveralls": "^3.1.1",
52-
"del": "^6.1.1",
58+
"del": "^7.1.0",
5359
"import-fresh": "^3.3.0",
5460
"nyc": "^15.1.0",
55-
"sinon": "^14.0.0",
56-
"tsd": "^0.21.0",
57-
"xo": "^0.50.0"
61+
"sinon": "^17.0.1",
62+
"tsd": "^0.29.0",
63+
"xo": "^0.56.0"
5864
},
5965
"xo": {
6066
"rules": {
61-
"unicorn/string-content": "off",
6267
"ava/assertion-arguments": "off"
6368
}
6469
},
6570
"ava": {
66-
"workerThreads": false
71+
"workerThreads": false,
72+
"serial": true
6773
}
6874
}

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
- 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.
88
- Resilient by using [graceful-fs](https://github.com/isaacs/node-graceful-fs).
99
- User-friendly by creating non-existent destination directories for you.
10-
- Can be safe by turning off [overwriting](#optionsoverwrite).
11-
- Preserves file mode, [but not ownership](https://github.com/sindresorhus/cp-file/issues/22#issuecomment-502079547).
10+
- Can be safe by turning off [overwriting](#overwrite).
11+
- Preserves file mode [but not ownership](https://github.com/sindresorhus/cp-file/issues/22#issuecomment-502079547).
1212
- User-friendly errors.
1313

1414
## Install

test/async.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import fs from 'node:fs';
55
import {fileURLToPath} from 'node:url';
66
import importFresh from 'import-fresh';
77
import clearModule from 'clear-module';
8-
import del from 'del';
8+
import {deleteSync} from 'del';
99
import test from 'ava';
1010
import sinon from 'sinon';
1111
import {copyFile} from '../index.js';
@@ -18,16 +18,17 @@ const THREE_HUNDRED_KILO = (100 * 3 * 1024) + 1;
1818

1919
test.before(() => {
2020
process.chdir(path.dirname(__dirname));
21+
deleteSync('temp'); // In case last test run failed.
22+
fs.mkdirSync('temp');
2123
});
2224

23-
test.beforeEach(t => {
24-
t.context.source = crypto.randomUUID();
25-
t.context.destination = crypto.randomUUID();
26-
t.context.creates = [t.context.source, t.context.destination];
25+
test.after(() => {
26+
deleteSync('temp');
2727
});
2828

29-
test.afterEach.always(t => {
30-
del.sync(t.context.creates);
29+
test.beforeEach(t => {
30+
t.context.source = path.join('temp', crypto.randomUUID());
31+
t.context.destination = path.join('temp', crypto.randomUUID());
3132
});
3233

3334
test('reject an Error on missing `source`', async t => {

test/progress.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import crypto from 'node:crypto';
33
import path from 'node:path';
44
import {fileURLToPath} from 'node:url';
55
import fs from 'node:fs';
6-
import del from 'del';
6+
import {deleteSync} from 'del';
77
import test from 'ava';
88
import {copyFile} from '../index.js';
99

@@ -13,18 +13,17 @@ const THREE_HUNDRED_KILO = (100 * 3 * 1024) + 1;
1313

1414
test.before(() => {
1515
process.chdir(path.dirname(__dirname));
16+
deleteSync('temp'); // In case last test run failed.
17+
fs.mkdirSync('temp');
1618
});
1719

18-
test.beforeEach(t => {
19-
t.context.source = crypto.randomUUID();
20-
t.context.destination = crypto.randomUUID();
21-
t.context.creates = [t.context.source, t.context.destination];
20+
test.after(() => {
21+
deleteSync('temp');
2222
});
2323

24-
test.afterEach.always(t => {
25-
for (const path of t.context.creates) {
26-
del.sync(path);
27-
}
24+
test.beforeEach(t => {
25+
t.context.source = path.join('temp', crypto.randomUUID());
26+
t.context.destination = path.join('temp', crypto.randomUUID());
2827
});
2928

3029
test('report progress', async t => {

test/sync.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import crypto from 'node:crypto';
33
import {fileURLToPath} from 'node:url';
44
import path from 'node:path';
55
import fs from 'node:fs';
6-
import del from 'del';
6+
import {deleteSync} from 'del';
77
import test from 'ava';
88
import sinon from 'sinon';
99
import {copyFileSync} from '../index.js';
@@ -16,18 +16,17 @@ const THREE_HUNDRED_KILO = (100 * 3 * 1024) + 1;
1616

1717
test.before(() => {
1818
process.chdir(path.dirname(__dirname));
19+
deleteSync('temp'); // In case last test run failed.
20+
fs.mkdirSync('temp');
1921
});
2022

21-
test.beforeEach(t => {
22-
t.context.source = crypto.randomUUID();
23-
t.context.destination = crypto.randomUUID();
24-
t.context.creates = [t.context.source, t.context.destination];
23+
test.after(() => {
24+
deleteSync('temp');
2525
});
2626

27-
test.afterEach.always(t => {
28-
for (const path_ of t.context.creates) {
29-
del.sync(path_);
30-
}
27+
test.beforeEach(t => {
28+
t.context.source = path.join('temp', crypto.randomUUID());
29+
t.context.destination = path.join('temp', crypto.randomUUID());
3130
});
3231

3332
test('throw an Error on missing `source`', t => {

0 commit comments

Comments
 (0)