git-ripper 1.4.6 → 1.4.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-ripper",
3
- "version": "1.4.6",
3
+ "version": "1.4.7",
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/archiver.js CHANGED
@@ -88,7 +88,7 @@ export const createArchive = (sourceDir, outputPath) => {
88
88
  const size = archive.pointer();
89
89
  console.log(
90
90
  chalk.green(
91
- `✓ Archive created: ${outputPath} (${(size / 1024 / 1024).toFixed(
91
+ `Archive created: ${outputPath} (${(size / 1024 / 1024).toFixed(
92
92
  2
93
93
  )} MB)`
94
94
  )
package/src/downloader.js CHANGED
@@ -52,7 +52,9 @@ const fetchFolderContents = async (owner, repo, branch, folderPath) => {
52
52
  if (!effectiveBranch) {
53
53
  // If no branch is specified, fetch the default branch for the repository
54
54
  try {
55
- const repoInfoUrl = `https://api.github.com/repos/${owner}/${repo}`;
55
+ const repoInfoUrl = `https://api.github.com/repos/${encodeURIComponent(
56
+ owner
57
+ )}/${encodeURIComponent(repo)}`;
56
58
  const repoInfoResponse = await axios.get(repoInfoUrl);
57
59
  effectiveBranch = repoInfoResponse.data.default_branch;
58
60
  if (!effectiveBranch) {
@@ -75,7 +77,11 @@ const fetchFolderContents = async (owner, repo, branch, folderPath) => {
75
77
  }
76
78
  }
77
79
 
78
- const apiUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${effectiveBranch}?recursive=1`;
80
+ const apiUrl = `https://api.github.com/repos/${encodeURIComponent(
81
+ owner
82
+ )}/${encodeURIComponent(repo)}/git/trees/${encodeURIComponent(
83
+ effectiveBranch
84
+ )}?recursive=1`;
79
85
 
80
86
  try {
81
87
  const response = await axios.get(apiUrl);
@@ -164,7 +170,9 @@ const downloadFile = async (owner, repo, branch, filePath, outputPath) => {
164
170
  // This check might be redundant if fetchFolderContents already resolved it,
165
171
  // but it's a good fallback for direct downloadFile calls if any.
166
172
  try {
167
- const repoInfoUrl = `https://api.github.com/repos/${owner}/${repo}`;
173
+ const repoInfoUrl = `https://api.github.com/repos/${encodeURIComponent(
174
+ owner
175
+ )}/${encodeURIComponent(repo)}`;
168
176
  const repoInfoResponse = await axios.get(repoInfoUrl);
169
177
  effectiveBranch = repoInfoResponse.data.default_branch;
170
178
  if (!effectiveBranch) {
@@ -191,10 +199,16 @@ const downloadFile = async (owner, repo, branch, filePath, outputPath) => {
191
199
  }
192
200
  }
193
201
 
194
- const baseUrl = `https://raw.githubusercontent.com/${owner}/${repo}`;
202
+ const baseUrl = `https://raw.githubusercontent.com/${encodeURIComponent(
203
+ owner
204
+ )}/${encodeURIComponent(repo)}`;
205
+ const encodedFilePath = filePath
206
+ .split("/")
207
+ .map((part) => encodeURIComponent(part))
208
+ .join("/");
195
209
  const fileUrlPath = effectiveBranch
196
- ? `/${effectiveBranch}/${filePath}`
197
- : `/${filePath}`; // filePath might be at root
210
+ ? `/${encodeURIComponent(effectiveBranch)}/${encodedFilePath}`
211
+ : `/${encodedFilePath}`; // filePath might be at root
198
212
  const url = `${baseUrl}${fileUrlPath}`;
199
213
 
200
214
  try {
@@ -483,7 +497,7 @@ const downloadFolder = async (
483
497
  // Don't claim success if files failed to download
484
498
  if (succeeded === 0) {
485
499
  console.log(
486
- chalk.red(`❌ Download failed: No files were downloaded successfully`)
500
+ chalk.red(`Download failed: No files were downloaded successfully`)
487
501
  );
488
502
  return {
489
503
  success: false,
@@ -492,7 +506,7 @@ const downloadFolder = async (
492
506
  isEmpty: false,
493
507
  };
494
508
  } else {
495
- console.log(chalk.yellow(`⚠️ Download completed with errors`));
509
+ console.log(chalk.yellow(`Download completed with errors`));
496
510
  return {
497
511
  success: false,
498
512
  filesDownloaded: succeeded,
@@ -502,7 +516,7 @@ const downloadFolder = async (
502
516
  }
503
517
  } else {
504
518
  console.log(
505
- chalk.green(`✅ All ${succeeded} files downloaded successfully!`)
519
+ chalk.green(`All ${succeeded} files downloaded successfully!`)
506
520
  );
507
521
  console.log(chalk.green(`Folder cloned successfully!`));
508
522
  return {
@@ -514,7 +528,7 @@ const downloadFolder = async (
514
528
  }
515
529
  } catch (error) {
516
530
  // Log the specific error details
517
- console.error(chalk.red(`❌ Error downloading folder: ${error.message}`));
531
+ console.error(chalk.red(`Error downloading folder: ${error.message}`));
518
532
 
519
533
  // Re-throw the error so the main CLI can exit with proper error code
520
534
  throw error;
@@ -539,9 +553,17 @@ const downloadFolderWithResume = async (
539
553
  }
540
554
 
541
555
  const resumeManager = new ResumeManager();
542
- const url = `https://github.com/${owner}/${repo}/tree/${branch || "main"}/${
543
- folderPath || ""
544
- }`;
556
+ const encodedFolderPath = folderPath
557
+ ? folderPath
558
+ .split("/")
559
+ .map((part) => encodeURIComponent(part))
560
+ .join("/")
561
+ : "";
562
+ const url = `https://github.com/${encodeURIComponent(
563
+ owner
564
+ )}/${encodeURIComponent(repo)}/tree/${encodeURIComponent(
565
+ branch || "main"
566
+ )}/${encodedFolderPath}`;
545
567
 
546
568
  // Clear checkpoint if force restart is requested
547
569
  if (forceRestart) {
@@ -554,14 +576,14 @@ const downloadFolderWithResume = async (
554
576
  if (checkpoint) {
555
577
  console.log(
556
578
  chalk.blue(
557
- `🔄 Found previous download from ${new Date(
579
+ `Found previous download from ${new Date(
558
580
  checkpoint.timestamp
559
581
  ).toLocaleString()}`
560
582
  )
561
583
  );
562
584
  console.log(
563
585
  chalk.blue(
564
- `📊 Progress: ${checkpoint.downloadedFiles.length}/${checkpoint.totalFiles} files completed`
586
+ `Progress: ${checkpoint.downloadedFiles.length}/${checkpoint.totalFiles} files completed`
565
587
  )
566
588
  );
567
589
 
@@ -587,11 +609,11 @@ const downloadFolderWithResume = async (
587
609
  if (corruptedCount > 0) {
588
610
  console.log(
589
611
  chalk.yellow(
590
- `🔧 Detected ${corruptedCount} corrupted files, will re-download`
612
+ `Detected ${corruptedCount} corrupted files, will re-download`
591
613
  )
592
614
  );
593
615
  }
594
- console.log(chalk.green(`✅ Verified ${validFiles.length} existing files`));
616
+ console.log(chalk.green(`Verified ${validFiles.length} existing files`));
595
617
  }
596
618
 
597
619
  console.log(
@@ -640,7 +662,7 @@ const downloadFolderWithResume = async (
640
662
  );
641
663
  console.log(
642
664
  chalk.cyan(
643
- `📥 Starting download of ${totalFiles} files from ${chalk.white(
665
+ `Starting download of ${totalFiles} files from ${chalk.white(
644
666
  owner + "/" + repo
645
667
  )}...`
646
668
  )
@@ -648,7 +670,7 @@ const downloadFolderWithResume = async (
648
670
  } else {
649
671
  // Update total files in case repository changed
650
672
  checkpoint.totalFiles = totalFiles;
651
- console.log(chalk.cyan(`📥 Resuming download...`));
673
+ console.log(chalk.cyan(`Resuming download...`));
652
674
  }
653
675
 
654
676
  // Get remaining files to download
@@ -663,13 +685,13 @@ const downloadFolderWithResume = async (
663
685
  });
664
686
 
665
687
  if (remainingFiles.length === 0) {
666
- console.log(chalk.green(`🎉 All files already downloaded!`));
688
+ console.log(chalk.green(`All files already downloaded!`));
667
689
  resumeManager.cleanupCheckpoint(url, outputDir);
668
690
  return;
669
691
  }
670
692
 
671
693
  console.log(
672
- chalk.cyan(`📥 Downloading ${remainingFiles.length} remaining files...`)
694
+ chalk.cyan(`Downloading ${remainingFiles.length} remaining files...`)
673
695
  );
674
696
 
675
697
  // Setup progress bar
@@ -753,10 +775,8 @@ const downloadFolderWithResume = async (
753
775
  if (error.name === "SIGINT") {
754
776
  resumeManager.saveCheckpoint(checkpoint);
755
777
  progressBar.stop();
756
- console.log(
757
- chalk.blue(`\n⏸️ Download interrupted. Progress saved.`)
758
- );
759
- console.log(chalk.blue(`💡 Run the same command again to resume.`));
778
+ console.log(chalk.blue(`\nDownload interrupted. Progress saved.`));
779
+ console.log(chalk.blue(`Run the same command again to resume.`));
760
780
  return;
761
781
  }
762
782
 
@@ -793,13 +813,13 @@ const downloadFolderWithResume = async (
793
813
  }
794
814
 
795
815
  console.log(
796
- chalk.blue(`💡 Run the same command again to retry failed downloads`)
816
+ chalk.blue(`Run the same command again to retry failed downloads`)
797
817
  );
798
818
 
799
819
  // Don't claim success if files failed to download
800
820
  if (succeeded === 0) {
801
821
  console.log(
802
- chalk.red(`❌ Download failed: No files were downloaded successfully`)
822
+ chalk.red(`Download failed: No files were downloaded successfully`)
803
823
  );
804
824
  return {
805
825
  success: false,
@@ -808,7 +828,7 @@ const downloadFolderWithResume = async (
808
828
  isEmpty: false,
809
829
  };
810
830
  } else {
811
- console.log(chalk.yellow(`⚠️ Download completed with errors`));
831
+ console.log(chalk.yellow(`Download completed with errors`));
812
832
  return {
813
833
  success: false,
814
834
  filesDownloaded: succeeded,
@@ -818,7 +838,7 @@ const downloadFolderWithResume = async (
818
838
  }
819
839
  } else {
820
840
  console.log(
821
- chalk.green(`🎉 All ${succeeded} files downloaded successfully!`)
841
+ chalk.green(`All ${succeeded} files downloaded successfully!`)
822
842
  );
823
843
  resumeManager.cleanupCheckpoint(url, outputDir);
824
844
  console.log(chalk.green(`Folder cloned successfully!`));
@@ -835,7 +855,7 @@ const downloadFolderWithResume = async (
835
855
  resumeManager.saveCheckpoint(checkpoint);
836
856
  }
837
857
 
838
- console.error(chalk.red(`❌ Error downloading folder: ${error.message}`));
858
+ console.error(chalk.red(`Error downloading folder: ${error.message}`));
839
859
  throw error;
840
860
  }
841
861
  };
package/src/index.js CHANGED
@@ -76,7 +76,7 @@ const initializeCLI = () => {
76
76
  return;
77
77
  }
78
78
 
79
- console.log(chalk.cyan("\n📋 Download Checkpoints:"));
79
+ console.log(chalk.cyan("\nDownload Checkpoints:"));
80
80
  checkpoints.forEach((cp, index) => {
81
81
  console.log(chalk.blue(`\n${index + 1}. ID: ${cp.id}`));
82
82
  console.log(` URL: ${cp.url}`);
@@ -151,12 +151,12 @@ const initializeCLI = () => {
151
151
  if (error) {
152
152
  const failMsg =
153
153
  operationType === "archive"
154
- ? `❌ Archive creation failed: ${error.message}`
155
- : `❌ Download failed: ${error.message}`;
154
+ ? `Archive creation failed: ${error.message}`
155
+ : `Download failed: ${error.message}`;
156
156
  console.error(chalk.red(failMsg));
157
157
  process.exit(1);
158
158
  } else if (!createArchive && result && !result.success) {
159
- console.error(chalk.red(`❌ Download failed`));
159
+ console.error(chalk.red(`Download failed`));
160
160
  process.exit(1);
161
161
  } else if (!createArchive && result && result.isEmpty) {
162
162
  console.log("Operation completed - no files to download!");
package/src/parser.js CHANGED
@@ -16,10 +16,10 @@ export function parseGitHubUrl(url) {
16
16
  }
17
17
 
18
18
  // Extract components from the matched pattern
19
- const owner = match[1];
20
- const repo = match[2];
21
- const branch = match[3]; // Branch might not be in the URL for root downloads
22
- const folderPath = match[4] || ""; // Empty string if no folder path
19
+ const owner = decodeURIComponent(match[1]);
20
+ const repo = decodeURIComponent(match[2]);
21
+ const branch = match[3] ? decodeURIComponent(match[3]) : ""; // Branch is an empty string if not present
22
+ const folderPath = match[4] ? decodeURIComponent(match[4]) : ""; // Empty string if no folder path
23
23
 
24
24
  // Additional validation
25
25
  if (!owner || !repo) {