Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor move API to async/await #1025

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions lib/move/__tests__/move.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const fs = require('graceful-fs')
const fs = require('../../fs')
const os = require('os')
const fse = require('../../')
const path = require('path')
Expand All @@ -12,14 +12,12 @@ const { differentDevice, ifCrossDeviceEnabled } = require('./cross-device-utils'
const describeIfWindows = process.platform === 'win32' ? describe : describe.skip

function createAsyncErrFn (errCode) {
const fn = function (...args) {
async function fn () {
fn.callCount++
const callback = args.pop()
setTimeout(() => {
const err = new Error()
err.code = errCode
callback(err)
}, 10)
const err = new Error()
err.code = errCode

return Promise.reject(err)
}
Comment on lines +15 to 21
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The move now uses the Promise version of the fs.rename API (instead of the callback version), so the stub here needs to be changed.

fn.callCount = 0
return fn
Expand Down
2 changes: 1 addition & 1 deletion lib/move/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const u = require('universalify').fromCallback
const u = require('universalify').fromPromise
module.exports = {
move: u(require('./move')),
moveSync: require('./move-sync')
Expand Down
89 changes: 36 additions & 53 deletions lib/move/move.js
Original file line number Diff line number Diff line change
@@ -1,76 +1,59 @@
'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(path.dirname(dest))
SukkaW marked this conversation as resolved.
Show resolved Hide resolved
}

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 = {
overwrite,
errorOnExist: true,
preserveTimestamps: true
}
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