mirror of
https://github.com/shelljs/shelljs.git
synced 2026-01-18 16:03:37 +00:00
Merge pull request #380 from shelljs/fix-cp-r-option
refactor(cp): clean up code and fix #376
This commit is contained in:
commit
5f4d3901f2
102
src/cp.js
102
src/cp.js
@ -53,8 +53,8 @@ function cpdirSyncRecursive(sourceDir, destDir, opts) {
|
||||
if (!opts) opts = {};
|
||||
|
||||
/* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */
|
||||
var checkDir = fs.statSync(sourceDir);
|
||||
try {
|
||||
var checkDir = fs.statSync(sourceDir);
|
||||
fs.mkdirSync(destDir, checkDir.mode);
|
||||
} catch (e) {
|
||||
//if the directory already exists, that's okay
|
||||
@ -117,95 +117,63 @@ function _cp(options, sources, dest) {
|
||||
// Get sources, dest
|
||||
if (arguments.length < 3) {
|
||||
common.error('missing <source> and/or <dest>');
|
||||
} else if (arguments.length > 3) {
|
||||
} else {
|
||||
sources = [].slice.call(arguments, 1, arguments.length - 1);
|
||||
dest = arguments[arguments.length - 1];
|
||||
} else if (typeof sources === 'string') {
|
||||
sources = [sources];
|
||||
} else if ('length' in sources) {
|
||||
sources = sources; // no-op for array
|
||||
} else {
|
||||
common.error('invalid arguments');
|
||||
}
|
||||
|
||||
var exists = fs.existsSync(dest),
|
||||
stats = exists && fs.statSync(dest);
|
||||
var destExists = fs.existsSync(dest),
|
||||
destStat = destExists && fs.statSync(dest);
|
||||
|
||||
// Dest is not existing dir, but multiple sources given
|
||||
if ((!exists || !stats.isDirectory()) && sources.length > 1)
|
||||
if ((!destExists || !destStat.isDirectory()) && sources.length > 1)
|
||||
common.error('dest is not a directory (too many sources)');
|
||||
|
||||
// Dest is an existing file, but no -f given
|
||||
if (exists && stats.isFile() && options.no_force)
|
||||
common.error('dest file already exists: ' + dest);
|
||||
|
||||
if (options.recursive) {
|
||||
// Recursive allows the shortcut syntax "sourcedir/" for "sourcedir/*"
|
||||
// (see Github issue #15)
|
||||
sources.forEach(function(src, i) {
|
||||
if (src[src.length - 1] === '/') {
|
||||
sources[i] += '*';
|
||||
// If src is a directory and dest doesn't exist, 'cp -r src dest' should copy src/* into dest
|
||||
} else if (fs.statSync(src).isDirectory() && !exists) {
|
||||
sources[i] += '/*';
|
||||
}
|
||||
});
|
||||
|
||||
// Create dest
|
||||
try {
|
||||
fs.mkdirSync(dest, parseInt('0777', 8));
|
||||
} catch (e) {
|
||||
// like Unix's cp, keep going even if we can't create dest dir
|
||||
}
|
||||
}
|
||||
|
||||
sources = common.expand(sources, {dot: true});
|
||||
// Dest is an existing file, but -n is given
|
||||
if (destExists && destStat.isFile() && options.no_force)
|
||||
common.error('dest file already destExists: ' + dest);
|
||||
|
||||
sources.forEach(function(src) {
|
||||
if (!fs.existsSync(src)) {
|
||||
common.error('no such file or directory: '+src, true);
|
||||
return; // skip file
|
||||
}
|
||||
|
||||
// If here, src exists
|
||||
if (fs.statSync(src).isDirectory()) {
|
||||
var srcStat = fs.statSync(src);
|
||||
if (srcStat.isDirectory()) {
|
||||
if (!options.recursive) {
|
||||
// Non-Recursive
|
||||
common.log(src + ' is a directory (not copied)');
|
||||
common.error("omitting directory '" + src + "'", true);
|
||||
} else {
|
||||
// Recursive
|
||||
// 'cp /a/source dest' should create 'source' in 'dest'
|
||||
var newDest = path.join(dest, path.basename(src)),
|
||||
checkDir = fs.statSync(src);
|
||||
try {
|
||||
fs.mkdirSync(newDest, checkDir.mode);
|
||||
} catch (e) {
|
||||
//if the directory already exists, that's okay
|
||||
if (e.code !== 'EEXIST') {
|
||||
common.error('dest file no such file or directory: ' + newDest, true);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
var newDest = (destStat && destStat.isDirectory()) ?
|
||||
path.join(dest, path.basename(src)) :
|
||||
dest;
|
||||
|
||||
cpdirSyncRecursive(src, newDest, {no_force: options.no_force});
|
||||
try {
|
||||
fs.statSync(path.dirname(dest));
|
||||
cpdirSyncRecursive(src, newDest, {no_force: options.no_force});
|
||||
} catch(e) {
|
||||
common.error("cannot create directory '" + dest + "': No such file or directory");
|
||||
}
|
||||
}
|
||||
return; // done with dir
|
||||
} else {
|
||||
// If here, src is a file
|
||||
|
||||
// When copying to '/path/dir':
|
||||
// thisDest = '/path/dir/file1'
|
||||
var thisDest = dest;
|
||||
if (destStat && destStat.isDirectory())
|
||||
thisDest = path.normalize(dest + '/' + path.basename(src));
|
||||
|
||||
if (fs.existsSync(thisDest) && options.no_force) {
|
||||
common.error('dest file already destExists: ' + thisDest, true);
|
||||
return; // skip file
|
||||
}
|
||||
|
||||
copyFileSync(src, thisDest);
|
||||
}
|
||||
|
||||
// If here, src is a file
|
||||
|
||||
// When copying to '/path/dir':
|
||||
// thisDest = '/path/dir/file1'
|
||||
var thisDest = dest;
|
||||
if (fs.existsSync(dest) && fs.statSync(dest).isDirectory())
|
||||
thisDest = path.normalize(dest + '/' + path.basename(src));
|
||||
|
||||
if (fs.existsSync(thisDest) && options.no_force) {
|
||||
common.error('dest file already exists: ' + thisDest, true);
|
||||
return; // skip file
|
||||
}
|
||||
|
||||
copyFileSync(src, thisDest);
|
||||
}); // forEach(src)
|
||||
}
|
||||
module.exports = _cp;
|
||||
|
||||
39
test/cp.js
39
test/cp.js
@ -142,7 +142,7 @@ assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp/cp') + '')
|
||||
shell.rm('-rf', 'tmp/*');
|
||||
shell.cp('-R', 'resources/cp/', 'tmp/');
|
||||
assert.equal(shell.error(), null);
|
||||
assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp') + '');
|
||||
assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp/cp') + '');
|
||||
|
||||
// recursive, globbing regular files with extension (see Github issue #376)
|
||||
shell.rm('-rf', 'tmp/*');
|
||||
@ -151,6 +151,13 @@ assert.equal(shell.error(), null);
|
||||
assert.ok(fs.existsSync('tmp/file1.txt'));
|
||||
assert.ok(fs.existsSync('tmp/file2.txt'));
|
||||
|
||||
// recursive, copying one regular file (also related to Github issue #376)
|
||||
shell.rm('-rf', 'tmp/*');
|
||||
shell.cp('-R', 'resources/file1.txt', 'tmp');
|
||||
assert.equal(shell.error(), null);
|
||||
assert.ok(fs.existsSync('tmp/file1.txt'));
|
||||
assert.ok(!fs.statSync('tmp/file1.txt').isDirectory()); // don't let it be a dir
|
||||
|
||||
//recursive, everything exists, no force flag
|
||||
shell.rm('-rf', 'tmp/*');
|
||||
shell.cp('-R', 'resources/cp', 'tmp');
|
||||
@ -179,12 +186,18 @@ shell.cp('-r', 'resources/issue44', 'tmp/dir2/dir3');
|
||||
assert.ok(shell.error());
|
||||
assert.equal(fs.existsSync('tmp/dir2'), false);
|
||||
|
||||
//recursive, creates dest dir, implicitly copies contents of source dir
|
||||
//recursive, copies entire directory
|
||||
shell.rm('-rf', 'tmp/*');
|
||||
shell.cp('-r', 'resources/cp/dir_a', 'tmp/dest');
|
||||
assert.equal(shell.error(), null);
|
||||
assert.equal(fs.existsSync('tmp/dest/z'), true);
|
||||
|
||||
//recursive, with trailing slash, does the exact same
|
||||
shell.rm('-rf', 'tmp/*');
|
||||
shell.cp('-r', 'resources/cp/dir_a/', 'tmp/dest');
|
||||
assert.equal(shell.error(), null);
|
||||
assert.equal(fs.existsSync('tmp/dest/z'), true);
|
||||
|
||||
// On Windows, permission bits are quite different so skip those tests for now
|
||||
if (common.platform !== 'win') {
|
||||
//preserve mode bits
|
||||
@ -201,4 +214,26 @@ shell.cp('-r', 'resources/ls/', 'tmp/');
|
||||
assert.ok(!shell.error());
|
||||
assert.ok(fs.existsSync('tmp/.hidden_file'));
|
||||
|
||||
// no-recursive will copy regular files only
|
||||
shell.rm('-rf', 'tmp/');
|
||||
shell.mkdir('tmp/');
|
||||
shell.cp('resources/file1.txt', 'resources/ls/', 'tmp/');
|
||||
assert.ok(shell.error());
|
||||
assert.ok(!fs.existsSync('tmp/.hidden_file')); // doesn't copy dir contents
|
||||
assert.ok(!fs.existsSync('tmp/ls')); // doesn't copy dir itself
|
||||
assert.ok(fs.existsSync('tmp/file1.txt'));
|
||||
|
||||
// no-recursive will copy regular files only
|
||||
shell.rm('-rf', 'tmp/');
|
||||
shell.mkdir('tmp/');
|
||||
shell.cp('resources/file1.txt', 'resources/file2.txt', 'resources/cp',
|
||||
'resources/ls/', 'tmp/');
|
||||
assert.ok(shell.error());
|
||||
assert.ok(!fs.existsSync('tmp/.hidden_file')); // doesn't copy dir contents
|
||||
assert.ok(!fs.existsSync('tmp/ls')); // doesn't copy dir itself
|
||||
assert.ok(!fs.existsSync('tmp/a')); // doesn't copy dir contents
|
||||
assert.ok(!fs.existsSync('tmp/cp')); // doesn't copy dir itself
|
||||
assert.ok(fs.existsSync('tmp/file1.txt'));
|
||||
assert.ok(fs.existsSync('tmp/file2.txt'));
|
||||
|
||||
shell.exit(123);
|
||||
|
||||
@ -6,7 +6,6 @@ var oldConfigSilent = shell.config.silent;
|
||||
shell.config.silent = true;
|
||||
|
||||
shell.rm('-rf', 'tmp');
|
||||
shell.mkdir('tmp');
|
||||
|
||||
//
|
||||
// Valids
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user