diff --git a/lib/server/ncp.js b/lib/server/ncp.js new file mode 100644 index 00000000..2d23ff2b --- /dev/null +++ b/lib/server/ncp.js @@ -0,0 +1,234 @@ +/* ncp 0.5.0 */ + +var fs = require('fs'), + path = require('path'); + +module.exports = ncp; +ncp.ncp = ncp; + +function ncp (source, dest, options, callback) { + var cback = callback; + + if (!callback) { + cback = options; + options = {}; + } + + var basePath = process.cwd(), + currentPath = path.resolve(basePath, source), + targetPath = path.resolve(basePath, dest), + filter = options.filter, + transform = options.transform, + clobber = options.clobber !== false, + errs = null, + eventName = /^v0\.10\.\d+$/.test(process.version) ? 'finish' : 'close', + started = 0, + finished = 0, + running = 0, + limit = options.limit || ncp.limit || 16; + + limit = (limit < 1) ? 1 : (limit > 512) ? 512 : limit; + + startCopy(currentPath); + + function startCopy(source) { + started++; + if (filter) { + if (filter instanceof RegExp) { + if (!filter.test(source)) { + return cb(true); + } + } + else if (typeof filter === 'function') { + if (!filter(source)) { + return cb(true); + } + } + } + return getStats(source); + } + + function defer(fn) { + if (typeof(setImmediate) === 'function') + return setImmediate(fn); + return process.nextTick(fn); + } + + function getStats(source) { + if (running >= limit) { + return defer(function () { + getStats(source); + }); + } + running++; + fs.lstat(source, function (err, stats) { + var item = {}; + if (err) { + return onError(err); + } + + // We need to get the mode from the stats object and preserve it. + item.name = source; + item.mode = stats.mode; + + if (stats.isDirectory()) { + return onDir(item); + } + else if (stats.isFile()) { + return onFile(item); + } + else if (stats.isSymbolicLink()) { + // Symlinks don't really need to know about the mode. + return onLink(source); + } + }); + } + + function onFile(file) { + var target = file.name.replace(currentPath, targetPath); + isWritable(target, function (writable) { + if (writable) { + return copyFile(file, target); + } + if(clobber) { + rmFile(target, function () { + copyFile(file, target); + }); + } else { + return cb(); + } + }); + } + + function copyFile(file, target) { + var readStream = fs.createReadStream(file.name), + writeStream = fs.createWriteStream(target, { mode: file.mode }); + readStream.on('error', onError); + writeStream.on('error', onError); + if(transform) { + transform(readStream, writeStream,file); + } else { + writeStream.on('open', function() { + readStream.pipe(writeStream); + }); + } + writeStream.once(eventName, cb); + } + + function rmFile(file, done) { + fs.unlink(file, function (err) { + if (err) { + return onError(err); + } + return done(); + }); + } + + function onDir(dir) { + var target = dir.name.replace(currentPath, targetPath); + isWritable(target, function (writable) { + if (writable) { + return mkDir(dir, target); + } + copyDir(dir.name); + }); + } + + function mkDir(dir, target) { + fs.mkdir(target, dir.mode, function (err) { + if (err) { + return onError(err); + } + copyDir(dir.name); + }); + } + + function copyDir(dir) { + fs.readdir(dir, function (err, items) { + if (err) { + return onError(err); + } + items.forEach(function (item) { + startCopy(dir + '/' + item); + }); + return cb(); + }); + } + + function onLink(link) { + var target = link.replace(currentPath, targetPath); + fs.readlink(link, function (err, resolvedPath) { + if (err) { + return onError(err); + } + checkLink(resolvedPath, target); + }); + } + + function checkLink(resolvedPath, target) { + isWritable(target, function (writable) { + if (writable) { + return makeLink(resolvedPath, target); + } + fs.readlink(target, function (err, targetDest) { + if (err) { + return onError(err); + } + if (targetDest === resolvedPath) { + return cb(); + } + return rmFile(target, function () { + makeLink(resolvedPath, target); + }); + }); + }); + } + + function makeLink(linkPath, target) { + fs.symlink(linkPath, target, function (err) { + if (err) { + return onError(err); + } + return cb(); + }); + } + + function isWritable(path, done) { + fs.lstat(path, function (err) { + if (err) { + if (err.code === 'ENOENT') return done(true); + return done(false); + } + return done(false); + }); + } + + function onError(err) { + if (options.stopOnError) { + return cback(err); + } + else if (!errs && options.errs) { + errs = fs.createWriteStream(options.errs); + } + else if (!errs) { + errs = []; + } + if (typeof errs.write === 'undefined') { + errs.push(err); + } + else { + errs.write(err.stack + '\n\n'); + } + return cb(); + } + + function cb(skipped) { + if (!skipped) running--; + finished++; + if ((started === finished) && (running === 0)) { + if (cback !== undefined ) { + return errs ? cback(errs) : cback(null); + } + } + } +} diff --git a/lib/server/rest.js b/lib/server/rest.js index 91eef56f..b35d5178 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -36,7 +36,7 @@ NOT_LOG = true, - fse = main.require('fs-extra') || { + fse = main.require('ncp') || { copy : function(from, to, callback) { pipe.create({ from : from,