Skip to content

Commit d347841

Browse files
author
Gyumeijie
committed
fix: #1
1 parent 885d222 commit d347841

File tree

3 files changed

+61
-305
lines changed

3 files changed

+61
-305
lines changed

index.js

Lines changed: 56 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ const fs = require('fs');
44
const os = require('os');
55
const url = require('url');
66
const axios = require('axios');
7-
const Promise = require('promise');
87
const shell = require('shelljs');
9-
const save = require('save-file');
108
const argsParser = require('args-parser');
119

1210
const AUTHOR = 1;
@@ -163,44 +161,13 @@ function parseInfo(repoInfo) {
163161
return info;
164162
}
165163

166-
function extractFilenameAndDirectoryFrom(path) {
167-
const components = path.split('/');
168-
const filename = components[components.length - 1];
169-
const directory = path.substring(0, path.length - filename.length);
170-
171-
return {
172-
filename,
173-
directory,
174-
};
175-
}
176-
177164
const basicOptions = {
178165
method: 'get',
179166
responseType: 'arrayBuffer',
180167
};
181168
// Global variable
182169
let repoInfo = {};
183170

184-
function saveFiles(files, requestPromises) {
185-
const rootDir = outputDirectory + repoInfo.rootDirectoryName;
186-
shell.mkdir('-p', rootDir);
187-
188-
Promise.all(requestPromises).then(() => {
189-
for (let i = 0; i < files.length; i++) {
190-
const pathForSave = extractFilenameAndDirectoryFrom(files[i].path.substring(decodeURI(repoInfo.resPath).length + 1));
191-
const dir = rootDir + pathForSave.directory;
192-
fs.exists(dir, ((i, dir, pathForSave, exists) => {
193-
if (!exists) {
194-
shell.mkdir('-p', dir);
195-
}
196-
save(files[i].data, dir + pathForSave.filename, (err) => {
197-
if (err) throw err;
198-
});
199-
}).bind(null, i, dir, pathForSave));
200-
}
201-
});
202-
}
203-
204171
function processClientError(error, retryCallback) {
205172
if (error.response.status === '401') {
206173
// Unauthorized
@@ -222,39 +189,61 @@ function processClientError(error, retryCallback) {
222189
}
223190
}
224191

225-
function fetchFile(path, url, files) {
226-
return axios({
227-
...basicOptions,
228-
url,
229-
...authenticationSwitch,
230-
}).then((file) => {
231-
console.log('downloading ', path);
232-
files.push({ path, data: file.data });
233-
}).catch((error) => {
234-
processClientError(error, fetchFile.bind(null, path, url, files));
235-
});
192+
function extractFilenameAndDirectoryFrom(path) {
193+
const components = path.split('/');
194+
const filename = components[components.length - 1];
195+
const directory = path.substring(0, path.length - filename.length);
196+
197+
return {
198+
filename,
199+
directory,
200+
};
201+
}
202+
203+
/*
204+
* @example
205+
* take fether --url='https://github.com/reduxjs/redux/tree/master/examples/async' for example:
206+
* all paths of files under the 'async' directory are prefixed with the so-called 'resPath', which
207+
* equals to 'example/async', and the 'rootDirectoryName' is 'async'. The 'resPath' could be very long,
208+
* and we don't need that deep path locally in fact. So we just remove the 'resPath' from the path of a file.
209+
*/
210+
function removeResPathFrom(path) {
211+
return path.substring(decodeURI(repoInfo.resPath).length + 1);
236212
}
237213

238-
function downloadFile(url) {
239-
console.log('downloading ', repoInfo.resPath);
214+
function constructLocalPathname(repoPath) {
215+
const partialPath = extractFilenameAndDirectoryFrom(removeResPathFrom(repoPath));
216+
const localRootDirectory = outputDirectory + repoInfo.rootDirectoryName;
217+
const localDirectory = localRootDirectory + partialPath.directory;
218+
219+
return {
220+
filename: partialPath.filename,
221+
directory: localDirectory,
222+
};
223+
}
240224

225+
function downloadFile(url, pathname) {
241226
axios({
242227
...basicOptions,
228+
responseType: 'stream',
243229
url,
244230
...authenticationSwitch,
245-
}).then((file) => {
246-
shell.mkdir('-p', outputDirectory);
247-
const pathForSave = extractFilenameAndDirectoryFrom(decodeURI(repoInfo.resPath));
231+
}).then((response) => {
232+
if (!fs.existsSync(pathname.directory)) {
233+
shell.mkdir('-p', pathname.directory);
234+
}
248235

249-
save(file.data, outputDirectory + pathForSave.filename, (err) => {
250-
if (err) throw err;
251-
});
236+
const localPathname = pathname.directory + pathname.filename;
237+
response.data.pipe(fs.createWriteStream(localPathname))
238+
.on('close', () => {
239+
console.log(`${localPathname} downloaded.`);
240+
});
252241
}).catch((error) => {
253-
processClientError(error, downloadFile.bind(null, url));
242+
processClientError(error, downloadFile.bind(null, url, pathname));
254243
});
255244
}
256245

257-
function iterateDirectory(dirPaths, files, requestPromises) {
246+
function iterateDirectory(dirPaths) {
258247
axios({
259248
...basicOptions,
260249
url: repoInfo.urlPrefix + dirPaths.pop() + repoInfo.urlPostfix,
@@ -265,59 +254,38 @@ function iterateDirectory(dirPaths, files, requestPromises) {
265254
if (data[i].type === 'dir') {
266255
dirPaths.push(data[i].path);
267256
} else if (data[i].download_url) {
268-
const promise = fetchFile(data[i].path, data[i].download_url, files);
269-
requestPromises.push(promise);
257+
const pathname = constructLocalPathname(data[i].path);
258+
downloadFile(data[i].download_url, pathname);
270259
} else {
271260
console.log(data[i]);
272261
}
273262
}
274263

275-
// Save files after we iterate all the directories
276-
if (dirPaths.length === 0) {
277-
saveFiles(files, requestPromises);
278-
} else {
279-
iterateDirectory(dirPaths, files, requestPromises);
264+
if (dirPaths.length !== 0) {
265+
iterateDirectory(dirPaths);
280266
}
281267
}).catch((error) => {
282-
processClientError(error, iterateDirectory.bind(null, dirPaths, files, requestPromises));
268+
processClientError(error, iterateDirectory.bind(null, dirPaths));
283269
});
284270
}
285271

286272
function downloadDirectory() {
287273
const dirPaths = [];
288-
const files = [];
289-
const requestPromises = [];
290-
291274
dirPaths.push(repoInfo.resPath);
292-
iterateDirectory(dirPaths, files, requestPromises);
275+
iterateDirectory(dirPaths);
293276
}
294277

295-
function initializeDownload(para) {
296-
repoInfo = parseInfo(para);
278+
function initializeDownload(paras) {
279+
repoInfo = parseInfo(paras);
297280

298281
if (!repoInfo.resPath || repoInfo.resPath === '') {
299282
if (!repoInfo.branch || repoInfo.branch === '') {
300283
repoInfo.branch = 'master';
301284
}
302285

303-
// Download the whole repository
286+
// Download the whole repository as a zip file
304287
const repoURL = `https://github.com/${repoInfo.author}/${repoInfo.repository}/archive/${repoInfo.branch}.zip`;
305-
306-
axios({
307-
...basicOptions,
308-
responseType: 'stream',
309-
url: repoURL,
310-
...authenticationSwitch,
311-
}).then((response) => {
312-
shell.mkdir('-p', outputDirectory);
313-
const filename = `${outputDirectory}${repoInfo.repository}.zip`;
314-
response.data.pipe(fs.createWriteStream(filename))
315-
.on('close', () => {
316-
console.log(`${filename} downloaded.`);
317-
});
318-
}).catch((error) => {
319-
processClientError(error, initializeDownload.bind(null, parameters));
320-
});
288+
downloadFile(repoURL, { directory: outputDirectory, filename: `${repoInfo.repository}.zip` });
321289
} else {
322290
// Download part(s) of repository
323291
axios({
@@ -328,10 +296,11 @@ function initializeDownload(para) {
328296
if (response.data instanceof Array) {
329297
downloadDirectory();
330298
} else {
331-
downloadFile(response.data.download_url);
299+
const partialPath = extractFilenameAndDirectoryFrom(decodeURI(repoInfo.resPath));
300+
downloadFile(response.data.download_url, { ...partialPath, directory: outputDirectory });
332301
}
333302
}).catch((error) => {
334-
processClientError(error, initializeDownload.bind(null, parameters));
303+
processClientError(error, initializeDownload.bind(null, paras));
335304
});
336305
}
337306
}

0 commit comments

Comments
 (0)