2025-10-22 22:19:42 +07:00
|
|
|
import { getInternalPluginMeta, getPluginDependencies } from '../_internal'
|
2025-07-20 23:06:56 +07:00
|
|
|
import type { AnyPlugin } from '../_internal'
|
|
|
|
|
|
|
|
|
|
/// PLUGIN DEPENDENCY GRAPHING
|
|
|
|
|
|
|
|
|
|
// We don't store the graph as a tree, but rather as a set of nodes.
|
|
|
|
|
|
|
|
|
|
// Root nodes are plugins that have dependencies, but no dependents. These plugins are the starting points of the dependency graph.
|
|
|
|
|
// Leaf nodes are plugins that have no dependencies, but may have dependents. These plugins are the end points of the dependency graph.
|
|
|
|
|
|
|
|
|
|
// Start order: Single nodes (no dependencies & dependents) -> Leaf nodes (no dependencies, maybe dependents) -> Root nodes (with dependencies, no dependents)
|
|
|
|
|
// This way we can ensure that all dependencies are started before the plugin itself.
|
|
|
|
|
|
|
|
|
|
export const pRootNodes = new Set<AnyPlugin>()
|
|
|
|
|
export const pLeafOrSingleNodes = new Set<AnyPlugin>()
|
|
|
|
|
|
2025-07-21 14:01:21 +07:00
|
|
|
// Visited non-leaf nodes
|
2025-07-26 01:58:17 +07:00
|
|
|
const visited = new Set<AnyPlugin>()
|
2025-07-20 23:34:31 +07:00
|
|
|
|
2025-07-20 23:06:56 +07:00
|
|
|
// Ordered list of plugins to be started
|
2025-07-20 23:34:31 +07:00
|
|
|
export const pListOrdered: AnyPlugin[] = []
|
2025-07-20 23:06:56 +07:00
|
|
|
// Pending plugins to be computed
|
|
|
|
|
export const pPending = new Set<AnyPlugin>()
|
|
|
|
|
|
|
|
|
|
export function computePendingNodes() {
|
|
|
|
|
for (const plugin of pPending) resolvePluginGraph(plugin)
|
|
|
|
|
|
2025-07-21 15:32:32 +07:00
|
|
|
for (const plugin of pLeafOrSingleNodes) pListOrdered.unshift(plugin)
|
|
|
|
|
|
2025-07-20 23:06:56 +07:00
|
|
|
const stack = [...pRootNodes]
|
|
|
|
|
while (stack.length) {
|
2025-07-21 21:27:17 +07:00
|
|
|
const plugin = stack.shift()!
|
2025-07-20 23:06:56 +07:00
|
|
|
|
2025-07-21 21:27:17 +07:00
|
|
|
if (visited.has(plugin)) {
|
2025-07-20 23:34:31 +07:00
|
|
|
pListOrdered.push(plugin)
|
2025-07-21 21:27:17 +07:00
|
|
|
continue
|
2025-07-20 23:34:31 +07:00
|
|
|
}
|
2025-07-21 21:27:17 +07:00
|
|
|
|
|
|
|
|
if (plugin.manifest.dependencies?.length) {
|
2025-07-25 20:10:33 +07:00
|
|
|
for (const dep of getPluginDependencies(plugin))
|
|
|
|
|
if (!pLeafOrSingleNodes.has(dep)) stack.push(dep)
|
|
|
|
|
|
2025-07-21 21:27:17 +07:00
|
|
|
stack.push(plugin)
|
|
|
|
|
visited.add(plugin)
|
|
|
|
|
} else pListOrdered.push(plugin)
|
2025-07-20 23:06:56 +07:00
|
|
|
}
|
2025-07-25 20:10:33 +07:00
|
|
|
|
|
|
|
|
pPending.clear()
|
|
|
|
|
pLeafOrSingleNodes.clear()
|
|
|
|
|
pRootNodes.clear()
|
2025-07-26 01:58:17 +07:00
|
|
|
visited.clear()
|
2025-07-20 23:06:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resolvePluginGraph(plugin: AnyPlugin) {
|
|
|
|
|
const { manifest } = plugin
|
|
|
|
|
|
|
|
|
|
if (manifest.dependencies?.length) {
|
|
|
|
|
// Optimisitically add to root nodes (if there are dependents, it will be removed later)
|
|
|
|
|
pRootNodes.add(plugin)
|
|
|
|
|
|
2025-07-21 14:01:21 +07:00
|
|
|
for (const dep of getPluginDependencies(plugin)) {
|
2025-10-22 22:19:42 +07:00
|
|
|
const depMeta = getInternalPluginMeta(dep)!
|
2025-07-20 23:06:56 +07:00
|
|
|
depMeta.dependents.push(plugin)
|
|
|
|
|
|
|
|
|
|
// Not a root node if it has dependencies
|
|
|
|
|
if (dep.manifest.dependencies?.length) pRootNodes.delete(dep)
|
|
|
|
|
}
|
|
|
|
|
} else pLeafOrSingleNodes.add(plugin)
|
|
|
|
|
}
|