A filesystem abstraction that makes directory traversal and other common tasks first-class citizens, representing files and directories as objects, and avoids the need for manipulating file paths as strings or worrying about whether a path is relative or absolute.
Some things you can do with oofs:
- Rename all files in a directory with a replacement regex/callback function.
- Work with .json files. Read and write the whole file, and update nested properties in one line.
- Quickly navigate from node to node with
.parent
,.child(relativePath)
and others. - Walk through all files, directories, or both; recursively or at the top level.
All methods have sync and async versions where it makes sense, and you can set sync to be the default to avoid typing the Sync suffix everywhere.
A quick example (full reference below):
// require the library
var oofs = require('oofs');
// create an instance with the 'defaultSync' option
// turned on to avoid "Sync" suffixes
var fs = oofs(true);
// create a Node representing /home/user
var dir = fs('/home/user');
// do something with all the files and directories:
dir.all(function(node) {
node.isDir(); // boolean
node.fullPath; // e.g. "/home/user/dir/file.txt"
node.all; // the same as dir.all (everything is a Node)
node.pathFrom(dir); // "dir/file.txt"
node.read(); // the contents of the file
node.write('new contents');
});
There are some useful variants of all
for dealing with only files, only directories, only direct children, etc:
dir.each(function(node) {
// all files and directories directly under dir
});
dir.allFiles(function(node) {
// all files recursively
});
Many of the methods from fs
are available on nodes, with the only difference being the lack of a path
parameter:
var file = fs('/home/user/file.txt');
file.write(data, 'utf8');
You can get the absolute path (as a string) from node.fullPath
.
You can get a new Node
representing a node's parent directory from node.parent
.
You can run functions conditionally using ifDir
and ifFile
:
node.ifDir(function() {
// runs if the node is a directory
});
node.ifFile(function() {
// runs if the node is a file
});
####Contents
#####index
#####Node
- Properties and getters
- Directory traversal
- Conditionals and checks
- General utilities
- Reading, writing, renaming and deleting
####oofs([options | defaultSync=false])
The package is a function that returns an instance of the library, and accepts an optional config object with the following defaults:
{
defaultSync: false,
mkdirs: false,
mkdirpOptions: {}
}
The mkdirs
option uses mkdirp to make sure any parent directories are created before writing to a file with write
, writeJson
, etc. mkdirpOptions
gets passed through to mkdirp
.
You can also pass a single boolean, which will be used as the defaultSync
option.
var oofs = require("oofs");
var syncApi = oofs(true);
var asyncApi = oofs();
syncApi("file.txt").read(); // returns file contents
asyncApi("file.txt").read(function(contents) { /* calls back with contents \*/ });
oofs({
mkdirs: true
})("/path/to/non-existent/dir/file.txt").write("data", function() {
// /path/to/non-existent/dir will be created before writing the file
});
Sync and async versions are always available with a suffix, e.g. readSync
or readAsync
.
To skip the step of calling the package with options to generate a library instance, you can require
one or more of the following:
var syncApi = require("oofs/sync");
var asyncApi = require("oofs/async");
var syncApiWithMkdirs = require("oofs/sync-mkdirs");
var asyncApiWithMkdirs = require("oofs/async-mkdirs");
var file = syncApiWithMkdirs("./path/to/file.txt");
file.write("etc");
Some of the methods below have a synchronous version that returns the result, but will also pass it to a callback if one is supplied, so your code doesn't have to know whether defaultSync is on.
###Properties and getters
####fullPath
The full, absolute path to the file or directory.
####name
The full name, e.g. "file.txt".
####basename
The name without the extension e.g. "file".
####extension
The file extension e.g. ".txt", or an empty string if there is no extension.
####isRoot
Whether the node represents the root (/).
####parent
A Node
representing the node's parent. If the node is root, parent
is a reference back to the same node.
fs('/path/to/file').parent.fullPath; // '/path/to'
####pathFrom(referenceNode | path)
Returns the relative path to the node from the supplied referenceNode
or path.
var file = fs('/home/user/dir/file.txt');
var anotherDir = fs('/home/user/another-dir');
file.pathFrom(anotherDir); // "../dir/file.txt"
file.pathFrom('/home'); // "user/dir/file.txt"
####child(relativePath)
Returns a Node representing the path at relativePath
, relative to the node's path.
fs('/home/user')
.child('dir/file.txt').fullPath; // "/home/user/dir/file.txt"
####rel(relativePath)
The same as child
but without the implied nesting in the name.
fs('/home/user')
.rel('dir/file.txt').fullPath; // "/home/user/dir/file.txt"
fs('/home/user')
.rel('../another-user').fullPath; // "/home/another-user"
####sibling(relativePath)
A child of the node's parent.
fs('/home/user/dir/file.txt')
.sibling('another-file.txt').fullPath; // "/home/user/dir/another-file.txt"
####within(parentPath|parentNode)
Checks whether the Node is a descendant of the supplied path or Node.
fs('/home/user/dir/file.txt').within('/home/user'); // true
###Directory traversal
These methods all have an identical Sync version -- the signature is the same (allSync
, allDirsSync
, etc).
####all(callback(node))
Calls callback with all files and directories under the node.
####allDirs(callback(node))
Calls callback with all directories under the node.
####allFiles(callback(node))
Calls callback with all files under the node.
####each(callback(node))
Calls callback with each file and directory directly under the node (not recursive).
####eachDir(callback(node))
Calls callback with each directory directly under the node (not recursive).
####eachFile(callback(node))
Calls callback with each file directly under the node (not recursive).
###Conditionals and checks
####ifDir(callback())
Calls callback if the node is a directory.
####ifFile(callback())
Calls callback if the node is a file.
####isDir(callback(boolean))
Calls callback with a boolean indicating whether the node is a directory.
####isDirSync([callback(boolean)])
Sync version with optional callback.
####isFile(callback(boolean))
Calls callback with a boolean indicating whether the node is a file.
####isFileSync([callback(boolean)])
Sync version with optional callback.
###General utilities
All have Sync versions (e.g.
filterSync
) with optional callbacks.
####filter(filter(node), callback(nodes))
Calls the supplied filter function on each direct child node, then calls the callback with an array of all the nodes for which the filter returned true.
####readdir(callback(filenames))
Calls the callback with an array of filenames directly under the node.
####ls(callback(nodes))
Calls callback with an array of Nodes representing the files and directories directly under the node.
####lsDirs(callback(nodes))
Calls callback with an array of Nodes representing the directories directly under the node.
####lsFiles(callback(nodes))
Calls callback with an array of Nodes representing the files directly under the node.
####lsAll([callback(nodes)])
Returns an array of Nodes for all files and directories recursively under the node. Synchronous only: there is currently no async version of this method. There are also file- and directory-specific variants lsAllDirs
and lsAllFiles
that work as expected.
####contains(filename)
Whether the directory contains a file or directory called filename
.
fs('site/views').contains('index.pug'); // true
####stat, lstat, statSync & lstatSync([callback(fs.Stats, error)])
These methods wrap the corresponding fs
methods, with the arguments to the async callback reversed ((result, error)
) for consistency with the optional sync callback.
oofs(true)('/some/file.txt').stat().isFile(); // boolean
oofs()('/some/file.txt').stat(function(stats) {
stats.isFile(); // boolean
});
###Reading, writing, renaming and deleting
All have async versions with a callback as the last argument, and Sync versions with an optional callback.
####delete([callback])
Deletes the file or directory. Use unlink
and rmdir
for file- and directory-specific versions, avoiding the need to check which type the node is.
####write(text[, callback])
Writes text to the file, creating it if necessary.
####read([callback(contents)])
Returns or calls back with a string of the contents of the file.
####replace(find, replace[, callback])
Does a find/replace on the contents of the file using String.replace
.
####writeJson(object[, callback])
Writes the JSON-stringification of object
to the file.
####readJson([callback(object)])
Returns the JSON-parsed contents of the file.
####updateJson(path, value[, callback])
Set the value at path
to value
, where path
is a dot-separated string of object properties and/or array indexes. A value of undefined
will delete the object property or splice out the array item at path
.
For example, if config.json contains the following:
{
"db": {
"host": "localhost"
}
}
Calling fs('config.json').updateJson('db.host', 'example.com')
will change it to:
{
"db": {
"host": "example.com"
}
}
Calling updateJson('db.host', undefined)
will change it to:
{
"db": {}
}
####rename(newPath[, callback])
Renames the file or directory. newPath
is calculated as a relative path from the node's parent.
var file = fs('/home/user/dir/file.txt');
file.rename('file2.txt');
file.fullPath; // /home/user/dir/file2.txt
file.rename('../another-dir/file.txt'); // moving to another directory
file.fullPath; // /home/user/another-dir/file.txt
####rename(find, replace[, callback])
Renames the file or directory by calling String.replace
on the name
(filename with extension).
For example, switching file extensions to migrate from Jade to Pug:
fs('./views').all(function(node) {
node.rename('.jade', '.pug');
});
Regular expressions and replacement callbacks can be used:
fs('/IMG0001.jpg').rename(/([A-Z]+)0*(\d+)/, function(all, name, n) {
return name.toLowerCase() + '-' + n;
}).name; // img-1.jpg
####empty()
Synchronous only. Recursively deletes everything in the directory.