Other modules, such as FileListView, DirListView, and TitleBarPath, consume the data from the filesystem, such as directory list, file list, and the current path. So we need to create a service that will provide this data:
./js/Service/Dir.js
const fs = require( "fs" ),
{ join, parse } = require( "path" );
class DirService {
constructor( dir = null ){
this.dir = dir || process.cwd();
}
static readDir( dir ) {
const fInfoArr = fs.readdirSync( dir, "utf-8" ).map(( fileName ) => {
const filePath = join( dir, fileName ),
stats = DirService.getStats( filePath );
if ( stats === false ) {
return false;
}
return {
fileName,
stats
};
});
return fInfoArr.filter( item => item !== false );
}
getDirList() {
const collection = DirService.readDir( this.dir ).filter(( fInfo )
=> fInfo.stats.isDirectory() );
if ( !this.isRoot() ) {
collection.unshift({ fileName: ".." });
}
return collection;
}
getFileList() {
return DirService.readDir( this.dir ).filter(( fInfo ) =>
fInfo.stats.isFile() );
}
isRoot(){
const { root } = parse( this.dir );
return ( root === this.dir );
}
static getStats( filePath ) {
try {
return fs.statSync( filePath );
} catch( e ) {
return false;
}
}
};
exports.DirService = DirService;
First of all, we import Node.js core module fs that provides us access to the filesystem. We also extract functions--join and parse--from the path module. We will need them for manipulations in the file/directory path.
Then, we declare the DirService class. On construction, it creates a dir property, which takes either a passed-in value or the current working directory (process.cwd()). We add a static method--readDir--to the class that reads the directory content on a given location. The fs.readdirSync method retrieves the content of a directory, but we extend the payload with file/directory stats (https://nodejs.org/api/fs.html#fs_class_fs_stats). In case the stats cannot be obtained, we replace its array element with false. To avoid such gaps in the output array, we will run the array filter method. Thus, on the exit point, we have a clean array of filenames and file stats.
The getFileList method requests readDir for the current directory content and filters the list to leave only files in there.
The getDirList method filters, evidently, the list for directories only. Besides, it prepends the list with a .. directory for upward navigation, but only if we are not in the system root.
So, we can get both lists from the modules consuming them. When the location changes and new directory and file lists get available, each of these modules have to update. To implement it, we will use the observe pattern:
./js/Service/Dir.js
//....
const EventEmitter = require( "events" );
class DirService extends EventEmitter {
constructor( dir = null ){
super();
this.dir = dir || process.cwd();
}
setDir( dir = "" ){
let newDir = path.join( this.dir, dir );
// Early exit
if ( DirService.getStats( newDir ) === false ) {
return;
}
this.dir = newDir;
this.notify();
}
notify(){
this.emit( "update" );
}
//...
}
We export from events, core module the EventEmitter class (https://nodejs.org/api/events.html). By extending it with DirService, we make the service an event emitter. It gives us the possibility to fire service events and to subscribe on them:
dirService.on( "customEvent", () => console.log( "fired customEvent" ));
dirService.emit( "customEvent" );
So whenever the setDir method is called to change the current location, it fires an event of type "update". Given the consuming modules are subscribed, they respond to the event by updating their views.