export function buildDiagram()

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;
}