dimpact 0.5.3

impact analysis from diff
Documentation
self.onmessage = function(ev){
  const msg = ev && ev.data ? ev.data : {};
  if(msg.cmd !== 'compute'){ return; }
  const dir = msg.dir || 'undirected';
  const roots = (Array.isArray(msg.roots) && msg.roots.length) ? msg.roots.slice() : [];
  const changed = Array.isArray(msg.changed) ? msg.changed.slice() : [];
  const start = roots.length ? roots : changed;
  const nodes = Array.isArray(msg.nodes) ? msg.nodes.slice() : [];
  const edges = Array.isArray(msg.edges) ? msg.edges.slice() : [];
  const impacted = Array.isArray(msg.impacted) ? msg.impacted.slice() : [];
  const adj = buildAdjacency(dir, nodes, edges);
  const bfs = runBfs(adj, start);
  const pairs = computePairs(adj, bfs.parent, new Set(start), impacted);
  const distArr = Array.from(bfs.dist.entries());
  const pairArr = Array.from(pairs);
  self.postMessage({ dist: distArr, pairs: pairArr });
};

function buildAdjacency(dir, nodes, edges){
  const adj = new Map();
  nodes.forEach((id)=>{ if(!adj.has(id)){ adj.set(id, []); } });
  edges.forEach((edge)=>{
    if(!edge || typeof edge.s !== 'string' || typeof edge.t !== 'string'){ return; }
    const s = edge.s;
    const t = edge.t;
    if(!adj.has(s)){ adj.set(s, []); }
    if(!adj.has(t)){ adj.set(t, []); }
    if(dir === 'callees' || dir === 'undirected'){ adj.get(s).push(t); }
    if(dir === 'callers' || dir === 'undirected'){ adj.get(t).push(s); }
  });
  return adj;
}

function runBfs(adj, roots){
  const queue = [];
  const dist = new Map();
  const parent = new Map();
  const seen = new Set();
  (roots || []).forEach((id)=>{
    if(!seen.has(id)){
      seen.add(id);
      dist.set(id, 0);
      parent.set(id, null);
      queue.push(id);
    }
  });
  while(queue.length){
    const u = queue.shift();
    const base = dist.get(u) || 0;
    const neigh = adj.get(u) || [];
    for(let i=0;i<neigh.length;i++){
      const v = neigh[i];
      if(!seen.has(v)){
        seen.add(v);
        dist.set(v, base + 1);
        parent.set(v, u);
        queue.push(v);
      }
    }
  }
  return { dist, parent };
}

function computePairs(adj, parent, roots, impacted){
  const pairs = new Set();
  if(!impacted || !impacted.length){ return pairs; }
  impacted.forEach((node)=>{
    if(!parent.has(node) && !roots.has(node)){ return; }
    let cur = node;
    while(parent.has(cur)){
      const p = parent.get(cur);
      if(p == null){ break; }
      const key = cur + "\t" + p;
      const rev = p + "\t" + cur;
      pairs.add(key);
      pairs.add(rev);
      if(roots.has(p)){ break; }
      cur = p;
    }
  });
  return pairs;
}