Skip to content

Commit e918c75

Browse files
committed
feat(ls): add -l option
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.
1 parent c072738 commit e918c75

File tree

3 files changed

+126
-8
lines changed

3 files changed

+126
-8
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,19 @@ Available options:
172172

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

176181
Examples:
177182

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

184190
Returns array of files in the given path, or in current directory if no path provided.

src/ls.js

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,19 @@ var _pwd = require('./pwd');
1111
//@
1212
//@ + `-R`: recursive
1313
//@ + `-A`: all files (include files beginning with `.`, except for `.` and `..`)
14+
//@ + `-d`: list directories themselves, not their contents
15+
//@ + `-l`: list objects representing each file, each with fields containing `ls
16+
//@ -l` output fields. See
17+
//@ [fs.Stats](https://nodejs.org/api/fs.html#fs_class_fs_stats)
18+
//@ for more info
1419
//@
1520
//@ Examples:
1621
//@
1722
//@ ```javascript
1823
//@ ls('projs/*.js');
1924
//@ ls('-R', '/users/me', '/tmp');
2025
//@ ls('-R', ['/users/me', '/tmp']); // same as above
26+
//@ ls('-l', 'file.txt'); // { name: 'file.txt', mode: 33188, nlink: 1, ...}
2127
//@ ```
2228
//@
2329
//@ Returns array of files in the given path, or in current directory if no path provided.
@@ -26,7 +32,8 @@ function _ls(options, paths) {
2632
'R': 'recursive',
2733
'A': 'all',
2834
'a': 'all_deprecated',
29-
'd': 'directory'
35+
'd': 'directory',
36+
'l': 'long'
3037
});
3138

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

5967
if (common.platform === 'win')
60-
file = file.replace(/\\/g, '/');
68+
name = name.replace(/\\/g, '/');
6169

70+
if (file.name) {
71+
file.name = name;
72+
} else {
73+
file = name;
74+
}
6275
list.push(file);
6376
return true;
6477
}
6578

6679
paths.forEach(function(p) {
6780
if (fs.existsSync(p)) {
68-
var stats = fs.statSync(p);
81+
var stats = ls_stat(p);
6982
// Simple file?
7083
if (stats.isFile()) {
71-
pushFile(p, p);
84+
if (options.long) {
85+
pushFile(stats, p);
86+
} else {
87+
pushFile(p, p);
88+
}
7289
return; // continue
7390
}
7491

@@ -79,15 +96,18 @@ function _ls(options, paths) {
7996
} else if (stats.isDirectory()) {
8097
// Iterate over p contents
8198
fs.readdirSync(p).forEach(function(file) {
99+
var orig_file = file;
100+
if (options.long)
101+
file = ls_stat(path.join(p, file));
82102
if (!pushFile(file, p))
83103
return;
84104

85105
// Recursive?
86106
if (options.recursive) {
87107
var oldDir = _pwd();
88108
_cd('', p);
89-
if (fs.statSync(file).isDirectory())
90-
list = list.concat(_ls('-R'+(options.all?'A':''), file+'/*'));
109+
if (fs.statSync(orig_file).isDirectory())
110+
list = list.concat(_ls('-R'+(options.all?'A':''), orig_file+'/*'));
91111
_cd('', oldDir);
92112
}
93113
});
@@ -108,7 +128,13 @@ function _ls(options, paths) {
108128
// Iterate over directory contents
109129
fs.readdirSync(dirname).forEach(function(file) {
110130
if (file.match(new RegExp(regexp))) {
111-
if (!pushFile(path.normalize(dirname+'/'+file), basename))
131+
var file_path = path.join(dirname, file);
132+
file_path = options.long ? ls_stat(file_path) : file_path;
133+
if (file_path.name)
134+
file_path.name = path.normalize(file_path.name);
135+
else
136+
file_path = path.normalize(file_path);
137+
if (!pushFile(file_path, basename))
112138
return;
113139

114140
// Recursive?
@@ -128,3 +154,15 @@ function _ls(options, paths) {
128154
return list;
129155
}
130156
module.exports = _ls;
157+
158+
159+
function ls_stat(path) {
160+
var stats = fs.statSync(path);
161+
// Note: this object will contain more information than .toString() returns
162+
stats.name = path;
163+
stats.toString = function() {
164+
// Return a string resembling unix's `ls -l` format
165+
return [this.mode, this.nlink, this.uid, this.gid, this.size, this.mtime, this.name].join(' ');
166+
};
167+
return stats;
168+
}

test/ls.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,78 @@ assert.ok(result.indexOf('resources/ls/file2') > -1);
220220
assert.ok(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1);
221221
assert.equal(result.length, 6);
222222

223+
// long option, single file
224+
var result = shell.ls('-l', 'resources/ls/file1')[0];
225+
assert.equal(shell.error(), null);
226+
assert.equal(result.name, 'resources/ls/file1');
227+
assert.equal(result.nlink, 1);
228+
assert.equal(result.size, 5);
229+
assert.ok(result.mode); // check that these keys exist
230+
assert.ok(process.platform === 'win32' || result.uid); // only on unix
231+
assert.ok(process.platform === 'win32' || result.gid); // only on unix
232+
assert.ok(result.mtime); // check that these keys exist
233+
assert.ok(result.atime); // check that these keys exist
234+
assert.ok(result.ctime); // check that these keys exist
235+
assert.ok(result.toString().match(/^(\d+ +){5}.*$/));
236+
237+
// long option, glob files
238+
var result = shell.ls('-l', 'resources/ls/f*le1')[0];
239+
assert.equal(shell.error(), null);
240+
assert.equal(result.name, 'resources/ls/file1');
241+
assert.equal(result.nlink, 1);
242+
assert.equal(result.size, 5);
243+
assert.ok(result.mode); // check that these keys exist
244+
assert.ok(process.platform === 'win32' || result.uid); // only on unix
245+
assert.ok(process.platform === 'win32' || result.gid); // only on unix
246+
assert.ok(result.mtime); // check that these keys exist
247+
assert.ok(result.atime); // check that these keys exist
248+
assert.ok(result.ctime); // check that these keys exist
249+
assert.ok(result.toString().match(/^(\d+ +){5}.*$/));
250+
251+
// long option, directory
252+
var result = shell.ls('-l', 'resources/ls');
253+
assert.equal(shell.error(), null);
254+
var idx;
255+
for (var k=0; k < result.length; k++) {
256+
if (result[k].name === 'resources/ls/file1') {
257+
idx = k;
258+
break;
259+
}
260+
}
261+
assert.ok(idx);
262+
result = result[idx];
263+
assert.equal(result.name, 'resources/ls/file1');
264+
assert.equal(result.nlink, 1);
265+
assert.equal(result.size, 5);
266+
assert.ok(result.mode); // check that these keys exist
267+
assert.ok(process.platform === 'win32' || result.uid); // only on unix
268+
assert.ok(process.platform === 'win32' || result.gid); // only on unix
269+
assert.ok(result.mtime); // check that these keys exist
270+
assert.ok(result.atime); // check that these keys exist
271+
assert.ok(result.ctime); // check that these keys exist
272+
assert.ok(result.toString().match(/^(\d+ +){5}.*$/));
273+
274+
// long option, directory, recursive
275+
var result = shell.ls('-lR', 'resources/ls/');
276+
assert.equal(shell.error(), null);
277+
var idx;
278+
for (var k=0; k < result.length; k++) {
279+
if (result[k].name === 'resources/ls/file1') {
280+
idx = k;
281+
break;
282+
}
283+
}
284+
assert.ok(idx);
285+
result = result[idx];
286+
assert.equal(result.name, 'resources/ls/file1');
287+
assert.equal(result.nlink, 1);
288+
assert.equal(result.size, 5);
289+
assert.ok(result.mode); // check that these keys exist
290+
assert.ok(process.platform === 'win32' || result.uid); // only on unix
291+
assert.ok(process.platform === 'win32' || result.gid); // only on unix
292+
assert.ok(result.mtime); // check that these keys exist
293+
assert.ok(result.atime); // check that these keys exist
294+
assert.ok(result.ctime); // check that these keys exist
295+
assert.ok(result.toString().match(/^(\d+ +){5}.*$/));
296+
223297
shell.exit(123);

0 commit comments

Comments
 (0)