UNPKG

gather-ts

Version:

A powerful code analysis and packaging tool designed for creating AI-friendly code representations for javascript and typescript projects.

370 lines 15.9 kB
"use strict"; // src/core/compiler/ArgumentParser.ts var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ArgumentParser = void 0; const path_1 = __importDefault(require("path")); const errors_1 = require("@/errors"); const services_1 = require("@/types/services"); class ArgumentParser extends services_1.BaseService { constructor(deps, options = {}) { super(); this.deps = deps; this.debug = options.debug || false; this.maxDepth = options.maxDepth || 10; this.defaultBatchSize = options.defaultBatchSize || 100; } async initialize() { await super.initialize(); this.logDebug("Initializing ArgumentParser"); } cleanup() { this.logDebug("Cleaning up ArgumentParser"); super.cleanup(); } logDebug(message) { if (this.debug) { this.deps.logger.debug(message); } } parseArguments(args, options = {}) { this.checkInitialized(); this.logDebug(`Parsing arguments: ${args.join(" ")}`); try { if (!args.length && !options.allowEmpty) { throw new errors_1.ValidationError("No arguments provided"); } // First parse options to get root directory const restArgs = args.slice(1); let outputFile = ""; const compileOptions = {}; // Process all args as options for (let i = 0; i < restArgs.length; i++) { const arg = restArgs[i]; switch (arg) { case "--output": case "-o": i++; if (i >= restArgs.length) { throw new errors_1.ValidationError("Output file path not provided"); } outputFile = this.parseOutputFile(restArgs[i], options.requireOutput); break; case "--root": case "-r": i++; if (i >= restArgs.length) { throw new errors_1.ValidationError("Root directory not provided"); } compileOptions.rootDir = this.parseRootDir(restArgs[i]); break; case "--depth": case "-d": i++; if (i >= restArgs.length) { throw new errors_1.ValidationError("Depth value not provided"); } compileOptions.maxDepth = this.parseDepth(restArgs[i]); break; case "--batch-size": i++; if (i >= restArgs.length) { throw new errors_1.ValidationError("Batch size not provided"); } const batchSize = this.deps.validator.validateNotEmpty(restArgs[i], "Batch size"); const parsedBatchSize = parseInt(batchSize); if (isNaN(parsedBatchSize) || parsedBatchSize <= 0) { throw new errors_1.ValidationError("Batch size must be a positive number"); } compileOptions.batchSize = parsedBatchSize; break; case "--config": case "-c": i++; if (i >= restArgs.length) { throw new errors_1.ValidationError("Config path not provided"); } const configPath = this.deps.validator.validateNotEmpty(restArgs[i], "Config path"); if (!this.deps.fileSystem.exists(configPath)) { throw new errors_1.ValidationError(`Config file not found: ${configPath}`); } compileOptions.config = { path: configPath }; break; case "--encoding": i++; if (i >= restArgs.length) { throw new errors_1.ValidationError("Encoding not provided"); } const encoding = this.deps.validator.validateNotEmpty(restArgs[i], "Encoding"); if (!this.isValidEncoding(encoding)) { throw new errors_1.ValidationError(`Invalid encoding: ${encoding}`); } compileOptions.encoding = encoding; break; case "--ignore": i++; compileOptions.ignorePatterns = this.parseIgnorePatterns(restArgs.slice(i)); i += (compileOptions.ignorePatterns?.length || 0) - 1; break; case "--require": i++; compileOptions.requiredFiles = this.parseRequiredFiles(restArgs.slice(i)); i += (compileOptions.requiredFiles?.length || 0) - 1; break; case "--init": compileOptions.init = true; break; case "--metrics": compileOptions.includeMetrics = true; break; case "--debug": compileOptions.debug = true; break; default: if (arg.startsWith("-")) { throw new errors_1.ValidationError(`Unknown option: ${arg}`); } } } // Extract entry files first (should be first argument) const entryFiles = args[0] ? this.parseEntryFiles(args[0], compileOptions.rootDir) : []; // Combine everything into final options const result = { entryFiles, outputFile, ...compileOptions, }; this.logDebug(`Parsed options: ${JSON.stringify(result)}`); return result; } catch (error) { throw new errors_1.ValidationError(`Failed to parse arguments: ${error instanceof Error ? error.message : String(error)}`); } } parseEntryFiles(filesArg, rootDir) { this.logDebug(`Parsing entry files: ${filesArg} with root: ${rootDir || "not specified"}`); this.deps.validator.validateNotEmpty(filesArg, "Entry files argument"); const files = filesArg .split(",") .map((f) => f.trim()) .filter((f) => f.length > 0); if (files.length === 0) { throw new errors_1.ValidationError("No valid entry files provided"); } // Validate each file exists files.forEach((file) => { this.deps.validator.validatePath(file, "Entry file"); const absolutePath = rootDir ? this.deps.fileSystem.resolvePath(rootDir, file) : this.deps.fileSystem.resolvePath(file); if (!this.deps.fileSystem.exists(absolutePath)) { throw new errors_1.ValidationError(`Entry file does not exist: ${file}`); } }); return files; } parseOutputFile(outputArg, required = true) { this.logDebug(`Parsing output file: ${outputArg}`); if (required) { this.deps.validator.validateNotEmpty(outputArg, "Output file argument"); } const outputFile = outputArg?.trim(); if (!outputFile && required) { throw new errors_1.ValidationError("Output file path cannot be empty"); } // Ensure output directory exists or can be created if (outputFile) { const outputDir = this.deps.fileSystem.getDirName(outputFile); if (!this.deps.fileSystem.exists(outputDir)) { try { this.deps.fileSystem.createDirectory(outputDir, true); this.logDebug(`Created output directory: ${outputDir}`); } catch (error) { throw new errors_1.ValidationError(`Cannot create output directory: ${error instanceof Error ? error.message : String(error)}`); } } } return outputFile; } parseOptions(args) { this.logDebug(`Parsing additional options: ${args.join(" ")}`); const options = {}; for (let i = 0; i < args.length; i++) { const arg = args[i]; switch (arg) { case "--root": case "-r": i++; options.rootDir = this.parseRootDir(args[i]); break; case "--depth": case "-d": i++; options.maxDepth = this.parseDepth(args[i]); break; case "--batch-size": i++; const batchSize = this.deps.validator.validateNotEmpty(args[i], "Batch size"); const parsedBatchSize = parseInt(batchSize); if (isNaN(parsedBatchSize) || parsedBatchSize <= 0) { throw new errors_1.ValidationError("Batch size must be a positive number"); } options.batchSize = parsedBatchSize; break; case "--config": case "-c": i++; const configPath = this.deps.validator.validateNotEmpty(args[i], "Config path"); if (!this.deps.fileSystem.exists(configPath)) { throw new errors_1.ValidationError(`Config file not found: ${configPath}`); } options.config = { path: configPath }; break; case "--encoding": i++; const encoding = this.deps.validator.validateNotEmpty(args[i], "Encoding"); if (!this.isValidEncoding(encoding)) { throw new errors_1.ValidationError(`Invalid encoding: ${encoding}`); } options.encoding = encoding; break; case "--ignore": i++; options.ignorePatterns = this.parseIgnorePatterns(args.slice(i)); i += (options.ignorePatterns?.length || 0) - 1; break; case "--require": i++; options.requiredFiles = this.parseRequiredFiles(args.slice(i)); i += (options.requiredFiles?.length || 0) - 1; break; case "--init": options.init = true; break; case "--metrics": options.includeMetrics = true; break; case "--debug": options.debug = true; break; default: if (arg.startsWith("-")) { throw new errors_1.ValidationError(`Unknown option: ${arg}`); } } } return options; } parseRootDir(rootDir) { this.logDebug(`Parsing root directory: ${rootDir}`); this.deps.validator.validateNotEmpty(rootDir, "Root directory argument"); const absolutePath = path_1.default.resolve(rootDir); if (!this.deps.fileSystem.exists(absolutePath)) { throw new errors_1.ValidationError("Root directory does not exist", { rootDir: absolutePath, }); } return absolutePath; } parseDepth(depthArg) { this.logDebug(`Parsing depth: ${depthArg}`); this.deps.validator.validateNotEmpty(depthArg, "Depth argument"); const depth = parseInt(depthArg); if (isNaN(depth)) { throw new errors_1.ValidationError("Depth must be a number"); } if (depth < 0) { throw new errors_1.ValidationError("Depth cannot be negative"); } if (depth > this.maxDepth) { throw new errors_1.ValidationError(`Depth cannot exceed ${this.maxDepth}`); } return depth; } isValidEncoding(encoding) { const validEncodings = [ "utf8", "utf-8", "utf16le", "latin1", "ascii", "base64", "hex", "binary", "ucs2", ]; return validEncodings.includes(encoding); } parseIgnorePatterns(args) { this.logDebug(`Parsing ignore patterns: ${args.join(" ")}`); const patterns = []; for (const arg of args) { if (arg.startsWith("-")) break; patterns.push(arg); } return patterns; } parseRequiredFiles(args) { this.logDebug(`Parsing required files: ${args.join(" ")}`); const files = []; for (const arg of args) { if (arg.startsWith("-")) break; this.deps.validator.validatePath(arg, "Required file"); if (!this.deps.fileSystem.exists(arg)) { this.deps.logger.warn(`Required file not found: ${arg}`); continue; } files.push(arg); } return files; } validatePaths(entryFiles, outputFile) { entryFiles.forEach((file) => { this.deps.validator.validatePath(file, "Entry file"); }); if (outputFile) { const outputDir = this.deps.fileSystem.getDirName(outputFile); this.deps.validator.validatePath(outputDir, "Output directory"); } } printUsage() { const usage = [ "Usage: gather-ts <files...> --output <output> [options]", "", "Arguments:", " files Entry files to analyze (comma or space separated)", "", "Options:", " -o, --output Output file path", " -r, --root Project root directory (default: current directory)", " -d, --depth Maximum depth for dependency analysis", " --debug Enable debug logging", " --batch-size Batch size for processing files", " --metrics Include performance metrics in output", " -c, --config Path to custom config file", " --encoding File encoding (default: utf8)", " --ignore Additional patterns to ignore", " --require Required files to include", " --init Initialize configuration in current directory", "", "Examples:", " # Single file with metrics", " $ gather-ts src/app/page.tsx --output output.txt --metrics", "", " # Multiple files with custom batch size", " $ gather-ts src/app/page.tsx,src/components/Button.tsx --output output.txt --batch-size 50", "", "For more information, visit: https://github.com/usexr/gather-ts", ].join("\n"); this.deps.logger.info(usage); } } exports.ArgumentParser = ArgumentParser; //# sourceMappingURL=ArgumentParser.js.map