function topologicalSorting (plugins) {
  /** @type {Map<string, Plugin>} */
  const pluginsMap = new Map(plugins.map(p => [p.id, p]))

  /** @type {Map<Plugin, number>} */
  const indegrees = new Map()

  /** @type {Map<Plugin, Array<Plugin>>} */
  const graph = new Map()

  plugins.forEach(p => {
    const after = getOrderParams(p).after
    indegrees.set(p, after.size)
    if (after.size === 0) return
    for (const id of after) {
      const prerequisite = pluginsMap.get(id)
      // remove invalid data
      if (!prerequisite) {
        indegrees.set(p, indegrees.get(p) - 1)
        continue
      }

      if (!graph.has(prerequisite)) {
        graph.set(prerequisite, [])
      }
      graph.get(prerequisite).push(p)
    }
  })

  const res = []
  const queue = []
  indegrees.forEach((d, p) => {
    if (d === 0) queue.push(p)
  })
  while (queue.length) {
    const cur = queue.shift()
    res.push(cur)
    const neighbors = graph.get(cur)
    if (!neighbors) continue

    neighbors.forEach(n => {
      const degree = indegrees.get(n) - 1
      indegrees.set(n, degree)
      if (degree === 0) {
        queue.push(n)
      }
    })
  }
  const valid = res.length === plugins.length
  if (!valid) {
    warn(`No proper plugin execution order found.`)
    return plugins
  }
  return res
}
See leetcode 210
@param {Array<Plugin>} plugins
@returns {Array<Plugin>}