@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
JavaScript
;
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] || '๐';
}