Skip to content

Commit 289d9aa

Browse files
authored
move: do not create parent directory if it is root (#897)
* move: do not create parent directory if it is root * fix move-sync test * move-sync test: remove dest after test is done * remove debug log * remove dest in afterEach * call done() after test is done
1 parent e6f8cb4 commit 289d9aa

File tree

4 files changed

+65
-10
lines changed

4 files changed

+65
-10
lines changed

lib/move-sync/__tests__/move-sync.test.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const assert = require('assert')
1010

1111
/* global afterEach, beforeEach, describe, it */
1212

13+
const describeIfWindows = process.platform === 'win32' ? describe : describe.skip
14+
1315
function createSyncErrFn (errCode) {
1416
const fn = function () {
1517
const err = new Error()
@@ -120,7 +122,7 @@ describe('moveSync()', () => {
120122

121123
const contents = fs.readFileSync(dest, 'utf8')
122124
const expected = /^sonic the hedgehog\r?\n$/
123-
assert.ok(contents.match(expected), `${contents} match ${expected}`)
125+
assert.ok(contents.match(expected))
124126
})
125127

126128
it('should overwrite the destination directory if overwrite = true', () => {
@@ -268,6 +270,23 @@ describe('moveSync()', () => {
268270
})
269271
})
270272

273+
describeIfWindows('> when dest parent is root', () => {
274+
let dest
275+
276+
afterEach(() => fse.removeSync(dest))
277+
278+
it('should not create parent directory', () => {
279+
const src = path.join(TEST_DIR, 'a-file')
280+
dest = path.join(path.parse(TEST_DIR).root, 'another-file')
281+
282+
fse.moveSync(src, dest)
283+
284+
const contents = fs.readFileSync(dest, 'utf8')
285+
const expected = /^sonic the hedgehog\r?\n$/
286+
assert(contents.match(expected))
287+
})
288+
})
289+
271290
describe('> when actually trying to move a folder across devices', () => {
272291
const differentDevice = '/mnt'
273292
let __skipTests = false

lib/move-sync/move-sync.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@ function moveSync (src, dest, opts) {
1313

1414
const { srcStat, isChangingCase = false } = stat.checkPathsSync(src, dest, 'move', opts)
1515
stat.checkParentPathsSync(src, srcStat, dest, 'move')
16-
mkdirpSync(path.dirname(dest))
16+
if (!isParentRoot(dest)) mkdirpSync(path.dirname(dest))
1717
return doRename(src, dest, overwrite, isChangingCase)
1818
}
1919

20+
function isParentRoot (dest) {
21+
const parent = path.dirname(dest)
22+
const parsedPath = path.parse(parent)
23+
return parsedPath.root === parent
24+
}
25+
2026
function doRename (src, dest, overwrite, isChangingCase) {
2127
if (isChangingCase) return rename(src, dest, overwrite)
2228
if (overwrite) {

lib/move/__tests__/move.test.js

+31-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const assert = require('assert')
88

99
/* global afterEach, beforeEach, describe, it */
1010

11+
const describeIfWindows = process.platform === 'win32' ? describe : describe.skip
12+
1113
function createAsyncErrFn (errCode) {
1214
const fn = function (...args) {
1315
fn.callCount++
@@ -63,7 +65,7 @@ describe('+ move()', () => {
6365
fs.readFile(dest, 'utf8', (err, contents) => {
6466
const expected = /^sonic the hedgehog\r?\n$/
6567
assert.ifError(err)
66-
assert.ok(contents.match(expected), `${contents} match ${expected}`)
68+
assert.ok(contents.match(expected))
6769
done()
6870
})
6971
})
@@ -114,7 +116,7 @@ describe('+ move()', () => {
114116
fs.readFile(path.join(dest, 'another-folder', 'file3'), 'utf8', (err, contents) => {
115117
const expected = /^knuckles\r?\n$/
116118
assert.ifError(err)
117-
assert.ok(contents.match(expected), `${contents} match ${expected}`)
119+
assert.ok(contents.match(expected))
118120
tearDownMockFs()
119121
done()
120122
})
@@ -132,7 +134,7 @@ describe('+ move()', () => {
132134
fs.readFile(dest, 'utf8', (err, contents) => {
133135
const expected = /^sonic the hedgehog\r?\n$/
134136
assert.ifError(err)
135-
assert.ok(contents.match(expected), `${contents} match ${expected}`)
137+
assert.ok(contents.match(expected))
136138
done()
137139
})
138140
})
@@ -206,7 +208,7 @@ describe('+ move()', () => {
206208
fs.readFile(dest, 'utf8', (err, contents) => {
207209
const expected = /^sonic the hedgehog\r?\n$/
208210
assert.ifError(err)
209-
assert.ok(contents.match(expected), `${contents} match ${expected}`)
211+
assert.ok(contents.match(expected))
210212
done()
211213
})
212214
})
@@ -225,7 +227,7 @@ describe('+ move()', () => {
225227
fs.readFile(dest, 'utf8', (err, contents) => {
226228
const expected = /^sonic the hedgehog\r?\n$/
227229
assert.ifError(err)
228-
assert.ok(contents.match(expected), `${contents} match ${expected}`)
230+
assert.ok(contents.match(expected))
229231
tearDownMockFs()
230232
done()
231233
})
@@ -244,7 +246,7 @@ describe('+ move()', () => {
244246
fs.readFile(path.join(dest, 'another-file'), 'utf8', (err, contents) => {
245247
const expected = /^tails\r?\n$/
246248
assert.ifError(err)
247-
assert.ok(contents.match(expected), `${contents} match ${expected}`)
249+
assert.ok(contents.match(expected))
248250
done()
249251
})
250252
})
@@ -263,14 +265,35 @@ describe('+ move()', () => {
263265
fs.readFile(path.join(dest, 'another-folder', 'file3'), 'utf8', (err, contents) => {
264266
const expected = /^knuckles\r?\n$/
265267
assert.ifError(err)
266-
assert.ok(contents.match(expected), `${contents} match ${expected}`)
268+
assert.ok(contents.match(expected))
267269
tearDownMockFs()
268270
done()
269271
})
270272
})
271273
})
272274
})
273275

276+
describeIfWindows('> when dest parent is root', () => {
277+
let dest
278+
279+
afterEach(done => fse.remove(dest, done))
280+
281+
it('should not create parent directory', done => {
282+
const src = path.join(TEST_DIR, 'a-file')
283+
dest = path.join(path.parse(TEST_DIR).root, 'another-file')
284+
285+
fse.move(src, dest, err => {
286+
assert.ifError(err)
287+
fs.readFile(dest, 'utf8', (err, contents) => {
288+
const expected = /^sonic the hedgehog\r?\n$/
289+
assert.ifError(err)
290+
assert.ok(contents.match(expected))
291+
done()
292+
})
293+
})
294+
})
295+
})
296+
274297
describe('> clobber', () => {
275298
it('should be an alias for overwrite', done => {
276299
const src = path.join(TEST_DIR, 'a-file')
@@ -284,7 +307,7 @@ describe('+ move()', () => {
284307
fs.readFile(dest, 'utf8', (err, contents) => {
285308
const expected = /^sonic the hedgehog\r?\n$/
286309
assert.ifError(err)
287-
assert.ok(contents.match(expected), `${contents} match ${expected}`)
310+
assert.ok(contents.match(expected))
288311
done()
289312
})
290313
})

lib/move/move.js

+7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ function move (src, dest, opts, cb) {
2121
const { srcStat, isChangingCase = false } = stats
2222
stat.checkParentPaths(src, srcStat, dest, 'move', err => {
2323
if (err) return cb(err)
24+
if (isParentRoot(dest)) return doRename(src, dest, overwrite, isChangingCase, cb)
2425
mkdirp(path.dirname(dest), err => {
2526
if (err) return cb(err)
2627
return doRename(src, dest, overwrite, isChangingCase, cb)
@@ -29,6 +30,12 @@ function move (src, dest, opts, cb) {
2930
})
3031
}
3132

33+
function isParentRoot (dest) {
34+
const parent = path.dirname(dest)
35+
const parsedPath = path.parse(parent)
36+
return parsedPath.root === parent
37+
}
38+
3239
function doRename (src, dest, overwrite, isChangingCase, cb) {
3340
if (isChangingCase) return rename(src, dest, overwrite, cb)
3441
if (overwrite) {

0 commit comments

Comments
 (0)