fix link and remove
This commit is contained in:
199
scripts/link.ce
199
scripts/link.ce
@@ -1,20 +1,27 @@
|
||||
// link <command> [args]
|
||||
// Commands:
|
||||
// add <alias> <path>
|
||||
// list
|
||||
// delete <alias>
|
||||
// clear
|
||||
// [package] <path|target>
|
||||
//
|
||||
// Examples:
|
||||
// cell link ../cell-steam (Links package defined in ../cell-steam to that path)
|
||||
// cell link my-pkg ../cell-steam (Links my-pkg to ../cell-steam)
|
||||
// cell link my-pkg other-pkg (Links my-pkg to other-pkg)
|
||||
|
||||
var shop = use('shop')
|
||||
var fd = use('fd')
|
||||
var toml = use('toml')
|
||||
|
||||
if (args.length < 1) {
|
||||
log.console("Usage: link <command> [args]")
|
||||
log.console("Usage: link <command> [args] or link [package] <target>")
|
||||
log.console("Commands:")
|
||||
log.console(" add <alias> <path> Link an alias to a local path or package")
|
||||
log.console(" list List all active links")
|
||||
log.console(" delete <alias> Remove a link")
|
||||
log.console(" clear Remove all links")
|
||||
log.console(" <path> Link the package in <path> to <path>")
|
||||
log.console(" <alias> <target> Link <alias> to <target> (path or package)")
|
||||
$_.stop()
|
||||
return
|
||||
}
|
||||
@@ -29,100 +36,126 @@ if (cmd == 'list') {
|
||||
count++
|
||||
}
|
||||
if (count == 0) log.console("No links.")
|
||||
} else if (cmd == 'add') {
|
||||
if (args.length < 3) {
|
||||
log.console("Usage: link add <alias|package> <path>")
|
||||
$_.stop()
|
||||
return
|
||||
}
|
||||
|
||||
var target_pkg = args[1]
|
||||
var target_path = args[2]
|
||||
|
||||
// Resolve target path to absolute
|
||||
if (fd.is_dir(target_path)) {
|
||||
target_path = fd.realpath(target_path)
|
||||
} else {
|
||||
log.console("Warning: Path '" + target_path + "' does not exist or is not a directory.")
|
||||
// Try to resolve anyway if relative
|
||||
if (!target_path.startsWith('/')) {
|
||||
target_path = fd.realpath('.') + '/' + target_path
|
||||
}
|
||||
}
|
||||
|
||||
var canonical_path = null
|
||||
|
||||
// Check if it's an alias in cell.toml
|
||||
var config = shop.load_config()
|
||||
if (config.dependencies && config.dependencies[target_pkg]) {
|
||||
var pkg = config.dependencies[target_pkg]
|
||||
canonical_path = pkg
|
||||
} else {
|
||||
// Assume it's a canonical package name
|
||||
canonical_path = target_pkg
|
||||
}
|
||||
|
||||
// Strip version
|
||||
if (canonical_path.includes('@')) {
|
||||
canonical_path = canonical_path.split('@')[0]
|
||||
}
|
||||
if (canonical_path.startsWith('/')) {
|
||||
canonical_path = canonical_path.substring(1)
|
||||
}
|
||||
|
||||
shop.add_link(canonical_path, target_path)
|
||||
|
||||
// Update to apply the link
|
||||
// If it was an alias, update the alias
|
||||
if (config.dependencies && config.dependencies[target_pkg]) {
|
||||
shop.update(target_pkg)
|
||||
} else {
|
||||
// If it was a raw package, update it directly
|
||||
shop.update(canonical_path)
|
||||
}
|
||||
|
||||
} else if (cmd == 'delete') {
|
||||
} else if (cmd == 'delete' || cmd == 'rm') {
|
||||
if (args.length < 2) {
|
||||
log.console("Usage: link delete <alias|package>")
|
||||
log.console("Usage: link delete <alias>")
|
||||
$_.stop()
|
||||
return
|
||||
}
|
||||
|
||||
var target = args[1]
|
||||
var canonical_path = null
|
||||
|
||||
var config = shop.load_config()
|
||||
if (config.dependencies && config.dependencies[target]) {
|
||||
var pkg = config.dependencies[target]
|
||||
canonical_path = pkg
|
||||
} else {
|
||||
canonical_path = target
|
||||
}
|
||||
|
||||
if (canonical_path.includes('@')) {
|
||||
canonical_path = canonical_path.split('@')[0]
|
||||
}
|
||||
if (canonical_path.startsWith('/')) {
|
||||
canonical_path = canonical_path.substring(1)
|
||||
}
|
||||
|
||||
if (shop.remove_link(canonical_path)) {
|
||||
// Update to restore original (download/unpack)
|
||||
// If we can map back to an alias, use it, otherwise update canonical
|
||||
if (config.dependencies && config.dependencies[target]) {
|
||||
shop.update(target)
|
||||
} else {
|
||||
shop.update(canonical_path)
|
||||
}
|
||||
// Try to remove directly
|
||||
if (shop.remove_link(target)) {
|
||||
// Update to restore original if possible
|
||||
log.console("Restoring " + target + "...")
|
||||
shop.update(target)
|
||||
} else {
|
||||
log.console("No link found for " + target)
|
||||
}
|
||||
|
||||
} else if (cmd == 'clear') {
|
||||
shop.clear_links()
|
||||
log.console("Links cleared. Run 'cell update' or 'link delete <alias>' to restore packages if needed.")
|
||||
log.console("Links cleared. Run 'cell update' to restore packages if needed.")
|
||||
} else {
|
||||
log.console("Unknown command: " + cmd)
|
||||
// Linking logic
|
||||
var pkg_name = null
|
||||
var target_path = null
|
||||
|
||||
// Check for 'add' compatibility
|
||||
var start_idx = 0
|
||||
if (cmd == 'add') {
|
||||
start_idx = 1
|
||||
}
|
||||
|
||||
// Parse arguments
|
||||
// usage: link [pkg] <target>
|
||||
// valid inputs:
|
||||
// link ../foo
|
||||
// link pkg ../foo
|
||||
// link pkg remote-pkg
|
||||
|
||||
var arg1 = args[start_idx]
|
||||
var arg2 = (args.length > start_idx + 1) ? args[start_idx + 1] : null
|
||||
|
||||
if (!arg1) {
|
||||
log.console("Error: specific target or package required")
|
||||
$_.stop()
|
||||
return
|
||||
}
|
||||
|
||||
if (arg2) {
|
||||
// Two arguments: explicit package name and target
|
||||
pkg_name = arg1
|
||||
target_path = arg2
|
||||
} else {
|
||||
// One argument: assume it's a path, and we need to infer the package name
|
||||
target_path = arg1
|
||||
|
||||
// Resolve path to check for cell.toml
|
||||
var resolved = target_path
|
||||
if (fd.is_dir(resolved)) resolved = fd.realpath(resolved)
|
||||
|
||||
var toml_path = resolved + '/cell.toml'
|
||||
if (!fd.is_file(toml_path)) {
|
||||
log.console("Error: No cell.toml found at " + resolved + ". Cannot infer package name.")
|
||||
log.console("If you meant to link an alias, provide the target: link <alias> <target>")
|
||||
$_.stop()
|
||||
return
|
||||
}
|
||||
|
||||
// Read package name
|
||||
try {
|
||||
var content = toml.decode(text(fd.slurp(toml_path)))
|
||||
if (content.package) {
|
||||
pkg_name = content.package
|
||||
// If package name has version, strip it?
|
||||
// Usually package = "name", version = "..." in cell.toml?
|
||||
// Or package = "name"
|
||||
// Standard is just name.
|
||||
} else {
|
||||
log.console("Error: cell.toml at " + resolved + " does not define a 'package' name.")
|
||||
$_.stop()
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
log.console("Error reading cell.toml: " + e)
|
||||
$_.stop()
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure target path is fully resolved since we inferred it
|
||||
if (fd.is_dir(target_path)) target_path = fd.realpath(target_path)
|
||||
}
|
||||
|
||||
// Process the link
|
||||
|
||||
// 1. Resolve target if it is a directory
|
||||
if (target_path != "." && fd.is_dir(target_path)) {
|
||||
target_path = fd.realpath(target_path)
|
||||
} else if (target_path == ".") {
|
||||
target_path = fd.realpath(target_path)
|
||||
} else if (target_path.startsWith('/') || target_path.startsWith('./') || target_path.startsWith('../')) {
|
||||
// It looks like a path but doesn't exist?
|
||||
log.console("Warning: Link target '" + target_path + "' does not exist locally. Linking as alias anyway.")
|
||||
if (target_path.startsWith('./') || target_path.startsWith('../')) {
|
||||
// Resolve relative path roughly?
|
||||
var cwd = fd.realpath('.')
|
||||
// simple concat (fd.realpath typically converts to abspath, but if file missing it might fail or return same)
|
||||
// Assuming user wants strict path if they used relative.
|
||||
// Using fd.realpath on CWD + path is safer if we want absolute.
|
||||
// But we don't have path manipulation lib easily exposed except implicit logic.
|
||||
// Leaving as provided string if not existing dir.
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Add link
|
||||
shop.add_link(pkg_name, target_path)
|
||||
|
||||
// 3. Update shop
|
||||
// "Doing this effectively adds another item to the shop"
|
||||
// We trigger update to ensure the shop recognizes the new link
|
||||
shop.update(pkg_name)
|
||||
}
|
||||
|
||||
$_.stop()
|
||||
@@ -1,9 +1,9 @@
|
||||
// cell remove <alias> - Remove a package from dependencies
|
||||
// cell remove <alias|path> - Remove a package from dependencies or shop
|
||||
|
||||
var shop = use('shop')
|
||||
|
||||
if (args.length < 1) {
|
||||
log.console("Usage: cell remove <alias>")
|
||||
log.console("Usage: cell remove <alias|path>")
|
||||
$_.stop()
|
||||
return
|
||||
}
|
||||
|
||||
106
scripts/shop.cm
106
scripts/shop.cm
@@ -595,11 +595,15 @@ Shop.resolve_package_info = function(pkg) {
|
||||
|
||||
var links = Shop.load_links()
|
||||
if (links[path]) {
|
||||
return { type: 'package', path: links[path] }
|
||||
var resolved = links[path]
|
||||
if (resolved.startsWith('/')) {
|
||||
return { type: 'local', path: resolved }
|
||||
}
|
||||
return { type: 'package', path: resolved }
|
||||
}
|
||||
|
||||
if (pkg.startsWith('/')) {
|
||||
return { type: 'package', path: pkg }
|
||||
return { type: 'local', path: pkg }
|
||||
}
|
||||
if (pkg.includes('gitea.')) {
|
||||
return { type: 'gitea' }
|
||||
@@ -609,6 +613,33 @@ Shop.resolve_package_info = function(pkg) {
|
||||
return { type: 'unknown' }
|
||||
}
|
||||
|
||||
// Resolve a filesystem path to a package identifier in the shop
|
||||
Shop.resolve_path_to_package = function(path_str) {
|
||||
var abs = fd.realpath(path_str)
|
||||
if (!abs) return null
|
||||
|
||||
var modules_dir = get_modules_dir()
|
||||
|
||||
// Case 1: Path is inside the modules directory (e.g. downloaded package)
|
||||
if (abs.startsWith(modules_dir + '/')) {
|
||||
var rel = abs.substring(modules_dir.length + 1)
|
||||
// Remove trailing slash if any
|
||||
if (rel.endsWith('/')) rel = rel.substring(0, rel.length - 1)
|
||||
return rel
|
||||
}
|
||||
|
||||
// Case 2: Path is a local directory linked into the shop
|
||||
// Local links are stored as modules_dir + abs_path (mirrored)
|
||||
var target = modules_dir + abs
|
||||
if (fd.is_dir(target)) {
|
||||
if (abs.startsWith('/')) return abs.substring(1)
|
||||
return abs
|
||||
}
|
||||
|
||||
// Case 3: Path is not linked. Return null.
|
||||
return null
|
||||
}
|
||||
|
||||
// Verify if a package name is valid and return status
|
||||
Shop.verify_package_name = function(pkg) {
|
||||
if (!pkg) throw new Error("Empty package name")
|
||||
@@ -1443,34 +1474,75 @@ function install_zip(zip_blob, target_dir) {
|
||||
|
||||
// High-level: Remove a package and clean up
|
||||
// Like `bun remove`
|
||||
Shop.remove = function(alias) {
|
||||
Shop.remove = function(alias_or_path) {
|
||||
var config = Shop.load_config()
|
||||
if (!config || !config.dependencies || !config.dependencies[alias]) {
|
||||
log.error("Dependency not found: " + alias)
|
||||
return false
|
||||
var is_dependency = config && config.dependencies && config.dependencies[alias_or_path]
|
||||
|
||||
var target_pkg = null
|
||||
var locator = null
|
||||
|
||||
if (is_dependency) {
|
||||
locator = config.dependencies[alias_or_path]
|
||||
var parsed = Shop.parse_package(locator)
|
||||
target_pkg = parsed.path
|
||||
|
||||
// Remove from config
|
||||
delete config.dependencies[alias_or_path]
|
||||
Shop.save_config(config)
|
||||
} else {
|
||||
// Check if it's a resolvable path
|
||||
var resolved = Shop.resolve_path_to_package(alias_or_path)
|
||||
if (resolved) {
|
||||
target_pkg = resolved
|
||||
} else {
|
||||
// Check if alias_or_path exists directly in modules dir?
|
||||
var direct_path = get_modules_dir() + '/' + alias_or_path
|
||||
if (fd.exists(direct_path)) {
|
||||
target_pkg = alias_or_path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var locator = config.dependencies[alias]
|
||||
var parsed = Shop.parse_package(locator)
|
||||
var target_dir = get_modules_dir() + '/' + parsed.path
|
||||
if (!target_pkg) {
|
||||
log.error("Package/Dependency not found: " + alias_or_path)
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove from config
|
||||
delete config.dependencies[alias]
|
||||
Shop.save_config(config)
|
||||
var target_dir = get_modules_dir() + '/' + target_pkg
|
||||
|
||||
// Remove from lock
|
||||
var lock = Shop.load_lock()
|
||||
if (lock[locator]) delete lock[locator]
|
||||
if (lock[alias]) delete lock[alias] // Cleanup old format
|
||||
Shop.save_lock(lock)
|
||||
var lock_changed = false
|
||||
|
||||
if (locator && lock[locator]) {
|
||||
delete lock[locator]
|
||||
lock_changed = true
|
||||
}
|
||||
if (lock[alias_or_path]) {
|
||||
delete lock[alias_or_path]
|
||||
lock_changed = true
|
||||
}
|
||||
|
||||
// Scan for any entry pointing to this target
|
||||
for (var k in lock) {
|
||||
if (lock[k].package == target_pkg || lock[k].package == '/' + target_pkg) {
|
||||
delete lock[k]
|
||||
lock_changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if (lock_changed) Shop.save_lock(lock)
|
||||
|
||||
// Remove directory
|
||||
if (fd.is_dir(target_dir)) {
|
||||
if (fd.is_link(target_dir)) {
|
||||
log.console("Unlinking " + target_dir)
|
||||
fd.unlink(target_dir)
|
||||
} else if (fd.is_dir(target_dir)) {
|
||||
log.console("Removing " + target_dir)
|
||||
fd.rmdir(target_dir)
|
||||
}
|
||||
|
||||
log.console("Removed " + alias)
|
||||
log.console("Removed " + (is_dependency ? alias_or_path : target_pkg))
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user