@@ -291,13 +291,21 @@ export class Graph<A> extends GraphNode<A> {
291
291
return topoSort ( new Set ( projectedDependencies . keys ( ) ) , projectedDependencies ) ;
292
292
}
293
293
294
- public consoleLog ( indent : number = 0 ) {
295
- process . stdout . write ( ' ' . repeat ( indent ) + this + depString ( this ) + '\n' ) ;
296
- for ( const node of this . nodes ) {
297
- if ( node instanceof Graph ) {
298
- node . consoleLog ( indent + 2 ) ;
299
- } else {
300
- process . stdout . write ( ' ' . repeat ( indent + 2 ) + node + depString ( node ) + '\n' ) ;
294
+ public render ( ) {
295
+ const lines = new Array < string > ( ) ;
296
+ recurse ( this , '' , true ) ;
297
+ return lines . join ( '\n' ) ;
298
+
299
+ function recurse ( x : GraphNode < A > , indent : string , last : boolean ) {
300
+ const bullet = last ? '└─' : '├─' ;
301
+ const follow = last ? ' ' : '│ ' ;
302
+ lines . push ( `${ indent } ${ bullet } ${ x } ${ depString ( x ) } ` ) ;
303
+ if ( x instanceof Graph ) {
304
+ let i = 0 ;
305
+ const sortedNodes = Array . prototype . concat . call ( [ ] , ...x . sortedChildren ( ) ) ;
306
+ for ( const child of sortedNodes ) {
307
+ recurse ( child , `${ indent } ${ follow } ` , i ++ == x . nodes . size - 1 ) ;
308
+ }
301
309
}
302
310
}
303
311
@@ -309,6 +317,79 @@ export class Graph<A> extends GraphNode<A> {
309
317
}
310
318
}
311
319
320
+ public renderDot ( ) {
321
+ const lines = new Array < string > ( ) ;
322
+
323
+ lines . push ( 'digraph G {' ) ;
324
+ lines . push ( ' # Arrows represent an "unlocks" relationship (opposite of dependency). So chosen' ) ;
325
+ lines . push ( ' # because the layout looks more natural that way.' ) ;
326
+ lines . push ( ' # To represent subgraph dependencies, subgraphs are represented by BEGIN/END nodes.' ) ;
327
+ lines . push ( ' # To render: `dot -Tsvg input.dot > graph.svg`, open in a browser.' ) ;
328
+ lines . push ( ' node [shape="box"];' ) ;
329
+ for ( const child of this . nodes ) {
330
+ recurse ( child ) ;
331
+ }
332
+ lines . push ( '}' ) ;
333
+
334
+ return lines . join ( '\n' ) ;
335
+
336
+ function recurse ( node : GraphNode < A > ) {
337
+ let dependencySource ;
338
+
339
+ if ( node instanceof Graph ) {
340
+ lines . push ( `${ graphBegin ( node ) } [shape="cds", style="filled", fillcolor="#b7deff"];` ) ;
341
+ lines . push ( `${ graphEnd ( node ) } [shape="cds", style="filled", fillcolor="#b7deff"];` ) ;
342
+ dependencySource = graphBegin ( node ) ;
343
+ } else {
344
+ dependencySource = nodeLabel ( node ) ;
345
+ lines . push ( `${ nodeLabel ( node ) } ;` ) ;
346
+ }
347
+
348
+ for ( const dep of node . dependencies ) {
349
+ const dst = dep instanceof Graph ? graphEnd ( dep ) : nodeLabel ( dep ) ;
350
+ lines . push ( `${ dst } -> ${ dependencySource } ;` ) ;
351
+ }
352
+
353
+ if ( node instanceof Graph && node . nodes . size > 0 ) {
354
+ for ( const child of node . nodes ) {
355
+ recurse ( child ) ;
356
+ }
357
+
358
+ // Add dependency arrows between the "subgraph begin" and the first rank of
359
+ // the children, and the last rank of the children and "subgraph end" nodes.
360
+ const sortedChildren = node . sortedChildren ( ) ;
361
+ for ( const first of sortedChildren [ 0 ] ) {
362
+ const src = first instanceof Graph ? graphBegin ( first ) : nodeLabel ( first ) ;
363
+ lines . push ( `${ graphBegin ( node ) } -> ${ src } ;` ) ;
364
+ }
365
+ for ( const last of sortedChildren [ sortedChildren . length - 1 ] ) {
366
+ const dst = last instanceof Graph ? graphEnd ( last ) : nodeLabel ( last ) ;
367
+ lines . push ( `${ dst } -> ${ graphEnd ( node ) } ;` ) ;
368
+ }
369
+ }
370
+ }
371
+
372
+ function id ( node : GraphNode < A > ) {
373
+ return node . rootPath ( ) . slice ( 1 ) . map ( n => n . id ) . join ( '.' ) ;
374
+ }
375
+
376
+ function nodeLabel ( node : GraphNode < A > ) {
377
+ return `"${ id ( node ) } "` ;
378
+ }
379
+
380
+ function graphBegin ( node : Graph < A > ) {
381
+ return `"BEGIN ${ id ( node ) } "` ;
382
+ }
383
+
384
+ function graphEnd ( node : Graph < A > ) {
385
+ return `"END ${ id ( node ) } "` ;
386
+ }
387
+ }
388
+
389
+ public consoleLog ( _indent : number = 0 ) {
390
+ process . stdout . write ( this . render ( ) + '\n' ) ;
391
+ }
392
+
312
393
/**
313
394
* Return the union of all dependencies of the descendants of this graph
314
395
*/
0 commit comments