git-ripper 1.5.1 → 1.5.3

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +89 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-ripper",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "CLI tool that lets you download specific folders from GitHub repositories without cloning the entire repo.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -1,12 +1,17 @@
1
1
  import { program } from "commander";
2
2
  import { parseGitHubUrl } from "./parser.js";
3
- import { downloadFolder, downloadFolderWithResume, downloadFile } from "./downloader.js";
3
+ import {
4
+ downloadFolder,
5
+ downloadFolderWithResume,
6
+ downloadFile,
7
+ } from "./downloader.js";
4
8
  import { downloadAndArchive } from "./archiver.js";
5
9
  import { ResumeManager } from "./resumeManager.js";
6
10
  import { fileURLToPath } from "node:url";
7
11
  import { dirname, join, resolve, basename } from "node:path";
8
12
  import fs from "node:fs";
9
13
  import process from "node:process";
14
+ import readline from "node:readline";
10
15
  import chalk from "chalk";
11
16
 
12
17
  // Get package.json for version
@@ -38,7 +43,7 @@ const validateOutputDirectory = (outputDir) => {
38
43
  const stats = fs.statSync(resolvedDir);
39
44
  if (!stats.isDirectory()) {
40
45
  throw new Error(
41
- `Output path exists but is not a directory: ${outputDir}`
46
+ `Output path exists but is not a directory: ${outputDir}`,
42
47
  );
43
48
  }
44
49
  }
@@ -61,7 +66,10 @@ const initializeCLI = () => {
61
66
  .description("Clone specific folders from GitHub repositories")
62
67
  .argument("[url]", "GitHub URL of the folder to clone")
63
68
  .option("-o, --output <directory>", "Output directory", process.cwd())
64
- .option("--gh-token <token>", "GitHub Personal Access Token for private repositories")
69
+ .option(
70
+ "--gh-token <token>",
71
+ "GitHub Personal Access Token for private repositories",
72
+ )
65
73
  .option("--zip [filename]", "Create ZIP archive of downloaded files")
66
74
  .option("--no-resume", "Disable resume functionality")
67
75
  .option("--force-restart", "Ignore existing checkpoints and start fresh")
@@ -85,7 +93,7 @@ const initializeCLI = () => {
85
93
  console.log(` Output: ${cp.outputDir}`);
86
94
  console.log(` Progress: ${cp.progress}`);
87
95
  console.log(
88
- ` Last Updated: ${new Date(cp.timestamp).toLocaleString()}`
96
+ ` Last Updated: ${new Date(cp.timestamp).toLocaleString()}`,
89
97
  );
90
98
  if (cp.failedFiles > 0) {
91
99
  console.log(chalk.yellow(` Failed Files: ${cp.failedFiles}`));
@@ -98,7 +106,7 @@ const initializeCLI = () => {
98
106
  // URL is required for download operations
99
107
  if (!url) {
100
108
  console.error(
101
- chalk.red("Error: URL is required for download operations")
109
+ chalk.red("Error: URL is required for download operations"),
102
110
  );
103
111
  console.log("Use --list-checkpoints to see existing downloads");
104
112
  process.exit(1);
@@ -107,6 +115,66 @@ const initializeCLI = () => {
107
115
  console.log(`Parsing URL: ${url}`);
108
116
  const parsedUrl = parseGitHubUrl(url);
109
117
 
118
+ // Warning for full repo links
119
+ if (!parsedUrl.branch && !parsedUrl.folderPath) {
120
+ console.log(
121
+ chalk.yellow(
122
+ "\n⚠️ WARNING: You've provided a full repository link.",
123
+ ),
124
+ );
125
+ console.log(
126
+ chalk.yellow(
127
+ "This tool will attempt to download the entire repository, which may:",
128
+ ),
129
+ );
130
+ console.log(chalk.yellow(" • Trigger GitHub API rate limits"));
131
+ console.log(
132
+ chalk.yellow(" • Take a long time for large repositories"),
133
+ );
134
+ console.log(chalk.yellow(" • Consume significant bandwidth\n"));
135
+ console.log(
136
+ chalk.cyan(
137
+ "💡 Recommendation: Use 'git clone' instead for full repositories:",
138
+ ),
139
+ );
140
+ console.log(
141
+ chalk.cyan(
142
+ ` git clone https://github.com/${parsedUrl.owner}/${parsedUrl.repo}.git\n`,
143
+ ),
144
+ );
145
+ console.log(
146
+ chalk.gray("To download a specific folder, use a URL like:"),
147
+ );
148
+ console.log(
149
+ chalk.gray(
150
+ `https://github.com/${parsedUrl.owner}/${parsedUrl.repo}/tree/<branch>/<folder-path>\n`,
151
+ ),
152
+ );
153
+
154
+ // Prompt user for confirmation
155
+ const rl = readline.createInterface({
156
+ input: process.stdin,
157
+ output: process.stdout,
158
+ });
159
+
160
+ const answer = await new Promise((resolve) => {
161
+ rl.question(
162
+ chalk.bold.white("Do you still want to proceed? (Y/n): "),
163
+ (ans) => {
164
+ rl.close();
165
+ resolve(ans.trim().toLowerCase());
166
+ },
167
+ );
168
+ });
169
+
170
+ if (answer !== "yes" && answer !== "y") {
171
+ console.log(chalk.yellow("\nOperation cancelled by user."));
172
+ process.exit(0);
173
+ }
174
+
175
+ console.log(chalk.green("\nProceeding with download...\n"));
176
+ }
177
+
110
178
  // Validate output directory
111
179
  try {
112
180
  options.output = validateOutputDirectory(options.output);
@@ -133,7 +201,12 @@ const initializeCLI = () => {
133
201
  try {
134
202
  if (createArchive) {
135
203
  console.log(`Creating ZIP archive...`);
136
- await downloadAndArchive(parsedUrl, options.output, archiveName, downloadOptions);
204
+ await downloadAndArchive(
205
+ parsedUrl,
206
+ options.output,
207
+ archiveName,
208
+ downloadOptions,
209
+ );
137
210
  } else if (parsedUrl.type === "blob") {
138
211
  console.log(`Downloading file to: ${options.output}`);
139
212
  const fileName = basename(parsedUrl.folderPath);
@@ -144,7 +217,7 @@ const initializeCLI = () => {
144
217
  parsedUrl.branch,
145
218
  parsedUrl.folderPath,
146
219
  outputPath,
147
- options.ghToken
220
+ options.ghToken,
148
221
  );
149
222
  } else {
150
223
  console.log(`Downloading folder to: ${options.output}`);
@@ -152,10 +225,14 @@ const initializeCLI = () => {
152
225
  result = await downloadFolderWithResume(
153
226
  parsedUrl,
154
227
  options.output,
155
- downloadOptions
228
+ downloadOptions,
156
229
  );
157
230
  } else {
158
- result = await downloadFolder(parsedUrl, options.output, downloadOptions);
231
+ result = await downloadFolder(
232
+ parsedUrl,
233
+ options.output,
234
+ downloadOptions,
235
+ );
159
236
  }
160
237
  }
161
238
  } catch (opError) {
@@ -165,9 +242,9 @@ const initializeCLI = () => {
165
242
  // Consolidated result and error handling
166
243
  if (error) {
167
244
  const failMsg =
168
- operationType === "archive"
169
- ? `Archive creation failed: ${error.message}`
170
- : `Download failed: ${error.message}`;
245
+ operationType === "archive" ?
246
+ `Archive creation failed: ${error.message}`
247
+ : `Download failed: ${error.message}`;
171
248
  console.error(chalk.red(failMsg));
172
249
  process.exit(1);
173
250
  } else if (!createArchive && result && !result.success) {