import graph

This commit is contained in:
2026-02-19 03:19:24 -06:00
parent bab4d50b2a
commit 06ad466b1a
4 changed files with 258 additions and 8 deletions

189
imports.ce Normal file
View File

@@ -0,0 +1,189 @@
// cell imports [<file>] - Trace module-level imports
//
// Usage:
// cell imports Trace imports for current package entry
// cell imports <file.ce> Trace imports starting from a .ce or .cm file
// cell imports <file.cm> Trace imports starting from a .cm file
//
// Options:
// --flat Flat list instead of tree
// --packages Only show unique packages, not individual modules
var shop = use('internal/shop')
var pkg = use('package')
var fd = use('fd')
var target = null
var flat_mode = false
var packages_only = false
var i = 0
var pkg_dir = null
var config = null
var target_path = null
for (i = 0; i < length(args); i++) {
if (args[i] == '--flat') {
flat_mode = true
} else if (args[i] == '--packages') {
packages_only = true
} else if (args[i] == '--help' || args[i] == '-h') {
log.console("Usage: cell imports [<file>] [options]")
log.console("")
log.console("Trace module-level imports across packages.")
log.console("")
log.console("Options:")
log.console(" --flat Flat list instead of tree")
log.console(" --packages Only show unique packages, not individual modules")
$stop()
} else if (!starts_with(args[i], '-')) {
target = args[i]
}
}
// Resolve target file
if (target) {
if (!ends_with(target, '.ce') && !ends_with(target, '.cm'))
target = target + '.ce'
if (fd.is_file(target))
target_path = fd.realpath(target)
else {
pkg_dir = pkg.find_package_dir('.')
if (pkg_dir)
target_path = pkg_dir + '/' + target
}
} else {
pkg_dir = pkg.find_package_dir('.')
if (pkg_dir) {
config = pkg.load_config(null)
if (config.entry)
target_path = pkg_dir + '/' + config.entry
}
}
if (!target_path || !fd.is_file(target_path)) {
log.error('Could not find file: ' + (target || '(no target specified)'))
$stop()
}
// Collect all imports recursively
var visited = {}
var all_imports = []
var all_packages = {}
function trace_imports(file_path, depth) {
if (visited[file_path]) return
visited[file_path] = true
var fi = shop.file_info(file_path)
var file_pkg = fi.package || '(local)'
var idx = null
var j = 0
var imp = null
var mod_path = null
var resolved = null
var imp_pkg = null
var imp_type = null
var rinfo = null
all_packages[file_pkg] = true
var _trace = function() {
idx = shop.index_file(file_path)
if (!idx || !idx.imports) return
j = 0
while (j < length(idx.imports)) {
imp = idx.imports[j]
mod_path = imp.module_path
resolved = null
imp_pkg = '?'
imp_type = 'unresolved'
// Use the full resolver that checks .cm files, C symbols, and aliases
rinfo = shop.resolve_import_info(mod_path, file_pkg)
if (rinfo) {
resolved = rinfo.resolved_path
imp_pkg = rinfo.package || '?'
imp_type = rinfo.type
}
all_packages[imp_pkg] = true
push(all_imports, {
from: file_path,
from_pkg: file_pkg,
module_path: mod_path,
resolved_path: resolved,
package: imp_pkg,
type: imp_type,
depth: depth
})
// Recurse into resolved scripts
if (resolved && (ends_with(resolved, '.cm') || ends_with(resolved, '.ce'))) {
trace_imports(resolved, depth + 1)
}
j = j + 1
}
} disruption {
// File might fail to parse/index
}
_trace()
}
trace_imports(target_path, 0)
// Output results
var fi2 = null
if (packages_only) {
log.console("Packages used by " + target_path + ":")
log.console("")
arrfor(array(all_packages), function(p) {
log.console(" " + p)
})
} else if (flat_mode) {
log.console("Imports from " + target_path + ":")
log.console("")
arrfor(all_imports, function(imp) {
var suffix = ''
if (imp.type == 'native') suffix = ' [native]'
if (imp.type == 'unresolved') suffix = ' [unresolved]'
log.console(" " + imp.module_path + " -> " + imp.package + suffix)
})
} else {
// Tree output
function print_tree(file_path, prefix, is_last) {
var children = filter(all_imports, function(imp) {
return imp.from == file_path
})
var j = 0
var imp = null
var last = false
var connector = null
var suffix = null
var child_prefix = null
while (j < length(children)) {
imp = children[j]
last = (j == length(children) - 1)
connector = last ? "\\-- " : "|-- "
suffix = " (" + imp.package + ")"
if (imp.type == 'native') suffix = suffix + " [native]"
if (imp.type == 'unresolved') suffix = suffix + " [unresolved]"
log.console(prefix + connector + imp.module_path + suffix)
if (imp.resolved_path) {
child_prefix = prefix + (last ? " " : "| ")
print_tree(imp.resolved_path, child_prefix, last)
}
j = j + 1
}
}
fi2 = shop.file_info(target_path)
log.console(target_path + " (" + (fi2.package || 'local') + ")")
print_tree(target_path, "", true)
}
$stop()

View File

@@ -420,7 +420,7 @@ core_extras.native_mode = native_mode
// NOW load shop -- it receives all of the above via env
var shop = use_core('internal/shop')
if (native_mode) use_core('build')
use_core('build')
var time = use_core('time')
var toml = use_core('toml')
@@ -498,10 +498,8 @@ function pretty_format(rec) {
var out = null
var i = 0
var fr = null
if (rec.source && rec.source.file)
src = rec.source.file + ":" + text(rec.source.line)
ev = is_text(rec.event) ? rec.event : json.encode(rec.event, false)
out = `[${aid}] [${rec.channel}] ${src} ${ev}\n`
out = `[${aid}] [${rec.channel}] ${ev}\n`
if (rec.stack && length(rec.stack) > 0) {
for (i = 0; i < length(rec.stack); i = i + 1) {
fr = rec.stack[i]
@@ -1248,6 +1246,20 @@ $_.clock(_ => {
}
var pkg = file_info ? file_info.package : null
// Pre-build C modules for all transitive dependencies
var _deps = null
var _di = 0
if (pkg && shop.ensure_package_dylibs) {
_deps = package.gather_dependencies(pkg)
_di = 0
while (_di < length(_deps)) {
shop.ensure_package_dylibs(_deps[_di])
_di = _di + 1
}
shop.ensure_package_dylibs(pkg)
}
env.use = function(path) {
var ck = 'core/' + path
if (use_cache[ck]) return use_cache[ck]

View File

@@ -1220,7 +1220,7 @@ function execute_module(info)
// C only
used = call_c_module(c_resolve)
} else {
print(`Module ${info.path} could not be found`); disrupt
log.shop(`Module could not be found (c_resolve scope=${info.c_resolve.scope}, mod_resolve scope=${info.mod_resolve.scope}, cache_key=${info.cache_key})`); disrupt
}
if (!used) { print(`Module ${info} returned null`); disrupt }
@@ -1230,8 +1230,21 @@ function execute_module(info)
function get_module(path, package_context) {
var info = resolve_module_info(path, package_context)
var _ctx_dir = null
var _alias = null
if (!info) { print(`Module ${path} could not be found in ${package_context}`); disrupt }
if (!info) {
log.shop(`Module '${path}' could not be found in package '${package_context}'`)
_ctx_dir = package_context ? (starts_with(package_context, '/') ? package_context : get_packages_dir() + '/' + safe_package_path(package_context)) : null
if (_ctx_dir) {
if (fd.is_file(_ctx_dir + '/' + path + '.c') || fd.is_file(_ctx_dir + '/' + path + '.cpp'))
log.shop(`C source exists at ${_ctx_dir}/${path}.c but was not compiled - run 'cell build'`)
}
_alias = pkg_tools.split_alias(package_context, path)
if (_alias == null && search(path, '/') != null)
log.shop(`Alias '${array(path, '/')[0]}' could not be resolved in package '${package_context}'`)
disrupt
}
return execute_module(info)
}
@@ -1254,7 +1267,20 @@ Shop.use = function use(path, package_context) {
}
var info = resolve_module_info(path, package_context)
if (!info) { print(`Module ${path} could not be found in ${package_context}`); disrupt }
var _ctx_dir2 = null
var _alias2 = null
if (!info) {
log.shop(`Module '${path}' could not be found in package '${package_context}'`)
_ctx_dir2 = package_context ? (starts_with(package_context, '/') ? package_context : get_packages_dir() + '/' + safe_package_path(package_context)) : null
if (_ctx_dir2) {
if (fd.is_file(_ctx_dir2 + '/' + path + '.c') || fd.is_file(_ctx_dir2 + '/' + path + '.cpp'))
log.shop(`C source exists at ${_ctx_dir2}/${path}.c but was not compiled - run 'cell build'`)
}
_alias2 = pkg_tools.split_alias(package_context, path)
if (_alias2 == null && search(path, '/') != null)
log.shop(`Alias '${array(path, '/')[0]}' could not be resolved in package '${package_context}'`)
disrupt
}
if (use_cache[info.cache_key])
return use_cache[info.cache_key]
@@ -1272,6 +1298,28 @@ Shop.resolve_use_path = function(path, ctx) {
return info.path
}
// Resolve a use() module path to {resolved_path, package, type} without compiling.
// type is 'script', 'native', or null. Checks .cm files, C symbols, and aliases.
Shop.resolve_import_info = function(path, ctx) {
var mod_info = resolve_path(path + '.cm', ctx)
var c_info = null
var c_pkg = null
if (mod_info)
return {resolved_path: mod_info.path, package: mod_info.pkg, type: 'script'}
c_info = resolve_c_symbol(path, ctx)
if (c_info && c_info.scope < 900) {
c_pkg = c_info.package
if (!c_pkg) {
if (c_info.scope == SCOPE_CORE) c_pkg = 'core'
else c_pkg = ctx
}
return {resolved_path: null, package: c_pkg, type: 'native'}
}
return null
}
// Get cache path for a package and commit
function get_cache_path(pkg, commit) {
return global_shop_path + '/cache/' + replace(replace(pkg, '@','_'), '/','_') + '_' + commit + '.zip'
@@ -1710,6 +1758,7 @@ Shop.get_lib_dir = function() {
}
Shop.ensure_dir = ensure_dir
Shop.ensure_package_dylibs = ensure_package_dylibs
Shop.get_local_dir = function() {
return global_shop_path + "/local"

View File

@@ -205,7 +205,7 @@ package.gather_dependencies = function(name)
package.list_files = function(pkg) {
var dir = get_path(pkg)
if (!fd.is_dir(dir)) return []
return fd.globfs(["*", "!.*"], dir)
return fd.globfs(["**/*", "!.*"], dir)
}
package.list_modules = function(name) {