info('RSX Framework Dependency Installation'); $this->info('====================================='); $this->line(''); // Check system requirements if (!$this->check_system_requirements()) { return 1; } // Install npm dependencies if (!$this->option('skip-npm')) { if (!$this->install_npm_dependencies()) { return 1; } } // Set up directories and permissions if (!$this->option('skip-permissions')) { $this->setup_directories(); } // Create initial configuration $this->create_initial_config(); $this->line(''); $this->info('✓ RSX dependencies installed successfully!'); $this->line(''); $this->line('Next steps:'); $this->line(' 1. Run "php artisan rsx:manifest:build" to build the initial manifest'); $this->line(' 2. Check "app/RSpade/Scripts/Parsers/" directory for JavaScript parser setup'); $this->line(' 3. Review the RSX documentation at docs/rsx_shell_implementation.md'); return 0; } /** * Check system requirements */ protected function check_system_requirements() { $this->info('Checking system requirements...'); $requirements = [ 'node' => 'Node.js', 'npm' => 'npm' ]; $missing = []; foreach ($requirements as $command => $name) { if (command_exists($command)) { $version = shell_exec("$command --version 2>&1"); $this->line(" ✓ $name: " . trim($version)); } else { $missing[] = $name; $this->error(" ✗ $name: Not found"); } } if (!empty($missing)) { $this->line(''); $this->error('Missing required dependencies: ' . implode(', ', $missing)); $this->line('Please install the missing dependencies and try again.'); return false; } return true; } /** * Install npm dependencies for JavaScript parsing */ protected function install_npm_dependencies() { $this->line(''); $this->info('Installing npm dependencies for JavaScript parsing...'); $parser_dir = base_path('app/RSpade/Scripts/Parsers'); // Check if package.json exists $package_json_path = $parser_dir . '/package.json'; if (!File::exists($package_json_path)) { $this->create_package_json($package_json_path); } // Check if node_modules exists and force flag not set if (File::isDirectory($parser_dir . '/node_modules') && !$this->option('force')) { $this->line(' ℹ npm dependencies already installed. Use --force to reinstall.'); return true; } // Change to parser directory $original_dir = getcwd(); chdir($parser_dir); try { // Install dependencies $this->line(''); $result = shell_exec_pretty('npm install', true, false); if ($result['exit_code'] !== 0) { $this->error('Failed to install npm dependencies'); return false; } $this->line(''); $this->info(' ✓ npm dependencies installed successfully'); } finally { // Restore original directory chdir($original_dir); } return true; } /** * Create package.json for parser dependencies */ protected function create_package_json($path) { $this->line(' Creating package.json for JavaScript parser...'); $package_config = [ 'name' => 'rsx-js-parser', 'version' => '1.0.0', 'description' => 'JavaScript parser for RSX manifest system', 'private' => true, 'dependencies' => [ '@babel/parser' => '^7.22.0', '@babel/traverse' => '^7.22.0', '@babel/types' => '^7.22.0' ], 'devDependencies' => [ 'eslint' => '^8.40.0', 'prettier' => '^2.8.0' ] ]; File::ensureDirectoryExists(dirname($path)); File::put($path, json_encode($package_config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); $this->line(' ✓ package.json created'); } /** * Set up RSX directories and permissions */ protected function setup_directories() { $this->line(''); $this->info('Setting up RSX directories...'); $directories = [ 'rsx' => 'Main RSX directory', 'rsx/controllers' => 'Controllers', 'rsx/api' => 'API endpoints', 'rsx/models' => 'Data models', 'rsx/views' => 'View templates', 'rsx/components' => 'UI components', 'rsx/services' => 'Business logic', 'rsx/js' => 'JavaScript files', 'rsx/styles' => 'SCSS/LESS files', 'app/RSpade/Scripts/Parsers' => 'Parser scripts', 'app/RSpade/Scripts/Parsers/node_modules' => 'npm packages', 'storage/rsx-build' => 'RSX build artifacts', 'storage/rsx-tmp' => 'RSX temporary files', 'storage/rsx-locks' => 'RSX lock files' ]; foreach ($directories as $dir => $description) { $path = base_path($dir); if (!File::isDirectory($path)) { File::makeDirectory($path, 0755, true); $this->line(" ✓ Created: $dir ($description)"); } else { $this->line(" - Exists: $dir"); } } // Set permissions on storage directories $this->line(''); $this->info('Setting permissions...'); $storage_dir = base_path('storage/rsx'); if (File::isDirectory($storage_dir)) { $result = shell_exec_pretty("chmod -R 775 $storage_dir", false, false); if ($result['exit_code'] === 0) { $this->line(' ✓ Storage permissions set'); } } } /** * Create initial RSX configuration */ protected function create_initial_config() { $this->line(''); $this->info('Creating initial configuration...'); // Create .gitignore for parsers directory $gitignore_path = base_path('app/RSpade/Scripts/Parsers/.gitignore'); if (!File::exists($gitignore_path)) { $gitignore_content = "node_modules/\n*.log\n.DS_Store\n"; File::put($gitignore_path, $gitignore_content); $this->line(' ✓ Created parsers .gitignore'); } // Create advanced parser script if not exists $parser_path = base_path('app/RSpade/Scripts/Parsers/js-parser.js'); if (!File::exists($parser_path)) { $this->create_advanced_parser($parser_path); } // Ensure simple parser is executable $simple_parser = base_path('app/RSpade/Scripts/Parsers/js-parser-simple.js'); if (File::exists($simple_parser)) { chmod($simple_parser, 0755); $this->line(' ✓ Set parser scripts as executable'); } } /** * Create the advanced JavaScript parser script */ protected function create_advanced_parser($path) { $script = <<<'JS' const fs = require('fs'); const parser = require('@babel/parser'); const traverse = require('@babel/traverse').default; const t = require('@babel/types'); // Get file path from command line const filePath = process.argv[2]; if (!filePath) { console.error('Usage: node js-parser.js '); process.exit(1); } // Read file let content; try { content = fs.readFileSync(filePath, 'utf8'); } catch (error) { console.error(`Error reading file: ${error.message}`); process.exit(1); } // Parse result object const result = { classes: {}, functions: {}, exports: {}, imports: [] }; try { // Parse with Babel const ast = parser.parse(content, { sourceType: 'module', plugins: [ 'jsx', 'typescript', 'classProperties', 'classPrivateProperties', 'classPrivateMethods', ['decorators', { decoratorsBeforeExport: true }], 'dynamicImport', 'exportDefaultFrom', 'exportNamespaceFrom', 'asyncGenerators', 'objectRestSpread', 'optionalCatchBinding', 'optionalChaining', 'nullishCoalescingOperator', 'classStaticBlock' ] }); // Traverse AST traverse(ast, { // Class declarations ClassDeclaration(path) { const className = path.node.id.name; const classInfo = { name: className, extends: path.node.superClass ? path.node.superClass.name : null, methods: {}, staticMethods: {}, properties: {}, staticProperties: {} }; // Extract methods and properties path.node.body.body.forEach(member => { if (t.isClassMethod(member)) { const methodInfo = { name: member.key.name, params: member.params.map(p => p.name || p.type), async: member.async, generator: member.generator, kind: member.kind }; if (member.static) { classInfo.staticMethods[member.key.name] = methodInfo; } else { classInfo.methods[member.key.name] = methodInfo; } } else if (t.isClassProperty(member) || t.isClassPrivateProperty(member)) { const propName = t.isIdentifier(member.key) ? member.key.name : t.isPrivateName(member.key) ? '#' + member.key.id.name : 'unknown'; const propInfo = { name: propName, static: member.static, value: member.value ? getValueType(member.value) : null }; if (member.static) { classInfo.staticProperties[propName] = propInfo; } else { classInfo.properties[propName] = propInfo; } } }); result.classes[className] = classInfo; }, // Function declarations FunctionDeclaration(path) { if (path.node.id) { const funcName = path.node.id.name; result.functions[funcName] = { name: funcName, params: path.node.params.map(p => p.name || p.type), async: path.node.async, generator: path.node.generator }; } }, // Imports ImportDeclaration(path) { const importInfo = { source: path.node.source.value, specifiers: [] }; path.node.specifiers.forEach(spec => { if (t.isImportDefaultSpecifier(spec)) { importInfo.specifiers.push({ type: 'default', local: spec.local.name }); } else if (t.isImportSpecifier(spec)) { importInfo.specifiers.push({ type: 'named', imported: spec.imported.name, local: spec.local.name }); } else if (t.isImportNamespaceSpecifier(spec)) { importInfo.specifiers.push({ type: 'namespace', local: spec.local.name }); } }); result.imports.push(importInfo); }, // Exports ExportNamedDeclaration(path) { if (path.node.declaration) { if (t.isClassDeclaration(path.node.declaration) && path.node.declaration.id) { result.exports[path.node.declaration.id.name] = 'class'; } else if (t.isFunctionDeclaration(path.node.declaration) && path.node.declaration.id) { result.exports[path.node.declaration.id.name] = 'function'; } else if (t.isVariableDeclaration(path.node.declaration)) { path.node.declaration.declarations.forEach(decl => { if (t.isIdentifier(decl.id)) { result.exports[decl.id.name] = 'variable'; } }); } } // Handle export specifiers if (path.node.specifiers) { path.node.specifiers.forEach(spec => { if (t.isExportSpecifier(spec)) { result.exports[spec.exported.name] = 'named'; } }); } }, ExportDefaultDeclaration(path) { result.exports.default = getExportType(path.node.declaration); } }); } catch (error) { console.error(`Parse error: ${error.message}`); process.exit(1); } // Helper functions function getValueType(node) { if (t.isStringLiteral(node)) return `"${node.value}"`; if (t.isNumericLiteral(node)) return node.value; if (t.isBooleanLiteral(node)) return node.value; if (t.isNullLiteral(node)) return null; if (t.isIdentifier(node)) return node.name; if (t.isArrayExpression(node)) return 'array'; if (t.isObjectExpression(node)) return 'object'; if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) return 'function'; return node.type; } function getExportType(node) { if (t.isClassDeclaration(node)) return 'class'; if (t.isFunctionDeclaration(node)) return 'function'; if (t.isIdentifier(node)) return 'identifier'; if (t.isCallExpression(node)) return 'expression'; return node.type; } // Output result as JSON console.log(JSON.stringify(result, null, 2)); JS; File::put($path, $script); chmod($path, 0755); $this->line(' ✓ Created advanced JavaScript parser'); } }