Skip to content

Commit

Permalink
Require Node.js 18
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Nov 5, 2023
1 parent 47b89a7 commit f9d72e1
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 48 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ jobs:
node-version:
- 20
- 18
- 16
os:
- ubuntu-latest
- macos-latest
- windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm install
Expand Down
10 changes: 4 additions & 6 deletions cpy-error.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import NestedError from 'nested-error-stacks';

export default class CpyError extends NestedError {
constructor(message, nested) {
super(message, nested);
Object.assign(this, nested);
export default class CpyError extends Error {
constructor(message, {cause} = {}) {
super(message, {cause});
Object.assign(this, cause);
this.name = 'CpyError';
}
}
4 changes: 2 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {type Options as GlobOptions} from 'globby';
import {type Options as CpFileOptions} from 'cp-file';
import {type Options as CopyFileOptions} from 'copy-file';

export type Entry = {
/**
Expand Down Expand Up @@ -103,7 +103,7 @@ export type Options = {
```
*/
readonly filter?: (file: Entry) => boolean | Promise<boolean>;
} & Readonly<GlobOptions> & CpFileOptions; // eslint-disable-line @typescript-eslint/no-redundant-type-constituents
} & Readonly<GlobOptions> & CopyFileOptions;

export type ProgressData = {
/**
Expand Down
27 changes: 8 additions & 19 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ import EventEmitter from 'node:events';
import path from 'node:path';
import os from 'node:os';
import pMap from 'p-map';
import arrify from 'arrify';
import {copyFile} from 'cp-file';
import {copyFile} from 'copy-file';
import pFilter from 'p-filter';
import {isDynamicPattern} from 'globby';
import micromatch from 'micromatch';
import CpyError from './cpy-error.js';
import GlobPattern from './glob-pattern.js';

const defaultConcurrency = (os.cpus().length || 1) * 2; // eslint-disable-line unicorn/explicit-length-check

/**
@type {import('./index').Options}
*/
Expand Down Expand Up @@ -139,7 +136,7 @@ const renameFile = (source, rename) => {
export default function cpy(
source,
destination,
{concurrency = defaultConcurrency, ...options} = {},
{concurrency = os.availableParallelism(), ...options} = {},
) {
/**
@type {Map<string, import('./index').CopyStatus>}
Expand Down Expand Up @@ -167,8 +164,8 @@ export default function cpy(
/**
@type {GlobPattern[]}
*/
let patterns = expandPatternsWithBraceExpansion(arrify(source))
.map(string => string.replace(/\\/g, '/'));
let patterns = expandPatternsWithBraceExpansion([source ?? []].flat())
.map(string => string.replaceAll('\\', '/'));
const sources = patterns.filter(item => !item.startsWith('!'));
const ignore = patterns.filter(item => item.startsWith('!'));

Expand All @@ -187,16 +184,11 @@ export default function cpy(
try {
matches = pattern.getMatches();
} catch (error) {
throw new CpyError(
`Cannot glob \`${pattern.originalPath}\`: ${error.message}`,
error,
);
throw new CpyError(`Cannot glob \`${pattern.originalPath}\`: ${error.message}`, {cause: error});
}

if (matches.length === 0 && !isDynamicPattern(pattern.originalPath) && !isDynamicPattern(ignore)) {
throw new CpyError(
`Cannot copy \`${pattern.originalPath}\`: the file doesn't exist`,
);
throw new CpyError(`Cannot copy \`${pattern.originalPath}\`: the file doesn't exist`);
}

entries = [
Expand All @@ -219,7 +211,7 @@ export default function cpy(
}

/**
@param {import('cp-file').ProgressData} event
@param {import('copy-file').ProgressData} event
*/
const fileProgressHandler = event => {
const fileStatus = copyStatus.get(event.sourcePath) || {
Expand Down Expand Up @@ -269,10 +261,7 @@ export default function cpy(
try {
await copyFile(entry.path, to, {...options, onProgress: fileProgressHandler});
} catch (error) {
throw new CpyError(
`Cannot copy from \`${entry.relativePath}\` to \`${to}\`: ${error.message}`,
error,
);
throw new CpyError(`Cannot copy from \`${entry.relativePath}\` to \`${to}\`: ${error.message}`, {cause: error});
}

return to;
Expand Down
2 changes: 1 addition & 1 deletion index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ expectType<Promise<string[]> & ProgressEmitter>(
cpy('foo.js', 'destination', {rename: 'foobar'}),
);
expectType<Promise<string[]> & ProgressEmitter>(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions

cpy('foo.js', 'destination', {rename: basename => `prefix-${basename}`}),
);
expectType<Promise<string[]> & ProgressEmitter>(
Expand Down
28 changes: 17 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": "./index.js",
"exports": {
"types": "./index.d.ts",
"default": "./index.js"
},
"engines": {
"node": ">=16"
"node": ">=18"
},
"scripts": {
"test": "xo && ava && tsd"
Expand Down Expand Up @@ -46,21 +49,24 @@
"directories"
],
"dependencies": {
"arrify": "^3.0.0",
"cp-file": "^10.0.0",
"globby": "^13.1.4",
"copy-file": "^11.0.0",
"globby": "^13.2.2",
"junk": "^4.0.1",
"micromatch": "^4.0.5",
"nested-error-stacks": "^2.1.1",
"p-filter": "^3.0.0",
"p-map": "^6.0.0"
},
"devDependencies": {
"ava": "^5.2.0",
"ava": "^5.3.1",
"proxyquire": "^2.1.3",
"rimraf": "^5.0.0",
"tempy": "^3.0.0",
"tsd": "^0.28.1",
"xo": "^0.54.2"
"rimraf": "^5.0.5",
"tempy": "^3.1.0",
"tsd": "^0.29.0",
"xo": "^0.56.0"
},
"xo": {
"rules": {
"unicorn/prefer-event-target": "off"
}
}
}
6 changes: 3 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

> Copy files
**IMPORTANT:** This package has a lot of problems and I unfortunately don't have time to fix them. I would recommend against using this package until these problems are resolved. Help welcome (see the issue tracker)!
**IMPORTANT:** This package has a lot of problems and I unfortunately don't have time to fix them. I would recommend against using this package until these problems are resolved. Help welcome (see the issue tracker) 🙏

## Why

- Fast by using streams.
- 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.
Expand Down Expand Up @@ -227,6 +227,6 @@ 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
- [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
6 changes: 3 additions & 3 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,9 @@ test('flatten single file', async t => {

// TODO: Enable again when ESM supports mocking.
// eslint-disable-next-line ava/no-skip-test
test.skip('cp-file errors are CpyErrors', async t => {
const cpy = cpyMockedError('cp-file');
await t.throwsAsync(cpy('license', t.context.dir), {message: /cp-file/, instanceOf: CpyError});
test.skip('copy-file errors are CpyErrors', async t => {
const cpy = cpyMockedError('copy-file');
await t.throwsAsync(cpy('license', t.context.dir), {message: /copy-file/, instanceOf: CpyError});
});

test('throws on non-existing file', async t => {
Expand Down

0 comments on commit f9d72e1

Please sign in to comment.