From f9d72e171e3c3c2494680b46ea27219d7373023f Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 5 Nov 2023 14:59:14 +0700 Subject: [PATCH] Require Node.js 18 --- .github/workflows/main.yml | 5 ++--- cpy-error.js | 10 ++++------ index.d.ts | 4 ++-- index.js | 27 ++++++++------------------- index.test-d.ts | 2 +- package.json | 28 +++++++++++++++++----------- readme.md | 6 +++--- test.js | 6 +++--- 8 files changed, 40 insertions(+), 48 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 08bdbd5..087eb91 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 diff --git a/cpy-error.js b/cpy-error.js index f5a1f40..b1905c4 100644 --- a/cpy-error.js +++ b/cpy-error.js @@ -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'; } } diff --git a/index.d.ts b/index.d.ts index 40198c8..08a3f7e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -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 = { /** @@ -103,7 +103,7 @@ export type Options = { ``` */ readonly filter?: (file: Entry) => boolean | Promise; -} & Readonly & CpFileOptions; // eslint-disable-line @typescript-eslint/no-redundant-type-constituents +} & Readonly & CopyFileOptions; export type ProgressData = { /** diff --git a/index.js b/index.js index ec04f72..53c3f82 100644 --- a/index.js +++ b/index.js @@ -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} */ @@ -139,7 +136,7 @@ const renameFile = (source, rename) => { export default function cpy( source, destination, - {concurrency = defaultConcurrency, ...options} = {}, + {concurrency = os.availableParallelism(), ...options} = {}, ) { /** @type {Map} @@ -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('!')); @@ -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 = [ @@ -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) || { @@ -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; diff --git a/index.test-d.ts b/index.test-d.ts index cd50991..7c57d33 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -8,7 +8,7 @@ expectType & ProgressEmitter>( cpy('foo.js', 'destination', {rename: 'foobar'}), ); expectType & ProgressEmitter>( - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + cpy('foo.js', 'destination', {rename: basename => `prefix-${basename}`}), ); expectType & ProgressEmitter>( diff --git a/package.json b/package.json index 462e7d4..887f84f 100644 --- a/package.json +++ b/package.json @@ -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" @@ -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" + } } } diff --git a/readme.md b/readme.md index 81db2b8..af163b4 100644 --- a/readme.md +++ b/readme.md @@ -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. @@ -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 diff --git a/test.js b/test.js index 7ef13a8..ac3f47c 100644 --- a/test.js +++ b/test.js @@ -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 => {