JSFiddle - React, Tailwind, and code Playground

JavaScript

class Job {
  serialize(){
    return this.n;
  }
  normalize(){ return this; }
};

class JobList extends Job {};

class ParallelJobList extends JobList {
  constructor(jobs){
    super();
    this.list = new Set(jobs);
  }
  add(job){
    this.list.add(job);
  }
  serialize(i=0){
    let s = '{\n';
    for(const x of this.list)
      s += '  '.repeat(i) + '  ' + x.serialize(i+1) + '\n';
    s += '  '.repeat(i) + '}';
    return s;
  }
  normalize(){
    let n = new ParallelJobList([...this.list].map(x=>x.normalize()));
    if(n.list.size == 1)
      return [...n.list][0];
    return n;
  }
};

class SequentialJobList extends JobList {
  constructor(jobs){
    super();
    this.list = [...(jobs??[])];
  }
  add(job){
    this.list.push(job);
  }
  serialize(i=0){
    let s = '(\n';
    for(const x of this.list)
      s += '  '.repeat(i) + '  ' + x.serialize(i+1) + '\n';
    s += '  '.repeat(i) + ')';
    return s;
  }
  normalize(){
    let n = new SequentialJobList(this.list.map(x=>x.normalize()));
    if(n.list.length == 1)
      return n.list[0];
    return n;
  }
};

const depends = Symbol('depends');
const dependants = Symbol('dependants');

const a = new Job(); a.n='a';
const b = new Job(); b.n='b';
const c = new Job(); c.n='c';
const d = new Job(); d.n='d';
const e = new Job(); e.n='e';
const f = new Job(); f.n='f';
const g = new Job(); g.n='g';
const h = new Job(); h.n='h';
const i = new Job(); i.n='i';
const jobs = [a,b,c,d,e,f,g,h,i];

for(let [x,y] of [
  [a,d],
  [b,d],
  [d,e],
  [d,f],
  [b,f],
  [g,e],
  [h,i]
]){
  (y[depends]??=new Set()).add(x);
  (x[dependants]??=new Set()).add(y);
}

  const top_nodes = new Set();
  for(const a of jobs)
    if(!a[depends])
      top_nodes.add(a);
  const component = Symbol('component');
  function flood_fill(group, node){
    if(node[component])
      return;
    node[component] = group;
    for(const dnode of [...(node[dependants]??[]), ...(node[depends]??[])])
      flood_fill(group, dnode);
  }
  {
    let...