UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

576 lines (575 loc) โ€ข 25.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.manageWorkspaceGraph = manageWorkspaceGraph; const chalk_1 = __importDefault(require("chalk")); const prompts_1 = __importDefault(require("prompts")); const path = __importStar(require("path")); const fs = __importStar(require("fs-extra")); const workspace_graph_1 = require("../utils/workspace-graph"); const workspace_schema_1 = require("../utils/workspace-schema"); const DEFAULT_WORKSPACE_FILE = 're-shell.workspaces.yaml'; async function manageWorkspaceGraph(options = {}) { const { spinner, verbose, json } = options; try { if (options.analyze) { await analyzeWorkspaceGraph(options, spinner); return; } if (options.cycles) { await detectWorkspaceCycles(options, spinner); return; } if (options.order) { await generateBuildOrder(options, spinner); return; } if (options.critical) { await findCriticalPath(options, spinner); return; } if (options.visualize) { await visualizeWorkspaceGraph(options, spinner); return; } if (options.interactive) { await interactiveGraphManagement(options, spinner); return; } // Default: show graph summary await showGraphSummary(options, spinner); } catch (error) { if (spinner) spinner.fail(chalk_1.default.red('Workspace graph operation failed')); throw error; } } async function analyzeWorkspaceGraph(options, spinner) { const inputFile = options.file || DEFAULT_WORKSPACE_FILE; const inputPath = path.resolve(inputFile); if (spinner) spinner.setText(`Analyzing workspace dependency graph: ${inputFile}`); try { const definition = await (0, workspace_schema_1.loadWorkspaceDefinition)(inputPath); const graph = (0, workspace_graph_1.createWorkspaceDependencyGraph)(definition); const analysis = graph.analyzeGraph(); if (spinner) spinner.stop(); if (options.json) { console.log(JSON.stringify(analysis, null, 2)); return; } displayGraphAnalysis(analysis, inputFile, options.detailed || false); } catch (error) { if (spinner) spinner.fail(chalk_1.default.red('Graph analysis failed')); throw error; } } async function detectWorkspaceCycles(options, spinner) { const inputFile = options.file || DEFAULT_WORKSPACE_FILE; const inputPath = path.resolve(inputFile); if (spinner) spinner.setText(`Detecting dependency cycles: ${inputFile}`); try { const definition = await (0, workspace_schema_1.loadWorkspaceDefinition)(inputPath); const graph = (0, workspace_graph_1.createWorkspaceDependencyGraph)(definition); const cycles = graph.detectCycles(); if (spinner) spinner.stop(); if (options.json) { console.log(JSON.stringify(cycles, null, 2)); return; } displayCycleDetection(cycles, inputFile, options.detailed || false); } catch (error) { if (spinner) spinner.fail(chalk_1.default.red('Cycle detection failed')); throw error; } } async function generateBuildOrder(options, spinner) { const inputFile = options.file || DEFAULT_WORKSPACE_FILE; const inputPath = path.resolve(inputFile); if (spinner) spinner.setText(`Generating build order: ${inputFile}`); try { const definition = await (0, workspace_schema_1.loadWorkspaceDefinition)(inputPath); const graph = (0, workspace_graph_1.createWorkspaceDependencyGraph)(definition); const buildOrder = graph.generateBuildOrder(); if (spinner) spinner.stop(); if (options.json) { const output = { ...buildOrder, dependencies: Object.fromEntries(buildOrder.dependencies) }; console.log(JSON.stringify(output, null, 2)); return; } displayBuildOrder(buildOrder, inputFile, options.detailed || false); } catch (error) { if (spinner) spinner.fail(chalk_1.default.red('Build order generation failed')); throw error; } } async function findCriticalPath(options, spinner) { const inputFile = options.file || DEFAULT_WORKSPACE_FILE; const inputPath = path.resolve(inputFile); if (spinner) spinner.setText(`Finding critical path: ${inputFile}`); try { const definition = await (0, workspace_schema_1.loadWorkspaceDefinition)(inputPath); const graph = (0, workspace_graph_1.createWorkspaceDependencyGraph)(definition); const criticalPath = graph.findCriticalPath(); if (spinner) spinner.stop(); if (options.json) { console.log(JSON.stringify({ criticalPath }, null, 2)); return; } displayCriticalPath(criticalPath, inputFile); } catch (error) { if (spinner) spinner.fail(chalk_1.default.red('Critical path analysis failed')); throw error; } } async function visualizeWorkspaceGraph(options, spinner) { const inputFile = options.file || DEFAULT_WORKSPACE_FILE; const inputPath = path.resolve(inputFile); if (spinner) spinner.setText(`Generating graph visualization: ${inputFile}`); try { const definition = await (0, workspace_schema_1.loadWorkspaceDefinition)(inputPath); const graph = (0, workspace_graph_1.createWorkspaceDependencyGraph)(definition); const vizData = graph.getVisualizationData(); if (spinner) spinner.stop(); if (options.output) { // Save visualization data to file const outputPath = path.resolve(options.output); await fs.writeJson(outputPath, vizData, { spaces: 2 }); console.log(chalk_1.default.green(`Visualization data saved to: ${options.output}`)); } else { // Display text-based visualization displayTextVisualization(vizData, inputFile); } } catch (error) { if (spinner) spinner.fail(chalk_1.default.red('Visualization generation failed')); throw error; } } async function showGraphSummary(options, spinner) { const inputFile = options.file || DEFAULT_WORKSPACE_FILE; const inputPath = path.resolve(inputFile); if (spinner) spinner.setText('Loading workspace dependency graph...'); try { if (!(await fs.pathExists(inputPath))) { if (spinner) spinner.stop(); console.log(chalk_1.default.yellow('\\nโš ๏ธ No workspace definition found')); console.log(chalk_1.default.gray(`Expected: ${inputFile}`)); console.log(chalk_1.default.cyan('\\n๐Ÿš€ Quick start:')); console.log(' re-shell workspace-def init'); return; } const definition = await (0, workspace_schema_1.loadWorkspaceDefinition)(inputPath); const graph = (0, workspace_graph_1.createWorkspaceDependencyGraph)(definition); const analysis = graph.analyzeGraph(); if (spinner) spinner.stop(); if (options.json) { const summary = { file: inputFile, workspaces: analysis.nodeCount, dependencies: analysis.edgeCount, hasCycles: analysis.cycles.hasCycles, cycleCount: analysis.cycles.cycles.length, maxDepth: analysis.statistics.maxDepth, orphaned: analysis.orphanedNodes.length }; console.log(JSON.stringify(summary, null, 2)); return; } console.log(chalk_1.default.cyan('\\n๐Ÿ“Š Workspace Dependency Graph Summary')); console.log(chalk_1.default.gray('โ•'.repeat(60))); console.log(`\\nFile: ${chalk_1.default.cyan(inputFile)}`); console.log(`Workspaces: ${chalk_1.default.yellow(analysis.nodeCount)}`); console.log(`Dependencies: ${chalk_1.default.yellow(analysis.edgeCount)}`); // Cycle status const cycleIcon = analysis.cycles.hasCycles ? 'โŒ' : 'โœ…'; const cycleStatus = analysis.cycles.hasCycles ? chalk_1.default.red(`${analysis.cycles.cycles.length} cycles detected`) : chalk_1.default.green('No cycles'); console.log(`\\nDependency Cycles: ${cycleIcon} ${cycleStatus}`); // Graph metrics console.log(`\\nGraph Metrics:`); console.log(` Max Depth: ${analysis.statistics.maxDepth}`); console.log(` Avg Dependencies: ${analysis.statistics.avgDependencies.toFixed(1)}`); console.log(` Avg Dependents: ${analysis.statistics.avgDependents.toFixed(1)}`); if (analysis.orphanedNodes.length > 0) { console.log(` Orphaned: ${chalk_1.default.yellow(analysis.orphanedNodes.length)}`); } // Critical path if (analysis.criticalPath.length > 0) { console.log(`\\nCritical Path: ${analysis.criticalPath.join(' โ†’ ')}`); } console.log(chalk_1.default.cyan('\\n๐Ÿ› ๏ธ Available Commands:')); console.log(' โ€ข re-shell workspace-graph analyze'); console.log(' โ€ข re-shell workspace-graph cycles'); console.log(' โ€ข re-shell workspace-graph order'); console.log(' โ€ข re-shell workspace-graph critical'); console.log(' โ€ข re-shell workspace-graph interactive'); } catch (error) { if (spinner) spinner.fail(chalk_1.default.red('Graph summary failed')); throw error; } } async function interactiveGraphManagement(options, spinner) { if (spinner) spinner.stop(); const inputFile = options.file || DEFAULT_WORKSPACE_FILE; const inputPath = path.resolve(inputFile); const exists = await fs.pathExists(inputPath); if (!exists) { console.log(chalk_1.default.yellow('\\nโš ๏ธ No workspace definition found')); console.log(chalk_1.default.gray('Create one first with: re-shell workspace-def init')); return; } const response = await (0, prompts_1.default)([ { type: 'select', name: 'action', message: 'What would you like to analyze?', choices: [ { title: '๐Ÿ“Š Complete graph analysis', value: 'analyze' }, { title: '๐Ÿ”„ Detect dependency cycles', value: 'cycles' }, { title: '๐Ÿ—๏ธ Generate build order', value: 'order' }, { title: '๐ŸŽฏ Find critical path', value: 'critical' }, { title: '๐Ÿ‘€ Visualize graph', value: 'visualize' }, { title: '๐Ÿ“‹ Show summary', value: 'summary' } ] } ]); if (!response.action) return; switch (response.action) { case 'analyze': await analyzeWorkspaceGraph({ ...options, interactive: false }); break; case 'cycles': await detectWorkspaceCycles({ ...options, interactive: false }); break; case 'order': await generateBuildOrder({ ...options, interactive: false }); break; case 'critical': await findCriticalPath({ ...options, interactive: false }); break; case 'visualize': await visualizeWorkspaceGraph({ ...options, interactive: false }); break; case 'summary': await showGraphSummary({ ...options, interactive: false }); break; } } // Display functions function displayGraphAnalysis(analysis, fileName, detailed) { console.log(chalk_1.default.cyan(`\\n๐Ÿ“Š Workspace Dependency Graph Analysis`)); console.log(chalk_1.default.gray(`File: ${fileName}`)); console.log(chalk_1.default.gray('โ•'.repeat(60))); // Basic statistics console.log(`\\n๐Ÿ“ˆ Graph Statistics:`); console.log(` Nodes (Workspaces): ${chalk_1.default.yellow(analysis.nodeCount)}`); console.log(` Edges (Dependencies): ${chalk_1.default.yellow(analysis.edgeCount)}`); console.log(` Maximum Depth: ${chalk_1.default.yellow(analysis.statistics.maxDepth)}`); console.log(` Average Dependencies per Workspace: ${chalk_1.default.yellow(analysis.statistics.avgDependencies.toFixed(1))}`); console.log(` Average Dependents per Workspace: ${chalk_1.default.yellow(analysis.statistics.avgDependents.toFixed(1))}`); // Cycle analysis console.log(`\\n๐Ÿ”„ Cycle Analysis:`); if (analysis.cycles.hasCycles) { console.log(chalk_1.default.red(` โŒ ${analysis.cycles.cycles.length} cycles detected`)); if (detailed) { for (let i = 0; i < analysis.cycles.cycles.length; i++) { const cycle = analysis.cycles.cycles[i]; const severityColor = cycle.severity === 'error' ? chalk_1.default.red : cycle.severity === 'warning' ? chalk_1.default.yellow : chalk_1.default.blue; console.log(`\\n Cycle ${i + 1}: ${severityColor(cycle.severity.toUpperCase())}`); console.log(` Path: ${cycle.path.join(' โ†’ ')}`); console.log(` Type: ${cycle.type}`); if (cycle.suggestions.length > 0) { console.log(` Suggestions:`); for (const suggestion of cycle.suggestions) { console.log(` โ€ข ${suggestion}`); } } } } } else { console.log(chalk_1.default.green(' โœ… No cycles detected')); } // Strongly connected components if (analysis.cycles.stronglyConnectedComponents.length > 0) { console.log(`\\n Strongly Connected Components: ${analysis.cycles.stronglyConnectedComponents.length}`); if (detailed) { for (let i = 0; i < analysis.cycles.stronglyConnectedComponents.length; i++) { const component = analysis.cycles.stronglyConnectedComponents[i]; console.log(` Component ${i + 1}: ${component.join(', ')}`); } } } // Build levels if (analysis.levels.length > 0) { console.log(`\\n๐Ÿ—๏ธ Build Levels: ${analysis.levels.length}`); if (detailed) { for (let i = 0; i < analysis.levels.length; i++) { const level = analysis.levels[i]; console.log(` Level ${i}: ${level.join(', ')}`); } } } // Critical path if (analysis.criticalPath.length > 0) { console.log(`\\n๐ŸŽฏ Critical Path (${analysis.criticalPath.length} workspaces):`); console.log(` ${analysis.criticalPath.join(' โ†’ ')}`); } // Orphaned nodes if (analysis.orphanedNodes.length > 0) { console.log(`\\n๐Ÿ๏ธ Orphaned Workspaces: ${analysis.orphanedNodes.length}`); if (detailed) { console.log(` ${analysis.orphanedNodes.join(', ')}`); } } // Topological order if (analysis.topologicalOrder.length > 0 && detailed) { console.log(`\\n๐Ÿ“‹ Topological Order:`); console.log(` ${analysis.topologicalOrder.join(' โ†’ ')}`); } } function displayCycleDetection(cycles, fileName, detailed) { console.log(chalk_1.default.cyan(`\\n๐Ÿ”„ Dependency Cycle Detection`)); console.log(chalk_1.default.gray(`File: ${fileName}`)); console.log(chalk_1.default.gray('โ•'.repeat(50))); if (!cycles.hasCycles) { console.log(chalk_1.default.green('\\nโœ… No dependency cycles detected')); console.log(chalk_1.default.gray('Your workspace dependency graph is acyclic.')); return; } console.log(chalk_1.default.red(`\\nโŒ ${cycles.cycles.length} dependency cycle(s) detected`)); // Group cycles by severity const errorCycles = cycles.cycles.filter(c => c.severity === 'error'); const warningCycles = cycles.cycles.filter(c => c.severity === 'warning'); const infoCycles = cycles.cycles.filter(c => c.severity === 'info'); if (errorCycles.length > 0) { console.log(chalk_1.default.red(`\\n๐Ÿšจ Critical Cycles (${errorCycles.length}):`)); displayCycles(errorCycles, detailed); } if (warningCycles.length > 0) { console.log(chalk_1.default.yellow(`\\nโš ๏ธ Warning Cycles (${warningCycles.length}):`)); displayCycles(warningCycles, detailed); } if (infoCycles.length > 0) { console.log(chalk_1.default.blue(`\\nโ„น๏ธ Info Cycles (${infoCycles.length}):`)); displayCycles(infoCycles, detailed); } // Strongly connected components if (cycles.stronglyConnectedComponents.length > 0) { console.log(chalk_1.default.cyan(`\\n๐Ÿ”— Strongly Connected Components (${cycles.stronglyConnectedComponents.length}):`)); for (let i = 0; i < cycles.stronglyConnectedComponents.length; i++) { const component = cycles.stronglyConnectedComponents[i]; console.log(` ${i + 1}. ${component.join(' โ†” ')}`); } } } function displayCycles(cycles, detailed) { for (let i = 0; i < cycles.length; i++) { const cycle = cycles[i]; console.log(`\\n ${i + 1}. ${cycle.path.join(' โ†’ ')}`); console.log(` Type: ${cycle.type}`); if (detailed && cycle.suggestions.length > 0) { console.log(` Suggestions:`); for (const suggestion of cycle.suggestions) { console.log(` โ€ข ${suggestion}`); } } } } function displayBuildOrder(buildOrder, fileName, detailed) { console.log(chalk_1.default.cyan(`\\n๐Ÿ—๏ธ Workspace Build Order`)); console.log(chalk_1.default.gray(`File: ${fileName}`)); console.log(chalk_1.default.gray('โ•'.repeat(50))); console.log(`\\n๐Ÿ“Š Build Statistics:`); console.log(` Total Levels: ${chalk_1.default.yellow(buildOrder.order.length)}`); console.log(` Parallelizable: ${buildOrder.parallelizable ? chalk_1.default.green('Yes') : chalk_1.default.red('No')}`); console.log(` Max Parallelism: ${chalk_1.default.yellow(buildOrder.maxParallelism)}`); console.log(` Estimated Time: ${chalk_1.default.yellow(Math.round(buildOrder.estimatedTime / 60))} minutes`); console.log(`\\n๐Ÿ”ข Build Order (Level by Level):`); for (let i = 0; i < buildOrder.order.length; i++) { const level = buildOrder.order[i]; console.log(`\\n Level ${i + 1}: ${level.length} workspace(s)`); if (level.length === 1) { console.log(` โ€ข ${level[0]}`); } else { console.log(` Parallel execution:`); for (const workspace of level) { console.log(` โ€ข ${workspace}`); } } } if (detailed) { console.log(`\\n๐Ÿ“‹ Dependencies:`); for (const [workspace, deps] of buildOrder.dependencies) { if (deps.length > 0) { console.log(` ${workspace}: ${deps.join(', ')}`); } } } console.log(chalk_1.default.cyan('\\n๐Ÿ’ก Build Command Example:')); console.log(' # Sequential build'); console.log(' for level in levels; do'); console.log(' for workspace in $level; do npm run build --workspace=$workspace; done'); console.log(' done'); console.log('\\n # Parallel build (within levels)'); console.log(' for level in levels; do'); console.log(' echo $level | xargs -n1 -P0 npm run build --workspace='); console.log(' done'); } function displayCriticalPath(criticalPath, fileName) { console.log(chalk_1.default.cyan(`\\n๐ŸŽฏ Critical Path Analysis`)); console.log(chalk_1.default.gray(`File: ${fileName}`)); console.log(chalk_1.default.gray('โ•'.repeat(50))); if (criticalPath.length === 0) { console.log(chalk_1.default.yellow('\\nโš ๏ธ Cannot determine critical path')); console.log(chalk_1.default.gray('This usually indicates dependency cycles in the graph.')); console.log(chalk_1.default.cyan('\\n๐Ÿ’ก Suggestion: Run cycle detection first:')); console.log(' re-shell workspace-graph cycles'); return; } console.log(`\\n๐Ÿ Critical Path (${criticalPath.length} workspaces):`); console.log(chalk_1.default.yellow(` ${criticalPath.join(' โ†’ ')}`)); console.log(`\\n๐Ÿ“Š Analysis:`); console.log(` Path Length: ${chalk_1.default.yellow(criticalPath.length)} workspaces`); console.log(` Bottleneck: ${chalk_1.default.red(criticalPath[criticalPath.length - 1])}`); console.log(chalk_1.default.cyan('\\n๐Ÿ’ก Optimization Tips:')); console.log(' โ€ข Focus optimization efforts on critical path workspaces'); console.log(' โ€ข Consider breaking up large workspaces on the critical path'); console.log(' โ€ข Parallel execution of non-critical workspaces can happen alongside'); console.log(` โ€ข The build time is primarily determined by: ${criticalPath.join(' โ†’ ')}`); } function displayTextVisualization(vizData, fileName) { console.log(chalk_1.default.cyan(`\\n๐Ÿ‘€ Workspace Graph Visualization`)); console.log(chalk_1.default.gray(`File: ${fileName}`)); console.log(chalk_1.default.gray('โ•'.repeat(50))); console.log(`\\n๐Ÿ“ฆ Workspaces (${vizData.nodes.length}):`); // Group nodes by type const nodesByType = vizData.nodes.reduce((acc, node) => { if (!acc[node.group]) acc[node.group] = []; acc[node.group].push(node.label); return acc; }, {}); for (const [type, nodes] of Object.entries(nodesByType)) { const typeIcon = getWorkspaceTypeIcon(type); console.log(`\\n ${typeIcon} ${type} (${nodes.length}):`); for (const node of nodes) { console.log(` โ€ข ${node}`); } } console.log(`\\n๐Ÿ”— Dependencies (${vizData.edges.length}):`); // Group edges by type const edgesByType = vizData.edges.reduce((acc, edge) => { if (!acc[edge.label]) acc[edge.label] = []; acc[edge.label].push(`${edge.from} โ†’ ${edge.to}`); return acc; }, {}); for (const [type, edges] of Object.entries(edgesByType)) { const typeIcon = getDependencyTypeIcon(type); console.log(`\\n ${typeIcon} ${type} (${edges.length}):`); for (const edge of edges.slice(0, 10)) { console.log(` โ€ข ${edge}`); } if (edges.length > 10) { console.log(` ... and ${edges.length - 10} more`); } } console.log(chalk_1.default.cyan('\\n๐Ÿ’พ Export Options:')); console.log(' โ€ข re-shell workspace-graph visualize --output graph.json'); console.log(' โ€ข Use graph.json with visualization tools like:'); console.log(' - Graphviz (dot format)'); console.log(' - D3.js force-directed graphs'); console.log(' - Cytoscape.js'); console.log(' - Network visualization libraries'); } // Utility functions function getWorkspaceTypeIcon(type) { const icons = { app: '๐Ÿ“ฑ', package: '๐Ÿ“ฆ', lib: '๐Ÿ“š', tool: '๐Ÿ”ง', service: 'โš™๏ธ', website: '๐ŸŒ' }; return icons[type] || '๐Ÿ“„'; } function getDependencyTypeIcon(type) { const icons = { build: '๐Ÿ—๏ธ', runtime: 'โšก', dev: '๐Ÿ”ง', test: '๐Ÿงช' }; return icons[type] || '๐Ÿ”—'; }