Comparing version 6.2.0 to 7.0.0
101
fs.js
'use strict'; | ||
const {promisify} = require('util'); | ||
const fs = require('graceful-fs'); | ||
const makeDir = require('make-dir'); | ||
const pify = require('pify'); | ||
const pEvent = require('p-event'); | ||
const CpFileError = require('./cp-file-error'); | ||
const fsP = pify(fs); | ||
const stat = promisify(fs.stat); | ||
const lstat = promisify(fs.lstat); | ||
const utimes = promisify(fs.utimes); | ||
const chmod = promisify(fs.chmod); | ||
const chown = promisify(fs.chown); | ||
@@ -12,68 +17,34 @@ exports.closeSync = fs.closeSync.bind(fs); | ||
exports.createReadStream = (path, options) => new Promise((resolve, reject) => { | ||
exports.createReadStream = async (path, options) => { | ||
const read = fs.createReadStream(path, options); | ||
read.once('error', error => { | ||
reject(new CpFileError(`Cannot read from \`${path}\`: ${error.message}`, error)); | ||
}); | ||
try { | ||
await pEvent(read, ['readable', 'end']); | ||
} catch (error) { | ||
throw new CpFileError(`Cannot read from \`${path}\`: ${error.message}`, error); | ||
} | ||
read.once('readable', () => { | ||
resolve(read); | ||
}); | ||
return read; | ||
}; | ||
read.once('end', () => { | ||
resolve(read); | ||
}); | ||
}); | ||
exports.stat = path => fsP.stat(path).catch(error => { | ||
exports.stat = path => stat(path).catch(error => { | ||
throw new CpFileError(`Cannot stat path \`${path}\`: ${error.message}`, error); | ||
}); | ||
exports.lstat = path => fsP.lstat(path).catch(error => { | ||
exports.lstat = path => lstat(path).catch(error => { | ||
throw new CpFileError(`lstat \`${path}\` failed: ${error.message}`, error); | ||
}); | ||
exports.utimes = (path, atime, mtime) => fsP.utimes(path, atime, mtime).catch(error => { | ||
exports.utimes = (path, atime, mtime) => utimes(path, atime, mtime).catch(error => { | ||
throw new CpFileError(`utimes \`${path}\` failed: ${error.message}`, error); | ||
}); | ||
exports.chmod = (path, mode) => fsP.chmod(path, mode).catch(error => { | ||
exports.chmod = (path, mode) => chmod(path, mode).catch(error => { | ||
throw new CpFileError(`chmod \`${path}\` failed: ${error.message}`, error); | ||
}); | ||
exports.chown = (path, uid, gid) => fsP.chown(path, uid, gid).catch(error => { | ||
exports.chown = (path, uid, gid) => chown(path, uid, gid).catch(error => { | ||
throw new CpFileError(`chown \`${path}\` failed: ${error.message}`, error); | ||
}); | ||
exports.openSync = (path, flags, mode) => { | ||
try { | ||
return fs.openSync(path, flags, mode); | ||
} catch (error) { | ||
if (flags.includes('w')) { | ||
throw new CpFileError(`Cannot write to \`${path}\`: ${error.message}`, error); | ||
} | ||
throw new CpFileError(`Cannot open \`${path}\`: ${error.message}`, error); | ||
} | ||
}; | ||
// eslint-disable-next-line max-params | ||
exports.readSync = (fileDescriptor, buffer, offset, length, position, path) => { | ||
try { | ||
return fs.readSync(fileDescriptor, buffer, offset, length, position); | ||
} catch (error) { | ||
throw new CpFileError(`Cannot read from \`${path}\`: ${error.message}`, error); | ||
} | ||
}; | ||
// eslint-disable-next-line max-params | ||
exports.writeSync = (fileDescriptor, buffer, offset, length, position, path) => { | ||
try { | ||
return fs.writeSync(fileDescriptor, buffer, offset, length, position); | ||
} catch (error) { | ||
throw new CpFileError(`Cannot write to \`${path}\`: ${error.message}`, error); | ||
} | ||
}; | ||
exports.statSync = path => { | ||
@@ -87,18 +58,2 @@ try { | ||
exports.fstatSync = (fileDescriptor, path) => { | ||
try { | ||
return fs.fstatSync(fileDescriptor); | ||
} catch (error) { | ||
throw new CpFileError(`fstat \`${path}\` failed: ${error.message}`, error); | ||
} | ||
}; | ||
exports.futimesSync = (fileDescriptor, atime, mtime, path) => { | ||
try { | ||
return fs.futimesSync(fileDescriptor, atime, mtime, path); | ||
} catch (error) { | ||
throw new CpFileError(`futimes \`${path}\` failed: ${error.message}`, error); | ||
} | ||
}; | ||
exports.utimesSync = (path, atime, mtime) => { | ||
@@ -140,10 +95,8 @@ try { | ||
if (fs.copyFileSync) { | ||
exports.copyFileSync = (source, destination, flags) => { | ||
try { | ||
fs.copyFileSync(source, destination, flags); | ||
} catch (error) { | ||
throw new CpFileError(`Cannot copy from \`${source}\` to \`${destination}\`: ${error.message}`, error); | ||
} | ||
}; | ||
} | ||
exports.copyFileSync = (source, destination, flags) => { | ||
try { | ||
fs.copyFileSync(source, destination, flags); | ||
} catch (error) { | ||
throw new CpFileError(`Cannot copy from \`${source}\` to \`${destination}\`: ${error.message}`, error); | ||
} | ||
}; |
@@ -73,7 +73,4 @@ declare namespace cpFile { | ||
sync(source: string, destination: string, options?: cpFile.Options): void; | ||
// TODO: Remove this for the next major release | ||
default: typeof cpFile; | ||
}; | ||
export = cpFile; |
158
index.js
'use strict'; | ||
const path = require('path'); | ||
const {constants: fsConstants} = require('fs'); | ||
const {Buffer} = require('safe-buffer'); | ||
const pEvent = require('p-event'); | ||
const CpFileError = require('./cp-file-error'); | ||
@@ -9,51 +9,58 @@ const fs = require('./fs'); | ||
const cpFile = (source, destination, options) => { | ||
if (!source || !destination) { | ||
return Promise.reject(new CpFileError('`source` and `destination` required')); | ||
} | ||
const cpFileAsync = async (source, destination, options, progressEmitter) => { | ||
let readError; | ||
const stat = await fs.stat(source); | ||
progressEmitter.size = stat.size; | ||
options = Object.assign({overwrite: true}, options); | ||
const read = await fs.createReadStream(source); | ||
await fs.makeDir(path.dirname(destination)); | ||
const write = fs.createWriteStream(destination, {flags: options.overwrite ? 'w' : 'wx'}); | ||
read.on('data', () => { | ||
progressEmitter.written = write.bytesWritten; | ||
}); | ||
read.once('error', error => { | ||
readError = new CpFileError(`Cannot read from \`${source}\`: ${error.message}`, error); | ||
write.end(); | ||
}); | ||
const progressEmitter = new ProgressEmitter(path.resolve(source), path.resolve(destination)); | ||
let updateStats = false; | ||
try { | ||
const writePromise = pEvent(write, 'close'); | ||
read.pipe(write); | ||
await writePromise; | ||
progressEmitter.written = progressEmitter.size; | ||
updateStats = true; | ||
} catch (error) { | ||
if (options.overwrite || error.code !== 'EEXIST') { | ||
throw new CpFileError(`Cannot write to \`${destination}\`: ${error.message}`, error); | ||
} | ||
} | ||
const promise = fs | ||
.stat(source) | ||
.then(stat => { | ||
progressEmitter.size = stat.size; | ||
}) | ||
.then(() => fs.createReadStream(source)) | ||
.then(read => fs.makeDir(path.dirname(destination)).then(() => read)) | ||
.then(read => new Promise((resolve, reject) => { | ||
const write = fs.createWriteStream(destination, {flags: options.overwrite ? 'w' : 'wx'}); | ||
if (readError) { | ||
throw readError; | ||
} | ||
read.on('data', () => { | ||
progressEmitter.written = write.bytesWritten; | ||
}); | ||
if (updateStats) { | ||
const stats = await fs.lstat(source); | ||
write.on('error', error => { | ||
if (!options.overwrite && error.code === 'EEXIST') { | ||
resolve(false); | ||
return; | ||
} | ||
return Promise.all([ | ||
fs.utimes(destination, stats.atime, stats.mtime), | ||
fs.chmod(destination, stats.mode), | ||
fs.chown(destination, stats.uid, stats.gid) | ||
]); | ||
} | ||
}; | ||
reject(new CpFileError(`Cannot write to \`${destination}\`: ${error.message}`, error)); | ||
}); | ||
const cpFile = (source, destination, options) => { | ||
if (!source || !destination) { | ||
return Promise.reject(new CpFileError('`source` and `destination` required')); | ||
} | ||
write.on('close', () => { | ||
progressEmitter.written = progressEmitter.size; | ||
resolve(true); | ||
}); | ||
options = { | ||
overwrite: true, | ||
...options | ||
}; | ||
read.pipe(write); | ||
})) | ||
.then(updateStats => { | ||
if (updateStats) { | ||
return fs.lstat(source).then(stats => Promise.all([ | ||
fs.utimes(destination, stats.atime, stats.mtime), | ||
fs.chmod(destination, stats.mode), | ||
fs.chown(destination, stats.uid, stats.gid) | ||
])); | ||
} | ||
}); | ||
const progressEmitter = new ProgressEmitter(path.resolve(source), path.resolve(destination)); | ||
const promise = cpFileAsync(source, destination, options, progressEmitter); | ||
promise.on = (...args) => { | ||
@@ -68,4 +75,2 @@ progressEmitter.on(...args); | ||
module.exports = cpFile; | ||
// TODO: Remove this for the next major release | ||
module.exports.default = cpFile; | ||
@@ -87,3 +92,12 @@ const checkSourceIsFile = (stat, source) => { | ||
const copySyncNative = (source, destination, options) => { | ||
module.exports.sync = (source, destination, options) => { | ||
if (!source || !destination) { | ||
throw new CpFileError('`source` and `destination` required'); | ||
} | ||
options = { | ||
overwrite: true, | ||
...options | ||
}; | ||
const stat = fs.statSync(source); | ||
@@ -107,55 +121,1 @@ checkSourceIsFile(stat, source); | ||
}; | ||
const copySyncFallback = (source, destination, options) => { | ||
let bytesRead; | ||
let position; | ||
let read; // eslint-disable-line prefer-const | ||
let write; | ||
const BUF_LENGTH = 100 * 1024; | ||
const buffer = Buffer.alloc(BUF_LENGTH); | ||
const readSync = position => fs.readSync(read, buffer, 0, BUF_LENGTH, position, source); | ||
const writeSync = () => fs.writeSync(write, buffer, 0, bytesRead, undefined, destination); | ||
read = fs.openSync(source, 'r'); | ||
bytesRead = readSync(0); | ||
position = bytesRead; | ||
fs.makeDirSync(path.dirname(destination)); | ||
try { | ||
write = fs.openSync(destination, options.overwrite ? 'w' : 'wx'); | ||
} catch (error) { | ||
if (!options.overwrite && error.code === 'EEXIST') { | ||
return; | ||
} | ||
throw error; | ||
} | ||
writeSync(); | ||
while (bytesRead === BUF_LENGTH) { | ||
bytesRead = readSync(position); | ||
writeSync(); | ||
position += bytesRead; | ||
} | ||
const stat = fs.fstatSync(read, source); | ||
fs.futimesSync(write, stat.atime, stat.mtime, destination); | ||
fs.closeSync(read); | ||
fs.closeSync(write); | ||
fixupAttributes(destination, stat); | ||
}; | ||
module.exports.sync = (source, destination, options) => { | ||
if (!source || !destination) { | ||
throw new CpFileError('`source` and `destination` required'); | ||
} | ||
options = Object.assign({overwrite: true}, options); | ||
if (fs.copyFileSync) { | ||
copySyncNative(source, destination, options); | ||
} else { | ||
copySyncFallback(source, destination, options); | ||
} | ||
}; |
{ | ||
"name": "cp-file", | ||
"version": "6.2.0", | ||
"version": "7.0.0", | ||
"description": "Copy a file", | ||
@@ -20,3 +20,3 @@ "license": "MIT", | ||
"engines": { | ||
"node": ">=6" | ||
"node": ">=8" | ||
}, | ||
@@ -50,6 +50,5 @@ "scripts": { | ||
"graceful-fs": "^4.1.2", | ||
"make-dir": "^2.0.0", | ||
"make-dir": "^3.0.0", | ||
"nested-error-stacks": "^2.0.0", | ||
"pify": "^4.0.1", | ||
"safe-buffer": "^5.0.1" | ||
"p-event": "^4.1.0" | ||
}, | ||
@@ -56,0 +55,0 @@ "devDependencies": { |
@@ -8,3 +8,3 @@ # cp-file [![Build Status](https://travis-ci.org/sindresorhus/cp-file.svg?branch=master)](https://travis-ci.org/sindresorhus/cp-file) [![Coverage Status](https://coveralls.io/repos/github/sindresorhus/cp-file/badge.svg?branch=master)](https://coveralls.io/github/sindresorhus/cp-file?branch=master) | ||
- Fast by using streams in the async version and [`fs.copyFileSync()`](https://nodejs.org/api/fs.html#fs_fs_copyfilesync_src_dest_flags) (when available) in the synchronous version. | ||
- 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. | ||
- Resilient by using [graceful-fs](https://github.com/isaacs/node-graceful-fs). | ||
@@ -11,0 +11,0 @@ - User-friendly by creating non-existent destination directories for you. |
4
12705
279
+ Addedp-event@^4.1.0
+ Addedmake-dir@3.1.0(transitive)
+ Addedp-event@4.2.0(transitive)
+ Addedp-finally@1.0.0(transitive)
+ Addedp-timeout@3.2.0(transitive)
+ Addedsemver@6.3.1(transitive)
- Removedpify@^4.0.1
- Removedsafe-buffer@^5.0.1
- Removedmake-dir@2.1.0(transitive)
- Removedpify@4.0.1(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsemver@5.7.2(transitive)
Updatedmake-dir@^3.0.0