UNPKG

scaffold-scripts

Version:

Simple CLI tool for managing and running your own scripts. Add any script, run it anywhere.

401 lines (400 loc) 17.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ScriptProcessor = void 0; const path_1 = require("path"); const scriptTypeDetector_js_1 = require("./scriptTypeDetector.js"); const scriptConverter_js_1 = require("./scriptConverter.js"); const scriptValidator_js_1 = require("./scriptValidator.js"); class ScriptProcessor { constructor() { this.detector = new scriptTypeDetector_js_1.ScriptTypeDetector(); this.converter = new scriptConverter_js_1.AdvancedScriptConverter(); this.validator = new scriptValidator_js_1.ScriptValidator(); } /** * Automatically fix interactive input issues in scripts */ fixInteractiveInput(script, scriptType) { let fixedScript = script; // Fix PowerShell Read-Host patterns if (scriptType.type === "powershell") { fixedScript = this.fixPowerShellInteractiveInput(fixedScript); } // Fix Bash read patterns else if (scriptType.type === "shell") { fixedScript = this.fixBashInteractiveInput(fixedScript); } // Fix Python input() patterns else if (scriptType.type === "python") { fixedScript = this.fixPythonInteractiveInput(fixedScript); } // Fix Node.js readline/prompt patterns else if (scriptType.type === "nodejs") { fixedScript = this.fixNodeJsInteractiveInput(fixedScript); } return fixedScript; } /** * Fix PowerShell Read-Host patterns */ fixPowerShellInteractiveInput(script) { // Check if script already has a param block const hasParamBlock = /^\s*param\s*\(/m.test(script); // Only apply fixing if there's no existing param block if (!hasParamBlock) { // Pattern: $var = Read-Host (may or may not have preceding Write-Host) const readHostPattern = /\$([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*Read-Host/g; const matches = [...script.matchAll(readHostPattern)]; if (matches.length > 0) { // Add parameter block at the beginning with smart defaults const paramNames = matches.map((match) => match[1]); const paramBlock = `param(\n${paramNames .map((name) => { // Use current directory as default for common path parameters if (name.toLowerCase().includes('root') || name.toLowerCase().includes('path')) { return ` [string]$${name} = (Get-Location).Path`; } return ` [string]$${name} = $null`; }) .join(",\n")}\n)\n\n`; // Replace Read-Host with parameter fallback that shows current directory for (const match of matches) { const varName = match[1]; const originalPattern = match[0]; let replacement; if (varName.toLowerCase().includes('root') || varName.toLowerCase().includes('path')) { replacement = `if (-not $${varName}) {\n Write-Host "Using current directory: $(Get-Location)" -ForegroundColor Green\n $${varName} = (Get-Location).Path\n}`; } else { replacement = `if (-not $${varName}) {\n ${originalPattern}\n}`; } script = script.replace(originalPattern, replacement); } script = paramBlock + script; } } return script; } /** * Fix Bash read patterns */ fixBashInteractiveInput(script) { // Pattern: read -p "prompt" variable const readPattern = /read\s+(?:-p\s+["'][^"']*["']\s+)?([a-zA-Z_][a-zA-Z0-9_]*)/g; const matches = [...script.matchAll(readPattern)]; if (matches.length > 0) { let paramIndex = 1; const paramDefaults = []; // Add parameter defaults at the beginning for (const match of matches) { const varName = match[1]; let defaultValue; if (varName.toLowerCase().includes('root') || varName.toLowerCase().includes('path')) { defaultValue = '$(pwd)'; } else { defaultValue = '"default"'; } paramDefaults.push(`${varName}=\${${paramIndex}:-${defaultValue}}`); paramIndex++; } // Add shebang if not present if (!script.startsWith('#!')) { script = '#!/bin/bash\n' + script; } // Insert parameter defaults after shebang const lines = script.split('\n'); const shebangIndex = lines[0].startsWith('#!') ? 1 : 0; lines.splice(shebangIndex, 0, '', '# Auto-generated parameter defaults', ...paramDefaults, ''); // Remove original read commands script = lines.join('\n'); for (const match of matches) { script = script.replace(match[0], `# Converted: ${match[0]}`); } } return script; } /** * Fix Python input() patterns */ fixPythonInteractiveInput(script) { // Pattern: variable = input("prompt") const inputPattern = /([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*input\s*\(\s*["'][^"']*["']\s*\)/g; const matches = [...script.matchAll(inputPattern)]; if (matches.length > 0) { let paramIndex = 1; const imports = ['import sys', 'import os']; const paramDefaults = []; // Add parameter defaults for (const match of matches) { const varName = match[1]; let defaultValue; if (varName.toLowerCase().includes('root') || varName.toLowerCase().includes('path')) { defaultValue = 'os.getcwd()'; } else { defaultValue = '"default"'; } paramDefaults.push(`${varName} = sys.argv[${paramIndex}] if len(sys.argv) > ${paramIndex} else ${defaultValue}`); paramIndex++; } // Add imports and defaults at the beginning const lines = script.split('\n'); let insertIndex = 0; // Skip shebang if present if (lines[0].startsWith('#!')) { insertIndex = 1; } lines.splice(insertIndex, 0, '', '# Auto-generated imports and parameter defaults', ...imports, '', ...paramDefaults, ''); // Remove original input() calls script = lines.join('\n'); for (const match of matches) { script = script.replace(match[0], `# Converted: ${match[0]}`); } } return script; } /** * Fix Node.js readline/prompt patterns */ fixNodeJsInteractiveInput(script) { // Check for readline interface pattern if (script.includes('readline.createInterface') || script.includes('rl.question')) { // Convert readline pattern to simple argv handling const simpleConversion = `// Auto-generated parameter handling const args = process.argv.slice(2); const projectName = args[0] || 'default'; const projectPath = args[1] || process.cwd(); console.log(\`Project: \${projectName}\`); console.log(\`Path: \${projectPath}\`); console.log('Setup completed!');`; return simpleConversion; } // Pattern: const variable = prompt('message'); const promptPattern = /const\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*prompt\s*\(\s*['"][^'"]*['"]\s*\)\s*;?/g; const matches = [...script.matchAll(promptPattern)]; if (matches.length > 0) { let paramIndex = 2; // process.argv starts at index 2 const paramDefaults = []; // Add parameter defaults for (const match of matches) { const varName = match[1]; let defaultValue; if (varName.toLowerCase().includes('root') || varName.toLowerCase().includes('path')) { defaultValue = 'process.cwd()'; } else { defaultValue = '"default"'; } paramDefaults.push(`const ${varName} = process.argv[${paramIndex}] || ${defaultValue};`); paramIndex++; } // Insert parameter defaults at the beginning script = '// Auto-generated parameter defaults\n' + paramDefaults.join('\n') + '\n\n' + script; // Remove original prompt calls and prompt-sync require script = script.replace(/const prompt = require\(["']prompt-sync["']\)\(\);?/g, '// Removed prompt-sync dependency'); for (const match of matches) { script = script.replace(match[0], `// Converted: ${match[0]}`); } } return script; } /** * Process a script file for storage in the database */ async processScriptFile(scriptPath, options = {}) { // 1. Validate file type and read the script const absolutePath = (0, path_1.resolve)(scriptPath); const validation = this.validator.validateFromFile(absolutePath, { strict: options.strict || false, allowNetworkAccess: options.allowNetworkAccess || !options.strict, allowSystemModification: options.allowSystemModification || !options.strict, }); // Early return if file type validation failed if (!validation.isValid) { return { original: "", windows: undefined, unix: undefined, crossPlatform: undefined, originalPlatform: "cross-platform", scriptType: "shell", validation: { isValid: validation.isValid, errors: validation.errors, warnings: validation.warnings, }, }; } const originalScript = validation.sanitizedScript; const contentResult = await this.processScriptContent(originalScript, options); // Merge file validation warnings with content validation return { ...contentResult, validation: { ...contentResult.validation, warnings: [ ...validation.warnings, ...contentResult.validation.warnings, ], }, }; } /** * Process script content directly */ async processScriptContent(originalScript, options = {}) { // 1. Validate the original script const validation = this.validator.validate(originalScript, { strict: options.strict || false, allowNetworkAccess: options.allowNetworkAccess || !options.strict, allowSystemModification: options.allowSystemModification || !options.strict, }); // 2. Use sanitized script for further processing let sanitizedScript = validation.sanitizedScript; // 3. Detect script type and original platform const scriptType = this.detector.detectType(sanitizedScript); const originalPlatform = this.detector.detectPlatform(sanitizedScript); // 4. Automatically fix interactive input issues (enabled by default) if (options.fixInteractiveInput !== false) { const fixedScript = this.fixInteractiveInput(sanitizedScript, scriptType); if (fixedScript !== sanitizedScript) { sanitizedScript = fixedScript; validation.warnings.push("Automatically converted interactive input to support command-line arguments"); } } // 5. Generate cross-platform versions const versions = this.converter.generateVersions(sanitizedScript, scriptType, originalPlatform); // 6. Additional validation warnings for cross-platform compatibility if (originalPlatform !== "cross-platform") { validation.warnings.push(`Script appears to be ${originalPlatform}-specific. Generated cross-platform versions may need manual review.`); } return { original: sanitizedScript, windows: versions.windows, unix: versions.unix, crossPlatform: versions.crossPlatform, originalPlatform, scriptType: scriptType.type, validation: { isValid: validation.isValid, errors: validation.errors, warnings: validation.warnings, }, }; } /** * Create a ScaffoldCommand from processed script */ createCommand(name, processedScript, options = {}) { const now = new Date().toISOString(); return { name, script_original: processedScript.original, script_windows: processedScript.windows, script_unix: processedScript.unix, script_cross_platform: processedScript.crossPlatform, original_platform: processedScript.originalPlatform, script_type: processedScript.scriptType, platform: options.platform || "all", alias: options.alias, description: options.description, createdAt: now, updatedAt: now, }; } /** * Get the best script version for execution on current platform */ getBestScript(command, targetPlatform) { const currentPlatform = targetPlatform || process.platform; return this.converter.getBestScriptForPlatform(command, currentPlatform); } /** * Get display information about all script versions */ getScriptVersionInfo(command) { const currentPlatform = process.platform; const bestScript = this.getBestScript(command); let bestVersion = "original"; if (bestScript === command.script_windows) bestVersion = "windows"; else if (bestScript === command.script_unix) bestVersion = "unix"; else if (bestScript === command.script_cross_platform) bestVersion = "cross-platform"; return { original: { content: command.script_original, platform: command.original_platform, type: command.script_type, }, windows: command.script_windows ? { content: command.script_windows, available: true, } : { content: "", available: false }, unix: command.script_unix ? { content: command.script_unix, available: true, } : { content: "", available: false }, crossPlatform: command.script_cross_platform ? { content: command.script_cross_platform, available: true, } : { content: "", available: false }, bestForCurrent: { content: bestScript, version: bestVersion, }, }; } /** * Validate platform compatibility */ validatePlatformCompatibility(command, targetPlatform) { const isWindows = targetPlatform === "win32"; const warnings = []; const recommendations = []; let compatible = true; // Check if we have appropriate version for target platform if (isWindows && !command.script_windows && command.original_platform === "unix") { warnings.push("This script was written for Unix/Linux and may not work properly on Windows"); recommendations.push("Consider adding a Windows-specific version with: scaffold update " + command.name + " /path/to/windows-script.ps1"); compatible = false; } else if (!isWindows && !command.script_unix && command.original_platform === "windows") { warnings.push("This script was written for Windows and may not work properly on Unix/Linux"); recommendations.push("Consider adding a Unix-specific version with: scaffold update " + command.name + " /path/to/unix-script.sh"); compatible = false; } // Check script type compatibility if (command.script_type === "powershell" && !isWindows) { warnings.push("PowerShell scripts may not be available on this platform"); recommendations.push("Install PowerShell Core or use the converted Unix version"); } else if (command.script_type === "batch" && !isWindows) { warnings.push("Batch scripts are not compatible with Unix/Linux platforms"); compatible = false; } return { compatible, warnings, recommendations, }; } } exports.ScriptProcessor = ScriptProcessor; //# sourceMappingURL=scriptProcessor.js.map