| | 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, |
| | rename = options.rename, |
| | transform = options.transform, |
| | clobber = options.clobber !== false, |
| | modified = options.modified, |
| | dereference = options.dereference, |
| | errs = null, |
| | 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 getStats(source) { |
| | var stat = dereference ? fs.stat : fs.lstat; |
| | if (running >= limit) { |
| | return setImmediate(function () { |
| | getStats(source); |
| | }); |
| | } |
| | running++; |
| | stat(source, function (err, stats) { |
| | var item = {}; |
| | if (err) { |
| | return onError(err); |
| | } |
| |
|
| | |
| | item.name = source; |
| | item.mode = stats.mode; |
| | item.mtime = stats.mtime; |
| | item.atime = stats.atime; |
| |
|
| | if (stats.isDirectory()) { |
| | return onDir(item); |
| | } |
| | else if (stats.isFile()) { |
| | return onFile(item); |
| | } |
| | else if (stats.isSymbolicLink()) { |
| | |
| | return onLink(source); |
| | } |
| | }); |
| | } |
| |
|
| | function onFile(file) { |
| | var target = file.name.replace(currentPath, targetPath); |
| | if(rename) { |
| | target = rename(target); |
| | } |
| | isWritable(target, function (writable) { |
| | if (writable) { |
| | return copyFile(file, target); |
| | } |
| | if(clobber) { |
| | rmFile(target, function () { |
| | copyFile(file, target); |
| | }); |
| | } |
| | if (modified) { |
| | var stat = dereference ? fs.stat : fs.lstat; |
| | stat(target, function(err, stats) { |
| | |
| | if (file.mtime.getTime()>stats.mtime.getTime()) |
| | copyFile(file, target); |
| | else return cb(); |
| | }); |
| | } |
| | 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('finish', function() { |
| | if (modified) { |
| | |
| | fs.utimesSync(target, file.atime, file.mtime); |
| | cb(); |
| | } |
| | else 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(path.join(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) { |
| | if (dereference) { |
| | resolvedPath = path.resolve(basePath, resolvedPath); |
| | } |
| | isWritable(target, function (writable) { |
| | if (writable) { |
| | return makeLink(resolvedPath, target); |
| | } |
| | fs.readlink(target, function (err, targetDest) { |
| | if (err) { |
| | return onError(err); |
| | } |
| | if (dereference) { |
| | targetDest = path.resolve(basePath, targetDest); |
| | } |
| | 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); |
| | } |
| | } |
| | } |
| | } |
| |
|
| |
|
| |
|