UNPKG

4.19 kBJavaScriptView Raw
1const path = require('path');
2const Git = require('./git');
3const async = require('async');
4const fs = require('fs-extra');
5
6/**
7 * Generate a list of unique directory paths given a list of file paths.
8 * @param {Array.<string>} files List of file paths.
9 * @return {Array.<string>} List of directory paths.
10 */
11const uniqueDirs = (exports.uniqueDirs = function(files) {
12 const dirs = {};
13 files.forEach(filepath => {
14 const parts = path.dirname(filepath).split(path.sep);
15 let partial = parts[0] || '/';
16 dirs[partial] = true;
17 for (let i = 1, ii = parts.length; i < ii; ++i) {
18 partial = path.join(partial, parts[i]);
19 dirs[partial] = true;
20 }
21 });
22 return Object.keys(dirs);
23});
24
25/**
26 * Sort function for paths. Sorter paths come first. Paths of equal length are
27 * sorted alphanumerically in path segment order.
28 * @param {string} a First path.
29 * @param {string} b Second path.
30 * @return {number} Comparison.
31 */
32const byShortPath = (exports.byShortPath = (a, b) => {
33 const aParts = a.split(path.sep);
34 const bParts = b.split(path.sep);
35 const aLength = aParts.length;
36 const bLength = bParts.length;
37 let cmp = 0;
38 if (aLength < bLength) {
39 cmp = -1;
40 } else if (aLength > bLength) {
41 cmp = 1;
42 } else {
43 let aPart, bPart;
44 for (let i = 0; i < aLength; ++i) {
45 aPart = aParts[i];
46 bPart = bParts[i];
47 if (aPart < bPart) {
48 cmp = -1;
49 break;
50 } else if (aPart > bPart) {
51 cmp = 1;
52 break;
53 }
54 }
55 }
56 return cmp;
57});
58
59/**
60 * Generate a list of directories to create given a list of file paths.
61 * @param {Array.<string>} files List of file paths.
62 * @return {Array.<string>} List of directory paths ordered by path length.
63 */
64const dirsToCreate = (exports.dirsToCreate = function(files) {
65 return uniqueDirs(files).sort(byShortPath);
66});
67
68/**
69 * Copy a file.
70 * @param {Object} obj Object with src and dest properties.
71 * @param {function(Error)} callback Callback
72 */
73const copyFile = (exports.copyFile = function(obj, callback) {
74 let called = false;
75 function done(err) {
76 if (!called) {
77 called = true;
78 callback(err);
79 }
80 }
81
82 const read = fs.createReadStream(obj.src);
83 read.on('error', err => {
84 done(err);
85 });
86
87 const write = fs.createWriteStream(obj.dest);
88 write.on('error', err => {
89 done(err);
90 });
91 write.on('close', () => {
92 done();
93 });
94
95 read.pipe(write);
96});
97
98/**
99 * Make directory, ignoring errors if directory already exists.
100 * @param {string} path Directory path.
101 * @param {function(Error)} callback Callback.
102 */
103function makeDir(path, callback) {
104 fs.mkdir(path, err => {
105 if (err) {
106 // check if directory exists
107 fs.stat(path, (err2, stat) => {
108 if (err2 || !stat.isDirectory()) {
109 callback(err);
110 } else {
111 callback();
112 }
113 });
114 } else {
115 callback();
116 }
117 });
118}
119
120/**
121 * Copy a list of files.
122 * @param {Array.<string>} files Files to copy.
123 * @param {string} base Base directory.
124 * @param {string} dest Destination directory.
125 * @return {Promise} A promise.
126 */
127exports.copy = function(files, base, dest) {
128 return new Promise((resolve, reject) => {
129 const pairs = [];
130 const destFiles = [];
131 files.forEach(file => {
132 const src = path.resolve(base, file);
133 const relative = path.relative(base, src);
134 const target = path.join(dest, relative);
135 pairs.push({
136 src: src,
137 dest: target
138 });
139 destFiles.push(target);
140 });
141
142 async.eachSeries(dirsToCreate(destFiles), makeDir, err => {
143 if (err) {
144 return reject(err);
145 }
146 async.each(pairs, copyFile, err => {
147 if (err) {
148 return reject(err);
149 } else {
150 return resolve();
151 }
152 });
153 });
154 });
155};
156
157exports.getUser = function(cwd) {
158 return Promise.all([
159 new Git(cwd).exec('config', 'user.name'),
160 new Git(cwd).exec('config', 'user.email')
161 ])
162 .then(results => {
163 return {name: results[0].output.trim(), email: results[1].output.trim()};
164 })
165 .catch(err => {
166 // git config exits with 1 if name or email is not set
167 return null;
168 });
169};