111 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			111 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // @ts-check
 | |
| const { warn } = require('./logger')
 | |
| 
 | |
| /** @typedef {{after?: string|Array<string>}} Apply */
 | |
| /** @typedef {{id: string, apply: Apply}} Plugin */
 | |
| /** @typedef {{after: Set<string>}} OrderParams */
 | |
| 
 | |
| /** @type {Map<string, OrderParams>} */
 | |
| const orderParamsCache = new Map()
 | |
| 
 | |
| /**
 | |
|  *
 | |
|  * @param {Plugin} plugin
 | |
|  * @returns {OrderParams}
 | |
|  */
 | |
| function getOrderParams (plugin) {
 | |
|   if (!process.env.VUE_CLI_TEST && orderParamsCache.has(plugin.id)) {
 | |
|     return orderParamsCache.get(plugin.id)
 | |
|   }
 | |
|   const apply = plugin.apply
 | |
| 
 | |
|   let after = new Set()
 | |
|   if (typeof apply.after === 'string') {
 | |
|     after = new Set([apply.after])
 | |
|   } else if (Array.isArray(apply.after)) {
 | |
|     after = new Set(apply.after)
 | |
|   }
 | |
|   if (!process.env.VUE_CLI_TEST) {
 | |
|     orderParamsCache.set(plugin.id, { after })
 | |
|   }
 | |
| 
 | |
|   return { after }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * See leetcode 210
 | |
|  * @param {Array<Plugin>} plugins
 | |
|  * @returns {Array<Plugin>}
 | |
|  */
 | |
| 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
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Arrange plugins by 'after' property.
 | |
|  * @param {Array<Plugin>} plugins
 | |
|  * @returns {Array<Plugin>}
 | |
|  */
 | |
| function sortPlugins (plugins) {
 | |
|   if (plugins.length < 2) return plugins
 | |
| 
 | |
|   return topologicalSorting(plugins)
 | |
| }
 | |
| 
 | |
| module.exports = {
 | |
|   topologicalSorting,
 | |
|   sortPlugins
 | |
| }
 |