@bratel/dgit 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +6 -0
- package/.eslintrc.js +30 -0
- package/.github/workflows/nodejs.yml +70 -0
- package/LICENSE +21 -0
- package/README.en_US.md +167 -0
- package/README.md +182 -0
- package/bin/cmd.js +2 -0
- package/lib/cmd/action.d.ts +4 -0
- package/lib/cmd/action.js +92 -0
- package/lib/cmd/main.d.ts +1 -0
- package/lib/cmd/main.js +47 -0
- package/lib/cmd/prompt.d.ts +5 -0
- package/lib/cmd/prompt.js +93 -0
- package/lib/cmd/type.d.ts +37 -0
- package/lib/cmd/type.js +2 -0
- package/lib/cmd/utils.d.ts +8 -0
- package/lib/cmd/utils.js +87 -0
- package/lib/dgit.d.ts +3 -0
- package/lib/dgit.js +179 -0
- package/lib/log.d.ts +2 -0
- package/lib/log.js +12 -0
- package/lib/repo.d.ts +5 -0
- package/lib/repo.js +7 -0
- package/lib/request.d.ts +7 -0
- package/lib/request.js +75 -0
- package/lib/type.d.ts +53 -0
- package/lib/type.js +2 -0
- package/package.json +77 -0
- package/src/cmd/action.ts +128 -0
- package/src/cmd/main.ts +70 -0
- package/src/cmd/prompt.ts +98 -0
- package/src/cmd/type.ts +41 -0
- package/src/cmd/utils.ts +88 -0
- package/src/dgit.ts +256 -0
- package/src/log.ts +12 -0
- package/src/repo.ts +6 -0
- package/src/request.ts +110 -0
- package/src/type.ts +61 -0
- package/test/dgit.test.ts +132 -0
- package/tsconfig.json +75 -0
- package/x-npmrc +2 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PasswordPrompt = exports.DownloadPrompt = exports.CreatePrompt = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
|
|
6
|
+
exports.CreatePrompt = (questions) => inquirer_1.default.prompt(questions);
|
|
7
|
+
exports.DownloadPrompt = (currentInfo) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
8
|
+
if (currentInfo.owner &&
|
|
9
|
+
currentInfo.repoName &&
|
|
10
|
+
currentInfo.ref &&
|
|
11
|
+
currentInfo.relativePath &&
|
|
12
|
+
currentInfo.dest)
|
|
13
|
+
return currentInfo;
|
|
14
|
+
const questions = [
|
|
15
|
+
{
|
|
16
|
+
type: 'input',
|
|
17
|
+
name: 'owner',
|
|
18
|
+
when() {
|
|
19
|
+
return !currentInfo.owner;
|
|
20
|
+
},
|
|
21
|
+
validate(input) {
|
|
22
|
+
return input && input.length > 0;
|
|
23
|
+
},
|
|
24
|
+
message: 'input github ownername.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'input',
|
|
28
|
+
name: 'repoName',
|
|
29
|
+
when() {
|
|
30
|
+
return !currentInfo.repoName;
|
|
31
|
+
},
|
|
32
|
+
validate(input) {
|
|
33
|
+
return input && input.length > 0;
|
|
34
|
+
},
|
|
35
|
+
message: 'input github repoName.',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'input',
|
|
39
|
+
name: 'ref',
|
|
40
|
+
when() {
|
|
41
|
+
return !currentInfo.ref;
|
|
42
|
+
},
|
|
43
|
+
validate(input) {
|
|
44
|
+
return input && input.length > 0;
|
|
45
|
+
},
|
|
46
|
+
'default': 'master',
|
|
47
|
+
message: 'input github branch or commit hash or tagname.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: 'input',
|
|
51
|
+
name: 'relativePath',
|
|
52
|
+
when() {
|
|
53
|
+
return !currentInfo.relativePath;
|
|
54
|
+
},
|
|
55
|
+
validate(input) {
|
|
56
|
+
return input && input.length > 0;
|
|
57
|
+
},
|
|
58
|
+
'default': '.',
|
|
59
|
+
message: 'input github relative path.',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
type: 'input',
|
|
63
|
+
name: 'dest',
|
|
64
|
+
when() {
|
|
65
|
+
return !currentInfo.dest;
|
|
66
|
+
},
|
|
67
|
+
validate(input) {
|
|
68
|
+
return input && input.length > 0;
|
|
69
|
+
},
|
|
70
|
+
'default': '.',
|
|
71
|
+
message: 'input template output dest path.',
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
const answer = yield exports.CreatePrompt(questions);
|
|
75
|
+
return {
|
|
76
|
+
owner: answer.owner || currentInfo.owner,
|
|
77
|
+
dest: answer.dest || currentInfo.dest,
|
|
78
|
+
repoName: answer.repoName || currentInfo.repoName,
|
|
79
|
+
relativePath: answer.relativePath || currentInfo.relativePath,
|
|
80
|
+
ref: answer.ref || currentInfo.ref,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
exports.PasswordPrompt = () => {
|
|
84
|
+
const question = {
|
|
85
|
+
type: 'password',
|
|
86
|
+
name: 'password',
|
|
87
|
+
validate(input) {
|
|
88
|
+
return input && input.length > 0;
|
|
89
|
+
},
|
|
90
|
+
message: 'input github account password.',
|
|
91
|
+
};
|
|
92
|
+
return exports.CreatePrompt([question]);
|
|
93
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface PackageInfo {
|
|
2
|
+
version: string;
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
export interface CommandInfo {
|
|
6
|
+
dest?: string;
|
|
7
|
+
owner?: string;
|
|
8
|
+
repoName?: string;
|
|
9
|
+
ref?: string;
|
|
10
|
+
relativePath?: string;
|
|
11
|
+
parallelLimit?: string;
|
|
12
|
+
username?: string;
|
|
13
|
+
password?: string;
|
|
14
|
+
token?: string;
|
|
15
|
+
exclude?: string;
|
|
16
|
+
include?: string;
|
|
17
|
+
log?: boolean;
|
|
18
|
+
logPrefix?: string;
|
|
19
|
+
proxy?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface DownloadPromptInfo {
|
|
22
|
+
dest: string;
|
|
23
|
+
owner: string;
|
|
24
|
+
repoName: string;
|
|
25
|
+
ref: string;
|
|
26
|
+
relativePath: string;
|
|
27
|
+
}
|
|
28
|
+
export interface GithubLinkInfo {
|
|
29
|
+
owner: string;
|
|
30
|
+
repoName: string;
|
|
31
|
+
ref: string;
|
|
32
|
+
relativePath: string;
|
|
33
|
+
type: string;
|
|
34
|
+
}
|
|
35
|
+
export interface PasswordPromptInfo {
|
|
36
|
+
password: string;
|
|
37
|
+
}
|
package/lib/cmd/type.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PackageInfo, GithubLinkInfo } from './type';
|
|
2
|
+
export declare const GetPackageInfo: () => PackageInfo;
|
|
3
|
+
export declare const GITHUB_ADDRESS = "https://github.com/";
|
|
4
|
+
export declare const isHttpsLink: (link: string) => boolean;
|
|
5
|
+
export declare const ParseGithubHttpsLink: (httpsLink: string) => GithubLinkInfo;
|
|
6
|
+
export declare const TextEllipsis: (text: string, maxLen: number) => string;
|
|
7
|
+
export declare const MakeDirs: (dirs: string) => void;
|
|
8
|
+
export declare const AddExtraRandomQs: (origin: string) => string;
|
package/lib/cmd/utils.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AddExtraRandomQs = exports.MakeDirs = exports.TextEllipsis = exports.ParseGithubHttpsLink = exports.isHttpsLink = exports.GITHUB_ADDRESS = exports.GetPackageInfo = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
6
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
|
+
exports.GetPackageInfo = () => {
|
|
8
|
+
const buffer = fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../../package.json'));
|
|
9
|
+
return JSON.parse(buffer.toString());
|
|
10
|
+
};
|
|
11
|
+
exports.GITHUB_ADDRESS = 'https://github.com/';
|
|
12
|
+
exports.isHttpsLink = (link) => link.trim().startsWith(exports.GITHUB_ADDRESS);
|
|
13
|
+
exports.ParseGithubHttpsLink = (httpsLink) => {
|
|
14
|
+
let nextLink = httpsLink.trim().slice(exports.GITHUB_ADDRESS.length);
|
|
15
|
+
let index = nextLink.indexOf('/');
|
|
16
|
+
if (index === -1)
|
|
17
|
+
throw new Error('invalid github address.');
|
|
18
|
+
const owner = nextLink.slice(0, index);
|
|
19
|
+
nextLink = nextLink.slice(owner.length + 1);
|
|
20
|
+
index = nextLink.indexOf('/');
|
|
21
|
+
let repoName;
|
|
22
|
+
if (index === -1) {
|
|
23
|
+
repoName = nextLink.slice(0);
|
|
24
|
+
if (!repoName)
|
|
25
|
+
throw new Error('invalid github address.');
|
|
26
|
+
return {
|
|
27
|
+
owner,
|
|
28
|
+
repoName,
|
|
29
|
+
ref: 'master',
|
|
30
|
+
relativePath: '',
|
|
31
|
+
type: 'tree',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
repoName = nextLink.slice(0, index);
|
|
35
|
+
nextLink = nextLink.slice(repoName.length + 1);
|
|
36
|
+
index = nextLink.indexOf('/');
|
|
37
|
+
let ref = 'master';
|
|
38
|
+
let relativePath = '';
|
|
39
|
+
let type = 'tree';
|
|
40
|
+
if (index === -1) {
|
|
41
|
+
if (repoName.endsWith('.git')) {
|
|
42
|
+
const lastIndex = -4;
|
|
43
|
+
repoName = repoName.slice(0, lastIndex);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
type = nextLink.slice(0, index);
|
|
48
|
+
nextLink = nextLink.slice(type.length + 1);
|
|
49
|
+
index = nextLink.indexOf('/');
|
|
50
|
+
if (index === -1) {
|
|
51
|
+
ref = nextLink.slice(0) || 'master';
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
ref = nextLink.slice(0, index);
|
|
55
|
+
relativePath = nextLink.slice(ref.length + 1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
owner,
|
|
60
|
+
repoName,
|
|
61
|
+
ref,
|
|
62
|
+
relativePath,
|
|
63
|
+
type,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
exports.TextEllipsis = (text, maxLen) => (text.length >= maxLen ? `${text.slice(0, maxLen)}...` : text);
|
|
67
|
+
exports.MakeDirs = (dirs) => {
|
|
68
|
+
const mkdirs = (dir, callback) => {
|
|
69
|
+
if (fs_1.default.existsSync(dir)) {
|
|
70
|
+
callback && callback();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
mkdirs(path_1.default.dirname(dir), () => {
|
|
74
|
+
fs_1.default.mkdirSync(dir);
|
|
75
|
+
callback && callback();
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
if (fs_1.default.existsSync(dirs))
|
|
79
|
+
return;
|
|
80
|
+
mkdirs(dirs);
|
|
81
|
+
};
|
|
82
|
+
exports.AddExtraRandomQs = (origin) => {
|
|
83
|
+
if (origin.indexOf('?') !== -1) {
|
|
84
|
+
return `${origin}&_t=${Math.random()}`;
|
|
85
|
+
}
|
|
86
|
+
return `${origin}?_t=${Math.random()}`;
|
|
87
|
+
};
|
package/lib/dgit.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { DgitGlobalOption, RepoOptionType, DgitLifeCycle, DgitLoadGitTree } from './type';
|
|
2
|
+
declare const dgit: (repoOption: RepoOptionType, dPath: string, dgitOptions?: DgitGlobalOption | undefined, hooks?: (DgitLifeCycle & DgitLoadGitTree) | undefined) => Promise<void>;
|
|
3
|
+
export default dgit;
|
package/lib/dgit.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
5
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
6
|
+
const async_1 = tslib_1.__importDefault(require("async"));
|
|
7
|
+
const repo_1 = tslib_1.__importDefault(require("./repo"));
|
|
8
|
+
const log_1 = require("./log");
|
|
9
|
+
const request_1 = require("./request");
|
|
10
|
+
const utils_1 = require("./cmd/utils");
|
|
11
|
+
const UserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36';
|
|
12
|
+
const DEFAULT_PARALLEL_LIMIT = 10;
|
|
13
|
+
const MAX_PARALLEL_LIMIT = 100;
|
|
14
|
+
const JSON_STRINGIFY_PADDING = 2;
|
|
15
|
+
const dgit = (repoOption, dPath, dgitOptions, hooks) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
const { username, password, token, githubLink, proxy = '', } = repoOption;
|
|
17
|
+
let { owner, repoName, ref = 'master', relativePath = '.', } = repoOption;
|
|
18
|
+
if (githubLink && utils_1.isHttpsLink(githubLink)) {
|
|
19
|
+
const parseResult = utils_1.ParseGithubHttpsLink(githubLink);
|
|
20
|
+
owner = parseResult.owner;
|
|
21
|
+
repoName = parseResult.repoName;
|
|
22
|
+
ref = parseResult.ref;
|
|
23
|
+
relativePath = parseResult.relativePath;
|
|
24
|
+
}
|
|
25
|
+
if (!owner || !repoName) {
|
|
26
|
+
throw new Error('invalid repo option.');
|
|
27
|
+
}
|
|
28
|
+
const logger = log_1.createLogger(dgitOptions);
|
|
29
|
+
const { exclude = [], include = [] } = dgitOptions || {};
|
|
30
|
+
let { parallelLimit = DEFAULT_PARALLEL_LIMIT } = dgitOptions || {};
|
|
31
|
+
if (!parallelLimit || parallelLimit <= 0) {
|
|
32
|
+
logger('parallelLimit value is invalid.');
|
|
33
|
+
parallelLimit = DEFAULT_PARALLEL_LIMIT;
|
|
34
|
+
}
|
|
35
|
+
parallelLimit > MAX_PARALLEL_LIMIT && (parallelLimit = MAX_PARALLEL_LIMIT);
|
|
36
|
+
const { onSuccess, onError, onProgress, onFinish, onRetry, onResolved, beforeLoadTree, afterLoadTree, } = hooks || {};
|
|
37
|
+
let onSuccessResolve = () => { };
|
|
38
|
+
let onErrorReject = () => { };
|
|
39
|
+
const prom = new Promise((resolve, reject) => {
|
|
40
|
+
onSuccessResolve = resolve;
|
|
41
|
+
onErrorReject = reject;
|
|
42
|
+
});
|
|
43
|
+
const { getRepoTreeUrl, getDownloadUrl } = repo_1.default(owner, repoName, ref, proxy);
|
|
44
|
+
const url = getRepoTreeUrl();
|
|
45
|
+
const headers = {
|
|
46
|
+
'User-Agent': UserAgent,
|
|
47
|
+
Authorization: token ? `token ${token}` : undefined,
|
|
48
|
+
};
|
|
49
|
+
const auth = username && password ?
|
|
50
|
+
{
|
|
51
|
+
user: username,
|
|
52
|
+
pass: password,
|
|
53
|
+
sendImmediately: true,
|
|
54
|
+
} :
|
|
55
|
+
undefined;
|
|
56
|
+
const options = {
|
|
57
|
+
url, headers, auth,
|
|
58
|
+
};
|
|
59
|
+
const destPath = path_1.default.isAbsolute(dPath) ? dPath : path_1.default.resolve(process.cwd(), dPath);
|
|
60
|
+
logger(' request repo tree options.');
|
|
61
|
+
logger(JSON.stringify(options, null, JSON_STRINGIFY_PADDING));
|
|
62
|
+
try {
|
|
63
|
+
logger(' loading remote repo tree...');
|
|
64
|
+
beforeLoadTree && beforeLoadTree();
|
|
65
|
+
const body = yield request_1.requestGetPromise(options, dgitOptions || {}, {
|
|
66
|
+
onRetry() {
|
|
67
|
+
logger(` request ${url} failed. Retrying...`);
|
|
68
|
+
onRetry && onRetry();
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
logger(' loading remote repo tree succeed.');
|
|
72
|
+
afterLoadTree && afterLoadTree();
|
|
73
|
+
const result = JSON.parse(body);
|
|
74
|
+
if (!result.tree || result.tree.length <= 0) {
|
|
75
|
+
throw new Error('404 repo not found!');
|
|
76
|
+
}
|
|
77
|
+
const treeNodeList = result.tree;
|
|
78
|
+
const includeTreeNodeList = treeNodeList.filter(node => {
|
|
79
|
+
const nPath = path_1.default.resolve(__dirname, node.path);
|
|
80
|
+
const rPath = path_1.default.resolve(__dirname, relativePath);
|
|
81
|
+
if (!nPath.startsWith(rPath) || node.type !== 'blob') {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (exclude.some(v => nPath.startsWith(path_1.default.resolve(rPath, v))) &&
|
|
85
|
+
include.every(v => !nPath.startsWith(path_1.default.resolve(rPath, v)))) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
});
|
|
90
|
+
if (includeTreeNodeList.length <= 0) {
|
|
91
|
+
throw new Error(`404 repo ${relativePath} not found!`);
|
|
92
|
+
}
|
|
93
|
+
const totalStatus = includeTreeNodeList.reduce((prev, cur) => {
|
|
94
|
+
if (cur.type === 'blob') {
|
|
95
|
+
prev.size += cur.size;
|
|
96
|
+
prev.count++;
|
|
97
|
+
}
|
|
98
|
+
return prev;
|
|
99
|
+
}, { size: 0, count: 0 });
|
|
100
|
+
let currentSize = 0;
|
|
101
|
+
let currentCount = 0;
|
|
102
|
+
onResolved &&
|
|
103
|
+
onResolved({
|
|
104
|
+
currentSize,
|
|
105
|
+
currentCount,
|
|
106
|
+
totalSize: totalStatus.size,
|
|
107
|
+
totalCount: totalStatus.count,
|
|
108
|
+
});
|
|
109
|
+
logger(' include files resolved.');
|
|
110
|
+
logger('', JSON.stringify({
|
|
111
|
+
currentSize,
|
|
112
|
+
currentCount,
|
|
113
|
+
totalSize: totalStatus.size,
|
|
114
|
+
totalCount: totalStatus.count,
|
|
115
|
+
}));
|
|
116
|
+
async_1.default.eachLimit(includeTreeNodeList, parallelLimit, (node, callback) => {
|
|
117
|
+
const downloadUrl = getDownloadUrl(node.path);
|
|
118
|
+
const rPath = path_1.default.resolve(destPath, relativePath);
|
|
119
|
+
const tPath = path_1.default.resolve(destPath, node.path);
|
|
120
|
+
const root = path_1.default.resolve(destPath, '.');
|
|
121
|
+
let targetPath;
|
|
122
|
+
if (rPath === tPath) {
|
|
123
|
+
targetPath = path_1.default.resolve(destPath, path_1.default.basename(tPath));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
targetPath = tPath.replace(rPath, root);
|
|
127
|
+
}
|
|
128
|
+
logger('', node.path, relativePath, targetPath);
|
|
129
|
+
if (!fs_1.default.existsSync(path_1.default.dirname(targetPath))) {
|
|
130
|
+
utils_1.MakeDirs(path_1.default.dirname(targetPath));
|
|
131
|
+
}
|
|
132
|
+
const ws = fs_1.default.createWriteStream(targetPath);
|
|
133
|
+
logger(` downloading from ${downloadUrl}...`);
|
|
134
|
+
request_1.requestOnStream(downloadUrl, ws, dgitOptions || {}, {
|
|
135
|
+
onSuccess() {
|
|
136
|
+
currentCount++;
|
|
137
|
+
currentSize += node.size;
|
|
138
|
+
logger(` write file ${node.path} succeed.
|
|
139
|
+
size: [${currentSize}/${totalStatus.size}],
|
|
140
|
+
count: [${currentCount}/${totalStatus.count}]`);
|
|
141
|
+
onProgress &&
|
|
142
|
+
onProgress({
|
|
143
|
+
totalCount: totalStatus.count,
|
|
144
|
+
totalSize: totalStatus.size,
|
|
145
|
+
currentSize,
|
|
146
|
+
currentCount,
|
|
147
|
+
}, node);
|
|
148
|
+
callback();
|
|
149
|
+
},
|
|
150
|
+
onError(err) {
|
|
151
|
+
logger('', err);
|
|
152
|
+
callback(new Error(` request ${downloadUrl} failed.`));
|
|
153
|
+
},
|
|
154
|
+
onRetry() {
|
|
155
|
+
logger(` request ${downloadUrl} failed. Retrying...`);
|
|
156
|
+
onRetry && onRetry();
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
}, err => {
|
|
160
|
+
if (err) {
|
|
161
|
+
onError && onError(err);
|
|
162
|
+
onFinish && onFinish();
|
|
163
|
+
onErrorReject(err);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
onSuccess && onSuccess();
|
|
167
|
+
onFinish && onFinish();
|
|
168
|
+
onSuccessResolve();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
onError && onError(error);
|
|
174
|
+
onFinish && onFinish();
|
|
175
|
+
onErrorReject(error);
|
|
176
|
+
}
|
|
177
|
+
return prom;
|
|
178
|
+
});
|
|
179
|
+
exports.default = dgit;
|
package/lib/log.d.ts
ADDED
package/lib/log.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLogger = void 0;
|
|
4
|
+
const DEFAULT_PREFIX = '[dgit-logger]';
|
|
5
|
+
exports.createLogger = (option) => (...message) => {
|
|
6
|
+
if (option && option.log) {
|
|
7
|
+
const prefix = option ?
|
|
8
|
+
option.logPrefix || DEFAULT_PREFIX :
|
|
9
|
+
DEFAULT_PREFIX;
|
|
10
|
+
console.log(prefix, ...message, '\n');
|
|
11
|
+
}
|
|
12
|
+
};
|
package/lib/repo.d.ts
ADDED
package/lib/repo.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const repoUtils = (owner, repoName, ref, proxy) => ({
|
|
4
|
+
getRepoTreeUrl: () => `https://api.github.com/repos/${owner}/${repoName}/git/trees/${ref}?recursive=1`,
|
|
5
|
+
getDownloadUrl: (path) => `${proxy ? `${proxy}/` : ''}https://raw.githubusercontent.com/${owner}/${repoName}/${ref}/${path}`,
|
|
6
|
+
});
|
|
7
|
+
exports.default = repoUtils;
|
package/lib/request.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { UrlOptions, CoreOptions } from 'request';
|
|
3
|
+
import { DgitGlobalOption, DgitLifeCycle } from './type';
|
|
4
|
+
declare type RequestOption = UrlOptions & CoreOptions;
|
|
5
|
+
export declare const requestGetPromise: (options: RequestOption, dgitOptions: DgitGlobalOption, hooks?: DgitLifeCycle | undefined) => Promise<any>;
|
|
6
|
+
export declare const requestOnStream: (url: string, ws: fs.WriteStream, dgitOptions: DgitGlobalOption, hooks?: DgitLifeCycle | undefined) => void;
|
|
7
|
+
export {};
|
package/lib/request.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requestOnStream = exports.requestGetPromise = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const request_1 = tslib_1.__importDefault(require("request"));
|
|
6
|
+
const utils_1 = require("./cmd/utils");
|
|
7
|
+
const log_1 = require("./log");
|
|
8
|
+
const REQUEST_RETRY_DELAY = 1500;
|
|
9
|
+
const DEFAULT_MAX_RETRY_COUNT = 5;
|
|
10
|
+
const requestGet = (options, maxRetryCount, hooks) => {
|
|
11
|
+
const { onSuccess, onError, onFinish, onRetry, } = hooks || {};
|
|
12
|
+
request_1.default.get(options, (err, _, body) => {
|
|
13
|
+
if (err) {
|
|
14
|
+
if (maxRetryCount < 1) {
|
|
15
|
+
onError && onError(err);
|
|
16
|
+
onFinish && onFinish();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
setTimeout(() => {
|
|
20
|
+
onRetry && onRetry();
|
|
21
|
+
requestGet(options, maxRetryCount - 1, hooks);
|
|
22
|
+
}, REQUEST_RETRY_DELAY);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
onSuccess && onSuccess(body);
|
|
26
|
+
onFinish && onFinish();
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
exports.requestGetPromise = (options, dgitOptions, hooks) => new Promise((resolve, reject) => {
|
|
30
|
+
const { maxRetryCount = DEFAULT_MAX_RETRY_COUNT } = dgitOptions;
|
|
31
|
+
const { onSuccess, onError, onFinish, onRetry, } = hooks || {};
|
|
32
|
+
const newHooks = {
|
|
33
|
+
onSuccess(data) {
|
|
34
|
+
resolve(data);
|
|
35
|
+
onSuccess && onSuccess(data);
|
|
36
|
+
},
|
|
37
|
+
onError(err) {
|
|
38
|
+
reject(err);
|
|
39
|
+
onError && onError(err);
|
|
40
|
+
},
|
|
41
|
+
onFinish,
|
|
42
|
+
onRetry,
|
|
43
|
+
};
|
|
44
|
+
requestGet(options, maxRetryCount, newHooks);
|
|
45
|
+
});
|
|
46
|
+
exports.requestOnStream = (url, ws, dgitOptions, hooks) => {
|
|
47
|
+
const { maxRetryCount = DEFAULT_MAX_RETRY_COUNT } = dgitOptions;
|
|
48
|
+
const logger = log_1.createLogger(dgitOptions);
|
|
49
|
+
const { onSuccess, onError, onFinish, onRetry, } = hooks || {};
|
|
50
|
+
const fn = (retryCount) => {
|
|
51
|
+
const downloadUrl = utils_1.AddExtraRandomQs(url);
|
|
52
|
+
logger(` dowloading from ${downloadUrl}...`);
|
|
53
|
+
request_1.default(encodeURI(downloadUrl))
|
|
54
|
+
.on('error', err => {
|
|
55
|
+
if (retryCount <= 0) {
|
|
56
|
+
onError && onError(err);
|
|
57
|
+
onFinish && onFinish();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setTimeout(() => {
|
|
61
|
+
onRetry && onRetry();
|
|
62
|
+
fn(retryCount - 1);
|
|
63
|
+
}, REQUEST_RETRY_DELAY);
|
|
64
|
+
})
|
|
65
|
+
.pipe(ws);
|
|
66
|
+
};
|
|
67
|
+
ws.on('finish', () => {
|
|
68
|
+
onSuccess && onSuccess();
|
|
69
|
+
onFinish && onFinish();
|
|
70
|
+
});
|
|
71
|
+
ws.on('error', () => {
|
|
72
|
+
logger(` ${url}, write stream failed.`);
|
|
73
|
+
});
|
|
74
|
+
fn(maxRetryCount);
|
|
75
|
+
};
|
package/lib/type.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export declare type GithubRelativePath = string;
|
|
2
|
+
export interface DgitGlobalOption {
|
|
3
|
+
maxRetryCount?: number;
|
|
4
|
+
parallelLimit?: number;
|
|
5
|
+
log?: boolean;
|
|
6
|
+
logPrefix?: string;
|
|
7
|
+
exclude?: GithubRelativePath[];
|
|
8
|
+
include?: GithubRelativePath[];
|
|
9
|
+
}
|
|
10
|
+
export interface PrivateOption {
|
|
11
|
+
username?: string;
|
|
12
|
+
password?: string;
|
|
13
|
+
token?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RepoOptionType extends PrivateOption {
|
|
16
|
+
owner?: string;
|
|
17
|
+
repoName?: string;
|
|
18
|
+
ref?: string;
|
|
19
|
+
relativePath?: string;
|
|
20
|
+
githubLink?: string;
|
|
21
|
+
proxy?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface RepoTreeNode {
|
|
24
|
+
path: string;
|
|
25
|
+
type: 'blob' | 'tree';
|
|
26
|
+
size: number;
|
|
27
|
+
url: string;
|
|
28
|
+
}
|
|
29
|
+
export interface DgitLifeCycle {
|
|
30
|
+
onSuccess?: (data?: any) => void;
|
|
31
|
+
onError?: (err?: any) => void;
|
|
32
|
+
onFinish?: () => void;
|
|
33
|
+
onRetry?: () => void;
|
|
34
|
+
onProgress?: (status: ProgressStatus, node: RepoTreeNode) => void;
|
|
35
|
+
onResolved?: (status: ProgressStatus) => void;
|
|
36
|
+
}
|
|
37
|
+
export interface DgitLoadGitTree {
|
|
38
|
+
beforeLoadTree?: () => void;
|
|
39
|
+
afterLoadTree?: () => void;
|
|
40
|
+
}
|
|
41
|
+
export interface ProgressStatus {
|
|
42
|
+
currentSize: number;
|
|
43
|
+
currentCount: number;
|
|
44
|
+
totalCount: number;
|
|
45
|
+
totalSize: number;
|
|
46
|
+
}
|
|
47
|
+
export interface GithubLinkInfo {
|
|
48
|
+
owner: string;
|
|
49
|
+
repoName: string;
|
|
50
|
+
ref: string;
|
|
51
|
+
relativePath: string;
|
|
52
|
+
type: string;
|
|
53
|
+
}
|
package/lib/type.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bratel/dgit",
|
|
3
|
+
"version": "0.0.13",
|
|
4
|
+
"description": "@dking/hasaki-cli init application",
|
|
5
|
+
"bin": {
|
|
6
|
+
"dgit": "./bin/cmd.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "./lib/dgit.js",
|
|
9
|
+
"types": "./lib/dgit.d.ts",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"dgit",
|
|
12
|
+
"github download tool"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/lsq/dgit"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"author": "JohnApache",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build:ts": "tsc --build",
|
|
25
|
+
"lint": "eslint src --ext .jsx --ext .js --ext .tsx --ext .ts --cache --fix",
|
|
26
|
+
"test:mocha": "nyc --reporter=text mocha --require ts-node/register 'test/**/*.{ts,tsx}' -t 60000",
|
|
27
|
+
"test:mocha:reporter": "nyc --reporter=lcov --reporter=text mocha --require ts-node/register 'test/**/*.{ts,tsx}' -t 60000 --reporter=mochawesome",
|
|
28
|
+
"watch:ts": "tsc --watch",
|
|
29
|
+
"prepublishOnly": "npm run test:mocha && npm run lint && npm run build:ts"
|
|
30
|
+
},
|
|
31
|
+
"lint-staged": {
|
|
32
|
+
"**/*.{jsx,js}": [
|
|
33
|
+
"npm run lint",
|
|
34
|
+
"git add"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"husky": {
|
|
38
|
+
"hooks": {
|
|
39
|
+
"pre-commit": "lint-staged"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@dking/eslint-config-typescript": "^0.0.4",
|
|
44
|
+
"@types/chai": "^4.2.0",
|
|
45
|
+
"@types/mocha": "^5.2.7",
|
|
46
|
+
"@types/node": "^12.7.5",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "^2.13.0",
|
|
48
|
+
"@typescript-eslint/parser": "^2.13.0",
|
|
49
|
+
"chai": "^4.2.0",
|
|
50
|
+
"eslint": "^6.8.0",
|
|
51
|
+
"eslint-import-resolver-typescript": "^2.0.0",
|
|
52
|
+
"eslint-plugin-import": "^2.19.1",
|
|
53
|
+
"eslint-plugin-promise": "^4.1.1",
|
|
54
|
+
"husky": "^2.3.0",
|
|
55
|
+
"lint-staged": "^8.1.7",
|
|
56
|
+
"mocha": "^6.2.0",
|
|
57
|
+
"mochawesome": "^4.1.0",
|
|
58
|
+
"nyc": "^14.1.1",
|
|
59
|
+
"ts-node": "^8.3.0",
|
|
60
|
+
"typescript": "^3.7.4"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@types/async": "^3.0.3",
|
|
64
|
+
"@types/commander": "^2.12.2",
|
|
65
|
+
"@types/inquirer": "^6.5.0",
|
|
66
|
+
"@types/ora": "^3.2.0",
|
|
67
|
+
"@types/progress": "^2.0.3",
|
|
68
|
+
"@types/request": "^2.48.3",
|
|
69
|
+
"async": "^3.1.0",
|
|
70
|
+
"chalk": "^3.0.0",
|
|
71
|
+
"commander": "^4.0.1",
|
|
72
|
+
"inquirer": "^7.0.0",
|
|
73
|
+
"ora": "^4.0.3",
|
|
74
|
+
"progress": "^2.0.3",
|
|
75
|
+
"request": "^2.88.0"
|
|
76
|
+
}
|
|
77
|
+
}
|