/
ls.js
184 lines (149 loc) · 5.71 KB
/
ls.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
module.exports = {
friendlyName: 'List contents (ls)',
description: 'List contents of a directory on the local filesystem.',
sideEffects: 'cacheable',
inputs: {
dir: {
friendlyName: 'Directory path',
example: '/Users/mikermcneil/.tmp/foo',
description: 'Path to the directory whose contents should be listed.',
extendedDescription: 'If a relative path is provided, it will be resolved to an absolute path from the context of the current working directory.',
required: true
},
depth: {
description: 'The maximum number of "hops" (i.e. directories deep) to include directory contents from.',
extendedDescription: 'For instance, if you are running `ls` on "foo/" which has a subdirectory "foo/bar/baz/", if `depth` is set to 2, the results will include "foo/bar/baz/", but none of the files/folders contained within.',
example: 1,
defaultsTo: 1
},
includeFiles: {
friendlyName: 'Include files?',
description: 'Whether or not to include files.',
example: true,
defaultsTo: true
},
includeDirs: {
friendlyName: 'Include directories?',
description: 'Whether or not to include directories.',
example: true,
defaultsTo: true
},
includeSymlinks: {
friendlyName: 'Include symlinks?',
description: 'Whether or not to include symbolic links.',
example: true,
defaultsTo: true
},
includeHidden: {
friendlyName: 'Include hidden entries?',
description: 'Whether or not to include hidden files/directories/symlinks.',
example: false,
defaultsTo: false
}
},
exits: {
success: {
outputFriendlyName: 'Directory contents',
outputDescription: 'A list of the files and subdirectories contained in the specified directory.',
outputExample: [
'/Users/mikermcneil/.tmp/foo/.gitignore'
]
},
doesNotExist: {
description: 'No directory was found at the specified directory path.'
}
},
fn: function (inputs, exits) {
// Import `path` and `walker`.
var path = require('path');
var Walker = require('walker');
// Ensure we've got an absolute path.
inputs.dir = path.resolve(inputs.dir);
// Determine the depth of the top-level directory we're walking,
// for comparison later on.
var topLvlDirDepth = inputs.dir.split(path.sep).length;
// Initialize the walker and teach it to skip walking directories
// that are:
// • deeper than requested, or
// • hidden (if the `includeHidden` input is set to false).
var walker = Walker(inputs.dir);
walker.filterDir(function(dir/*, stat*/) {
// Too deep.
if (dir.split(path.sep).length > (topLvlDirDepth + inputs.depth)) {
return false;
}
// Too hidden.
if (path.basename(dir).match(/^\./) && !inputs.includeHidden) {
return false;
}
return true;
});
// Accumulate results array by listing file, directory, and/or symlink
// entries from the specified directory.
var results = [];
// If `inputs.includeFiles` is `true`, look for files.
if (inputs.includeFiles) {
walker.on('file', function (entry/*, stat*/) {
// Add the new entry to our result list unless it is:
// • hidden (and the `includeHidden` input is set to false), or
// • too deep
var tooHidden = path.basename(entry).match(/^\./) && !inputs.includeHidden;
var tooDeep = entry.split(path.sep).length > (topLvlDirDepth + inputs.depth);
if ( !tooHidden && !tooDeep ) {
results.push(entry);
}
});
}
// If `inputs.includeDirs` is `true`, look for directories.
if (inputs.includeDirs) {
walker.on('dir', function (entry/*, stat*/) {
// If this is the top-level directory, exclude it.
if (entry === inputs.dir) { return; }
// Add the new entry to our result list unless it is:
// • hidden (and the `includeHidden` input is set to false)
var tooHidden = path.basename(entry).match(/^\./) && !inputs.includeHidden;
if ( !tooHidden ) {
results.push(entry);
}
});
}
// If `inputs.includeSymlinks` is `true`, look for symbolic links.
if (inputs.includeSymlinks) {
walker.on('symlink', function (entry/*, stat*/) {
// If this is the top-level directory, exclude it.
if (entry===inputs.dir) { return; }
// Add the new entry to our result list unless it is:
// • hidden (and the `includeHidden` input is set to false)
var tooHidden = path.basename(entry).match(/^\./) && !inputs.includeHidden;
if ( !tooHidden ) {
results.push(entry);
}
});
}
// Declare a var to act as a spinlock, so that duplicate events can be ignored.
var spinlock;
// If we receive an `error` event from Walker...
walker.on('error', function (err){
// If the spinlock is already engaged, do nothing.
if (spinlock) { return; }
// Engage the spinlock.
spinlock = true;
// If the error was ENOENT, it means the requested directory does not exist,
// so we'll return through the `doesNotExist` exit.
if (err.code === 'ENOENT') {
return exits.doesNotExist();
}
// Otherwise forward the unknown error through the `error` exit.
return exits.error(err);
});
// If we receive a `done` event from Walker...
walker.on('end', function (){
// If the spinlock is already engaged, do nothing.
if (spinlock) { return; }
// Engage the spinlock.
spinlock = true;
// Return the accumulated directory contents through the `success` exit.
return exits.success(results);
});
}
};