pluginOrder.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // @ts-check
  2. const { warn } = require('./logger')
  3. /** @typedef {{after?: string|Array<string>}} Apply */
  4. /** @typedef {{id: string, apply: Apply}} Plugin */
  5. /** @typedef {{after: Set<string>}} OrderParams */
  6. /** @type {Map<string, OrderParams>} */
  7. const orderParamsCache = new Map()
  8. /**
  9. *
  10. * @param {Plugin} plugin
  11. * @returns {OrderParams}
  12. */
  13. function getOrderParams (plugin) {
  14. if (!process.env.VUE_CLI_TEST && orderParamsCache.has(plugin.id)) {
  15. return orderParamsCache.get(plugin.id)
  16. }
  17. const apply = plugin.apply
  18. let after = new Set()
  19. if (typeof apply.after === 'string') {
  20. after = new Set([apply.after])
  21. } else if (Array.isArray(apply.after)) {
  22. after = new Set(apply.after)
  23. }
  24. if (!process.env.VUE_CLI_TEST) {
  25. orderParamsCache.set(plugin.id, { after })
  26. }
  27. return { after }
  28. }
  29. /**
  30. * See leetcode 210
  31. * @param {Array<Plugin>} plugins
  32. * @returns {Array<Plugin>}
  33. */
  34. function topologicalSorting (plugins) {
  35. /** @type {Map<string, Plugin>} */
  36. const pluginsMap = new Map(plugins.map(p => [p.id, p]))
  37. /** @type {Map<Plugin, number>} */
  38. const indegrees = new Map()
  39. /** @type {Map<Plugin, Array<Plugin>>} */
  40. const graph = new Map()
  41. plugins.forEach(p => {
  42. const after = getOrderParams(p).after
  43. indegrees.set(p, after.size)
  44. if (after.size === 0) return
  45. for (const id of after) {
  46. const prerequisite = pluginsMap.get(id)
  47. // remove invalid data
  48. if (!prerequisite) {
  49. indegrees.set(p, indegrees.get(p) - 1)
  50. continue
  51. }
  52. if (!graph.has(prerequisite)) {
  53. graph.set(prerequisite, [])
  54. }
  55. graph.get(prerequisite).push(p)
  56. }
  57. })
  58. const res = []
  59. const queue = []
  60. indegrees.forEach((d, p) => {
  61. if (d === 0) queue.push(p)
  62. })
  63. while (queue.length) {
  64. const cur = queue.shift()
  65. res.push(cur)
  66. const neighbors = graph.get(cur)
  67. if (!neighbors) continue
  68. neighbors.forEach(n => {
  69. const degree = indegrees.get(n) - 1
  70. indegrees.set(n, degree)
  71. if (degree === 0) {
  72. queue.push(n)
  73. }
  74. })
  75. }
  76. const valid = res.length === plugins.length
  77. if (!valid) {
  78. warn(`No proper plugin execution order found.`)
  79. return plugins
  80. }
  81. return res
  82. }
  83. /**
  84. * Arrange plugins by 'after' property.
  85. * @param {Array<Plugin>} plugins
  86. * @returns {Array<Plugin>}
  87. */
  88. function sortPlugins (plugins) {
  89. if (plugins.length < 2) return plugins
  90. return topologicalSorting(plugins)
  91. }
  92. module.exports = {
  93. topologicalSorting,
  94. sortPlugins
  95. }