copy-dir
Version:
181 lines (174 loc) • 4.67 kB
JavaScript
var fs = require('fs');
var path = require('path');
/*
options: {
utimes: false, // Boolean | Object, keep utimes if true
mode: false, // Boolean | Number, keep file mode if true
cover: true, // Boolean, cover if file exists
filter: true, // Boolean | Function, file filter
}
*/
function copydir(from, to, options, callback) {
if (typeof options === 'function') {
if(!callback) {
callback = options;
options = {
filter: function() { return true; }
};
} else {
options = {
filter: options
};
}
}
if(typeof callback !== 'function') {
callback = function() {};
}
if(typeof options.cover === 'undefined') {
options.cover = true;
}
options.filter = typeof options.filter === 'function' ? options.filter : function(state, filepath, filename) {
return options.filter;
};
fs.lstat(from, function(err, stats) {
if (err) {
callback(err);
} else {
var statsname = stats.isDirectory() ? 'directory' :
stats.isFile() ? 'file' :
stats.isSymbolicLink() ? 'symbolicLink' :
'';
var valid = options.filter(statsname, from, path.basename(from));
if (statsname === 'directory' || statsname === 'symbolicLink') {
// Directory or SymbolicLink
if(valid) {
fs.stat(to, function(err) {
if(err) {
if(err.code === 'ENOENT') {
fs.mkdir(to, function(err) {
if(err) {
callback(err);
} else {
options.debug && console.log('>> ' + to);
rewrite(to, options, stats, function(err) {
if(err) {
callback(err);
} else {
listDirectory(from, to, options, callback);
}
});
}
});
} else {
callback(err);
}
} else {
rewrite(to, options, stats, function(err) {
if(err) {
callback(err);
} else {
listDirectory(from, to, options, callback);
}
});
}
});
} else {
callback();
}
} else if(stats.isFile()) {
// File
if(valid) {
if(options.cover) {
writeFile(from, to, options, stats, callback);
} else {
fs.stat(to, function(err) {
if(err) {
if(err.code === 'ENOENT') {
writeFile(from, to, options, stats, callback);
} else {
callback(err);
}
} else {
callback();
}
});
}
} else {
callback();
}
} else {
callback(new Error('stats invalid: '+ from));
}
}
});
}
function listDirectory(from, to, options, callback) {
fs.readdir(from, function(err, files) {
if(err) {
callback(err);
} else {
copyFromArray(files, from, to, options, callback);
}
});
}
function copyFromArray(files, from, to, options, callback) {
if(files.length === 0) {
callback(null);
} else {
var f = files.shift();
copydir(path.join(from, f), path.join(to, f), options, function(err) {
if(err) {
callback(err);
} else {
copyFromArray(files, from, to, options, callback);
}
});
}
}
function chmod(f, mode, callback) {
if(mode) {
fs.chmod(f, mode, callback);
} else {
callback();
}
}
function utimes(f, mode, callback) {
if(mode) {
fs.utimes(f, mode.atime, mode.mtime, callback);
} else {
callback();
}
}
function writeFile(from, to, options, stats, callback) {
fs.readFile(from, 'binary', function(err, data) {
if(err) {
callback(err);
} else {
fs.writeFile(to, data, 'binary', function(err) {
if(err) {
callback(err);
} else {
options.debug && console.log('>> ' + to);
rewrite(to, options, stats, callback);
}
});
}
});
}
function rewrite(f, options, stats, callback) {
if(options.cover) {
chmod(f, options.mode === true ? stats.mode : options.mode, function(err) {
if(err) {
callback(err);
} else {
utimes(f, options.utimes === true ? {
atime: stats.atime,
mtime: stats.mtime
} : options.utimes, callback);
}
});
} else {
callback();
}
}
module.exports = copydir;