in packages/cdk-graph-plugin-diagram/src/internal/graphviz/diagram.ts [130:315]
export function buildDiagram(
store: Graph.Store,
options: DiagramOptions
): Diagram.Diagram {
const { title, nodePositions } = options;
const edgeResolve = new EdgeResolver();
GraphTheme.init(options.theme);
const entities: Map<UUID, TDiagramEntity> = new Map();
const diagram = new Diagram.Diagram(title, AwsArchitecture.assetDirectory);
function visit(
gNode: Graph.Node,
parent: Diagram.Subgraph | Diagram.Diagram
) {
if (gNode.isDestroyed) return;
let entity: Diagram.Container | Diagram.Node;
switch (gNode.nodeType) {
case NodeTypeEnum.RESOURCE: {
entity = new Diagram.ResourceNode(gNode as Graph.ResourceNode);
break;
}
case NodeTypeEnum.CFN_RESOURCE: {
entity = new Diagram.CfnResourceNode(gNode as Graph.CfnResourceNode);
break;
}
case NodeTypeEnum.NESTED_STACK: {
entity = new Diagram.NestedStackCluster(gNode as Graph.NestedStackNode);
break;
}
case NodeTypeEnum.STACK: {
if (
GraphTheme.instance.rendering.stack &&
new RegExp(GraphTheme.instance.rendering.stack).test(gNode.id) ===
false
) {
// Ignore non-matching root stacks
return;
}
entity = new Diagram.StackCluster(gNode as Graph.StackNode);
break;
}
default: {
if (gNode.isLeaf) {
if (gNode.hasFlag(FlagEnum.CUSTOM_RESOURCE)) {
entity = new Diagram.CustomResourceNode(gNode);
} else {
entity = new Diagram.Node(gNode);
}
} else {
entity = new Diagram.Cluster(gNode);
gNode.addFlag(FlagEnum.CLUSTER);
}
break;
}
}
if (nodePositions && entity instanceof Diagram.Node) {
if (entity.graphNode.id in nodePositions) {
entity.position = nodePositions[entity.graphNode.id]!;
}
}
if (entity instanceof ImageNode && entity.image) {
diagram.trackImage(entity.image);
}
if (parent instanceof Diagram.Container && parent.linkChildren) {
edgeResolve.trackEdge(new ChildLink(parent, entity));
}
if (gNode.isLeaf) {
entities.set(gNode.uuid, entity);
parent.addNode(entity as Diagram.Node);
} else {
if (entity instanceof Diagram.Node) {
entity = asContainer(entity);
}
parent.addSubgraph(entity as Diagram.Container);
entities.set(gNode.uuid, entity);
gNode.children.forEach((child) =>
visit(child, entity as Diagram.Container)
);
}
}
if (store.stages.length > 1) {
const stageRendering = GraphTheme.instance.rendering.stage || "last";
let stages: Graph.StageNode[];
switch (stageRendering) {
case "all": {
stages = store.stages;
break;
}
case "first": {
stages = store.stages.slice(0, 1);
break;
}
case "last": {
stages = store.stages.slice(-1);
break;
}
default: {
stages = store.stages.filter((stage) => stage.id.match(stageRendering));
}
}
// traverse all stages
stages.forEach((gStage) => {
const dStage = new Diagram.StageCluster(gStage);
diagram.addSubgraph(dStage);
entities.set(gStage.uuid, dStage);
gStage.children.forEach((child) => visit(child, dStage));
});
} else if (store.rootStacks.length) {
// traverse all root stack
store.rootStacks.forEach((gStack) => {
const dStack = new Diagram.StackCluster(gStack);
diagram.addSubgraph(dStack);
entities.set(gStack.uuid, dStack);
gStack.children.forEach((child) => visit(child, dStack));
});
} else {
store.root.children.forEach((gChild) => {
if (gChild.isGraphContainer) {
gChild.children.forEach((_gChild) => {
visit(_gChild, diagram);
});
} else {
visit(gChild, diagram);
}
});
}
// apply all edges
store.edges.forEach((gEdge) => {
if (gEdge.isDestroyed) return;
const dSource = entities.get(gEdge.source.uuid) as
| Diagram.Container
| Diagram.Node;
const dTarget = entities.get(gEdge.target.uuid) as
| Diagram.Container
| Diagram.Node;
if (!dSource || !dTarget) {
IS_DEBUG &&
console.warn(
"Diagram.Edge unresolved source and/or target:",
`source(${gEdge.source} => ${dSource})`,
`target(${gEdge.target} => ${dTarget})`
);
return;
}
let edge: Diagram.Edge | undefined = undefined;
switch (gEdge.edgeType) {
case EdgeTypeEnum.REFERENCE: {
edge = new Diagram.ReferenceLink(gEdge, dSource, dTarget);
break;
}
case EdgeTypeEnum.DEPENDENCY: {
edge = new Diagram.DependencyLink(gEdge, dSource, dTarget);
break;
}
}
if (edge) {
entities.set(gEdge.uuid, edge);
edgeResolve.trackEdge(edge);
}
});
edgeResolve.resolveEdges(options).forEach((edge) => {
diagram.addEdge(edge);
});
return diagram;
}