170 lines
4.1 KiB
JavaScript
170 lines
4.1 KiB
JavaScript
|
const path = require('path');
|
||
|
const Git = require('./git');
|
||
|
const async = require('async');
|
||
|
const fs = require('fs-extra');
|
||
|
|
||
|
/**
|
||
|
* Generate a list of unique directory paths given a list of file paths.
|
||
|
* @param {Array.<string>} files List of file paths.
|
||
|
* @return {Array.<string>} List of directory paths.
|
||
|
*/
|
||
|
const uniqueDirs = (exports.uniqueDirs = function(files) {
|
||
|
const dirs = {};
|
||
|
files.forEach(filepath => {
|
||
|
const parts = path.dirname(filepath).split(path.sep);
|
||
|
let partial = parts[0] || '/';
|
||
|
dirs[partial] = true;
|
||
|
for (let i = 1, ii = parts.length; i < ii; ++i) {
|
||
|
partial = path.join(partial, parts[i]);
|
||
|
dirs[partial] = true;
|
||
|
}
|
||
|
});
|
||
|
return Object.keys(dirs);
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Sort function for paths. Sorter paths come first. Paths of equal length are
|
||
|
* sorted alphanumerically in path segment order.
|
||
|
* @param {string} a First path.
|
||
|
* @param {string} b Second path.
|
||
|
* @return {number} Comparison.
|
||
|
*/
|
||
|
const byShortPath = (exports.byShortPath = (a, b) => {
|
||
|
const aParts = a.split(path.sep);
|
||
|
const bParts = b.split(path.sep);
|
||
|
const aLength = aParts.length;
|
||
|
const bLength = bParts.length;
|
||
|
let cmp = 0;
|
||
|
if (aLength < bLength) {
|
||
|
cmp = -1;
|
||
|
} else if (aLength > bLength) {
|
||
|
cmp = 1;
|
||
|
} else {
|
||
|
let aPart, bPart;
|
||
|
for (let i = 0; i < aLength; ++i) {
|
||
|
aPart = aParts[i];
|
||
|
bPart = bParts[i];
|
||
|
if (aPart < bPart) {
|
||
|
cmp = -1;
|
||
|
break;
|
||
|
} else if (aPart > bPart) {
|
||
|
cmp = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return cmp;
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Generate a list of directories to create given a list of file paths.
|
||
|
* @param {Array.<string>} files List of file paths.
|
||
|
* @return {Array.<string>} List of directory paths ordered by path length.
|
||
|
*/
|
||
|
const dirsToCreate = (exports.dirsToCreate = function(files) {
|
||
|
return uniqueDirs(files).sort(byShortPath);
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Copy a file.
|
||
|
* @param {Object} obj Object with src and dest properties.
|
||
|
* @param {function(Error)} callback Callback
|
||
|
*/
|
||
|
const copyFile = (exports.copyFile = function(obj, callback) {
|
||
|
let called = false;
|
||
|
function done(err) {
|
||
|
if (!called) {
|
||
|
called = true;
|
||
|
callback(err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const read = fs.createReadStream(obj.src);
|
||
|
read.on('error', err => {
|
||
|
done(err);
|
||
|
});
|
||
|
|
||
|
const write = fs.createWriteStream(obj.dest);
|
||
|
write.on('error', err => {
|
||
|
done(err);
|
||
|
});
|
||
|
write.on('close', () => {
|
||
|
done();
|
||
|
});
|
||
|
|
||
|
read.pipe(write);
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Make directory, ignoring errors if directory already exists.
|
||
|
* @param {string} path Directory path.
|
||
|
* @param {function(Error)} callback Callback.
|
||
|
*/
|
||
|
function makeDir(path, callback) {
|
||
|
fs.mkdir(path, err => {
|
||
|
if (err) {
|
||
|
// check if directory exists
|
||
|
fs.stat(path, (err2, stat) => {
|
||
|
if (err2 || !stat.isDirectory()) {
|
||
|
callback(err);
|
||
|
} else {
|
||
|
callback();
|
||
|
}
|
||
|
});
|
||
|
} else {
|
||
|
callback();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Copy a list of files.
|
||
|
* @param {Array.<string>} files Files to copy.
|
||
|
* @param {string} base Base directory.
|
||
|
* @param {string} dest Destination directory.
|
||
|
* @return {Promise} A promise.
|
||
|
*/
|
||
|
exports.copy = function(files, base, dest) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const pairs = [];
|
||
|
const destFiles = [];
|
||
|
files.forEach(file => {
|
||
|
const src = path.resolve(base, file);
|
||
|
const relative = path.relative(base, src);
|
||
|
const target = path.join(dest, relative);
|
||
|
pairs.push({
|
||
|
src: src,
|
||
|
dest: target
|
||
|
});
|
||
|
destFiles.push(target);
|
||
|
});
|
||
|
|
||
|
async.eachSeries(dirsToCreate(destFiles), makeDir, err => {
|
||
|
if (err) {
|
||
|
return reject(err);
|
||
|
}
|
||
|
async.each(pairs, copyFile, err => {
|
||
|
if (err) {
|
||
|
return reject(err);
|
||
|
} else {
|
||
|
return resolve();
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
};
|
||
|
|
||
|
exports.getUser = function(cwd) {
|
||
|
return Promise.all([
|
||
|
new Git(cwd).exec('config', 'user.name'),
|
||
|
new Git(cwd).exec('config', 'user.email')
|
||
|
])
|
||
|
.then(results => {
|
||
|
return {name: results[0].output.trim(), email: results[1].output.trim()};
|
||
|
})
|
||
|
.catch(err => {
|
||
|
// git config exits with 1 if name or email is not set
|
||
|
return null;
|
||
|
});
|
||
|
};
|