From 7701ef2406b9ec5162a4d72ccca407fd8d873419 Mon Sep 17 00:00:00 2001 From: Mani Maghsoudlou Date: Tue, 7 Aug 2018 04:01:51 -0700 Subject: [PATCH] fix ensureDir() doc --- docs/copy-sync.md | 2 +- docs/copy.md | 2 +- docs/emptyDir.md | 2 +- docs/ensureFile.md | 2 +- docs/ensureLink.md | 2 +- docs/ensureSymlink-sync.md | 2 +- docs/ensureSymlink.md | 2 +- docs/move-sync.md | 2 +- docs/move.md | 2 +- docs/outputFile-sync.md | 2 +- docs/outputFile.md | 2 +- docs/outputJson-sync.md | 2 +- docs/outputJson.md | 2 +- docs/readJson-sync.md | 2 +- docs/readJson.md | 2 +- docs/remove.md | 2 +- docs/writeJson-sync.md | 2 +- docs/writeJson.md | 2 +- .../copy-without-checking-paths.test.js | 295 ++++++++++++++++++ 19 files changed, 313 insertions(+), 18 deletions(-) create mode 100644 lib/copy/__tests__/copy-without-checking-paths.test.js diff --git a/docs/copy-sync.md b/docs/copy-sync.md index 76bfd477..3fca1330 100644 --- a/docs/copy-sync.md +++ b/docs/copy-sync.md @@ -1,4 +1,4 @@ -# copySync(src, dest, [options]) +# copySync(src, dest[, options]) Copy a file or directory. The directory can have contents. Like `cp -r`. diff --git a/docs/copy.md b/docs/copy.md index e5f8582b..4add5684 100644 --- a/docs/copy.md +++ b/docs/copy.md @@ -1,4 +1,4 @@ -# copy(src, dest, [options, callback]) +# copy(src, dest[, options][, callback]) Copy a file or directory. The directory can have contents. Like `cp -r`. diff --git a/docs/emptyDir.md b/docs/emptyDir.md index 3d129d42..8f26dc10 100644 --- a/docs/emptyDir.md +++ b/docs/emptyDir.md @@ -1,4 +1,4 @@ -# emptyDir(dir, [callback]) +# emptyDir(dir[, callback]) Ensures that a directory is empty. Deletes directory contents if the directory is not empty. If the directory does not exist, it is created. The directory itself is not deleted. diff --git a/docs/ensureFile.md b/docs/ensureFile.md index aa8e82ac..a560e966 100644 --- a/docs/ensureFile.md +++ b/docs/ensureFile.md @@ -1,4 +1,4 @@ -# ensureFile(file, [callback]) +# ensureFile(file[, callback]) Ensures that the file exists. If the file that is requested to be created is in directories that do not exist, these directories are created. If the file already exists, it is **NOT MODIFIED**. diff --git a/docs/ensureLink.md b/docs/ensureLink.md index 3f39f07b..7bdddebf 100644 --- a/docs/ensureLink.md +++ b/docs/ensureLink.md @@ -1,4 +1,4 @@ -# ensureLink(srcpath, dstpath, [callback]) +# ensureLink(srcpath, dstpath[, callback]) Ensures that the link exists. If the directory structure does not exist, it is created. diff --git a/docs/ensureSymlink-sync.md b/docs/ensureSymlink-sync.md index 328d4c45..72b39c29 100644 --- a/docs/ensureSymlink-sync.md +++ b/docs/ensureSymlink-sync.md @@ -1,4 +1,4 @@ -# ensureSymlinkSync(srcpath, dstpath, [type]) +# ensureSymlinkSync(srcpath, dstpath[, type]) Ensures that the symlink exists. If the directory structure does not exist, it is created. diff --git a/docs/ensureSymlink.md b/docs/ensureSymlink.md index 39c09568..383c49f0 100644 --- a/docs/ensureSymlink.md +++ b/docs/ensureSymlink.md @@ -1,4 +1,4 @@ -# ensureSymlink(srcpath, dstpath, [type, callback]) +# ensureSymlink(srcpath, dstpath[, type][, callback]) Ensures that the symlink exists. If the directory structure does not exist, it is created. diff --git a/docs/move-sync.md b/docs/move-sync.md index cd701fef..d5153b58 100644 --- a/docs/move-sync.md +++ b/docs/move-sync.md @@ -1,4 +1,4 @@ -# moveSync(src, dest, [options]) +# moveSync(src, dest[, options]) Moves a file or directory, even across devices. diff --git a/docs/move.md b/docs/move.md index 343d10be..5f70853e 100644 --- a/docs/move.md +++ b/docs/move.md @@ -1,4 +1,4 @@ -# move(src, dest, [options, callback]) +# move(src, dest[, options][, callback]) Moves a file or directory, even across devices. diff --git a/docs/outputFile-sync.md b/docs/outputFile-sync.md index 38eee8b6..fc7abdf9 100644 --- a/docs/outputFile-sync.md +++ b/docs/outputFile-sync.md @@ -1,4 +1,4 @@ -# outputFileSync(file, data, [options]) +# outputFileSync(file, data[, options]) Almost the same as `writeFileSync` (i.e. it [overwrites](http://pages.citebite.com/v2o5n8l2f5reb)), except that if the parent directory does not exist, it's created. `file` must be a file path (a buffer or a file descriptor is not allowed). `options` are what you'd pass to [`fs.writeFileSync()`](https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options). diff --git a/docs/outputFile.md b/docs/outputFile.md index 34efdc94..ed9c19d6 100644 --- a/docs/outputFile.md +++ b/docs/outputFile.md @@ -1,4 +1,4 @@ -# outputFile(file, data, [options, callback]) +# outputFile(file, data[, options][, callback]) Almost the same as `writeFile` (i.e. it [overwrites](http://pages.citebite.com/v2o5n8l2f5reb)), except that if the parent directory does not exist, it's created. `file` must be a file path (a buffer or a file descriptor is not allowed). `options` are what you'd pass to [`fs.writeFile()`](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback). diff --git a/docs/outputJson-sync.md b/docs/outputJson-sync.md index ef78f802..c42497cf 100644 --- a/docs/outputJson-sync.md +++ b/docs/outputJson-sync.md @@ -1,4 +1,4 @@ -# outputJsonSync(file, object, [options]) +# outputJsonSync(file, object[, options]) Almost the same as [`writeJsonSync`](writeJson-sync.md), except that if the directory does not exist, it's created. diff --git a/docs/outputJson.md b/docs/outputJson.md index aa265aee..d5a6d7c7 100644 --- a/docs/outputJson.md +++ b/docs/outputJson.md @@ -1,4 +1,4 @@ -# outputJson(file, object, [options, callback]) +# outputJson(file, object[, options][, callback]) Almost the same as [`writeJson`](writeJson.md), except that if the directory does not exist, it's created. diff --git a/docs/readJson-sync.md b/docs/readJson-sync.md index a1356379..1fc6519d 100644 --- a/docs/readJson-sync.md +++ b/docs/readJson-sync.md @@ -1,4 +1,4 @@ -# readJsonSync(file, [options]) +# readJsonSync(file[, options]) Reads a JSON file and then parses it into an object. `options` are the same that you'd pass to [`jsonFile.readFileSync`](https://github.com/jprichardson/node-jsonfile#readfilesyncfilename-options). diff --git a/docs/readJson.md b/docs/readJson.md index baf80ac8..881e7972 100644 --- a/docs/readJson.md +++ b/docs/readJson.md @@ -1,4 +1,4 @@ -# readJson(file, [options, callback]) +# readJson(file[, options][, callback]) Reads a JSON file and then parses it into an object. `options` are the same that you'd pass to [`jsonFile.readFile`](https://github.com/jprichardson/node-jsonfile#readfilefilename-options-callback). diff --git a/docs/remove.md b/docs/remove.md index bcc49e39..0cdcf7ac 100644 --- a/docs/remove.md +++ b/docs/remove.md @@ -1,4 +1,4 @@ -# remove(path, [callback]) +# remove(path[, callback]) Removes a file or directory. The directory can have contents. Like `rm -rf`. diff --git a/docs/writeJson-sync.md b/docs/writeJson-sync.md index c22459db..b98e0c7d 100644 --- a/docs/writeJson-sync.md +++ b/docs/writeJson-sync.md @@ -1,4 +1,4 @@ -# writeJsonSync(file, object, [options]) +# writeJsonSync(file, object[, options]) Writes an object to a JSON file. diff --git a/docs/writeJson.md b/docs/writeJson.md index 56278029..216d1348 100644 --- a/docs/writeJson.md +++ b/docs/writeJson.md @@ -1,4 +1,4 @@ -# writeJson(file, object, [options, callback]) +# writeJson(file, object[, options][, callback]) Writes an object to a JSON file. diff --git a/lib/copy/__tests__/copy-without-checking-paths.test.js b/lib/copy/__tests__/copy-without-checking-paths.test.js new file mode 100644 index 00000000..68c394c3 --- /dev/null +++ b/lib/copy/__tests__/copy-without-checking-paths.test.js @@ -0,0 +1,295 @@ +'use strict' + +const assert = require('assert') +const os = require('os') +const path = require('path') +const fs = require('../../') +const klawSync = require('klaw-sync') + +/* global beforeEach, afterEach, describe, it */ + +const FILES = [ + 'file0.txt', + path.join('dir1', 'file1.txt'), + path.join('dir1', 'dir2', 'file2.txt'), + path.join('dir1', 'dir2', 'dir3', 'file3.txt') +] + +const dat0 = 'file0' +const dat1 = 'file1' +const dat2 = 'file2' +const dat3 = 'file3' + +describe('+ copy() - without checking paths', () => { + let TEST_DIR, src + + beforeEach(done => { + TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-without-checking-paths') + src = path.join(TEST_DIR, 'src') + fs.mkdirpSync(src) + + fs.outputFileSync(path.join(src, FILES[0]), dat0) + fs.outputFileSync(path.join(src, FILES[1]), dat1) + fs.outputFileSync(path.join(src, FILES[2]), dat2) + fs.outputFileSync(path.join(src, FILES[3]), dat3) + done() + }) + + afterEach(done => fs.remove(TEST_DIR, done)) + + describe('> when source is a file', () => { + it('should copy the file successfully even if dest parent is a subdir of src', done => { + const srcFile = path.join(TEST_DIR, 'src', 'srcfile.txt') + const destFile = path.join(TEST_DIR, 'src', 'dest', 'destfile.txt') + fs.writeFileSync(srcFile, dat0) + + fs.copy(srcFile, destFile, { checkPathsBeforeCopying: false }, err => { + assert.ifError(err) + + assert(fs.existsSync(destFile)) + const out = fs.readFileSync(destFile, 'utf8') + assert.strictEqual(out, dat0) + done() + }) + }) + }) + + // test for these cases: + // - src is directory, dest is directory + // - src is directory, dest is symlink + // - src is symlink, dest is directory + // - src is symlink, dest is symlink + + describe('> when source is a directory', () => { + describe('>> when dest is a directory', () => { + it(`of not itself`, done => { + const dest = path.join(TEST_DIR, src.replace(/^\w:/, '')) + return testSuccess(src, dest, done) + }) + it(`should copy the directory successfully when dest is 'src_dest'`, done => { + const dest = path.join(TEST_DIR, 'src_dest') + return testSuccess(src, dest, done) + }) + it(`should copy the directory successfully when dest is 'src-dest'`, done => { + const dest = path.join(TEST_DIR, 'src-dest') + return testSuccess(src, dest, done) + }) + + it(`should copy the directory successfully when dest is 'dest_src'`, done => { + const dest = path.join(TEST_DIR, 'dest_src') + return testSuccess(src, dest, done) + }) + + it(`should copy the directory successfully when dest is 'src_dest/src'`, done => { + const dest = path.join(TEST_DIR, 'src_dest', 'src') + return testSuccess(src, dest, done) + }) + + it(`should copy the directory successfully when dest is 'src-dest/src'`, done => { + const dest = path.join(TEST_DIR, 'src-dest', 'src') + return testSuccess(src, dest, done) + }) + + it(`should copy the directory successfully when dest is 'dest_src/src'`, done => { + const dest = path.join(TEST_DIR, 'dest_src', 'src') + return testSuccess(src, dest, done) + }) + + it(`should copy the directory successfully when dest is 'src_src/dest'`, done => { + const dest = path.join(TEST_DIR, 'src_src', 'dest') + return testSuccess(src, dest, done) + }) + + it(`should copy the directory successfully when dest is 'src-src/dest'`, done => { + const dest = path.join(TEST_DIR, 'src-src', 'dest') + return testSuccess(src, dest, done) + }) + + it(`should copy the directory successfully when dest is 'srcsrc/dest'`, done => { + const dest = path.join(TEST_DIR, 'srcsrc', 'dest') + return testSuccess(src, dest, done) + }) + + it(`should copy the directory successfully when dest is 'dest/src'`, done => { + const dest = path.join(TEST_DIR, 'dest', 'src') + return testSuccess(src, dest, done) + }) + + it('should copy the directory successfully when dest is very nested that all its parents need to be created', done => { + const dest = path.join(TEST_DIR, 'dest', 'src', 'foo', 'bar', 'baz', 'qux', 'quux', 'waldo', + 'grault', 'garply', 'fred', 'plugh', 'thud', 'some', 'highly', 'deeply', + 'badly', 'nasty', 'crazy', 'mad', 'nested', 'dest') + return testSuccess(src, dest, done) + }) + }) + + describe('>> when dest is a symlink', () => { + it('should copy the directory successfully when src is a subdir of resolved dest path', done => { + const srcInDest = path.join(TEST_DIR, 'dest', 'some', 'nested', 'src') + const destLink = path.join(TEST_DIR, 'dest-symlink') + fs.copySync(src, srcInDest) // put some stuff in srcInDest + + const dest = path.join(TEST_DIR, 'dest') + fs.symlinkSync(dest, destLink, 'dir') + + const srclen = klawSync(srcInDest).length + const destlenBefore = klawSync(dest).length + + assert(srclen > 2) + fs.copy(srcInDest, destLink, { checkPathsBeforeCopying: false }, err => { + assert.ifError(err) + + const destlenAfter = klawSync(dest).length + + // assert dest length is oldlen + length of stuff copied from src + assert.strictEqual(destlenAfter, destlenBefore + srclen, 'dest length should be equal to old length + copied legnth') + + FILES.forEach(f => assert(fs.existsSync(path.join(dest, f)))) + + const o0 = fs.readFileSync(path.join(dest, FILES[0]), 'utf8') + const o1 = fs.readFileSync(path.join(dest, FILES[1]), 'utf8') + const o2 = fs.readFileSync(path.join(dest, FILES[2]), 'utf8') + const o3 = fs.readFileSync(path.join(dest, FILES[3]), 'utf8') + + assert.strictEqual(o0, dat0) + assert.strictEqual(o1, dat1) + assert.strictEqual(o2, dat2) + assert.strictEqual(o3, dat3) + done() + }) + }) + }) + }) + + describe('> when source is a symlink', () => { + describe('>> when dest is a directory', () => { + it('should error when resolved src path points to dest', done => { + const srcLink = path.join(TEST_DIR, 'src-symlink') + fs.symlinkSync(src, srcLink, 'dir') + + const dest = path.join(TEST_DIR, 'src') + + fs.copy(srcLink, dest, { checkPathsBeforeCopying: false }, err => { + assert(err) + // assert source not affected + const link = fs.readlinkSync(srcLink) + assert.strictEqual(link, src) + done() + }) + }) + + it('should error when dest is a subdir of resolved src path', done => { + const srcLink = path.join(TEST_DIR, 'src-symlink') + fs.symlinkSync(src, srcLink, 'dir') + + const dest = path.join(TEST_DIR, 'src', 'some', 'nested', 'dest') + fs.mkdirsSync(dest) + + fs.copy(srcLink, dest, { checkPathsBeforeCopying: false }, err => { + assert(err) + const link = fs.readlinkSync(srcLink) + assert.strictEqual(link, src) + done() + }) + }) + + it('should error when resolved src path is a subdir of dest', done => { + const dest = path.join(TEST_DIR, 'dest') + + const resolvedSrcPath = path.join(dest, 'contains', 'src') + const srcLink = path.join(TEST_DIR, 'src-symlink') + fs.copySync(src, resolvedSrcPath) + + // make symlink that points to a subdir in dest + fs.symlinkSync(resolvedSrcPath, srcLink, 'dir') + + fs.copy(srcLink, dest, { checkPathsBeforeCopying: false }, err => { + assert(err) + done() + }) + }) + + it(`should copy the directory successfully when dest is 'src_src/dest'`, done => { + const srcLink = path.join(TEST_DIR, 'src-symlink') + fs.symlinkSync(src, srcLink, 'dir') + + const dest = path.join(TEST_DIR, 'src_src', 'dest') + testSuccess(srcLink, dest, () => { + const link = fs.readlinkSync(dest) + assert.strictEqual(link, src) + done() + }) + }) + + it(`should copy the directory successfully when dest is 'srcsrc/dest'`, done => { + const srcLink = path.join(TEST_DIR, 'src-symlink') + fs.symlinkSync(src, srcLink, 'dir') + + const dest = path.join(TEST_DIR, 'srcsrc', 'dest') + testSuccess(srcLink, dest, () => { + const link = fs.readlinkSync(dest) + assert.strictEqual(link, src) + done() + }) + }) + }) + + describe('>> when dest is a symlink', () => { + it('should error when resolved dest path is a subdir of resolved src path', done => { + const srcLink = path.join(TEST_DIR, 'src-symlink') + fs.symlinkSync(src, srcLink, 'dir') + + const destLink = path.join(TEST_DIR, 'dest-symlink') + const resolvedDestPath = path.join(TEST_DIR, 'src', 'some', 'nested', 'dest') + fs.ensureFileSync(path.join(resolvedDestPath, 'subdir', 'somefile.txt')) + fs.symlinkSync(resolvedDestPath, destLink, 'dir') + + fs.copy(srcLink, destLink, { checkPathsBeforeCopying: false }, err => { + assert.strictEqual(err.message, `Cannot copy '${src}' to a subdirectory of itself, '${resolvedDestPath}'.`) + const destln = fs.readlinkSync(destLink) + assert.strictEqual(destln, resolvedDestPath) + done() + }) + }) + + it('should error when resolved src path is a subdir of resolved dest path', done => { + const srcInDest = path.join(TEST_DIR, 'dest', 'some', 'nested', 'src') + const srcLink = path.join(TEST_DIR, 'src-symlink') + const destLink = path.join(TEST_DIR, 'dest-symlink') + const dest = path.join(TEST_DIR, 'dest') + + fs.ensureDirSync(srcInDest) + fs.ensureSymlinkSync(srcInDest, srcLink, 'dir') + fs.ensureSymlinkSync(dest, destLink, 'dir') + + fs.copy(srcLink, destLink, { checkPathsBeforeCopying: false }, err => { + assert.strictEqual(err.message, `Cannot overwrite '${dest}' with '${srcInDest}'.`) + const destln = fs.readlinkSync(destLink) + assert.strictEqual(destln, dest) + done() + }) + }) + }) + }) +}) + +function testSuccess (src, dest, done) { + const srclen = klawSync(src).length + assert(srclen > 2) + fs.copy(src, dest, { checkPathsBeforeCopying: false }, err => { + assert.ifError(err) + + FILES.forEach(f => assert(fs.existsSync(path.join(dest, f)))) + + const o0 = fs.readFileSync(path.join(dest, FILES[0]), 'utf8') + const o1 = fs.readFileSync(path.join(dest, FILES[1]), 'utf8') + const o2 = fs.readFileSync(path.join(dest, FILES[2]), 'utf8') + const o3 = fs.readFileSync(path.join(dest, FILES[3]), 'utf8') + + assert.strictEqual(o0, dat0) + assert.strictEqual(o1, dat1) + assert.strictEqual(o2, dat2) + assert.strictEqual(o3, dat3) + done() + }) +}