clean up cmd line
This commit is contained in:
236
graph.ce
Normal file
236
graph.ce
Normal file
@@ -0,0 +1,236 @@
|
||||
// cell graph [<locator>] - Emit dependency graph
|
||||
//
|
||||
// Usage:
|
||||
// cell graph Graph current directory package
|
||||
// cell graph . Graph current directory package
|
||||
// cell graph <locator> Graph specific package
|
||||
// cell graph --world Graph all packages in shop (world set)
|
||||
//
|
||||
// Options:
|
||||
// --format <fmt> Output format: tree (default), dot, json
|
||||
// --resolved Show resolved view with links applied (default)
|
||||
// --locked Show lock view without links
|
||||
// --world Graph all packages in shop
|
||||
|
||||
var shop = use('internal/shop')
|
||||
var pkg = use('package')
|
||||
var link = use('link')
|
||||
var fd = use('fd')
|
||||
var json = use('json')
|
||||
|
||||
var target_locator = null
|
||||
var format = 'tree'
|
||||
var show_locked = false
|
||||
var show_world = false
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
if (args[i] == '--format' || args[i] == '-f') {
|
||||
if (i + 1 < args.length) {
|
||||
format = args[++i]
|
||||
if (format != 'tree' && format != 'dot' && format != 'json') {
|
||||
log.error('Invalid format: ' + format + '. Must be tree, dot, or json')
|
||||
$stop()
|
||||
}
|
||||
} else {
|
||||
log.error('--format requires a format type')
|
||||
$stop()
|
||||
}
|
||||
} else if (args[i] == '--resolved') {
|
||||
show_locked = false
|
||||
} else if (args[i] == '--locked') {
|
||||
show_locked = true
|
||||
} else if (args[i] == '--world') {
|
||||
show_world = true
|
||||
} else if (args[i] == '--help' || args[i] == '-h') {
|
||||
log.console("Usage: cell graph [<locator>] [options]")
|
||||
log.console("")
|
||||
log.console("Emit the dependency graph.")
|
||||
log.console("")
|
||||
log.console("Options:")
|
||||
log.console(" --format <fmt> Output format: tree (default), dot, json")
|
||||
log.console(" --resolved Show resolved view with links applied (default)")
|
||||
log.console(" --locked Show lock view without links")
|
||||
log.console(" --world Graph all packages in shop")
|
||||
$stop()
|
||||
} else if (!args[i].startsWith('-')) {
|
||||
target_locator = args[i]
|
||||
}
|
||||
}
|
||||
|
||||
var links = show_locked ? {} : link.load()
|
||||
|
||||
// Get effective locator (after links)
|
||||
function get_effective(locator) {
|
||||
return links[locator] || locator
|
||||
}
|
||||
|
||||
// Build graph data structure
|
||||
var nodes = {}
|
||||
var edges = []
|
||||
|
||||
function add_node(locator) {
|
||||
if (nodes[locator]) return
|
||||
|
||||
var lock = shop.load_lock()
|
||||
var lock_entry = lock[locator]
|
||||
var link_target = links[locator]
|
||||
var info = shop.resolve_package_info(locator)
|
||||
|
||||
nodes[locator] = {
|
||||
id: locator,
|
||||
effective: get_effective(locator),
|
||||
linked: link_target != null,
|
||||
local: info == 'local',
|
||||
commit: lock_entry && lock_entry.commit ? lock_entry.commit.substring(0, 8) : null
|
||||
}
|
||||
}
|
||||
|
||||
function gather_graph(locator, visited) {
|
||||
if (visited[locator]) return
|
||||
visited[locator] = true
|
||||
|
||||
add_node(locator)
|
||||
|
||||
try {
|
||||
var deps = pkg.dependencies(locator)
|
||||
if (deps) {
|
||||
for (var alias in deps) {
|
||||
var dep_locator = deps[alias]
|
||||
add_node(dep_locator)
|
||||
edges.push({ from: locator, to: dep_locator, alias: alias })
|
||||
gather_graph(dep_locator, visited)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Package might not have dependencies
|
||||
}
|
||||
}
|
||||
|
||||
// Gather graph from roots
|
||||
var roots = []
|
||||
|
||||
if (show_world) {
|
||||
// Use all packages in shop as roots
|
||||
var packages = shop.list_packages()
|
||||
for (var p of packages) {
|
||||
if (p != 'core') {
|
||||
roots.push(p)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default to current directory
|
||||
if (!target_locator) {
|
||||
target_locator = '.'
|
||||
}
|
||||
|
||||
// Resolve local paths
|
||||
if (target_locator == '.' || target_locator.startsWith('./') || target_locator.startsWith('../') || fd.is_dir(target_locator)) {
|
||||
var resolved = fd.realpath(target_locator)
|
||||
if (resolved) {
|
||||
target_locator = resolved
|
||||
}
|
||||
}
|
||||
|
||||
roots.push(target_locator)
|
||||
}
|
||||
|
||||
for (var root of roots) {
|
||||
gather_graph(root, {})
|
||||
}
|
||||
|
||||
// Output based on format
|
||||
if (format == 'tree') {
|
||||
function print_tree(locator, prefix, is_last, visited) {
|
||||
if (visited[locator]) {
|
||||
log.console(prefix + (is_last ? "\\-- " : "|-- ") + locator + " (circular)")
|
||||
return
|
||||
}
|
||||
visited[locator] = true
|
||||
|
||||
var node = nodes[locator]
|
||||
var suffix = ""
|
||||
if (node.linked) suffix += " -> " + node.effective
|
||||
if (node.commit) suffix += " @" + node.commit
|
||||
if (node.local) suffix += " (local)"
|
||||
|
||||
log.console(prefix + (is_last ? "\\-- " : "|-- ") + locator + suffix)
|
||||
|
||||
// Get children
|
||||
var children = []
|
||||
for (var e of edges) {
|
||||
if (e.from == locator) {
|
||||
children.push(e)
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child_prefix = prefix + (is_last ? " " : "| ")
|
||||
print_tree(children[i].to, child_prefix, i == children.length - 1, visited)
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < roots.length; i++) {
|
||||
log.console(roots[i])
|
||||
|
||||
var children = []
|
||||
for (var e of edges) {
|
||||
if (e.from == roots[i]) {
|
||||
children.push(e)
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < children.length; j++) {
|
||||
print_tree(children[j].to, "", j == children.length - 1, {})
|
||||
}
|
||||
|
||||
if (i < roots.length - 1) log.console("")
|
||||
}
|
||||
|
||||
} else if (format == 'dot') {
|
||||
log.console("digraph dependencies {")
|
||||
log.console(" rankdir=TB;")
|
||||
log.console(" node [shape=box];")
|
||||
log.console("")
|
||||
|
||||
// Node definitions
|
||||
for (var id in nodes) {
|
||||
var node = nodes[id]
|
||||
var label = id
|
||||
if (node.commit) label += "\\n@" + node.commit
|
||||
var attrs = 'label="' + label + '"'
|
||||
if (node.linked) attrs += ', style=dashed'
|
||||
if (node.local) attrs += ', color=blue'
|
||||
|
||||
// Safe node ID for dot
|
||||
var safe_id = id.replaceAll(/[^a-zA-Z0-9]/g, '_')
|
||||
log.console(' ' + safe_id + ' [' + attrs + '];')
|
||||
}
|
||||
|
||||
log.console("")
|
||||
|
||||
// Edges
|
||||
for (var e of edges) {
|
||||
var from_id = e.from.replaceAll(/[^a-zA-Z0-9]/g, '_')
|
||||
var to_id = e.to.replaceAll(/[^a-zA-Z0-9]/g, '_')
|
||||
var label = e.alias != e.to ? 'label="' + e.alias + '"' : ''
|
||||
log.console(' ' + from_id + ' -> ' + to_id + (label ? ' [' + label + ']' : '') + ';')
|
||||
}
|
||||
|
||||
log.console("}")
|
||||
|
||||
} else if (format == 'json') {
|
||||
var output = {
|
||||
nodes: [],
|
||||
edges: []
|
||||
}
|
||||
|
||||
for (var id in nodes) {
|
||||
output.nodes.push(nodes[id])
|
||||
}
|
||||
|
||||
output.edges = edges
|
||||
|
||||
log.console(json.encode(output))
|
||||
}
|
||||
|
||||
$stop()
|
||||
Reference in New Issue
Block a user