From 3a7eb3f2ad453771158ab7b74f67aefbb207c608 Mon Sep 17 00:00:00 2001 From: Nate Fischer Date: Wed, 10 Feb 2016 18:17:50 -0800 Subject: [PATCH] feat(glob): glob support for (almost) all commands --- shell.js | 36 ++++++++++++++++++------------------ src/cat.js | 2 -- src/chmod.js | 13 +++++-------- src/common.js | 31 +++++++++++++++++++++++++++---- src/mv.js | 4 ---- src/rm.js | 7 ++----- src/sed.js | 2 -- test/ln.js | 14 ++++++++++++++ test/ls.js | 42 ++++++++++++++++++++++++++++++++++-------- test/mkdir.js | 9 +++++++++ test/sed.js | 10 ++++++++++ test/test.js | 2 +- test/to.js | 6 ++++++ test/toEnd.js | 7 +++++++ test/touch.js | 9 +++++++++ 15 files changed, 142 insertions(+), 52 deletions(-) diff --git a/shell.js b/shell.js index 93aff70..dc00580 100644 --- a/shell.js +++ b/shell.js @@ -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'); diff --git a/src/cat.js b/src/cat.js index 5840b4e..7205713 100644 --- a/src/cat.js +++ b/src/cat.js @@ -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); diff --git a/src/chmod.js b/src/chmod.js index 6c6de10..356ad7d 100644 --- a/src/chmod.js +++ b/src/chmod.js @@ -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) { diff --git a/src/common.js b/src/common.js index 33198bd..5684eea 100644 --- a/src/common.js +++ b/src/common.js @@ -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) { diff --git a/src/mv.js b/src/mv.js index 69cc03f..28b1d34 100644 --- a/src/mv.js +++ b/src/mv.js @@ -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); diff --git a/src/rm.js b/src/rm.js index cf2e95b..30af7a7 100644 --- a/src/rm.js +++ b/src/rm.js @@ -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)) { diff --git a/src/sed.js b/src/sed.js index baa385b..1d2ae72 100644 --- a/src/sed.js +++ b/src/sed.js @@ -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)) { diff --git a/test/ln.js b/test/ln.js index e7cd049..49de41c 100644 --- a/test/ln.js +++ b/test/ln.js @@ -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( diff --git a/test/ls.js b/test/ls.js index 5c915da..cd5abfe 100644 --- a/test/ls.js +++ b/test/ls.js @@ -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 diff --git a/test/mkdir.js b/test/mkdir.js index 0ae8e98..0a1bc3e 100644 --- a/test/mkdir.js +++ b/test/mkdir.js @@ -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); diff --git a/test/sed.js b/test/sed.js index 98a9ba5..489bd03 100644 --- a/test/sed.js +++ b/test/sed.js @@ -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); diff --git a/test/test.js b/test/test.js index 1718165..8fc9e8c 100644 --- a/test/test.js +++ b/test/test.js @@ -1,5 +1,5 @@ var shell = require('..'); -var common = require('./common'); +var common = require('../src/common'); var assert = require('assert'); diff --git a/test/to.js b/test/to.js index fea4665..7b70543 100644 --- a/test/to.js +++ b/test/to.js @@ -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); diff --git a/test/toEnd.js b/test/toEnd.js index 15d3290..b229fbb 100644 --- a/test/toEnd.js +++ b/test/toEnd.js @@ -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); diff --git a/test/touch.js b/test/touch.js index 36aa847..cfd6291 100644 --- a/test/touch.js +++ b/test/touch.js @@ -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);