Skip to content

Commit

Permalink
feat(ls): add -l option
Browse files Browse the repository at this point in the history
The `-l` option will now cause `ls()` to return an object containing file stats.
These objects will also have a toString() method that formats it into something
analogous to `ls -l`'s output format.
  • Loading branch information
nfischer committed Jan 31, 2016
1 parent c072738 commit e918c75
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 8 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,19 @@ Available options:

+ `-R`: recursive
+ `-A`: all files (include files beginning with `.`, except for `.` and `..`)
+ `-d`: list directories themselves, not their contents
+ `-l`: list objects representing each file, each with fields containing `ls
-l` output fields. See
[fs.Stats](https://nodejs.org/api/fs.html#fs_class_fs_stats)
for more info

Examples:

```javascript
ls('projs/*.js');
ls('-R', '/users/me', '/tmp');
ls('-R', ['/users/me', '/tmp']); // same as above
ls('-l', 'file.txt'); // { name: 'file.txt', mode: 33188, nlink: 1, ...}
```

Returns array of files in the given path, or in current directory if no path provided.
Expand Down
54 changes: 46 additions & 8 deletions src/ls.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ var _pwd = require('./pwd');
//@
//@ + `-R`: recursive
//@ + `-A`: all files (include files beginning with `.`, except for `.` and `..`)
//@ + `-d`: list directories themselves, not their contents
//@ + `-l`: list objects representing each file, each with fields containing `ls
//@ -l` output fields. See
//@ [fs.Stats](https://nodejs.org/api/fs.html#fs_class_fs_stats)
//@ for more info
//@
//@ Examples:
//@
//@ ```javascript
//@ ls('projs/*.js');
//@ ls('-R', '/users/me', '/tmp');
//@ ls('-R', ['/users/me', '/tmp']); // same as above
//@ ls('-l', 'file.txt'); // { name: 'file.txt', mode: 33188, nlink: 1, ...}
//@ ```
//@
//@ Returns array of files in the given path, or in current directory if no path provided.
Expand All @@ -26,7 +32,8 @@ function _ls(options, paths) {
'R': 'recursive',
'A': 'all',
'a': 'all_deprecated',
'd': 'directory'
'd': 'directory',
'l': 'long'
});

if (options.all_deprecated) {
Expand All @@ -49,26 +56,36 @@ function _ls(options, paths) {
// Conditionally pushes file to list - returns true if pushed, false otherwise
// (e.g. prevents hidden files to be included unless explicitly told so)
function pushFile(file, query) {
var name = file.name || file;
// hidden file?
if (path.basename(file)[0] === '.') {
if (path.basename(name)[0] === '.') {
// not explicitly asking for hidden files?
if (!options.all && !(path.basename(query)[0] === '.' && path.basename(query).length > 1))
return false;
}

if (common.platform === 'win')
file = file.replace(/\\/g, '/');
name = name.replace(/\\/g, '/');

if (file.name) {
file.name = name;
} else {
file = name;
}
list.push(file);
return true;
}

paths.forEach(function(p) {
if (fs.existsSync(p)) {
var stats = fs.statSync(p);
var stats = ls_stat(p);
// Simple file?
if (stats.isFile()) {
pushFile(p, p);
if (options.long) {
pushFile(stats, p);
} else {
pushFile(p, p);
}
return; // continue
}

Expand All @@ -79,15 +96,18 @@ function _ls(options, paths) {
} else if (stats.isDirectory()) {
// Iterate over p contents
fs.readdirSync(p).forEach(function(file) {
var orig_file = file;
if (options.long)
file = ls_stat(path.join(p, file));
if (!pushFile(file, p))
return;

// Recursive?
if (options.recursive) {
var oldDir = _pwd();
_cd('', p);
if (fs.statSync(file).isDirectory())
list = list.concat(_ls('-R'+(options.all?'A':''), file+'/*'));
if (fs.statSync(orig_file).isDirectory())
list = list.concat(_ls('-R'+(options.all?'A':''), orig_file+'/*'));
_cd('', oldDir);
}
});
Expand All @@ -108,7 +128,13 @@ function _ls(options, paths) {
// Iterate over directory contents
fs.readdirSync(dirname).forEach(function(file) {
if (file.match(new RegExp(regexp))) {
if (!pushFile(path.normalize(dirname+'/'+file), basename))
var file_path = path.join(dirname, file);
file_path = options.long ? ls_stat(file_path) : file_path;
if (file_path.name)
file_path.name = path.normalize(file_path.name);
else
file_path = path.normalize(file_path);
if (!pushFile(file_path, basename))
return;

// Recursive?
Expand All @@ -128,3 +154,15 @@ function _ls(options, paths) {
return list;
}
module.exports = _ls;


function ls_stat(path) {
var stats = fs.statSync(path);
// Note: this object will contain more information than .toString() returns
stats.name = path;
stats.toString = function() {
// Return a string resembling unix's `ls -l` format
return [this.mode, this.nlink, this.uid, this.gid, this.size, this.mtime, this.name].join(' ');
};
return stats;
}
74 changes: 74 additions & 0 deletions test/ls.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,78 @@ assert.ok(result.indexOf('resources/ls/file2') > -1);
assert.ok(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1);
assert.equal(result.length, 6);

// long option, single file
var result = shell.ls('-l', 'resources/ls/file1')[0];
assert.equal(shell.error(), null);
assert.equal(result.name, 'resources/ls/file1');
assert.equal(result.nlink, 1);
assert.equal(result.size, 5);
assert.ok(result.mode); // check that these keys exist
assert.ok(process.platform === 'win32' || result.uid); // only on unix
assert.ok(process.platform === 'win32' || result.gid); // only on unix
assert.ok(result.mtime); // check that these keys exist
assert.ok(result.atime); // check that these keys exist
assert.ok(result.ctime); // check that these keys exist
assert.ok(result.toString().match(/^(\d+ +){5}.*$/));

// long option, glob files
var result = shell.ls('-l', 'resources/ls/f*le1')[0];
assert.equal(shell.error(), null);
assert.equal(result.name, 'resources/ls/file1');
assert.equal(result.nlink, 1);
assert.equal(result.size, 5);
assert.ok(result.mode); // check that these keys exist
assert.ok(process.platform === 'win32' || result.uid); // only on unix
assert.ok(process.platform === 'win32' || result.gid); // only on unix
assert.ok(result.mtime); // check that these keys exist
assert.ok(result.atime); // check that these keys exist
assert.ok(result.ctime); // check that these keys exist
assert.ok(result.toString().match(/^(\d+ +){5}.*$/));

// long option, directory
var result = shell.ls('-l', 'resources/ls');
assert.equal(shell.error(), null);
var idx;
for (var k=0; k < result.length; k++) {
if (result[k].name === 'resources/ls/file1') {
idx = k;
break;
}
}
assert.ok(idx);
result = result[idx];
assert.equal(result.name, 'resources/ls/file1');
assert.equal(result.nlink, 1);
assert.equal(result.size, 5);
assert.ok(result.mode); // check that these keys exist
assert.ok(process.platform === 'win32' || result.uid); // only on unix
assert.ok(process.platform === 'win32' || result.gid); // only on unix
assert.ok(result.mtime); // check that these keys exist
assert.ok(result.atime); // check that these keys exist
assert.ok(result.ctime); // check that these keys exist
assert.ok(result.toString().match(/^(\d+ +){5}.*$/));

// long option, directory, recursive
var result = shell.ls('-lR', 'resources/ls/');
assert.equal(shell.error(), null);
var idx;
for (var k=0; k < result.length; k++) {
if (result[k].name === 'resources/ls/file1') {
idx = k;
break;
}
}
assert.ok(idx);
result = result[idx];
assert.equal(result.name, 'resources/ls/file1');
assert.equal(result.nlink, 1);
assert.equal(result.size, 5);
assert.ok(result.mode); // check that these keys exist
assert.ok(process.platform === 'win32' || result.uid); // only on unix
assert.ok(process.platform === 'win32' || result.gid); // only on unix
assert.ok(result.mtime); // check that these keys exist
assert.ok(result.atime); // check that these keys exist
assert.ok(result.ctime); // check that these keys exist
assert.ok(result.toString().match(/^(\d+ +){5}.*$/));

shell.exit(123);

0 comments on commit e918c75

Please sign in to comment.