@boses/github-clone 1.1.6 → 1.2.1
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/README.md +210 -72
- package/dist/config/mirror-store.d.ts +10 -0
- package/dist/config/mirror-store.d.ts.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/core/clone-repo.d.ts +4 -0
- package/dist/core/clone-repo.d.ts.map +1 -0
- package/dist/core/git-client.d.ts +13 -0
- package/dist/core/git-client.d.ts.map +1 -0
- package/dist/core/github-url.d.ts +11 -0
- package/dist/core/github-url.d.ts.map +1 -0
- package/dist/gitClone.esm.js +108 -2
- package/dist/gitClone.esm.js.map +1 -1
- package/dist/gitClone.js +132 -2
- package/dist/gitClone.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/main.js +335 -2
- package/dist/main.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +51 -56
- package/dist/src/config.d.ts +0 -11
- package/dist/src/gitClone.d.ts +0 -9
- package/dist/src/main.d.ts +0 -2
- package/dist/src/utils.d.ts +0 -17
- /package/{License → LICENSE} +0 -0
package/README.md
CHANGED
|
@@ -1,127 +1,265 @@
|
|
|
1
1
|
# GitHub-clone
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
解决国内 GitHub `clone` 速度慢、连接不稳定问题的 CLI 工具。
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
>
|
|
9
|
-
> `[]`代表这个字段必填,`<>`则为选填,而`name?:string`,表示这个参数为非必填为`string`类型。
|
|
7
|
+
安装后使用 `g` 命令。配置镜像后,工具会**通过镜像加速下载**,克隆完成后**自动将 `origin` 恢复为 GitHub 官方地址**,便于后续 `push`。
|
|
10
8
|
|
|
11
|
-
##
|
|
9
|
+
## 快速开始
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
```sh
|
|
12
|
+
g mirror set kgithub # 配置镜像(首次使用建议执行)
|
|
13
|
+
g mirror test # 可选:探测镜像是否可用
|
|
14
|
+
g clone https://github.com/owner/repo
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
未配置镜像时,`g clone` 将直连 `github.com`。
|
|
18
|
+
|
|
19
|
+
## 工作原理
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
输入 URL →(若已配置镜像)替换为镜像域名 → git clone → git remote set-url origin(恢复官方地址)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
1. 若已通过 `g mirror set` 配置镜像,将 URL 中的 `github.com` 替换为镜像域名
|
|
26
|
+
2. 执行 `git clone`
|
|
27
|
+
3. 使用镜像时,克隆完成后将 `origin` 改回 `https://github.com/<owner>/<repo>.git`
|
|
16
28
|
|
|
17
|
-
|
|
29
|
+
镜像配置保存在 `~/.g.config`(纯文本,内容为镜像域名,如 `kgithub.com`)。
|
|
18
30
|
|
|
19
|
-
|
|
31
|
+
## 安装
|
|
20
32
|
|
|
21
33
|
```sh
|
|
22
|
-
|
|
34
|
+
pnpm add -g @boses/github-clone
|
|
35
|
+
# 或
|
|
36
|
+
npm i -g @boses/github-clone
|
|
23
37
|
```
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
```sh
|
|
40
|
+
g mirror set kgithub
|
|
41
|
+
g clone https://github.com/bosens-China/github-clone
|
|
42
|
+
g --help
|
|
43
|
+
```
|
|
26
44
|
|
|
27
|
-
|
|
45
|
+
## CLI 命令
|
|
28
46
|
|
|
29
|
-
|
|
47
|
+
### `g clone` — 克隆仓库
|
|
30
48
|
|
|
31
|
-
|
|
49
|
+
```sh
|
|
50
|
+
g clone <url> [dir] [options]
|
|
51
|
+
```
|
|
32
52
|
|
|
33
|
-
|
|
53
|
+
| 参数 / 选项 | 说明 |
|
|
54
|
+
|-------------|------|
|
|
55
|
+
| `url` | GitHub 仓库地址(必填) |
|
|
56
|
+
| `dir` | 本地目录名;省略则使用仓库名 |
|
|
57
|
+
| `-b, --branch <name>` | 只检出指定分支(等价于 `git clone --branch`) |
|
|
58
|
+
| `--depth <n>` | 浅克隆:只拉取最近 `n` 次提交。`--depth 1` 表示只要最新版本,更快更省空间,但本地没有完整 Git 历史 |
|
|
59
|
+
| `--single-branch` | 只克隆单个分支,不下载其他远程分支。常与 `-b` 联用;单独使用时默认只拉远程默认分支(通常是 `main`) |
|
|
60
|
+
| `--no-mirror` | 忽略已配置的镜像,强制直连 `github.com` |
|
|
61
|
+
| `--verbose` | 克隆前输出详细计划:镜像/直连模式、实际克隆地址、origin 恢复地址、本地目录、Git 参数 |
|
|
34
62
|
|
|
35
|
-
|
|
36
|
-
- require:`true`
|
|
63
|
+
**支持的 URL 格式:**
|
|
37
64
|
|
|
38
|
-
|
|
65
|
+
| 类型 | 示例 |
|
|
66
|
+
|------|------|
|
|
67
|
+
| HTTPS | `https://github.com/owner/repo` |
|
|
68
|
+
| HTTPS + `.git` | `https://github.com/owner/repo.git` |
|
|
69
|
+
| SSH | `git@github.com:owner/repo` 或 `git@github.com:owner/repo.git` |
|
|
39
70
|
|
|
40
|
-
|
|
41
|
-
| ------------------------------------------------ | ----------------------------- |
|
|
42
|
-
| https://github.com/bosens-China/github-clone | 默认浏览器导航栏的地址 |
|
|
43
|
-
| https://github.com/bosens-China/github-clone.git | Github 右侧 Code HTTPS 的地址 |
|
|
44
|
-
| git@github.com:bosens-China/breeze-clone.git | Github 右侧 Code SSH 的地址 |
|
|
71
|
+
**示例:**
|
|
45
72
|
|
|
46
|
-
|
|
73
|
+
```sh
|
|
74
|
+
# 配置镜像后克隆
|
|
75
|
+
g mirror set kgithub
|
|
76
|
+
g clone https://github.com/bosens-China/github-clone
|
|
47
77
|
|
|
48
|
-
|
|
49
|
-
|
|
78
|
+
# 指定目录与分支
|
|
79
|
+
g clone https://github.com/bosens-China/github-clone.git my-dir -b dev
|
|
50
80
|
|
|
51
|
-
|
|
81
|
+
# 最小体积:只要 main 分支最近一次提交
|
|
82
|
+
g clone https://github.com/bosens-China/github-clone -b main --single-branch --depth 1
|
|
52
83
|
|
|
53
|
-
|
|
84
|
+
# 已配置镜像但本次强制直连
|
|
85
|
+
g clone https://github.com/bosens-China/github-clone --no-mirror --verbose
|
|
54
86
|
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
# SSH 地址同样支持
|
|
88
|
+
g clone git@github.com:bosens-China/github-clone.git
|
|
89
|
+
```
|
|
57
90
|
|
|
58
|
-
|
|
91
|
+
**默认输出示例:**
|
|
59
92
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
93
|
+
```text
|
|
94
|
+
→ 使用镜像 kgithub.com
|
|
95
|
+
... git clone 进度 ...
|
|
96
|
+
✓ 克隆完成:repo
|
|
97
|
+
已恢复 origin 为 GitHub 官方地址
|
|
98
|
+
```
|
|
63
99
|
|
|
64
|
-
|
|
100
|
+
**`--verbose` 输出示例:**
|
|
65
101
|
|
|
66
|
-
|
|
102
|
+
```text
|
|
103
|
+
克隆计划
|
|
104
|
+
模式 : 镜像(kgithub.com)
|
|
105
|
+
克隆地址 : https://kgithub.com/owner/repo.git
|
|
106
|
+
恢复 origin: https://github.com/owner/repo.git
|
|
107
|
+
本地目录 : repo
|
|
108
|
+
Git 参数 : --branch dev --depth 1 --single-branch
|
|
67
109
|
|
|
68
|
-
|
|
69
|
-
|
|
110
|
+
... git clone 进度 ...
|
|
111
|
+
✓ 克隆完成:repo,分支 dev
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `g mirror` — 镜像管理
|
|
70
115
|
|
|
71
|
-
|
|
116
|
+
| 命令 | 说明 |
|
|
117
|
+
|------|------|
|
|
118
|
+
| `g mirror list`(`ls`) | 列出内置镜像预设,标注当前使用的镜像 |
|
|
119
|
+
| `g mirror set <host\|preset>` | 设置镜像域名或预设名(`kgithub` / `moeyy`) |
|
|
120
|
+
| `g mirror get` | 查看 `~/.g.config` 中的自定义镜像 |
|
|
121
|
+
| `g mirror test [host]` | 对镜像发起 HTTP HEAD 探测是否可达(超时 10 秒) |
|
|
122
|
+
| `g mirror unset` | 删除 `~/.g.config` |
|
|
72
123
|
|
|
73
|
-
|
|
124
|
+
**内置预设:**
|
|
74
125
|
|
|
75
|
-
|
|
126
|
+
| 预设名 | 域名 | 说明 |
|
|
127
|
+
|--------|------|------|
|
|
128
|
+
| `kgithub` | `kgithub.com` | KGitHub 镜像(域名替换) |
|
|
129
|
+
| `moeyy` | `github.moeyy.xyz` | Moeyy 镜像(域名替换) |
|
|
76
130
|
|
|
77
|
-
|
|
131
|
+
**镜像与克隆的关系:**
|
|
78
132
|
|
|
79
|
-
|
|
133
|
+
| 场景 | `g mirror get` | `g clone` 行为 |
|
|
134
|
+
|------|----------------|----------------|
|
|
135
|
+
| 未配置镜像 | 提示未配置 | 直连 GitHub |
|
|
136
|
+
| 已 `mirror set` | 显示配置的域名 | 使用配置的镜像 |
|
|
137
|
+
| 已 `mirror unset` | 提示未配置 | 直连 GitHub |
|
|
138
|
+
| 已配置镜像 + `--no-mirror` | — | 强制直连 GitHub |
|
|
139
|
+
|
|
140
|
+
## 编程式 API
|
|
80
141
|
|
|
81
142
|
```sh
|
|
82
|
-
|
|
143
|
+
pnpm add @boses/github-clone
|
|
83
144
|
```
|
|
84
145
|
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
clone('https://github.com/
|
|
146
|
+
```ts
|
|
147
|
+
import clone from '@boses/github-clone';
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
clone('https://github.com/bosens-China/github-clone', {
|
|
151
|
+
mirrorHost: 'kgithub.com',
|
|
152
|
+
branch: 'main',
|
|
153
|
+
depth: 1,
|
|
154
|
+
singleBranch: true,
|
|
155
|
+
silence: true,
|
|
156
|
+
});
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error(error);
|
|
159
|
+
}
|
|
90
160
|
```
|
|
91
161
|
|
|
92
|
-
|
|
162
|
+
### `clone(url, options?)`
|
|
163
|
+
|
|
164
|
+
同步 API,请用 `try/catch` 处理错误。不会读取 `~/.g.config`,镜像需通过 `mirrorHost` 显式传入。
|
|
165
|
+
|
|
166
|
+
| 选项 | 类型 | 说明 |
|
|
167
|
+
|------|------|------|
|
|
168
|
+
| `dirName` | `string` | 本地目录名 |
|
|
169
|
+
| `branch` | `string` | 分支名 |
|
|
170
|
+
| `mirrorHost` | `string` | 镜像域名(API 不会读取 `~/.g.config`,需显式传入) |
|
|
171
|
+
| `cwd` | `string` | 执行目录,默认 `process.cwd()` |
|
|
172
|
+
| `silence` | `boolean` | 静默模式,不继承 Git 输出 |
|
|
173
|
+
| `depth` | `number` | 浅克隆深度 |
|
|
174
|
+
| `singleBranch` | `boolean` | 仅克隆指定分支 |
|
|
175
|
+
|
|
176
|
+
## 项目结构
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
src/
|
|
180
|
+
├── index.ts # 库 API 入口
|
|
181
|
+
├── constants.ts
|
|
182
|
+
├── types.ts
|
|
183
|
+
├── cli/
|
|
184
|
+
│ ├── index.ts # CLI 入口
|
|
185
|
+
│ └── commands/
|
|
186
|
+
│ ├── clone-command.ts
|
|
187
|
+
│ └── mirror-command.ts
|
|
188
|
+
├── config/
|
|
189
|
+
│ └── mirror-store.ts
|
|
190
|
+
└── core/
|
|
191
|
+
├── clone-repo.ts
|
|
192
|
+
├── git-client.ts
|
|
193
|
+
└── github-url.ts
|
|
194
|
+
|
|
195
|
+
test/ # 测试目录(与 src 目录结构对应)
|
|
196
|
+
```
|
|
93
197
|
|
|
94
|
-
|
|
198
|
+
命名约定:多词文件名使用 **kebab-case**;`index.ts` 作为模块入口。
|
|
95
199
|
|
|
96
|
-
|
|
200
|
+
## 开发
|
|
97
201
|
|
|
98
|
-
|
|
202
|
+
要求:Node ≥ 22,pnpm,TypeScript strict。
|
|
203
|
+
|
|
204
|
+
```sh
|
|
205
|
+
pnpm install
|
|
206
|
+
pnpm dev # 监听模式运行 CLI(src/cli/index.ts)
|
|
207
|
+
pnpm test # 运行测试
|
|
208
|
+
pnpm test:coverage # 测试 + 覆盖率
|
|
209
|
+
pnpm type-check # TypeScript 类型检查
|
|
210
|
+
pnpm lint # ESLint 检查
|
|
211
|
+
pnpm format # ESLint 自动修复(与 pre-commit 钩子一致)
|
|
212
|
+
pnpm build # Rolldown 打包 + 生成 .d.ts
|
|
213
|
+
```
|
|
99
214
|
|
|
100
|
-
-
|
|
101
|
-
- require: `true`
|
|
215
|
+
`pnpm install` 会通过 `prepare` 脚本自动安装 Husky 钩子。提交时 `pre-commit` 会对暂存的 `*.{ts,mjs}` 文件执行 `eslint --fix`。
|
|
102
216
|
|
|
103
|
-
|
|
217
|
+
## 发布流程
|
|
104
218
|
|
|
105
|
-
|
|
219
|
+
本仓库通过 GitHub Actions + [release-please](https://github.com/googleapis/release-please) + npm Trusted Publishing 自动化发布,无需手动维护版本号、CHANGELOG,也无需 `NPM_TOKEN`。
|
|
220
|
+
|
|
221
|
+
**两条 workflow:**
|
|
222
|
+
|
|
223
|
+
| 文件 | 触发 | 作用 |
|
|
224
|
+
|------|------|------|
|
|
225
|
+
| `.github/workflows/ci.yml` | `pull_request` / `push` 到 `main` | lint + type-check + 测试 + 构建校验 |
|
|
226
|
+
| `.github/workflows/release.yml` | `push` 到 `main` | release-please 维护 release PR;该 PR 合并后自动 `npm publish` |
|
|
227
|
+
|
|
228
|
+
**贡献提交规范(Conventional Commits):**
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
feat: ... # 新功能(minor)
|
|
232
|
+
fix: ... # bug 修复(patch)
|
|
233
|
+
perf: ... # 性能优化(patch)
|
|
234
|
+
refactor: ... # 重构(patch)
|
|
235
|
+
docs: ... # 文档
|
|
236
|
+
test: ... # 测试
|
|
237
|
+
chore: ... # 杂项(不进 CHANGELOG)
|
|
238
|
+
|
|
239
|
+
# 破坏性变更(major)
|
|
240
|
+
feat!: ...
|
|
241
|
+
或在 body 中写 BREAKING CHANGE: ...
|
|
242
|
+
```
|
|
106
243
|
|
|
107
|
-
|
|
108
|
-
| ------------- | --------- | -------- | ------------------------------------------ |
|
|
109
|
-
| dirName | `string` | `false` | 拉取的目录名称 |
|
|
110
|
-
| branch | `string` | `false` | 拉取的分支名称 |
|
|
111
|
-
| mirrorAddress | `string` | `false` | 镜像网站,如果你需要使用镜像可以填写此网站 |
|
|
112
|
-
| silence | `boolean` | `false` | 是否静默模式执行 clone |
|
|
113
|
-
| cwd | `string` | `false` | 执行 clone 所执行的目录路径 |
|
|
244
|
+
**发布步骤:**
|
|
114
245
|
|
|
115
|
-
|
|
246
|
+
1. 在 PR 中按上述格式写 commit
|
|
247
|
+
2. PR 合并到 `main`
|
|
248
|
+
3. release-please 自动在仓库里维护一个 `chore(main): release x.y.z` PR,里面包含 CHANGELOG 与版本号更新
|
|
249
|
+
4. Review 并合并该 PR → 自动打 tag → 触发 `npm publish`(OIDC,无 token)
|
|
250
|
+
5. 在 npmjs.com 用 2FA approve staged package → 上线
|
|
116
251
|
|
|
117
|
-
|
|
252
|
+
## 限制
|
|
118
253
|
|
|
119
|
-
|
|
254
|
+
- 仅支持 GitHub(`github.com`),不支持 GitLab / Gitee / GitHub Enterprise
|
|
255
|
+
- 镜像为**域名替换型**,不支持 `ghproxy.com/https://github.com/...` 前缀代理型
|
|
256
|
+
- 不支持 `git pull` / `git fetch` 加速
|
|
257
|
+
- `mirror test` 仅验证 HTTP 可达,不保证 `git clone` 一定成功
|
|
120
258
|
|
|
121
|
-
##
|
|
259
|
+
## 反馈
|
|
122
260
|
|
|
123
|
-
|
|
261
|
+
如有问题或建议,欢迎在 [Issues](https://github.com/bosens-China/github-clone) 反馈。
|
|
124
262
|
|
|
125
263
|
## 协议
|
|
126
264
|
|
|
127
|
-
[MIT License](/
|
|
265
|
+
[MIT License](/LICENSE)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class ConfigStore {
|
|
2
|
+
private readonly configPath;
|
|
3
|
+
constructor(configPath?: string);
|
|
4
|
+
getMirrorHost(): string | undefined;
|
|
5
|
+
setMirrorHost(host: string): void;
|
|
6
|
+
unsetMirrorHost(): void;
|
|
7
|
+
getConfigPath(): string;
|
|
8
|
+
}
|
|
9
|
+
export declare const defaultConfigStore: ConfigStore;
|
|
10
|
+
//# sourceMappingURL=mirror-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mirror-store.d.ts","sourceRoot":"","sources":["../../src/config/mirror-store.ts"],"names":[],"mappings":"AAaA,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,SAAoB;IAE3D,aAAa,IAAI,MAAM,GAAG,SAAS;IAQnC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKjC,eAAe,IAAI,IAAI;IAMvB,aAAa,IAAI,MAAM;CAGxB;AAED,eAAO,MAAM,kBAAkB,aAAoB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** 域名替换型镜像:将 URL 中的 github.com 替换为镜像主机 */
|
|
2
|
+
export interface MirrorPreset {
|
|
3
|
+
name: string;
|
|
4
|
+
host: string;
|
|
5
|
+
description: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const MIRROR_PRESETS: readonly MirrorPreset[];
|
|
8
|
+
/** 用于 mirror test 的探测仓库路径(GitHub 官方示例仓库,长期稳定) */
|
|
9
|
+
export declare const MIRROR_PROBE_REPO = "octocat/Hello-World";
|
|
10
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAWxC,CAAC;AAEX,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clone-repo.d.ts","sourceRoot":"","sources":["../../src/core/clone-repo.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAI7C,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAkCzE;AAED,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface GitCloneParams {
|
|
2
|
+
url: string;
|
|
3
|
+
dirName?: string;
|
|
4
|
+
branch?: string;
|
|
5
|
+
depth?: number;
|
|
6
|
+
singleBranch?: boolean;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
silence?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function gitExists(): boolean;
|
|
11
|
+
export declare function runGitClone(params: GitCloneParams): void;
|
|
12
|
+
export declare function setRemoteOrigin(dir: string, url: string, cwd: string): void;
|
|
13
|
+
//# sourceMappingURL=git-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-client.d.ts","sourceRoot":"","sources":["../../src/core/git-client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA8BD,wBAAgB,SAAS,IAAI,OAAO,CAOnC;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAOxD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAQ3E"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface ParsedGithubUrl {
|
|
2
|
+
owner: string;
|
|
3
|
+
repo: string;
|
|
4
|
+
normalizedHttps: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function isGithubLink(url: string): boolean;
|
|
7
|
+
export declare function parseGithubUrl(url: string): ParsedGithubUrl;
|
|
8
|
+
export declare function replaceMirrorHost(url: string, mirrorHost: string): string;
|
|
9
|
+
export declare function getRepoDirName(url: string): string;
|
|
10
|
+
export declare function buildMirrorProbeUrl(mirrorHost: string, repoPath: string): string;
|
|
11
|
+
//# sourceMappingURL=github-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-url.d.ts","sourceRoot":"","sources":["../../src/core/github-url.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGjD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAuB3D;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGzE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhF"}
|
package/dist/gitClone.esm.js
CHANGED
|
@@ -1,2 +1,108 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execaSync } from "execa";
|
|
4
|
+
//#region src/core/git-client.ts
|
|
5
|
+
function toExecaStdio(silence) {
|
|
6
|
+
return silence ? "pipe" : "inherit";
|
|
7
|
+
}
|
|
8
|
+
function buildCloneArgs(params) {
|
|
9
|
+
const args = ["clone", params.url];
|
|
10
|
+
if (params.branch) args.push("--branch", params.branch);
|
|
11
|
+
if (params.depth !== void 0) args.push("--depth", String(params.depth));
|
|
12
|
+
if (params.singleBranch) args.push("--single-branch");
|
|
13
|
+
if (params.dirName) args.push(params.dirName);
|
|
14
|
+
return args;
|
|
15
|
+
}
|
|
16
|
+
function assertGitSuccess(result) {
|
|
17
|
+
if (result.failed || result.exitCode !== void 0 && result.exitCode !== 0) {
|
|
18
|
+
const stderr = result.stderr?.trim();
|
|
19
|
+
throw new Error(stderr || `Git 命令执行失败,退出码 ${result.exitCode ?? "未知"}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function gitExists() {
|
|
23
|
+
try {
|
|
24
|
+
execaSync("git", ["--version"]);
|
|
25
|
+
return true;
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function runGitClone(params) {
|
|
31
|
+
assertGitSuccess(execaSync("git", buildCloneArgs(params), {
|
|
32
|
+
cwd: params.cwd,
|
|
33
|
+
stdio: toExecaStdio(params.silence),
|
|
34
|
+
reject: false
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
function setRemoteOrigin(dir, url, cwd) {
|
|
38
|
+
const dirPath = path.join(cwd, dir);
|
|
39
|
+
assertGitSuccess(execaSync("git", [
|
|
40
|
+
"remote",
|
|
41
|
+
"set-url",
|
|
42
|
+
"origin",
|
|
43
|
+
url
|
|
44
|
+
], {
|
|
45
|
+
cwd: dirPath,
|
|
46
|
+
stdio: "pipe",
|
|
47
|
+
reject: false
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/core/github-url.ts
|
|
52
|
+
const GITHUB_HTTPS_RE = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/.*)?$/;
|
|
53
|
+
const GITHUB_SSH_RE = /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/;
|
|
54
|
+
function parseGithubUrl(url) {
|
|
55
|
+
const trimmed = url.trim();
|
|
56
|
+
const httpsMatch = GITHUB_HTTPS_RE.exec(trimmed);
|
|
57
|
+
if (httpsMatch) {
|
|
58
|
+
const [, owner, repo] = httpsMatch;
|
|
59
|
+
return {
|
|
60
|
+
owner,
|
|
61
|
+
repo,
|
|
62
|
+
normalizedHttps: `https://github.com/${owner}/${repo}.git`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const sshMatch = GITHUB_SSH_RE.exec(trimmed);
|
|
66
|
+
if (sshMatch) {
|
|
67
|
+
const [, owner, repo] = sshMatch;
|
|
68
|
+
return {
|
|
69
|
+
owner,
|
|
70
|
+
repo,
|
|
71
|
+
normalizedHttps: `https://github.com/${owner}/${repo}.git`
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`无效的 GitHub 仓库地址:${url}`);
|
|
75
|
+
}
|
|
76
|
+
function replaceMirrorHost(url, mirrorHost) {
|
|
77
|
+
const { normalizedHttps } = parseGithubUrl(url);
|
|
78
|
+
return normalizedHttps.replace("github.com", mirrorHost);
|
|
79
|
+
}
|
|
80
|
+
function getRepoDirName(url) {
|
|
81
|
+
const { repo } = parseGithubUrl(url);
|
|
82
|
+
return repo;
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/core/clone-repo.ts
|
|
86
|
+
function cloneGithubRepo(url, options) {
|
|
87
|
+
if (!gitExists()) throw new Error("未检测到 Git,请先安装 Git,并确保其在 PATH 中可用");
|
|
88
|
+
const { normalizedHttps } = parseGithubUrl(url);
|
|
89
|
+
const mirrorHost = options?.mirrorHost;
|
|
90
|
+
const cloneUrl = mirrorHost ? replaceMirrorHost(url, mirrorHost) : normalizedHttps;
|
|
91
|
+
const { dirName, branch, cwd = process.cwd(), silence, depth, singleBranch } = options ?? {};
|
|
92
|
+
const requestedDir = dirName?.trim() ? dirName.trim() : void 0;
|
|
93
|
+
const targetDir = requestedDir ?? getRepoDirName(url);
|
|
94
|
+
runGitClone({
|
|
95
|
+
url: cloneUrl,
|
|
96
|
+
dirName: requestedDir,
|
|
97
|
+
branch,
|
|
98
|
+
depth,
|
|
99
|
+
singleBranch,
|
|
100
|
+
cwd,
|
|
101
|
+
silence
|
|
102
|
+
});
|
|
103
|
+
if (mirrorHost) setRemoteOrigin(targetDir, normalizedHttps, cwd);
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
export { cloneGithubRepo as default };
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=gitClone.esm.js.map
|
package/dist/gitClone.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitClone.esm.js","sources":["../src/
|
|
1
|
+
{"version":3,"file":"gitClone.esm.js","names":[],"sources":["../src/core/git-client.ts","../src/core/github-url.ts","../src/core/clone-repo.ts"],"sourcesContent":["import path from 'node:path';\nimport { execaSync } from 'execa';\n\nexport interface GitCloneParams {\n url: string;\n dirName?: string;\n branch?: string;\n depth?: number;\n singleBranch?: boolean;\n cwd?: string;\n silence?: boolean;\n}\n\nfunction toExecaStdio(silence?: boolean): 'pipe' | 'inherit' {\n return silence ? 'pipe' : 'inherit';\n}\n\nfunction buildCloneArgs(params: GitCloneParams): string[] {\n const args = ['clone', params.url];\n if (params.branch) {\n args.push('--branch', params.branch);\n }\n if (params.depth !== undefined) {\n args.push('--depth', String(params.depth));\n }\n if (params.singleBranch) {\n args.push('--single-branch');\n }\n if (params.dirName) {\n args.push(params.dirName);\n }\n return args;\n}\n\nfunction assertGitSuccess(result: { failed: boolean; exitCode?: number; stderr?: string }): void {\n if (result.failed || (result.exitCode !== undefined && result.exitCode !== 0)) {\n const stderr = result.stderr?.trim();\n throw new Error(stderr || `Git 命令执行失败,退出码 ${result.exitCode ?? '未知'}`);\n }\n}\n\nexport function gitExists(): boolean {\n try {\n execaSync('git', ['--version']);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function runGitClone(params: GitCloneParams): void {\n const result = execaSync('git', buildCloneArgs(params), {\n cwd: params.cwd,\n stdio: toExecaStdio(params.silence),\n reject: false,\n });\n assertGitSuccess(result);\n}\n\nexport function setRemoteOrigin(dir: string, url: string, cwd: string): void {\n const dirPath = path.join(cwd, dir);\n const result = execaSync('git', ['remote', 'set-url', 'origin', url], {\n cwd: dirPath,\n stdio: 'pipe',\n reject: false,\n });\n assertGitSuccess(result);\n}\n","const GITHUB_HTTPS_RE = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?(?:\\/.*)?$/;\nconst GITHUB_SSH_RE = /^git@github\\.com:([^/]+)\\/([^/]+?)(?:\\.git)?$/;\n\nexport interface ParsedGithubUrl {\n owner: string;\n repo: string;\n normalizedHttps: string;\n}\n\nexport function isGithubLink(url: string): boolean {\n const trimmed = url.trim();\n return GITHUB_HTTPS_RE.test(trimmed) || GITHUB_SSH_RE.test(trimmed);\n}\n\nexport function parseGithubUrl(url: string): ParsedGithubUrl {\n const trimmed = url.trim();\n const httpsMatch = GITHUB_HTTPS_RE.exec(trimmed);\n if (httpsMatch) {\n const [, owner, repo] = httpsMatch as RegExpExecArray & [string, string, string];\n return {\n owner,\n repo,\n normalizedHttps: `https://github.com/${owner}/${repo}.git`,\n };\n }\n\n const sshMatch = GITHUB_SSH_RE.exec(trimmed);\n if (sshMatch) {\n const [, owner, repo] = sshMatch as RegExpExecArray & [string, string, string];\n return {\n owner,\n repo,\n normalizedHttps: `https://github.com/${owner}/${repo}.git`,\n };\n }\n\n throw new Error(`无效的 GitHub 仓库地址:${url}`);\n}\n\nexport function replaceMirrorHost(url: string, mirrorHost: string): string {\n const { normalizedHttps } = parseGithubUrl(url);\n return normalizedHttps.replace('github.com', mirrorHost);\n}\n\nexport function getRepoDirName(url: string): string {\n const { repo } = parseGithubUrl(url);\n return repo;\n}\n\nexport function buildMirrorProbeUrl(mirrorHost: string, repoPath: string): string {\n return `https://${mirrorHost}/${repoPath}`;\n}\n","import process from 'node:process';\nimport type { CloneOptions } from '../types';\nimport { gitExists, runGitClone, setRemoteOrigin } from './git-client';\nimport { getRepoDirName, parseGithubUrl, replaceMirrorHost } from './github-url';\n\nexport function cloneGithubRepo(url: string, options?: CloneOptions): void {\n if (!gitExists()) {\n throw new Error('未检测到 Git,请先安装 Git,并确保其在 PATH 中可用');\n }\n\n const { normalizedHttps } = parseGithubUrl(url);\n const mirrorHost = options?.mirrorHost;\n const cloneUrl = mirrorHost ? replaceMirrorHost(url, mirrorHost) : normalizedHttps;\n\n const {\n dirName,\n branch,\n cwd = process.cwd(),\n silence,\n depth,\n singleBranch,\n } = options ?? {};\n\n const requestedDir = dirName?.trim() ? dirName.trim() : undefined;\n const targetDir = requestedDir ?? getRepoDirName(url);\n\n runGitClone({\n url: cloneUrl,\n dirName: requestedDir,\n branch,\n depth,\n singleBranch,\n cwd,\n silence,\n });\n\n if (mirrorHost) {\n setRemoteOrigin(targetDir, normalizedHttps, cwd);\n }\n}\n\nexport default cloneGithubRepo;\n"],"mappings":";;;;AAaA,SAAS,aAAa,SAAuC;CAC3D,OAAO,UAAU,SAAS;AAC5B;AAEA,SAAS,eAAe,QAAkC;CACxD,MAAM,OAAO,CAAC,SAAS,OAAO,GAAG;CACjC,IAAI,OAAO,QACT,KAAK,KAAK,YAAY,OAAO,MAAM;CAErC,IAAI,OAAO,UAAU,KAAA,GACnB,KAAK,KAAK,WAAW,OAAO,OAAO,KAAK,CAAC;CAE3C,IAAI,OAAO,cACT,KAAK,KAAK,iBAAiB;CAE7B,IAAI,OAAO,SACT,KAAK,KAAK,OAAO,OAAO;CAE1B,OAAO;AACT;AAEA,SAAS,iBAAiB,QAAuE;CAC/F,IAAI,OAAO,UAAW,OAAO,aAAa,KAAA,KAAa,OAAO,aAAa,GAAI;EAC7E,MAAM,SAAS,OAAO,QAAQ,KAAK;EACnC,MAAM,IAAI,MAAM,UAAU,kBAAkB,OAAO,YAAY,MAAM;CACvE;AACF;AAEA,SAAgB,YAAqB;CACnC,IAAI;EACF,UAAU,OAAO,CAAC,WAAW,CAAC;EAC9B,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,YAAY,QAA8B;CAMxD,iBALe,UAAU,OAAO,eAAe,MAAM,GAAG;EACtD,KAAK,OAAO;EACZ,OAAO,aAAa,OAAO,OAAO;EAClC,QAAQ;CACV,CACsB,CAAC;AACzB;AAEA,SAAgB,gBAAgB,KAAa,KAAa,KAAmB;CAC3E,MAAM,UAAU,KAAK,KAAK,KAAK,GAAG;CAMlC,iBALe,UAAU,OAAO;EAAC;EAAU;EAAW;EAAU;CAAG,GAAG;EACpE,KAAK;EACL,OAAO;EACP,QAAQ;CACV,CACsB,CAAC;AACzB;;;ACnEA,MAAM,kBAAkB;AACxB,MAAM,gBAAgB;AAatB,SAAgB,eAAe,KAA8B;CAC3D,MAAM,UAAU,IAAI,KAAK;CACzB,MAAM,aAAa,gBAAgB,KAAK,OAAO;CAC/C,IAAI,YAAY;EACd,MAAM,GAAG,OAAO,QAAQ;EACxB,OAAO;GACL;GACA;GACA,iBAAiB,sBAAsB,MAAM,GAAG,KAAK;EACvD;CACF;CAEA,MAAM,WAAW,cAAc,KAAK,OAAO;CAC3C,IAAI,UAAU;EACZ,MAAM,GAAG,OAAO,QAAQ;EACxB,OAAO;GACL;GACA;GACA,iBAAiB,sBAAsB,MAAM,GAAG,KAAK;EACvD;CACF;CAEA,MAAM,IAAI,MAAM,mBAAmB,KAAK;AAC1C;AAEA,SAAgB,kBAAkB,KAAa,YAA4B;CACzE,MAAM,EAAE,oBAAoB,eAAe,GAAG;CAC9C,OAAO,gBAAgB,QAAQ,cAAc,UAAU;AACzD;AAEA,SAAgB,eAAe,KAAqB;CAClD,MAAM,EAAE,SAAS,eAAe,GAAG;CACnC,OAAO;AACT;;;AC1CA,SAAgB,gBAAgB,KAAa,SAA8B;CACzE,IAAI,CAAC,UAAU,GACb,MAAM,IAAI,MAAM,kCAAkC;CAGpD,MAAM,EAAE,oBAAoB,eAAe,GAAG;CAC9C,MAAM,aAAa,SAAS;CAC5B,MAAM,WAAW,aAAa,kBAAkB,KAAK,UAAU,IAAI;CAEnE,MAAM,EACJ,SACA,QACA,MAAM,QAAQ,IAAI,GAClB,SACA,OACA,iBACE,WAAW,CAAC;CAEhB,MAAM,eAAe,SAAS,KAAK,IAAI,QAAQ,KAAK,IAAI,KAAA;CACxD,MAAM,YAAY,gBAAgB,eAAe,GAAG;CAEpD,YAAY;EACV,KAAK;EACL,SAAS;EACT;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,IAAI,YACF,gBAAgB,WAAW,iBAAiB,GAAG;AAEnD"}
|