feat(glob): glob support for (almost) all commands

This commit is contained in:
Nate Fischer 2016-02-10 18:17:50 -08:00
parent 90569dcdac
commit 3a7eb3f2ad
15 changed files with 142 additions and 52 deletions

View File

@ -15,7 +15,7 @@ var common = require('./src/common');
//@include ./src/cd
var _cd = require('./src/cd');
exports.cd = common.wrap('cd', _cd);
exports.cd = common.wrap('cd', _cd, {idx: 1});
//@include ./src/pwd
var _pwd = require('./src/pwd');
@ -23,27 +23,27 @@ exports.pwd = common.wrap('pwd', _pwd);
//@include ./src/ls
var _ls = require('./src/ls');
exports.ls = common.wrap('ls', _ls);
exports.ls = common.wrap('ls', _ls, {idx: 1});
//@include ./src/find
var _find = require('./src/find');
exports.find = common.wrap('find', _find);
exports.find = common.wrap('find', _find, {idx: 1});
//@include ./src/cp
var _cp = require('./src/cp');
exports.cp = common.wrap('cp', _cp);
exports.cp = common.wrap('cp', _cp, {idx: 1});
//@include ./src/rm
var _rm = require('./src/rm');
exports.rm = common.wrap('rm', _rm);
exports.rm = common.wrap('rm', _rm, {idx: 1});
//@include ./src/mv
var _mv = require('./src/mv');
exports.mv = common.wrap('mv', _mv);
exports.mv = common.wrap('mv', _mv, {idx: 1});
//@include ./src/mkdir
var _mkdir = require('./src/mkdir');
exports.mkdir = common.wrap('mkdir', _mkdir);
exports.mkdir = common.wrap('mkdir', _mkdir, {idx: 1});
//@include ./src/test
var _test = require('./src/test');
@ -51,23 +51,23 @@ exports.test = common.wrap('test', _test);
//@include ./src/cat
var _cat = require('./src/cat');
exports.cat = common.wrap('cat', _cat);
exports.cat = common.wrap('cat', _cat, {idx: 1});
//@include ./src/to
var _to = require('./src/to');
String.prototype.to = common.wrap('to', _to);
String.prototype.to = common.wrap('to', _to, {idx: 1});
//@include ./src/toEnd
var _toEnd = require('./src/toEnd');
String.prototype.toEnd = common.wrap('toEnd', _toEnd);
String.prototype.toEnd = common.wrap('toEnd', _toEnd, {idx: 1});
//@include ./src/sed
var _sed = require('./src/sed');
exports.sed = common.wrap('sed', _sed);
exports.sed = common.wrap('sed', _sed, {idx: 3});
//@include ./src/grep
var _grep = require('./src/grep');
exports.grep = common.wrap('grep', _grep);
exports.grep = common.wrap('grep', _grep, {idx: 2});
//@include ./src/which
var _which = require('./src/which');
@ -79,15 +79,15 @@ exports.echo = _echo; // don't common.wrap() as it could parse '-options'
//@include ./src/dirs
var _dirs = require('./src/dirs').dirs;
exports.dirs = common.wrap("dirs", _dirs);
exports.dirs = common.wrap("dirs", _dirs, {idx: 1});
var _pushd = require('./src/dirs').pushd;
exports.pushd = common.wrap('pushd', _pushd);
exports.pushd = common.wrap('pushd', _pushd, {idx: 1});
var _popd = require('./src/dirs').popd;
exports.popd = common.wrap("popd", _popd);
exports.popd = common.wrap("popd", _popd, {idx: 1});
//@include ./src/ln
var _ln = require('./src/ln');
exports.ln = common.wrap('ln', _ln);
exports.ln = common.wrap('ln', _ln, {idx: 1});
//@
//@ ### exit(code)
@ -105,11 +105,11 @@ exports.exec = common.wrap('exec', _exec, {notUnix:true});
//@include ./src/chmod
var _chmod = require('./src/chmod');
exports.chmod = common.wrap('chmod', _chmod);
exports.chmod = common.wrap('chmod', _chmod, {idx: 1});
//@include ./src/touch
var _touch = require('./src/touch');
exports.touch = common.wrap('touch', _touch);
exports.touch = common.wrap('touch', _touch, {idx: 1});
//@include ./src/set
var _set = require('./src/set');

View File

@ -26,8 +26,6 @@ function _cat(options, files) {
files = [].slice.call(arguments, 1);
// if it's array leave it as it is
files = common.expand(files);
files.forEach(function(file) {
if (!fs.existsSync(file))
common.error('no such file or directory: ' + file);

View File

@ -62,9 +62,7 @@ function _chmod(options, mode, filePattern) {
// Special case where the specified file permissions started with - to subtract perms, which
// get picked up by the option parser as command flags.
// If we are down by one argument and options starts with -, shift everything over.
filePattern = mode;
mode = options;
options = '';
[].unshift.call(arguments, '');
}
else {
common.error('You must specify a file.');
@ -77,15 +75,14 @@ function _chmod(options, mode, filePattern) {
'v': 'verbose'
});
if (typeof filePattern === 'string') {
filePattern = [ filePattern ];
}
filePattern = [].slice.call(arguments, 2);
var files;
// TODO: replace this with a call to common.expand()
if (options.recursive) {
files = [];
common.expand(filePattern).forEach(function addFile(expandedFile) {
filePattern.forEach(function addFile(expandedFile) {
var stat = fs.lstatSync(expandedFile);
if (!stat.isSymbolicLink()) {
@ -100,7 +97,7 @@ function _chmod(options, mode, filePattern) {
});
}
else {
files = common.expand(filePattern);
files = filePattern;
}
files.forEach(function innerChmod(file) {

View File

@ -127,10 +127,17 @@ exports.parseOptions = parseOptions;
// expand(['file*.js']) = ['file1.js', 'file2.js', ...]
// (if the files 'file1.js', 'file2.js', etc, exist in the current dir)
function expand(list) {
if (!Array.isArray(list)) {
throw new TypeError('must be an array');
}
var expanded = [];
list.forEach(function(listEl) {
// Don't expand non-strings
if (typeof listEl !== 'string') {
expanded.push(listEl);
}
// Wildcard present on directory names ?
if(listEl.search(/\*[^\/]*\//) > -1 || listEl.search(/\*\*[^\/]*\//) > -1) {
else if(listEl.search(/\*[^\/]*\//) > -1 || listEl.search(/\*\*[^\/]*\//) > -1) {
var match = listEl.match(/^([^*]+\/|)(.*)/);
var root = match[1];
var rest = match[2];
@ -145,9 +152,14 @@ function expand(list) {
}
// Wildcard present on file names ?
else if (listEl.search(/\*/) > -1) {
_ls('', listEl).forEach(function(file) {
expanded.push(file);
});
var matches = _ls('', listEl);
if (matches.length === 0) {
expanded.push(listEl); // Interpret this as a literal string
} else {
matches.forEach(function(file) {
expanded.push(file);
});
}
} else {
expanded.push(listEl);
}
@ -229,6 +241,15 @@ function wrap(cmd, fn, options) {
} else if (args.length === 0 || typeof args[0] !== 'string' || args[0].length <= 1 || args[0][0] !== '-') {
args.unshift(''); // only add dummy option if '-option' not already present
}
args = args.reduce(function(accum, cur) {
if (Array.isArray(cur)) {
return accum.concat(cur);
} else {
accum.push(cur);
return accum;
}
}, []);
// Expand the '~' if appropriate
var homeDir = getUserHome();
args = args.map(function(arg) {
@ -237,6 +258,8 @@ function wrap(cmd, fn, options) {
else
return arg;
});
if (options && typeof options.idx === 'number')
args = args.slice(0, options.idx).concat(expand(args.slice(options.idx)));
retValue = fn.apply(this, args);
}
} catch (e) {

View File

@ -33,14 +33,10 @@ function _mv(options, sources, dest) {
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');
}
sources = common.expand(sources);
var exists = fs.existsSync(dest),
stats = exists && fs.statSync(dest);

View File

@ -114,11 +114,8 @@ function _rm(options, files) {
if (!files)
common.error('no paths given');
if (typeof files === 'string')
files = [].slice.call(arguments, 1);
// if it's array leave it as it is
files = common.expand(files);
// Convert to array
files = [].slice.call(arguments, 1);
files.forEach(function(file) {
if (!fs.existsSync(file)) {

View File

@ -40,8 +40,6 @@ function _sed(options, regex, replacement, files) {
files = [].slice.call(arguments, 3);
// if it's array leave it as it is
files = common.expand(files);
var sed = [];
files.forEach(function(file) {
if (!fs.existsSync(file)) {

View File

@ -71,6 +71,20 @@ assert.equal(
'new content 1'
);
// With glob
shell.rm('tmp/linkfile1');
shell.ln('tmp/fi*1', 'tmp/linkfile1');
assert(fs.existsSync('tmp/linkfile1'));
assert.equal(
fs.readFileSync('tmp/file1').toString(),
fs.readFileSync('tmp/linkfile1').toString()
);
fs.writeFileSync('tmp/file1', 'new content 1');
assert.equal(
fs.readFileSync('tmp/linkfile1').toString(),
'new content 1'
);
skipOnWinForEPERM(shell.ln.bind(shell, '-s', 'file2', 'tmp/linkfile2'), function () {
assert(fs.existsSync('tmp/linkfile2'));
assert.equal(

View File

@ -81,6 +81,13 @@ assert.equal(result.indexOf('.hidden_dir') > -1, true);
assert.equal(result.length, 8);
shell.cd('../..');
// wildcard, very simple
var result = shell.ls('resources/cat/*');
assert.equal(shell.error(), null);
assert.equal(result.indexOf('resources/cat/file1') > -1, true);
assert.equal(result.indexOf('resources/cat/file2') > -1, true);
assert.equal(result.length, 2);
// wildcard, simple
var result = shell.ls('resources/ls/*');
assert.equal(shell.error(), null);
@ -89,11 +96,24 @@ assert.equal(result.indexOf('resources/ls/file2') > -1, true);
assert.equal(result.indexOf('resources/ls/file1.js') > -1, true);
assert.equal(result.indexOf('resources/ls/file2.js') > -1, true);
assert.equal(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1, true);
assert.equal(result.indexOf('resources/ls/a_dir') > -1, true);
assert.ok(result.indexOf('resources/ls/a_dir') === -1); // this shouldn't be there
assert.ok(result.indexOf('nada') > -1);
assert.ok(result.indexOf('b_dir') > -1);
assert.equal(result.length, 7);
// wildcard, simple, with -d
var result = shell.ls('-d', 'resources/ls/*');
assert.equal(shell.error(), null);
assert.equal(result.indexOf('resources/ls/file1') > -1, true);
assert.equal(result.indexOf('resources/ls/file2') > -1, true);
assert.equal(result.indexOf('resources/ls/file1.js') > -1, true);
assert.equal(result.indexOf('resources/ls/file2.js') > -1, true);
assert.equal(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1, true);
assert.ok(result.indexOf('resources/ls/a_dir') > -1);
assert.equal(result.length, 6);
// wildcard, hidden only
var result = shell.ls('resources/ls/.*');
var result = shell.ls('-d', 'resources/ls/.*');
assert.equal(shell.error(), null);
assert.equal(result.indexOf('resources/ls/.hidden_file') > -1, true);
assert.equal(result.indexOf('resources/ls/.hidden_dir') > -1, true);
@ -116,6 +136,12 @@ assert.equal(result.length, 2);
assert.equal(result.indexOf('resources/ls/file1.js') > -1, true);
assert.equal(result.indexOf('resources/ls/file2.js') > -1, true);
// one file that exists, one that doesn't
var result = shell.ls('resources/ls/file1.js', 'resources/ls/thisdoesntexist');
assert.ok(shell.error());
assert.equal(result.length, 1);
assert.equal(result.indexOf('resources/ls/file1.js') > -1, true);
// wildcard, should not do partial matches
var result = shell.ls('resources/ls/*.j'); // shouldn't get .js
assert.equal(shell.error(), null);
@ -144,7 +170,7 @@ assert.equal(shell.error(), null);
assert.equal(result.length, 4);
assert.equal(result.indexOf('resources/ls/file1.js') > -1, true);
assert.equal(result.indexOf('resources/ls/file2.js') > -1, true);
assert.equal(result.indexOf('resources/ls/a_dir/b_dir') > -1, true);
assert.equal(result.indexOf('z') > -1, true);
assert.equal(result.indexOf('resources/ls/a_dir/nada') > -1, true);
// wildcard for both paths, array
@ -153,7 +179,7 @@ assert.equal(shell.error(), null);
assert.equal(result.length, 4);
assert.equal(result.indexOf('resources/ls/file1.js') > -1, true);
assert.equal(result.indexOf('resources/ls/file2.js') > -1, true);
assert.equal(result.indexOf('resources/ls/a_dir/b_dir') > -1, true);
assert.equal(result.indexOf('z') > -1, true);
assert.equal(result.indexOf('resources/ls/a_dir/nada') > -1, true);
// recursive, no path
@ -184,11 +210,11 @@ assert.equal(result.indexOf('a_dir/.hidden_dir/nada') > -1, true);
assert.equal(result.length, 14);
// recursive, wildcard
var result = shell.ls('-R', 'resources/ls/*');
var result = shell.ls('-R', 'resources/ls');
assert.equal(shell.error(), null);
assert.equal(result.indexOf('resources/ls/a_dir') > -1, true);
assert.equal(result.indexOf('resources/ls/a_dir/b_dir') > -1, true);
assert.equal(result.indexOf('resources/ls/a_dir/b_dir/z') > -1, true);
assert.equal(result.indexOf('a_dir') > -1, true);
assert.equal(result.indexOf('a_dir/b_dir') > -1, true);
assert.equal(result.indexOf('a_dir/b_dir/z') > -1, true);
assert.equal(result.length, 9);
// directory option, single arg

View File

@ -69,4 +69,13 @@ assert.equal(fs.existsSync('tmp/yyya'), true);
assert.equal(fs.existsSync('tmp/yyyb'), true);
assert.equal(fs.existsSync('tmp/yyyc'), true);
// globbed dir
shell.mkdir('-p', 'tmp/mydir');
assert.equal(shell.error(), null);
assert.equal(fs.existsSync('tmp/mydir'), true);
shell.mkdir('-p', 'tmp/m*ir');
assert.equal(shell.error(), null);
assert.equal(fs.existsSync('tmp/mydir'), true);
assert.equal(fs.existsSync('tmp/m*ir'), false); // doesn't create literal name
shell.exit(123);

View File

@ -101,4 +101,14 @@ assert.equal(result, 'hello1\nhello2');
assert.equal(shell.cat('tmp/file1'), 'hello1');
assert.equal(shell.cat('tmp/file2'), 'hello2');
// glob file names, with in-place-replacement
shell.cp('resources/file*.txt', 'tmp/');
assert.equal(shell.cat('tmp/file1.txt'), 'test1\n');
assert.equal(shell.cat('tmp/file2.txt'), 'test2\n');
var result = shell.sed('-i', 'test', 'hello', 'tmp/file*.txt');
assert.equal(shell.error(), null);
assert.equal(result, 'hello1\n\nhello2\n'); // TODO: fix sed's behavior
assert.equal(shell.cat('tmp/file1.txt'), 'hello1\n');
assert.equal(shell.cat('tmp/file2.txt'), 'hello2\n');
shell.exit(123);

View File

@ -1,5 +1,5 @@
var shell = require('..');
var common = require('./common');
var common = require('../src/common');
var assert = require('assert');

View File

@ -31,4 +31,10 @@ result = shell.cat('tmp/to2');
assert.equal(shell.error(), null);
assert.equal(result, 'hello world');
// With a glob
'goodbye'.to('tmp/t*1');
var result = shell.cat('tmp/to1');
assert.equal(shell.error(), null);
assert.equal(result, 'goodbye');
shell.exit(123);

View File

@ -33,4 +33,11 @@ result = shell.cat('tmp/toEnd2');
assert.equal(shell.error(), null);
assert.equal(result, 'world'); //Check that the result is what we expect
// With a glob
'good'.to('tmp/toE*1');
'bye'.toEnd('tmp/toE*1');
var result = shell.cat('tmp/toEnd1');
assert.equal(shell.error(), null);
assert.equal(result, 'goodbye');
shell.exit(123);

View File

@ -30,6 +30,15 @@ var testFile = tmpFile(true);
shell.touch('-c', testFile);
assert.ok(!fs.existsSync(testFile));
// handles globs correctly
shell.touch('tmp/file.txt');
shell.touch('tmp/file.js');
shell.touch('tmp/file*');
var files = shell.ls('tmp/file*');
assert.ok(files.indexOf('tmp/file.txt') > -1);
assert.ok(files.indexOf('tmp/file.js') > -1);
assert.equal(files.length, 2);
// errors if reference file is not found
var testFile = tmpFile();
var refFile = tmpFile(true);