When we call print, we pass in process.argv[2]; if its value is false, then we alternatively pass a dot (.) (meaning current working directory).
The argv property on process is an array of command line arguments, including the call to node (at process.argv[0]) and the file being executed (at process.argv[1]).
When we ran node meta.js my-folder, process.argv[2] had the value my-folder.
Our print function uses fs.readdirSync to get an array of all the files and folders in the specified dir (in our case, the dir was my-folder).
We use a functional approach in this recipe (and elsewhere throughout this book). If this is unfamiliar, check out the functional programming workshop at http://nodeschool.io
We call map on the returned array to create a new array of objects containing both the file and the dir (we need to keep a reference to the dir so it can eventually be passed to the output function).
We call map again, on the previously mapped array of objects, this time passing the toMeta function as the transformer function.
The toMeta function uses the ES2015 (a.k.a ES6) destructuring syntax to accept an object and break its file and dir property into variables that are local to the function. Then toMeta passes the file and dir into path.join (to assemble a complete cross-platform to the file), which, in turn, is passed into fs.statSync. The fs.statSync method (and its asynchronous counter fs.stat) is a light wrapper around the POSIX stat operation.
It supplies an object with the following information about a file or directory:
Information Point |
Namespace |
ID of device holding file |
dev |
Inode number |
ino |
Access permissions |
mode |
Number of hard links contained |
nlink |
User ID |
uid |
Group ID |
gid |
Device ID of special device file |
rdev |
Total bytes |
size |
Filesystem block size |
blksize |
Number of 512 byte blocks allocated for file |
blocks |
Time of last access |
atime |
Time of last modification |
mtime |
Time of last status change |
ctime |
Time of file creation |
birthtime |
We use assignment destructuring in our toMeta function to grab the birthtime, ino, mode, nlink, and size values. We ensure birthtime is a standard UTC string (instead of local time), and convert the mode from a decimal to the more familiar octal permissions representation.
The stats object supplies some methods:
- isFile
- isDirectory
- isBlockDevice
- isCharacterDevice
- isFIFO
- isSocket
- isSymbolicLink
The isSymbolicLink method is only available when the file stats have been retrieved with fs.lstat (not with fs.stat). See the There's more... section for an example where we use fs.lstat.
In the object returned from toMeta, we add an isDir property, the value of which is determined by the stats.isDirectory() call.
We also add the file and dir using ES2015 property shorthand , and an array of our selected stats on the info property.
The ES2015 (ES6) defines a host of convenient syntax extensions for JavaScript, 96 percent of which is supported in Node v6. One such extension is property shorthand, where a variable can be placed in an object without specifying the property name or value. Under the rules of property shorthand, the property name corresponds to the variable name, the value to the value referenced by the variable.
Once the second map call in our print function has looped over every element, passing each to the toMeta function, we are left with a new array composed of objects as returned from toMeta - containing file, dir, info, and isDir properties.
The forEach method is called on this array of metadata, and it's passed the output function. This means that each piece of metadata is processed by the output function.
In similar form to the toMeta function, the output function likewise deconstructs the passed in object into file, dir, info, and isDir references. We then pass the file string and all of the elements in the info array, using the ES2015 spread operator, to our write function (as supplied by the third-party tableaux module).
If we're only dealing with a file (that is, if isDir is not true), we exit the output function by returning early.
If, however, isDir is true then we call write.arrow (which writes a Unicode arrow to the terminal), reads the directory, calls forEach on the returned array of directory contents, and calls fs.statSync on each item in the directory.
We then check whether the item is a directory (that is, a subdirectory) using the returned stats object. If it is, we write the directory name to the terminal in bold; if it isn't, we write it in a dulled down white color.