import { Box, Static, Text, useApp } from 'ink'; import Spinner from 'ink-spinner'; import * as util from 'node:util'; import React, { type ReactElement, useEffect, useState } from 'react'; import type { DependencyGraph, GraphResult, Package } from '../deps/DependencyGraph.js'; import { GraphTaskError } from '../errors/GraphTaskError.js'; export interface GraphWalkerProps { graph: DependencyGraph; exec: (pkg: Package) => Promise; skipPackages?: string[]; linear?: boolean; debug?: boolean; } interface ResultInfo { package: string; error?: Error; result?: GraphResult; } export const GraphWalker = ({ graph, exec, skipPackages, linear, debug }: GraphWalkerProps): ReactElement => { const [runningEntries, setRunningEntries] = useState([]); const [finishedEntries, setFinishedEntries] = useState([]); const { exit } = useApp(); useEffect(() => { if (linear) { void (async () => { let errors = 0; for (const pkg of graph.getPackages()) { setRunningEntries(prev => [...prev, pkg.name]); try { const result = await exec(pkg); setFinishedEntries(prev => [...prev, { package: pkg.name, result }]); setRunningEntries(prev => prev.filter(item => item !== pkg.name)); } catch (e) { errors++; setFinishedEntries(prev => [...prev, { package: pkg.name, error: e as Error }]); setRunningEntries(prev => prev.filter(item => item !== pkg.name)); } } if (errors) { exit(new Error(`Error processing ${errors} packages; please check the result manually`)); } else { exit(); } })(); } else { void graph .walkAsync( async (pkg): Promise => { setRunningEntries(prev => [...prev, pkg.name]); const result = await exec(pkg); setFinishedEntries(prev => [...prev, { package: pkg.name, result }]); setRunningEntries(prev => prev.filter(item => item !== pkg.name)); return result; }, (e, pkg) => { setFinishedEntries(prev => [...prev, { package: pkg.name, error: e }]); setRunningEntries(prev => prev.filter(item => item !== pkg.name)); }, skipPackages ) .then( () => exit(), e => exit(e) ); } }, [graph, exec, linear]); return ( {item => item.error ? ( item.error instanceof GraphTaskError && item.error.code === 'skipped' ? ( ? {item.package} skipped {item.error.additionalInfo ? ` (${item.error.additionalInfo})` : ''} ) : ( {'\u2717'} {item.package} {debug && {util.inspect(item.error)}} ) ) : item.result ? ( item.result.status === 'skipped' ? ( ? {item.package} skipped {item.result.additionalInfo ? ` (${item.result.additionalInfo})` : ''} ) : item.result.status === 'success' ? ( {'\u2713'} {item.package} ) : item.result.status === 'error' ? ( {'\u2717'} {item.package} {debug && item.result.additionalInfo && {item.result.additionalInfo}} ) : null ) : ( {'\u2713'} {item.package} ) } {runningEntries.map(item => ( {item} ))} ); };