UNPKG

git-ripper

Version:

CLI tool that lets you download specific folders from GitHub repositories without cloning the entire repo.

116 lines (99 loc) 4.28 kB
import { program } from 'commander'; import { parseGitHubUrl } from './parser.js'; import { downloadFolder } from './downloader.js'; import { downloadAndArchive } from './archiver.js'; import { fileURLToPath } from 'url'; import { dirname, join, resolve } from 'path'; import fs from 'fs'; // Get package.json for version const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const packagePath = join(__dirname, '..', 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8')); /** * Validates and ensures the output directory exists * @param {string} outputDir - The directory path to validate * @returns {string} - The resolved directory path * @throws {Error} - If the directory is invalid or cannot be created */ const validateOutputDirectory = (outputDir) => { if (!outputDir) { throw new Error('Output directory is required'); } // Resolve to absolute path const resolvedDir = resolve(outputDir); try { // Check if directory exists, if not try to create it if (!fs.existsSync(resolvedDir)) { fs.mkdirSync(resolvedDir, { recursive: true }); } else { // Check if it's actually a directory const stats = fs.statSync(resolvedDir); if (!stats.isDirectory()) { throw new Error(`Output path exists but is not a directory: ${outputDir}`); } } // Check if the directory is writable fs.accessSync(resolvedDir, fs.constants.W_OK); return resolvedDir; } catch (error) { if (error.code === 'EACCES') { throw new Error(`Permission denied: Cannot write to ${outputDir}`); } throw new Error(`Invalid output directory: ${error.message}`); } }; const initializeCLI = () => { program .version(packageJson.version) .description('Clone specific folders from GitHub repositories') .argument('<url>', 'GitHub URL of the folder to clone') .option('-o, --output <directory>', 'Output directory', process.cwd()) .option('--zip [filename]', 'Create ZIP archive of downloaded files') .option('--tar [filename]', 'Create TAR archive of downloaded files') .option('--compression-level <level>', 'Compression level (1-9)', '6') .action(async (url, options) => { try { console.log(`Parsing URL: ${url}`); const parsedUrl = parseGitHubUrl(url); // Validate options if (options.compressionLevel) { const level = parseInt(options.compressionLevel, 10); if (isNaN(level) || level < 1 || level > 9) { throw new Error('Compression level must be a number between 1 and 9'); } } if (options.zip && options.tar) { throw new Error('Cannot specify both --zip and --tar options at the same time'); } // Validate output directory try { options.output = validateOutputDirectory(options.output); } catch (dirError) { throw new Error(`Output directory error: ${dirError.message}`); } // Handle archive options const archiveFormat = options.zip ? 'zip' : options.tar ? 'tar' : null; const archiveName = typeof options.zip === 'string' ? options.zip : typeof options.tar === 'string' ? options.tar : null; const compressionLevel = parseInt(options.compressionLevel, 10) || 6; if (archiveFormat) { console.log(`Creating ${archiveFormat.toUpperCase()} archive...`); await downloadAndArchive(parsedUrl, options.output, archiveFormat, archiveName, compressionLevel); } else { console.log(`Downloading folder to: ${options.output}`); await downloadFolder(parsedUrl, options.output); } console.log('Operation completed successfully!'); } catch (error) { console.error('Error:', error.message); process.exit(1); } }); program.parse(process.argv); }; // Ensure function is executed when run directly if (import.meta.url === `file://${process.argv[1]}`) { initializeCLI(); } export { initializeCLI, downloadFolder };