commit b9b29b564e7cb8374c2d056686e38596fc8b524f Author: Ryan McGrath Date: Sat Mar 6 02:46:03 2010 -0500 Initial commit; this is currently in production on Luno (http://github.com/ryanmcgrath/luno) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a85a94a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2010 Ryan McGrath + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..66165ff --- /dev/null +++ b/readme.md @@ -0,0 +1,26 @@ +wrench.js - Recursive file operations in Node.js +---------------------------------------------------------------------------- +While I love Node.js, I've found myself missing some functions. Things like +recursively deleting/chmodding a directory (or even deep copying a directory) +shouldn't need to be re-invented time and time again. + +That said, here's my attempt at a re-usable solution, at least until something +more formalized gets integrated into Node.js (*hint hint*). wrench.js is fairly simple +to use - check out the documentation/examples below: + + var wrench = require("./wrench"); + + // Recursively delete the entire sub-tree of a directory, then kill the directory + wrench.rmdirSyncRecursive("my_directory_name"); + + // Recursively chmod the entire sub-tree of a directory + wrench.chmodSyncRecursive("my_directory_name", 0755); + + // Deep-copy an existing directory + wrench.copyDirSyncRecursive("directory_to_copy", "location_where_copy_should_end_up"); + +It should be noted that these are all currently synchronous operations. I'll be adding +asynchronous versions of chmod/copy in the near future, but rmdir is one that really can't +exist in an asynchronous fashion. + +Questions, comments? Hit me up. (ryan [at] venodesigns.net | http://twitter.com/ryanmcgrath) diff --git a/wrench.js b/wrench.js new file mode 100644 index 0000000..d0e6da8 --- /dev/null +++ b/wrench.js @@ -0,0 +1,114 @@ +/* wrench.js + * + * A collection of various utility functions I've found myself in need of + * for use with Node.js (http://nodejs.org/). This includes things like: + * + * - Recursively deleting directories in Node.js (Sync, not Async) + * - Recursively copying directories in Node.js (Sync, not Async) + * - Recursively chmoding a directory structure from Node.js (Sync, not Async) + * - Other things that I'll add here as time goes on. Shhhh... + * + * These are all developed due to my work on Luno (http://github.com/ryanmcgrath/luno). + * + * ~ Ryan McGrath (ryan [at] venodesigns.net) + */ + +var fs = require("fs"), + sys = require("sys"); + +/* wrench.rmdirSyncRecursive("directory_path"); + * + * Recursively dives through directories and obliterates everything about it. This is a + * Sync-function, which blocks things until it's done. No idea why anybody would want an + * Asynchronous version. :\ + */ +exports.rmdirSyncRecursive = function(path) { + var files = fs.readdirSync(path), + currDir = path; + + /* Loop through and delete everything in the sub-tree after checking it */ + for(var i = 0; i < files.length; i++) { + var currFile = fs.statSync(currDir + "/" + files[i]); + + if(currFile.isDirectory()) // Recursive function back to the beginning + exports.rmdirSyncRecursive(currDir + "/" + files[i]); + + else if(currFile.isSymbolicLink()) // Unlink symlinks + fs.unlinkSync(currDir + "/" + files[i]); + + else // Assume it's a file - perhaps a try/catch belongs here? + fs.unlinkSync(currDir + "/" + files[i]); + } + + /* Now that we know everything in the sub-tree has been deleted, we can delete the main + directory. Huzzah for the shopkeep. */ + return fs.rmdirSync(path); +}; + +/* wrench.copyDirSyncRecursive("directory_to_copy", "new_directory_location", opts); + * + * Recursively dives through a directory and moves all its files to a new location. This is a + * Synchronous function, which blocks things until it's done. If you need/want to do this in + * an Asynchronous manner, look at wrench.copyDirRecursively() below. + * + * Note: Directories should be passed to this function without a trailing slash. + */ +exports.copyDirSyncRecursive = function(sourceDir, newDirLocation) { + /* Copying over something is... tricky. The user should know what they're doing at this point, so... + * blow any existing directory away! + */ + try { + if(fs.statSync(newDirLocation).isDirectory()) exports.rmdirSyncRecursive(newDirLocation); + } catch(e) { } + + /* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */ + var checkDir = fs.statSync(sourceDir); + fs.mkdirSync(newDirLocation, checkDir.mode); + + var files = fs.readdirSync(sourceDir); + + for(var i = 0; i < files.length; i++) { + var currFile = fs.statSync(sourceDir + "/" + files[i]); + + if(currFile.isDirectory()) { + /* Create a new directory in our copied version... */ + fs.mkdirSync(newDirLocation + "/" + files[i], currFile.mode); + + /* ...and then recursion this thing right on back. */ + exports.copyDirSyncRecursive(sourceDir + "/" + files[i], newDirLocation + "/" + files[i]); + } else if(currFile.isSymbolicLink()) { + var symlinkFull = fs.readlinkSync(sourceDir + "/" + files[i]); + fs.symlinkSync(symlinkFull, newDirLocation + "/" + files[i]); + } else { + /* At this point, we've hit a file actually worth copying... so copy it on over. */ + var contents = fs.readFileSync(sourceDir + "/" + files[i], encoding="utf8"); + fs.writeFileSync(newDirLocation + "/" + files[i], contents, encoding="utf8"); + } + } +}; + +/* wrench.chmodSyncRecursive("directory", filemode); + * + * Recursively dives through a directory and chmods everything to the desired mode. This is a + * Synchronous function, which blocks things until it's done. + * + * Note: Directories should be passed to this function without a trailing slash. + */ +exports.chmodSyncRecursive = function(sourceDir, filemode) { + var files = fs.readdirSync(sourceDir); + + for(var i = 0; i < files.length; i++) { + var currFile = fs.statSync(sourceDir + "/" + files[i]); + + if(currFile.isDirectory()) { + /* ...and recursion this thing right on back. */ + exports.chmodSyncRecursive(sourceDir + "/" + files[i], filemode); + } else { + /* At this point, we've hit a file actually worth copying... so copy it on over. */ + fs.chmod(sourceDir + "/" + files[i], filemode); + } + } + + /* Finally, chmod the parent directory */ + fs.chmod(sourceDir, filemode); +};