diff --git a/lib/move-sync/__tests__/move-sync-case-insensitive-paths.test.js b/lib/move-sync/__tests__/move-sync-case-insensitive-paths.test.js index 32600b888..4cf29e1b6 100644 --- a/lib/move-sync/__tests__/move-sync-case-insensitive-paths.test.js +++ b/lib/move-sync/__tests__/move-sync-case-insensitive-paths.test.js @@ -4,7 +4,6 @@ const assert = require('assert') const os = require('os') const path = require('path') const fs = require('../../') -const platform = os.platform() /* global beforeEach, afterEach, describe, it */ @@ -21,104 +20,56 @@ describe('+ moveSync() - case insensitive paths', () => { afterEach(() => fs.removeSync(TEST_DIR)) describe('> when src is a directory', () => { - it('should behave correctly based on the OS', () => { + it('should move successfully', () => { src = path.join(TEST_DIR, 'srcdir') fs.outputFileSync(path.join(src, 'subdir', 'file.txt'), 'some data') dest = path.join(TEST_DIR, 'srcDir') - let errThrown = false - try { - fs.moveSync(src, dest) - } catch (err) { - if (platform === 'darwin' || platform === 'win32') { - assert.strictEqual(err.message, 'Source and destination must not be the same.') - errThrown = true - } - } - if (platform === 'darwin' || platform === 'win32') assert(errThrown) - if (platform === 'linux') { - assert(fs.existsSync(dest)) - assert.strictEqual(fs.readFileSync(path.join(dest, 'subdir', 'file.txt'), 'utf8'), 'some data') - assert(!errThrown) - } + fs.moveSync(src, dest) + assert(fs.existsSync(dest)) + assert.strictEqual(fs.readFileSync(path.join(dest, 'subdir', 'file.txt'), 'utf8'), 'some data') }) }) describe('> when src is a file', () => { - it('should behave correctly based on the OS', () => { + it('should move successfully', () => { src = path.join(TEST_DIR, 'srcfile') fs.outputFileSync(src, 'some data') dest = path.join(TEST_DIR, 'srcFile') - let errThrown = false - try { - fs.moveSync(src, dest) - } catch (err) { - if (platform === 'darwin' || platform === 'win32') { - assert.strictEqual(err.message, 'Source and destination must not be the same.') - errThrown = true - } - } - if (platform === 'darwin' || platform === 'win32') assert(errThrown) - if (platform === 'linux') { - assert(fs.existsSync(dest)) - assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'some data') - assert(!errThrown) - } + fs.moveSync(src, dest) + assert(fs.existsSync(dest)) + assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'some data') }) }) describe('> when src is a symlink', () => { - it('should behave correctly based on the OS, symlink dir', () => { + it('should move successfully, symlink dir', () => { src = path.join(TEST_DIR, 'srcdir') fs.outputFileSync(path.join(src, 'subdir', 'file.txt'), 'some data') const srcLink = path.join(TEST_DIR, 'src-symlink') fs.symlinkSync(src, srcLink, 'dir') dest = path.join(TEST_DIR, 'src-Symlink') - let errThrown = false - try { - fs.moveSync(srcLink, dest) - } catch (err) { - if (platform === 'darwin' || platform === 'win32') { - assert.strictEqual(err.message, 'Source and destination must not be the same.') - errThrown = true - } - } - if (platform === 'darwin' || platform === 'win32') assert(errThrown) - if (platform === 'linux') { - assert(fs.existsSync(dest)) - assert.strictEqual(fs.readFileSync(path.join(dest, 'subdir', 'file.txt'), 'utf8'), 'some data') - const destLink = fs.readlinkSync(dest) - assert.strictEqual(destLink, src) - assert(!errThrown) - } + fs.moveSync(srcLink, dest) + assert(fs.existsSync(dest)) + assert.strictEqual(fs.readFileSync(path.join(dest, 'subdir', 'file.txt'), 'utf8'), 'some data') + const destLink = fs.readlinkSync(dest) + assert.strictEqual(destLink, src) }) - it('should behave correctly based on the OS, symlink file', () => { + it('should move successfully, symlink file', () => { src = path.join(TEST_DIR, 'srcfile') fs.outputFileSync(src, 'some data') const srcLink = path.join(TEST_DIR, 'src-symlink') fs.symlinkSync(src, srcLink, 'file') dest = path.join(TEST_DIR, 'src-Symlink') - let errThrown = false - try { - fs.moveSync(srcLink, dest) - } catch (err) { - if (platform === 'darwin' || platform === 'win32') { - assert.strictEqual(err.message, 'Source and destination must not be the same.') - errThrown = true - } - } - if (platform === 'darwin' || platform === 'win32') assert(errThrown) - if (platform === 'linux') { - assert(fs.existsSync(dest)) - assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'some data') - const destLink = fs.readlinkSync(dest) - assert.strictEqual(destLink, src) - assert(!errThrown) - } + fs.moveSync(srcLink, dest) + assert(fs.existsSync(dest)) + assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'some data') + const destLink = fs.readlinkSync(dest) + assert.strictEqual(destLink, src) }) }) }) diff --git a/lib/move-sync/move-sync.js b/lib/move-sync/move-sync.js index 40244dcf9..7e13587af 100644 --- a/lib/move-sync/move-sync.js +++ b/lib/move-sync/move-sync.js @@ -11,13 +11,14 @@ function moveSync (src, dest, opts) { opts = opts || {} const overwrite = opts.overwrite || opts.clobber || false - const { srcStat } = stat.checkPathsSync(src, dest, 'move', opts) + const { srcStat, isChangingCase = false } = stat.checkPathsSync(src, dest, 'move', opts) stat.checkParentPathsSync(src, srcStat, dest, 'move') mkdirpSync(path.dirname(dest)) - return doRename(src, dest, overwrite) + return doRename(src, dest, overwrite, isChangingCase) } -function doRename (src, dest, overwrite) { +function doRename (src, dest, overwrite, isChangingCase) { + if (isChangingCase) return rename(src, dest, overwrite) if (overwrite) { removeSync(dest) return rename(src, dest, overwrite) diff --git a/lib/move/__tests__/move-case-insensitive-paths.test.js b/lib/move/__tests__/move-case-insensitive-paths.test.js index 307632493..3e85f11b6 100644 --- a/lib/move/__tests__/move-case-insensitive-paths.test.js +++ b/lib/move/__tests__/move-case-insensitive-paths.test.js @@ -4,7 +4,6 @@ const assert = require('assert') const os = require('os') const path = require('path') const fs = require('../../') -const platform = os.platform() /* global beforeEach, afterEach, describe, it */ @@ -21,47 +20,37 @@ describe('+ move() - case insensitive paths', () => { afterEach(done => fs.remove(TEST_DIR, done)) describe('> when src is a directory', () => { - it('should behave correctly based on the OS', done => { + it('should move successfully', done => { src = path.join(TEST_DIR, 'srcdir') fs.outputFileSync(path.join(src, 'subdir', 'file.txt'), 'some data') dest = path.join(TEST_DIR, 'srcDir') fs.move(src, dest, err => { - if (platform === 'linux') { - assert.ifError(err) - assert(fs.existsSync(dest)) - assert.strictEqual(fs.readFileSync(path.join(dest, 'subdir', 'file.txt'), 'utf8'), 'some data') - } - if (platform === 'darwin' || platform === 'win32') { - assert.strictEqual(err.message, 'Source and destination must not be the same.') - } + assert.ifError(err) + assert(fs.existsSync(dest)) + assert.strictEqual(fs.readFileSync(path.join(dest, 'subdir', 'file.txt'), 'utf8'), 'some data') done() }) }) }) describe('> when src is a file', () => { - it('should behave correctly based on the OS', done => { + it('should move successfully', done => { src = path.join(TEST_DIR, 'srcfile') fs.outputFileSync(src, 'some data') dest = path.join(TEST_DIR, 'srcFile') fs.move(src, dest, err => { - if (platform === 'linux') { - assert.ifError(err) - assert(fs.existsSync(dest)) - assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'some data') - } - if (platform === 'darwin' || platform === 'win32') { - assert.strictEqual(err.message, 'Source and destination must not be the same.') - } + assert.ifError(err) + assert(fs.existsSync(dest)) + assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'some data') done() }) }) }) describe('> when src is a symlink', () => { - it('should behave correctly based on the OS, symlink dir', done => { + it('should move successfully, symlink dir', done => { src = path.join(TEST_DIR, 'srcdir') fs.outputFileSync(path.join(src, 'subdir', 'file.txt'), 'some data') const srcLink = path.join(TEST_DIR, 'src-symlink') @@ -69,21 +58,16 @@ describe('+ move() - case insensitive paths', () => { dest = path.join(TEST_DIR, 'src-Symlink') fs.move(srcLink, dest, err => { - if (platform === 'linux') { - assert.ifError(err) - assert(fs.existsSync(dest)) - assert.strictEqual(fs.readFileSync(path.join(dest, 'subdir', 'file.txt'), 'utf8'), 'some data') - const destLink = fs.readlinkSync(dest) - assert.strictEqual(destLink, src) - } - if (platform === 'darwin' || platform === 'win32') { - assert.strictEqual(err.message, 'Source and destination must not be the same.') - } + assert.ifError(err) + assert(fs.existsSync(dest)) + assert.strictEqual(fs.readFileSync(path.join(dest, 'subdir', 'file.txt'), 'utf8'), 'some data') + const destLink = fs.readlinkSync(dest) + assert.strictEqual(destLink, src) done() }) }) - it('should behave correctly based on the OS, symlink file', done => { + it('should move successfully, symlink file', done => { src = path.join(TEST_DIR, 'srcfile') fs.outputFileSync(src, 'some data') const srcLink = path.join(TEST_DIR, 'src-symlink') @@ -91,16 +75,11 @@ describe('+ move() - case insensitive paths', () => { dest = path.join(TEST_DIR, 'src-Symlink') fs.move(srcLink, dest, err => { - if (platform === 'linux') { - assert.ifError(err) - assert(fs.existsSync(dest)) - assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'some data') - const destLink = fs.readlinkSync(dest) - assert.strictEqual(destLink, src) - } - if (platform === 'darwin' || platform === 'win32') { - assert.strictEqual(err.message, 'Source and destination must not be the same.') - } + assert.ifError(err) + assert(fs.existsSync(dest)) + assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'some data') + const destLink = fs.readlinkSync(dest) + assert.strictEqual(destLink, src) done() }) }) diff --git a/lib/move/move.js b/lib/move/move.js index 4687c6ab7..23840e027 100644 --- a/lib/move/move.js +++ b/lib/move/move.js @@ -18,18 +18,19 @@ function move (src, dest, opts, cb) { stat.checkPaths(src, dest, 'move', opts, (err, stats) => { if (err) return cb(err) - const { srcStat } = stats + const { srcStat, isChangingCase = false } = stats stat.checkParentPaths(src, srcStat, dest, 'move', err => { if (err) return cb(err) mkdirp(path.dirname(dest), err => { if (err) return cb(err) - return doRename(src, dest, overwrite, cb) + return doRename(src, dest, overwrite, isChangingCase, cb) }) }) }) } -function doRename (src, dest, overwrite, cb) { +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) diff --git a/lib/util/stat.js b/lib/util/stat.js index 7927900c7..c278e95f1 100644 --- a/lib/util/stat.js +++ b/lib/util/stat.js @@ -42,6 +42,13 @@ function checkPaths (src, dest, funcName, opts, cb) { 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.')) } if (srcStat.isDirectory() && !destStat.isDirectory()) { @@ -64,6 +71,13 @@ function checkPathsSync (src, dest, funcName, 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 } + } throw new Error('Source and destination must not be the same.') } if (srcStat.isDirectory() && !destStat.isDirectory()) {