blob: fcff3a1d536e54c7a97e689fc4a93e50fd6fb011 [file] [log] [blame]
# [dep-graph](http://github.com/TrevorBurnham/dep-graph)
_ = require 'underscore'
class DepGraph
constructor: ->
# The internal representation of the dependency graph in the format
# `id: [ids]`, indicating only *direct* dependencies.
@map = {}
# Add a direct dependency. Returns `false` if that dependency is a duplicate.
add: (id, depId) ->
@map[id] ?= []
return false if depId in @map[id]
@map[id].push depId
@map[id]
# Generate a list of all dependencies (direct and indirect) for the given id,
# in logical order with no duplicates.
getChain: (id) ->
# First, get a list of all dependencies (unordered)
deps = @descendantsOf id
# Second, order them (using the Tarjan algorithm)
chain = []
visited = {}
visit = (node) =>
return if visited[node] or node is id
visited[node] = true
visit parent for parent in @parentsOf(node) when parent in deps
chain.unshift node
for leafNode in _.intersection(deps, @leafNodes()).reverse()
visit leafNode
chain
leafNodes: ->
allNodes = _.uniq _.flatten _.values @map
node for node in allNodes when !@map[node]?.length
parentsOf: (child) ->
node for node in _.keys(@map) when child in @map[node]
descendantsOf: (parent, descendants = [], branch = []) ->
descendants.push parent
branch.push parent
for child in @map[parent] ? []
if child in branch # cycle
throw new Error("Cyclic dependency from #{parent} to #{child}")
continue if child in descendants # duplicate
@descendantsOf child, descendants, branch.slice(0)
descendants[1..]
# Export the class in Node, make it global in the browser.
if module?.exports?
module.exports = DepGraph
else
@DepGraph = DepGraph