1 var fs = require('fs'),
2 path = require('path');
7 function ncp (source, dest, options, callback) {
13 var basePath = process.cwd(),
14 currentPath = path.resolve(basePath, source),
15 targetPath = path.resolve(basePath, dest),
16 filter = options.filter,
17 transform = options.transform,
18 clobber = options.clobber !== false,
23 limit = options.limit || ncp.limit || 16;
25 limit = (limit < 1) ? 1 : (limit > 512) ? 512 : limit;
27 startCopy(currentPath);
29 function startCopy(source) {
32 if (filter instanceof RegExp) {
33 if (!filter.test(source)) {
37 else if (typeof filter === 'function') {
38 if (!filter(source)) {
43 return getStats(source);
47 if (typeof(setImmediate) === 'function')
48 return setImmediate(fn);
49 return process.nextTick(fn);
52 function getStats(source) {
53 if (running >= limit) {
54 return defer(function () {
59 fs.lstat(source, function (err, stats) {
65 // We need to get the mode from the stats object and preserve it.
67 item.mode = stats.mode;
69 if (stats.isDirectory()) {
72 else if (stats.isFile()) {
75 else if (stats.isSymbolicLink()) {
76 // Symlinks don't really need to know about the mode.
77 return onLink(source);
82 function onFile(file) {
83 var target = file.name.replace(currentPath, targetPath);
84 isWritable(target, function (writable) {
86 return copyFile(file, target);
89 rmFile(target, function () {
90 copyFile(file, target);
95 function copyFile(file, target) {
96 var readStream = fs.createReadStream(file.name),
97 writeStream = fs.createWriteStream(target, { mode: file.mode });
99 transform(readStream, writeStream,file);
101 readStream.pipe(writeStream);
103 readStream.once('end', cb);
106 function rmFile(file, done) {
107 fs.unlink(file, function (err) {
115 function onDir(dir) {
116 var target = dir.name.replace(currentPath, targetPath);
117 isWritable(target, function (writable) {
119 return mkDir(dir, target);
125 function mkDir(dir, target) {
126 fs.mkdir(target, dir.mode, function (err) {
134 function copyDir(dir) {
135 fs.readdir(dir, function (err, items) {
139 items.forEach(function (item) {
140 startCopy(dir + '/' + item);
146 function onLink(link) {
147 var target = link.replace(currentPath, targetPath);
148 fs.readlink(link, function (err, resolvedPath) {
152 checkLink(resolvedPath, target);
156 function checkLink(resolvedPath, target) {
157 isWritable(target, function (writable) {
159 return makeLink(resolvedPath, target);
161 fs.readlink(target, function (err, targetDest) {
165 if (targetDest === resolvedPath) {
168 return rmFile(target, function () {
169 makeLink(resolvedPath, target);
175 function makeLink(linkPath, target) {
176 fs.symlink(linkPath, target, function (err) {
184 function isWritable(path, done) {
185 fs.lstat(path, function (err, stats) {
187 if (err.code === 'ENOENT') return done(true);
194 function onError(err) {
195 if (options.stopOnError) {
196 return callback(err);
198 else if (!errs && options.errs) {
199 errs = fs.createWriteStream(options.errs);
204 if (typeof errs.write === 'undefined') {
208 errs.write(err.stack + '\n\n');
213 function cb(skipped) {
214 if (!skipped) running--;
216 if ((started === finished) && (running === 0)) {
217 return errs ? callback(errs) : callback(null);