1
1
#!/usr/bin/env node
2
- // ls.js — ESM basic version (no flags)
3
- // - If no paths: list '.'
4
- // - Excludes dotfiles by default
5
- // - One entry per line, alphabetical
6
- // - Multiple paths: print "path:" header before each directory group
7
- // - GNU-like errors; exit code 1 on any failure
2
+
8
3
9
4
import fs from "node:fs" ;
10
5
import { pathToFileURL } from "node:url" ;
11
6
12
- const args = process . argv . slice ( 2 ) ;
13
- const targets = args . length ? args : [ "." ] ;
14
- let hadError = false ;
7
+ const raw = process . argv . slice ( 2 ) ;
15
8
16
- for ( let i = 0 ; i < targets . length ; i ++ ) {
17
- const target = targets [ i ] ;
9
+ let includeAll = false ; // -a
10
+ let onePerLine = false ; // -1 (format already one-per-line)
11
+ const targets = [ ] ;
18
12
13
+ // Parse flags (supports combined like -a1 / -1a). Treat lone "-" as a path.
14
+ for ( const arg of raw ) {
15
+ if ( arg === "-" || ! arg . startsWith ( "-" ) ) {
16
+ targets . push ( arg ) ;
17
+ continue ;
18
+ }
19
+ if ( arg === "-1" ) {
20
+ onePerLine = true ;
21
+ continue ;
22
+ }
23
+ for ( const ch of arg . slice ( 1 ) ) {
24
+ if ( ch === "a" ) includeAll = true ;
25
+ else if ( ch === "1" ) onePerLine = true ;
26
+ else {
27
+ // ignore unknown short flags
28
+ }
29
+ }
30
+ }
31
+
32
+ const paths = targets . length ? targets : [ "." ] ;
33
+ let hadError = false ;
34
+
35
+ for ( let i = 0 ; i < paths . length ; i ++ ) {
36
+ const p = paths [ i ] ;
19
37
try {
20
- const stat = await fs . promises . lstat ( target ) ;
38
+ const st = await fs . promises . lstat ( p ) ;
39
+
40
+ if ( st . isDirectory ( ) ) {
41
+ if ( paths . length > 1 ) console . log ( `${ p } :` ) ;
21
42
22
- if ( stat . isDirectory ( ) ) {
23
- if ( targets . length > 1 ) console . log ( ` ${ target } :` ) ;
43
+ const entries = await fs . promises . readdir ( p , { withFileTypes : true } ) ;
44
+ let names = entries . map ( d => d . name ) ;
24
45
25
- const entries = await fs . promises . readdir ( target , { withFileTypes : true } ) ;
26
- const names = entries
27
- . map ( d => d . name )
28
- . filter ( n => ! n . startsWith ( "." ) ) // no dotfiles (we’ll add -a later)
29
- . sort ( ( a , b ) => a . localeCompare ( b ) ) ;
46
+ if ( ! includeAll ) {
47
+ names = names . filter ( n => ! n . startsWith ( "." ) ) ;
48
+ } else {
49
+ // mimic `ls -a` by including "." and ".."
50
+ names = [ "." , ".." , ...names ] ;
51
+ }
30
52
31
- for ( const name of names ) console . log ( name ) ;
53
+ names . sort ( ( a , b ) => a . localeCompare ( b ) ) ;
54
+ for ( const name of names ) {
55
+ // one-per-line output; -1 flag just affirms it
56
+ console . log ( name ) ;
57
+ }
32
58
33
- if ( targets . length > 1 && i !== targets . length - 1 ) console . log ( "" ) ; // blank line between dir groups
59
+ if ( paths . length > 1 && i !== paths . length - 1 ) console . log ( "" ) ;
34
60
} else {
35
- // plain file or symlink - > print the argument as given
36
- console . log ( target ) ;
61
+ // file or symlink = > print the argument as given
62
+ console . log ( p ) ;
37
63
}
38
64
} catch ( err ) {
39
65
if ( err ?. code === "ENOENT" ) {
40
- console . error ( `ls: cannot access '${ target } ': No such file or directory` ) ;
66
+ console . error ( `ls: cannot access '${ p } ': No such file or directory` ) ;
41
67
} else if ( err ?. code === "EACCES" ) {
42
- console . error ( `ls: cannot open directory '${ target } ': Permission denied` ) ;
68
+ console . error ( `ls: cannot open directory '${ p } ': Permission denied` ) ;
43
69
} else {
44
- console . error ( `ls: ${ target } : ${ err ?. message || "Error" } ` ) ;
70
+ console . error ( `ls: ${ p } : ${ err ?. message || "Error" } ` ) ;
45
71
}
46
72
hadError = true ;
47
73
}
@@ -52,5 +78,5 @@ if (hadError) process.exitCode = 1;
52
78
// run only when executed directly
53
79
const isDirect = import . meta. url === pathToFileURL ( process . argv [ 1 ] ) . href ;
54
80
if ( ! isDirect ) {
55
- // allow import in tests without auto-running
81
+ // allow importing in tests without auto-executing
56
82
}
0 commit comments