Comparing version 11.1.1 to 11.2.0
'use strict' | ||
const fs = require('graceful-fs') | ||
const fs = require('../fs') | ||
const path = require('path') | ||
const mkdirs = require('../mkdirs').mkdirs | ||
const pathExists = require('../path-exists').pathExists | ||
const utimesMillis = require('../util/utimes').utimesMillis | ||
const { mkdirs } = require('../mkdirs') | ||
const { pathExists } = require('../path-exists') | ||
const { utimesMillis } = require('../util/utimes') | ||
const stat = require('../util/stat') | ||
function copy (src, dest, opts, cb) { | ||
if (typeof opts === 'function' && !cb) { | ||
cb = opts | ||
opts = {} | ||
} else if (typeof opts === 'function') { | ||
async function copy (src, dest, opts = {}) { | ||
if (typeof opts === 'function') { | ||
opts = { filter: opts } | ||
} | ||
cb = cb || function () {} | ||
opts = opts || {} | ||
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now | ||
@@ -33,86 +27,75 @@ opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber | ||
stat.checkPaths(src, dest, 'copy', opts, (err, stats) => { | ||
if (err) return cb(err) | ||
const { srcStat, destStat } = stats | ||
stat.checkParentPaths(src, srcStat, dest, 'copy', err => { | ||
if (err) return cb(err) | ||
runFilter(src, dest, opts, (err, include) => { | ||
if (err) return cb(err) | ||
if (!include) return cb() | ||
const { srcStat, destStat } = await stat.checkPaths(src, dest, 'copy', opts) | ||
checkParentDir(destStat, src, dest, opts, cb) | ||
}) | ||
}) | ||
}) | ||
} | ||
await stat.checkParentPaths(src, srcStat, dest, 'copy') | ||
function checkParentDir (destStat, src, dest, opts, cb) { | ||
const include = await runFilter(src, dest, opts) | ||
if (!include) return | ||
// check if the parent of dest exists, and create it if it doesn't exist | ||
const destParent = path.dirname(dest) | ||
pathExists(destParent, (err, dirExists) => { | ||
if (err) return cb(err) | ||
if (dirExists) return getStats(destStat, src, dest, opts, cb) | ||
mkdirs(destParent, err => { | ||
if (err) return cb(err) | ||
return getStats(destStat, src, dest, opts, cb) | ||
}) | ||
}) | ||
const dirExists = await pathExists(destParent) | ||
if (!dirExists) { | ||
await mkdirs(destParent) | ||
} | ||
await getStatsAndPerformCopy(destStat, src, dest, opts) | ||
} | ||
function runFilter (src, dest, opts, cb) { | ||
if (!opts.filter) return cb(null, true) | ||
Promise.resolve(opts.filter(src, dest)) | ||
.then(include => cb(null, include), error => cb(error)) | ||
async function runFilter (src, dest, opts) { | ||
if (!opts.filter) return true | ||
return opts.filter(src, dest) | ||
} | ||
function getStats (destStat, src, dest, opts, cb) { | ||
const stat = opts.dereference ? fs.stat : fs.lstat | ||
stat(src, (err, srcStat) => { | ||
if (err) return cb(err) | ||
async function getStatsAndPerformCopy (destStat, src, dest, opts) { | ||
const statFn = opts.dereference ? fs.stat : fs.lstat | ||
const srcStat = await statFn(src) | ||
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts, cb) | ||
else if (srcStat.isFile() || | ||
srcStat.isCharacterDevice() || | ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts, cb) | ||
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts, cb) | ||
else if (srcStat.isSocket()) return cb(new Error(`Cannot copy a socket file: ${src}`)) | ||
else if (srcStat.isFIFO()) return cb(new Error(`Cannot copy a FIFO pipe: ${src}`)) | ||
return cb(new Error(`Unknown file: ${src}`)) | ||
}) | ||
} | ||
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts) | ||
function onFile (srcStat, destStat, src, dest, opts, cb) { | ||
if (!destStat) return copyFile(srcStat, src, dest, opts, cb) | ||
return mayCopyFile(srcStat, src, dest, opts, cb) | ||
if ( | ||
srcStat.isFile() || | ||
srcStat.isCharacterDevice() || | ||
srcStat.isBlockDevice() | ||
) return onFile(srcStat, destStat, src, dest, opts) | ||
if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts) | ||
if (srcStat.isSocket()) throw new Error(`Cannot copy a socket file: ${src}`) | ||
if (srcStat.isFIFO()) throw new Error(`Cannot copy a FIFO pipe: ${src}`) | ||
throw new Error(`Unknown file: ${src}`) | ||
} | ||
function mayCopyFile (srcStat, src, dest, opts, cb) { | ||
async function onFile (srcStat, destStat, src, dest, opts) { | ||
if (!destStat) return copyFile(srcStat, src, dest, opts) | ||
if (opts.overwrite) { | ||
fs.unlink(dest, err => { | ||
if (err) return cb(err) | ||
return copyFile(srcStat, src, dest, opts, cb) | ||
}) | ||
} else if (opts.errorOnExist) { | ||
return cb(new Error(`'${dest}' already exists`)) | ||
} else return cb() | ||
await fs.unlink(dest) | ||
return copyFile(srcStat, src, dest, opts) | ||
} | ||
if (opts.errorOnExist) { | ||
throw new Error(`'${dest}' already exists`) | ||
} | ||
} | ||
function copyFile (srcStat, src, dest, opts, cb) { | ||
fs.copyFile(src, dest, err => { | ||
if (err) return cb(err) | ||
if (opts.preserveTimestamps) return handleTimestampsAndMode(srcStat.mode, src, dest, cb) | ||
return setDestMode(dest, srcStat.mode, cb) | ||
}) | ||
} | ||
async function copyFile (srcStat, src, dest, opts) { | ||
await fs.copyFile(src, dest) | ||
if (opts.preserveTimestamps) { | ||
// Make sure the file is writable before setting the timestamp | ||
// otherwise open fails with EPERM when invoked with 'r+' | ||
// (through utimes call) | ||
if (fileIsNotWritable(srcStat.mode)) { | ||
await makeFileWritable(dest, srcStat.mode) | ||
} | ||
function handleTimestampsAndMode (srcMode, src, dest, cb) { | ||
// Make sure the file is writable before setting the timestamp | ||
// otherwise open fails with EPERM when invoked with 'r+' | ||
// (through utimes call) | ||
if (fileIsNotWritable(srcMode)) { | ||
return makeFileWritable(dest, srcMode, err => { | ||
if (err) return cb(err) | ||
return setDestTimestampsAndMode(srcMode, src, dest, cb) | ||
}) | ||
// Set timestamps and mode correspondingly | ||
// Note that The initial srcStat.atime cannot be trusted | ||
// because it is modified by the read(2) system call | ||
// (See https://nodejs.org/api/fs.html#fs_stat_time_values) | ||
const updatedSrcStat = await fs.stat(src) | ||
await utimesMillis(dest, updatedSrcStat.atime, updatedSrcStat.mtime) | ||
} | ||
return setDestTimestampsAndMode(srcMode, src, dest, cb) | ||
return fs.chmod(dest, srcStat.mode) | ||
} | ||
@@ -124,117 +107,73 @@ | ||
function makeFileWritable (dest, srcMode, cb) { | ||
return setDestMode(dest, srcMode | 0o200, cb) | ||
function makeFileWritable (dest, srcMode) { | ||
return fs.chmod(dest, srcMode | 0o200) | ||
} | ||
function setDestTimestampsAndMode (srcMode, src, dest, cb) { | ||
setDestTimestamps(src, dest, err => { | ||
if (err) return cb(err) | ||
return setDestMode(dest, srcMode, cb) | ||
}) | ||
} | ||
async function onDir (srcStat, destStat, src, dest, opts) { | ||
// the dest directory might not exist, create it | ||
if (!destStat) { | ||
await fs.mkdir(dest) | ||
} | ||
function setDestMode (dest, srcMode, cb) { | ||
return fs.chmod(dest, srcMode, cb) | ||
} | ||
const items = await fs.readdir(src) | ||
function setDestTimestamps (src, dest, cb) { | ||
// The initial srcStat.atime cannot be trusted | ||
// because it is modified by the read(2) system call | ||
// (See https://nodejs.org/api/fs.html#fs_stat_time_values) | ||
fs.stat(src, (err, updatedSrcStat) => { | ||
if (err) return cb(err) | ||
return utimesMillis(dest, updatedSrcStat.atime, updatedSrcStat.mtime, cb) | ||
}) | ||
} | ||
// loop through the files in the current directory to copy everything | ||
await Promise.all(items.map(async item => { | ||
const srcItem = path.join(src, item) | ||
const destItem = path.join(dest, item) | ||
function onDir (srcStat, destStat, src, dest, opts, cb) { | ||
if (!destStat) return mkDirAndCopy(srcStat.mode, src, dest, opts, cb) | ||
return copyDir(src, dest, opts, cb) | ||
} | ||
// skip the item if it is matches by the filter function | ||
const include = await runFilter(srcItem, destItem, opts) | ||
if (!include) return | ||
function mkDirAndCopy (srcMode, src, dest, opts, cb) { | ||
fs.mkdir(dest, err => { | ||
if (err) return cb(err) | ||
copyDir(src, dest, opts, err => { | ||
if (err) return cb(err) | ||
return setDestMode(dest, srcMode, cb) | ||
}) | ||
}) | ||
} | ||
const { destStat } = await stat.checkPaths(srcItem, destItem, 'copy', opts) | ||
function copyDir (src, dest, opts, cb) { | ||
fs.readdir(src, (err, items) => { | ||
if (err) return cb(err) | ||
return copyDirItems(items, src, dest, opts, cb) | ||
}) | ||
} | ||
// If the item is a copyable file, `getStatsAndPerformCopy` will copy it | ||
// If the item is a directory, `getStatsAndPerformCopy` will call `onDir` recursively | ||
return getStatsAndPerformCopy(destStat, srcItem, destItem, opts) | ||
})) | ||
function copyDirItems (items, src, dest, opts, cb) { | ||
const item = items.pop() | ||
if (!item) return cb() | ||
return copyDirItem(items, item, src, dest, opts, cb) | ||
if (!destStat) { | ||
await fs.chmod(dest, srcStat.mode) | ||
} | ||
} | ||
function copyDirItem (items, item, src, dest, opts, cb) { | ||
const srcItem = path.join(src, item) | ||
const destItem = path.join(dest, item) | ||
runFilter(srcItem, destItem, opts, (err, include) => { | ||
if (err) return cb(err) | ||
if (!include) return copyDirItems(items, src, dest, opts, cb) | ||
async function onLink (destStat, src, dest, opts) { | ||
let resolvedSrc = await fs.readlink(src) | ||
if (opts.dereference) { | ||
resolvedSrc = path.resolve(process.cwd(), resolvedSrc) | ||
} | ||
if (!destStat) { | ||
return fs.symlink(resolvedSrc, dest) | ||
} | ||
stat.checkPaths(srcItem, destItem, 'copy', opts, (err, stats) => { | ||
if (err) return cb(err) | ||
const { destStat } = stats | ||
getStats(destStat, srcItem, destItem, opts, err => { | ||
if (err) return cb(err) | ||
return copyDirItems(items, src, dest, opts, cb) | ||
}) | ||
}) | ||
}) | ||
} | ||
let resolvedDest = null | ||
try { | ||
resolvedDest = await fs.readlink(dest) | ||
} catch (e) { | ||
// dest exists and is a regular file or directory, | ||
// Windows may throw UNKNOWN error. If dest already exists, | ||
// fs throws error anyway, so no need to guard against it here. | ||
if (e.code === 'EINVAL' || e.code === 'UNKNOWN') return fs.symlink(resolvedSrc, dest) | ||
throw e | ||
} | ||
if (opts.dereference) { | ||
resolvedDest = path.resolve(process.cwd(), resolvedDest) | ||
} | ||
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) { | ||
throw new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`) | ||
} | ||
function onLink (destStat, src, dest, opts, cb) { | ||
fs.readlink(src, (err, resolvedSrc) => { | ||
if (err) return cb(err) | ||
if (opts.dereference) { | ||
resolvedSrc = path.resolve(process.cwd(), resolvedSrc) | ||
} | ||
// do not copy if src is a subdir of dest since unlinking | ||
// dest in this case would result in removing src contents | ||
// and therefore a broken symlink would be created. | ||
if (stat.isSrcSubdir(resolvedDest, resolvedSrc)) { | ||
throw new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`) | ||
} | ||
if (!destStat) { | ||
return fs.symlink(resolvedSrc, dest, cb) | ||
} else { | ||
fs.readlink(dest, (err, resolvedDest) => { | ||
if (err) { | ||
// dest exists and is a regular file or directory, | ||
// Windows may throw UNKNOWN error. If dest already exists, | ||
// fs throws error anyway, so no need to guard against it here. | ||
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlink(resolvedSrc, dest, cb) | ||
return cb(err) | ||
} | ||
if (opts.dereference) { | ||
resolvedDest = path.resolve(process.cwd(), resolvedDest) | ||
} | ||
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) { | ||
return cb(new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`)) | ||
} | ||
// do not copy if src is a subdir of dest since unlinking | ||
// dest in this case would result in removing src contents | ||
// and therefore a broken symlink would be created. | ||
if (stat.isSrcSubdir(resolvedDest, resolvedSrc)) { | ||
return cb(new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`)) | ||
} | ||
return copyLink(resolvedSrc, dest, cb) | ||
}) | ||
} | ||
}) | ||
// copy the link | ||
await fs.unlink(dest) | ||
return fs.symlink(resolvedSrc, dest) | ||
} | ||
function copyLink (resolvedSrc, dest, cb) { | ||
fs.unlink(dest, err => { | ||
if (err) return cb(err) | ||
return fs.symlink(resolvedSrc, dest, cb) | ||
}) | ||
} | ||
module.exports = copy |
'use strict' | ||
const u = require('universalify').fromCallback | ||
const u = require('universalify').fromPromise | ||
module.exports = { | ||
@@ -5,0 +5,0 @@ copy: u(require('./copy')), |
'use strict' | ||
const u = require('universalify').fromCallback | ||
const u = require('universalify').fromPromise | ||
const path = require('path') | ||
const fs = require('graceful-fs') | ||
const fs = require('../fs') | ||
const mkdir = require('../mkdirs') | ||
function createFile (file, callback) { | ||
function makeFile () { | ||
fs.writeFile(file, '', err => { | ||
if (err) return callback(err) | ||
callback() | ||
}) | ||
async function createFile (file) { | ||
let stats | ||
try { | ||
stats = await fs.stat(file) | ||
} catch { } | ||
if (stats && stats.isFile()) return | ||
const dir = path.dirname(file) | ||
let dirStats = null | ||
try { | ||
dirStats = await fs.stat(dir) | ||
} catch (err) { | ||
// if the directory doesn't exist, make it | ||
if (err.code === 'ENOENT') { | ||
await mkdir.mkdirs(dir) | ||
await fs.writeFile(file, '') | ||
return | ||
} else { | ||
throw err | ||
} | ||
} | ||
fs.stat(file, (err, stats) => { // eslint-disable-line handle-callback-err | ||
if (!err && stats.isFile()) return callback() | ||
const dir = path.dirname(file) | ||
fs.stat(dir, (err, stats) => { | ||
if (err) { | ||
// if the directory doesn't exist, make it | ||
if (err.code === 'ENOENT') { | ||
return mkdir.mkdirs(dir, err => { | ||
if (err) return callback(err) | ||
makeFile() | ||
}) | ||
} | ||
return callback(err) | ||
} | ||
if (stats.isDirectory()) makeFile() | ||
else { | ||
// parent is not a directory | ||
// This is just to cause an internal ENOTDIR error to be thrown | ||
fs.readdir(dir, err => { | ||
if (err) return callback(err) | ||
}) | ||
} | ||
}) | ||
}) | ||
if (dirStats.isDirectory()) { | ||
await fs.writeFile(file, '') | ||
} else { | ||
// parent is not a directory | ||
// This is just to cause an internal ENOTDIR error to be thrown | ||
await fs.readdir(dir) | ||
} | ||
} | ||
@@ -47,3 +44,3 @@ | ||
stats = fs.statSync(file) | ||
} catch {} | ||
} catch { } | ||
if (stats && stats.isFile()) return | ||
@@ -50,0 +47,0 @@ |
'use strict' | ||
const u = require('universalify').fromCallback | ||
const u = require('universalify').fromPromise | ||
const path = require('path') | ||
const fs = require('graceful-fs') | ||
const fs = require('../fs') | ||
const mkdir = require('../mkdirs') | ||
const pathExists = require('../path-exists').pathExists | ||
const { pathExists } = require('../path-exists') | ||
const { areIdentical } = require('../util/stat') | ||
function createLink (srcpath, dstpath, callback) { | ||
function makeLink (srcpath, dstpath) { | ||
fs.link(srcpath, dstpath, err => { | ||
if (err) return callback(err) | ||
callback(null) | ||
}) | ||
async function createLink (srcpath, dstpath) { | ||
let dstStat | ||
try { | ||
dstStat = await fs.lstat(dstpath) | ||
} catch { | ||
// ignore error | ||
} | ||
fs.lstat(dstpath, (_, dstStat) => { | ||
fs.lstat(srcpath, (err, srcStat) => { | ||
if (err) { | ||
err.message = err.message.replace('lstat', 'ensureLink') | ||
return callback(err) | ||
} | ||
if (dstStat && areIdentical(srcStat, dstStat)) return callback(null) | ||
let srcStat | ||
try { | ||
srcStat = await fs.lstat(srcpath) | ||
} catch (err) { | ||
err.message = err.message.replace('lstat', 'ensureLink') | ||
throw err | ||
} | ||
const dir = path.dirname(dstpath) | ||
pathExists(dir, (err, dirExists) => { | ||
if (err) return callback(err) | ||
if (dirExists) return makeLink(srcpath, dstpath) | ||
mkdir.mkdirs(dir, err => { | ||
if (err) return callback(err) | ||
makeLink(srcpath, dstpath) | ||
}) | ||
}) | ||
}) | ||
}) | ||
if (dstStat && areIdentical(srcStat, dstStat)) return | ||
const dir = path.dirname(dstpath) | ||
const dirExists = await pathExists(dir) | ||
if (!dirExists) { | ||
await mkdir.mkdirs(dir) | ||
} | ||
await fs.link(srcpath, dstpath) | ||
} | ||
@@ -38,0 +38,0 @@ |
'use strict' | ||
const path = require('path') | ||
const fs = require('graceful-fs') | ||
const pathExists = require('../path-exists').pathExists | ||
const fs = require('../fs') | ||
const { pathExists } = require('../path-exists') | ||
const u = require('universalify').fromPromise | ||
/** | ||
@@ -29,44 +31,44 @@ * Function that returns two types of paths, one relative to symlink, and one | ||
function symlinkPaths (srcpath, dstpath, callback) { | ||
async function symlinkPaths (srcpath, dstpath) { | ||
if (path.isAbsolute(srcpath)) { | ||
return fs.lstat(srcpath, (err) => { | ||
if (err) { | ||
err.message = err.message.replace('lstat', 'ensureSymlink') | ||
return callback(err) | ||
} | ||
return callback(null, { | ||
toCwd: srcpath, | ||
toDst: srcpath | ||
}) | ||
}) | ||
} else { | ||
const dstdir = path.dirname(dstpath) | ||
const relativeToDst = path.join(dstdir, srcpath) | ||
return pathExists(relativeToDst, (err, exists) => { | ||
if (err) return callback(err) | ||
if (exists) { | ||
return callback(null, { | ||
toCwd: relativeToDst, | ||
toDst: srcpath | ||
}) | ||
} else { | ||
return fs.lstat(srcpath, (err) => { | ||
if (err) { | ||
err.message = err.message.replace('lstat', 'ensureSymlink') | ||
return callback(err) | ||
} | ||
return callback(null, { | ||
toCwd: srcpath, | ||
toDst: path.relative(dstdir, srcpath) | ||
}) | ||
}) | ||
} | ||
}) | ||
try { | ||
await fs.lstat(srcpath) | ||
} catch (err) { | ||
err.message = err.message.replace('lstat', 'ensureSymlink') | ||
throw err | ||
} | ||
return { | ||
toCwd: srcpath, | ||
toDst: srcpath | ||
} | ||
} | ||
const dstdir = path.dirname(dstpath) | ||
const relativeToDst = path.join(dstdir, srcpath) | ||
const exists = await pathExists(relativeToDst) | ||
if (exists) { | ||
return { | ||
toCwd: relativeToDst, | ||
toDst: srcpath | ||
} | ||
} | ||
try { | ||
await fs.lstat(srcpath) | ||
} catch (err) { | ||
err.message = err.message.replace('lstat', 'ensureSymlink') | ||
throw err | ||
} | ||
return { | ||
toCwd: srcpath, | ||
toDst: path.relative(dstdir, srcpath) | ||
} | ||
} | ||
function symlinkPathsSync (srcpath, dstpath) { | ||
let exists | ||
if (path.isAbsolute(srcpath)) { | ||
exists = fs.existsSync(srcpath) | ||
const exists = fs.existsSync(srcpath) | ||
if (!exists) throw new Error('absolute srcpath does not exist') | ||
@@ -77,25 +79,25 @@ return { | ||
} | ||
} else { | ||
const dstdir = path.dirname(dstpath) | ||
const relativeToDst = path.join(dstdir, srcpath) | ||
exists = fs.existsSync(relativeToDst) | ||
if (exists) { | ||
return { | ||
toCwd: relativeToDst, | ||
toDst: srcpath | ||
} | ||
} else { | ||
exists = fs.existsSync(srcpath) | ||
if (!exists) throw new Error('relative srcpath does not exist') | ||
return { | ||
toCwd: srcpath, | ||
toDst: path.relative(dstdir, srcpath) | ||
} | ||
} | ||
const dstdir = path.dirname(dstpath) | ||
const relativeToDst = path.join(dstdir, srcpath) | ||
const exists = fs.existsSync(relativeToDst) | ||
if (exists) { | ||
return { | ||
toCwd: relativeToDst, | ||
toDst: srcpath | ||
} | ||
} | ||
const srcExists = fs.existsSync(srcpath) | ||
if (!srcExists) throw new Error('relative srcpath does not exist') | ||
return { | ||
toCwd: srcpath, | ||
toDst: path.relative(dstdir, srcpath) | ||
} | ||
} | ||
module.exports = { | ||
symlinkPaths, | ||
symlinkPaths: u(symlinkPaths), | ||
symlinkPathsSync | ||
} |
'use strict' | ||
const fs = require('graceful-fs') | ||
const fs = require('../fs') | ||
const u = require('universalify').fromPromise | ||
function symlinkType (srcpath, type, callback) { | ||
callback = (typeof type === 'function') ? type : callback | ||
type = (typeof type === 'function') ? false : type | ||
if (type) return callback(null, type) | ||
fs.lstat(srcpath, (err, stats) => { | ||
if (err) return callback(null, 'file') | ||
type = (stats && stats.isDirectory()) ? 'dir' : 'file' | ||
callback(null, type) | ||
}) | ||
async function symlinkType (srcpath, type) { | ||
if (type) return type | ||
let stats | ||
try { | ||
stats = await fs.lstat(srcpath) | ||
} catch { | ||
return 'file' | ||
} | ||
return (stats && stats.isDirectory()) ? 'dir' : 'file' | ||
} | ||
function symlinkTypeSync (srcpath, type) { | ||
if (type) return type | ||
let stats | ||
if (type) return type | ||
try { | ||
@@ -29,4 +32,4 @@ stats = fs.lstatSync(srcpath) | ||
module.exports = { | ||
symlinkType, | ||
symlinkType: u(symlinkType), | ||
symlinkTypeSync | ||
} |
'use strict' | ||
const u = require('universalify').fromCallback | ||
const u = require('universalify').fromPromise | ||
const path = require('path') | ||
const fs = require('../fs') | ||
const _mkdirs = require('../mkdirs') | ||
const mkdirs = _mkdirs.mkdirs | ||
const mkdirsSync = _mkdirs.mkdirsSync | ||
const _symlinkPaths = require('./symlink-paths') | ||
const symlinkPaths = _symlinkPaths.symlinkPaths | ||
const symlinkPathsSync = _symlinkPaths.symlinkPathsSync | ||
const { mkdirs, mkdirsSync } = require('../mkdirs') | ||
const _symlinkType = require('./symlink-type') | ||
const symlinkType = _symlinkType.symlinkType | ||
const symlinkTypeSync = _symlinkType.symlinkTypeSync | ||
const { symlinkPaths, symlinkPathsSync } = require('./symlink-paths') | ||
const { symlinkType, symlinkTypeSync } = require('./symlink-type') | ||
const pathExists = require('../path-exists').pathExists | ||
const { pathExists } = require('../path-exists') | ||
const { areIdentical } = require('../util/stat') | ||
function createSymlink (srcpath, dstpath, type, callback) { | ||
callback = (typeof type === 'function') ? type : callback | ||
type = (typeof type === 'function') ? false : type | ||
async function createSymlink (srcpath, dstpath, type) { | ||
let stats | ||
try { | ||
stats = await fs.lstat(dstpath) | ||
} catch { } | ||
fs.lstat(dstpath, (err, stats) => { | ||
if (!err && stats.isSymbolicLink()) { | ||
Promise.all([ | ||
fs.stat(srcpath), | ||
fs.stat(dstpath) | ||
]).then(([srcStat, dstStat]) => { | ||
if (areIdentical(srcStat, dstStat)) return callback(null) | ||
_createSymlink(srcpath, dstpath, type, callback) | ||
}) | ||
} else _createSymlink(srcpath, dstpath, type, callback) | ||
}) | ||
} | ||
if (stats && stats.isSymbolicLink()) { | ||
const [srcStat, dstStat] = await Promise.all([ | ||
fs.stat(srcpath), | ||
fs.stat(dstpath) | ||
]) | ||
function _createSymlink (srcpath, dstpath, type, callback) { | ||
symlinkPaths(srcpath, dstpath, (err, relative) => { | ||
if (err) return callback(err) | ||
srcpath = relative.toDst | ||
symlinkType(relative.toCwd, type, (err, type) => { | ||
if (err) return callback(err) | ||
const dir = path.dirname(dstpath) | ||
pathExists(dir, (err, dirExists) => { | ||
if (err) return callback(err) | ||
if (dirExists) return fs.symlink(srcpath, dstpath, type, callback) | ||
mkdirs(dir, err => { | ||
if (err) return callback(err) | ||
fs.symlink(srcpath, dstpath, type, callback) | ||
}) | ||
}) | ||
}) | ||
}) | ||
if (areIdentical(srcStat, dstStat)) return | ||
} | ||
const relative = await symlinkPaths(srcpath, dstpath) | ||
srcpath = relative.toDst | ||
const toType = await symlinkType(relative.toCwd, type) | ||
const dir = path.dirname(dstpath) | ||
if (!(await pathExists(dir))) { | ||
await mkdirs(dir) | ||
} | ||
return fs.symlink(srcpath, dstpath, toType) | ||
} | ||
@@ -62,3 +47,3 @@ | ||
stats = fs.lstatSync(dstpath) | ||
} catch {} | ||
} catch { } | ||
if (stats && stats.isSymbolicLink()) { | ||
@@ -65,0 +50,0 @@ const srcStat = fs.statSync(srcpath) |
'use strict' | ||
const u = require('universalify').fromCallback | ||
const u = require('universalify').fromPromise | ||
module.exports = { | ||
@@ -5,0 +5,0 @@ move: u(require('./move')), |
'use strict' | ||
const fs = require('graceful-fs') | ||
const fs = require('../fs') | ||
const path = require('path') | ||
const copy = require('../copy').copy | ||
const remove = require('../remove').remove | ||
const mkdirp = require('../mkdirs').mkdirp | ||
const pathExists = require('../path-exists').pathExists | ||
const { copy } = require('../copy') | ||
const { remove } = require('../remove') | ||
const { mkdirp } = require('../mkdirs') | ||
const { pathExists } = require('../path-exists') | ||
const stat = require('../util/stat') | ||
function move (src, dest, opts, cb) { | ||
if (typeof opts === 'function') { | ||
cb = opts | ||
opts = {} | ||
} | ||
async function move (src, dest, opts = {}) { | ||
const overwrite = opts.overwrite || opts.clobber || false | ||
opts = opts || {} | ||
const { srcStat, isChangingCase = false } = await stat.checkPaths(src, dest, 'move', opts) | ||
const overwrite = opts.overwrite || opts.clobber || false | ||
await stat.checkParentPaths(src, srcStat, dest, 'move') | ||
stat.checkPaths(src, dest, 'move', opts, (err, stats) => { | ||
if (err) return cb(err) | ||
const { srcStat, isChangingCase = false } = stats | ||
stat.checkParentPaths(src, srcStat, dest, 'move', err => { | ||
if (err) return cb(err) | ||
if (isParentRoot(dest)) return doRename(src, dest, overwrite, isChangingCase, cb) | ||
mkdirp(path.dirname(dest), err => { | ||
if (err) return cb(err) | ||
return doRename(src, dest, overwrite, isChangingCase, cb) | ||
}) | ||
}) | ||
}) | ||
} | ||
// If the parent of dest is not root, make sure it exists before proceeding | ||
const destParent = path.dirname(dest) | ||
const parsedParentPath = path.parse(destParent) | ||
if (parsedParentPath.root !== destParent) { | ||
await mkdirp(destParent) | ||
} | ||
function isParentRoot (dest) { | ||
const parent = path.dirname(dest) | ||
const parsedPath = path.parse(parent) | ||
return parsedPath.root === parent | ||
return doRename(src, dest, overwrite, isChangingCase) | ||
} | ||
function doRename (src, dest, overwrite, isChangingCase, cb) { | ||
if (isChangingCase) return rename(src, dest, overwrite, cb) | ||
if (overwrite) { | ||
return remove(dest, err => { | ||
if (err) return cb(err) | ||
return rename(src, dest, overwrite, cb) | ||
}) | ||
async function doRename (src, dest, overwrite, isChangingCase) { | ||
if (!isChangingCase) { | ||
if (overwrite) { | ||
await remove(dest) | ||
} else if (await pathExists(dest)) { | ||
throw new Error('dest already exists.') | ||
} | ||
} | ||
pathExists(dest, (err, destExists) => { | ||
if (err) return cb(err) | ||
if (destExists) return cb(new Error('dest already exists.')) | ||
return rename(src, dest, overwrite, cb) | ||
}) | ||
} | ||
function rename (src, dest, overwrite, cb) { | ||
fs.rename(src, dest, err => { | ||
if (!err) return cb() | ||
if (err.code !== 'EXDEV') return cb(err) | ||
return moveAcrossDevice(src, dest, overwrite, cb) | ||
}) | ||
try { | ||
// Try w/ rename first, and try copy + remove if EXDEV | ||
await fs.rename(src, dest) | ||
} catch (err) { | ||
if (err.code !== 'EXDEV') { | ||
throw err | ||
} | ||
await moveAcrossDevice(src, dest, overwrite) | ||
} | ||
} | ||
function moveAcrossDevice (src, dest, overwrite, cb) { | ||
async function moveAcrossDevice (src, dest, overwrite) { | ||
const opts = { | ||
@@ -70,8 +54,7 @@ overwrite, | ||
} | ||
copy(src, dest, opts, err => { | ||
if (err) return cb(err) | ||
return remove(src, cb) | ||
}) | ||
await copy(src, dest, opts) | ||
return remove(src) | ||
} | ||
module.exports = move |
'use strict' | ||
const u = require('universalify').fromCallback | ||
const fs = require('graceful-fs') | ||
const u = require('universalify').fromPromise | ||
const fs = require('../fs') | ||
const path = require('path') | ||
@@ -9,19 +9,10 @@ const mkdir = require('../mkdirs') | ||
function outputFile (file, data, encoding, callback) { | ||
if (typeof encoding === 'function') { | ||
callback = encoding | ||
encoding = 'utf8' | ||
} | ||
async function outputFile (file, data, encoding = 'utf-8') { | ||
const dir = path.dirname(file) | ||
pathExists(dir, (err, itDoes) => { | ||
if (err) return callback(err) | ||
if (itDoes) return fs.writeFile(file, data, encoding, callback) | ||
mkdir.mkdirs(dir, err => { | ||
if (err) return callback(err) | ||
if (!(await pathExists(dir))) { | ||
await mkdir.mkdirs(dir) | ||
} | ||
fs.writeFile(file, data, encoding, callback) | ||
}) | ||
}) | ||
return fs.writeFile(file, data, encoding) | ||
} | ||
@@ -31,6 +22,6 @@ | ||
const dir = path.dirname(file) | ||
if (fs.existsSync(dir)) { | ||
return fs.writeFileSync(file, ...args) | ||
if (!fs.existsSync(dir)) { | ||
mkdir.mkdirsSync(dir) | ||
} | ||
mkdir.mkdirsSync(dir) | ||
fs.writeFileSync(file, ...args) | ||
@@ -37,0 +28,0 @@ } |
@@ -5,3 +5,3 @@ 'use strict' | ||
const path = require('path') | ||
const util = require('util') | ||
const u = require('universalify').fromPromise | ||
@@ -36,31 +36,28 @@ function getStats (src, dest, opts) { | ||
function checkPaths (src, dest, funcName, opts, cb) { | ||
util.callbackify(getStats)(src, dest, opts, (err, stats) => { | ||
if (err) return cb(err) | ||
const { srcStat, destStat } = stats | ||
if (destStat) { | ||
if (areIdentical(srcStat, destStat)) { | ||
const srcBaseName = path.basename(src) | ||
const destBaseName = path.basename(dest) | ||
if (funcName === 'move' && | ||
srcBaseName !== destBaseName && | ||
srcBaseName.toLowerCase() === destBaseName.toLowerCase()) { | ||
return cb(null, { srcStat, destStat, isChangingCase: true }) | ||
} | ||
return cb(new Error('Source and destination must not be the same.')) | ||
async function checkPaths (src, dest, funcName, opts) { | ||
const { srcStat, destStat } = await getStats(src, dest, opts) | ||
if (destStat) { | ||
if (areIdentical(srcStat, destStat)) { | ||
const srcBaseName = path.basename(src) | ||
const destBaseName = path.basename(dest) | ||
if (funcName === 'move' && | ||
srcBaseName !== destBaseName && | ||
srcBaseName.toLowerCase() === destBaseName.toLowerCase()) { | ||
return { srcStat, destStat, isChangingCase: true } | ||
} | ||
if (srcStat.isDirectory() && !destStat.isDirectory()) { | ||
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)) | ||
} | ||
if (!srcStat.isDirectory() && destStat.isDirectory()) { | ||
return cb(new Error(`Cannot overwrite directory '${dest}' with non-directory '${src}'.`)) | ||
} | ||
throw new Error('Source and destination must not be the same.') | ||
} | ||
if (srcStat.isDirectory() && !destStat.isDirectory()) { | ||
throw new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`) | ||
} | ||
if (!srcStat.isDirectory() && destStat.isDirectory()) { | ||
throw new Error(`Cannot overwrite directory '${dest}' with non-directory '${src}'.`) | ||
} | ||
} | ||
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) { | ||
return cb(new Error(errMsg(src, dest, funcName))) | ||
} | ||
return cb(null, { srcStat, destStat }) | ||
}) | ||
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) { | ||
throw new Error(errMsg(src, dest, funcName)) | ||
} | ||
return { srcStat, destStat } | ||
} | ||
@@ -100,16 +97,20 @@ | ||
// parent and stops once it reaches the src parent or the root path. | ||
function checkParentPaths (src, srcStat, dest, funcName, cb) { | ||
async function checkParentPaths (src, srcStat, dest, funcName) { | ||
const srcParent = path.resolve(path.dirname(src)) | ||
const destParent = path.resolve(path.dirname(dest)) | ||
if (destParent === srcParent || destParent === path.parse(destParent).root) return cb() | ||
fs.stat(destParent, { bigint: true }, (err, destStat) => { | ||
if (err) { | ||
if (err.code === 'ENOENT') return cb() | ||
return cb(err) | ||
} | ||
if (areIdentical(srcStat, destStat)) { | ||
return cb(new Error(errMsg(src, dest, funcName))) | ||
} | ||
return checkParentPaths(src, srcStat, destParent, funcName, cb) | ||
}) | ||
if (destParent === srcParent || destParent === path.parse(destParent).root) return | ||
let destStat | ||
try { | ||
destStat = await fs.stat(destParent, { bigint: true }) | ||
} catch (err) { | ||
if (err.code === 'ENOENT') return | ||
throw err | ||
} | ||
if (areIdentical(srcStat, destStat)) { | ||
throw new Error(errMsg(src, dest, funcName)) | ||
} | ||
return checkParentPaths(src, srcStat, destParent, funcName) | ||
} | ||
@@ -143,3 +144,3 @@ | ||
const destArr = path.resolve(dest).split(path.sep).filter(i => i) | ||
return srcArr.reduce((acc, cur, i) => acc && destArr[i] === cur, true) | ||
return srcArr.every((cur, i) => destArr[i] === cur) | ||
} | ||
@@ -152,8 +153,11 @@ | ||
module.exports = { | ||
checkPaths, | ||
// checkPaths | ||
checkPaths: u(checkPaths), | ||
checkPathsSync, | ||
checkParentPaths, | ||
// checkParent | ||
checkParentPaths: u(checkParentPaths), | ||
checkParentPathsSync, | ||
// Misc | ||
isSrcSubdir, | ||
areIdentical | ||
} |
'use strict' | ||
const fs = require('graceful-fs') | ||
const fs = require('../fs') | ||
const u = require('universalify').fromPromise | ||
function utimesMillis (path, atime, mtime, callback) { | ||
async function utimesMillis (path, atime, mtime) { | ||
// if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback) | ||
fs.open(path, 'r+', (err, fd) => { | ||
if (err) return callback(err) | ||
fs.futimes(fd, atime, mtime, futimesErr => { | ||
fs.close(fd, closeErr => { | ||
if (callback) callback(futimesErr || closeErr) | ||
}) | ||
}) | ||
}) | ||
const fd = await fs.open(path, 'r+') | ||
let closeErr = null | ||
try { | ||
await fs.futimes(fd, atime, mtime) | ||
} finally { | ||
try { | ||
await fs.close(fd) | ||
} catch (e) { | ||
closeErr = e | ||
} | ||
} | ||
if (closeErr) { | ||
throw closeErr | ||
} | ||
} | ||
@@ -24,4 +34,4 @@ | ||
module.exports = { | ||
utimesMillis, | ||
utimesMillis: u(utimesMillis), | ||
utimesMillisSync | ||
} |
{ | ||
"name": "fs-extra", | ||
"version": "11.1.1", | ||
"version": "11.2.0", | ||
"description": "fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as recursive mkdir, copy, and remove.", | ||
@@ -5,0 +5,0 @@ "engines": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
54905
1233