The Filesystem API

Plugins often want to interact with files, processes and network within the user's workspace. Because all Cloud9 plugins run in the browser and there is a network between the browser and the user's workspace we looked for an API that would work asynchronously. The Node.js APIs were an obvious match and so we based our Filesystem (fs), Process (proc) and Network (net) APIs on Node's fs, child_process and net modules.

Getting a reference to the plugin

The following examples show how to gain access to the fs APIs.

First, make sure to add "fs" to your list of plugins you depend on like so:

main.consumes = ["Plugin", "fs"];

And then get a reference to the plugin in the main function:

function main(options, imports, register) {
    var fs = imports.fs;

Filesystem

For a complete reference to all the methods and events of the fs plugin check out the fs api reference.

The following table lists of all the methods supported by the fs plugin.

appendFileAppends a file in the workspace
chmodChange the mode of a file within the workspace
copyCopy a file within the workspace
existsTests if a file or directory exists
metadataSet metadata for a file.
mkdirCreate a directory at path.
mkdirPCreates all non-existing directories of the path.
readFileReads the entire contents from a file in the workspace.
readFileWithMetadataRead the contents of a file including the metadata
readdirRead the contents of a directory as an array of stat objects.
renameRename/move a file or directory.
rmdirDelete a directory at path
rmfileDelete a file at path.
statLoads the stat information for a single path entity.
symlinkCreate a special symlink file at path.
unlinkDelete a file at path
unwatchStop watching for changes on a file or directory
watchWatches for changes on a file or directory
writeFileWrite the contents of a file

The following examples show some of the most common use cases dealing with the file system. The paths are always relative to the workspace directory. One exception are paths that are in the home dir of the owner. These can be accessed by prefixing paths with ~/.

Getting a directory listing:

fs.readdir("/plugins", function(err, list){
    if (err) return console.error(err);
    
    list.forEach(function(stat){
        console.log("Name:", stat.name, "Size:", stat.size);
    });
});

Reading a file:

fs.readFile("/README.md", function(err, content){
    if (err) return console.error(err);

    console.log("Contents of the file:", content);
});

Writing to a file:

fs.writeFile("/empty.json", "{}", function(err){
    if (err) return console.error(err);

    console.log("File is written");
});

Removing a directory tree:

fs.rmdir("/plugins", { recursive: true }, function(err){
    if (err) return console.error(err);

    console.log("The directory and all it's children are deleted");
});

Events

The fs plugin provides events before and after each method call. This allows your plugin to hook into and interact with filesystem changes before they happen or after they have occurred. The before events are cancelable.

fs.on("beforeWriteFile", function(e){
    console.log("Path:", e.path, "Args:", e.args);
    
    return false; // Cancels this action
});

File Watchers

We advice not to use the fs.watch() and fs.unwatch() methods. If every plugin would set watchers on the same files there would be extra overhead. Instead use the watcher plugin to watch files.

watcher.watch("/example.css");
watcher.on("change", function(e){
    if (e.path == "/example.css")
        console.log("example.css is updated. New file size: ", e.stat.size);
});

See the watcher api reference. for more information

Latency Considerations

Because the code of your plugin is running in the user's browser it takes time to go from there to the user's workspace and back. We are continuously adding new servers and locations and our aim is to get worldwide latency down to 30ms or less. The truth is that seeing latency between 50ms - 100ms is not unusual. For most operations this is fine. However, it is important to realize that if you are writing a script that is going to deal with a large number of files, it is often best to create a small Node.js or Bash script. Simply use the process APIs to execute such a script instead of using the file system APIs.