|
|
|
|
@@ -152,42 +152,34 @@ Shop.save_config = function(config) {
|
|
|
|
|
slurpwrite(shop_path, toml.encode(config));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function lock_path(pkg)
|
|
|
|
|
{
|
|
|
|
|
if (pkg)
|
|
|
|
|
return `.cell/modules/${pkg}/.cell/lock.toml`
|
|
|
|
|
else
|
|
|
|
|
return '.cell/lock.toml'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load lock.toml configuration
|
|
|
|
|
Shop.load_lock = function(pkg) {
|
|
|
|
|
var path = lock_path(pkg)
|
|
|
|
|
Shop.load_lock = function() {
|
|
|
|
|
var path = '.cell/lock.toml'
|
|
|
|
|
|
|
|
|
|
if (!fd.is_file(path))
|
|
|
|
|
return null
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
var content = text(fd.slurp(lock_path))
|
|
|
|
|
var content = text(fd.slurp(path))
|
|
|
|
|
if (!content.length) return {}
|
|
|
|
|
return toml.decode(content)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save lock.toml configuration
|
|
|
|
|
Shop.save_lock = function(pkg, lock) {
|
|
|
|
|
var path = lock_path(pkg)
|
|
|
|
|
slurpwrite(path, toml.encode(lock));
|
|
|
|
|
Shop.save_lock = function(lock) {
|
|
|
|
|
slurpwrite('.cell/lock.toml', toml.encode(lock));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize .cell directory structure
|
|
|
|
|
Shop.init = function() {
|
|
|
|
|
if (!fd.is_directory('.cell')) {
|
|
|
|
|
if (!fd.is_dir('.cell')) {
|
|
|
|
|
fd.mkdir('.cell')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!fd.is_directory('.cell/modules')) {
|
|
|
|
|
if (!fd.is_dir('.cell/modules')) {
|
|
|
|
|
fd.mkdir('.cell/modules')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!fd.is_directory('.cell/build')) {
|
|
|
|
|
if (!fd.is_dir('.cell/build')) {
|
|
|
|
|
fd.mkdir('.cell/build')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -230,46 +222,25 @@ Shop.parse_locator = function(locator) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert module locator to download URL
|
|
|
|
|
Shop.get_download_url = function(locator) {
|
|
|
|
|
Shop.get_download_url = function(locator, commit_hash) {
|
|
|
|
|
var parsed = Shop.parse_locator(locator)
|
|
|
|
|
if (!parsed) return null
|
|
|
|
|
|
|
|
|
|
// Handle different git hosting patterns
|
|
|
|
|
if (locator.startsWith('https://')) {
|
|
|
|
|
// Remove https:// prefix for parsing
|
|
|
|
|
var cleanLocator = locator.substring(8)
|
|
|
|
|
var hostAndPath = cleanLocator.split('@')[0]
|
|
|
|
|
if (parsed.path.includes('gitea.')) {
|
|
|
|
|
var parts = parsed.path.split('/')
|
|
|
|
|
var host = parts[0]
|
|
|
|
|
var user = parts[1]
|
|
|
|
|
var repo = parts[2]
|
|
|
|
|
|
|
|
|
|
// Gitea pattern: gitea.pockle.world/user/repo@branch
|
|
|
|
|
if (hostAndPath.includes('gitea.')) {
|
|
|
|
|
return 'https://' + hostAndPath + '/archive/' + parsed.version + '.zip'
|
|
|
|
|
if (!commit_hash) {
|
|
|
|
|
log.error("No commit hash available for download URL")
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GitHub pattern: github.com/user/repo@tag
|
|
|
|
|
if (hostAndPath.includes('github.com')) {
|
|
|
|
|
return 'https://' + hostAndPath + '/archive/refs/tags/' + parsed.version + '.zip'
|
|
|
|
|
return 'https://' + host + '/' + user + '/' + repo + '/archive/' + commit_hash + '.zip'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GitLab pattern: gitlab.com/user/repo@tag
|
|
|
|
|
if (hostAndPath.includes('gitlab.')) {
|
|
|
|
|
return 'https://' + hostAndPath + '/-/archive/' + parsed.version + '/' + parsed.name + '-' + parsed.version + '.zip'
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Implicit https
|
|
|
|
|
var hostAndPath = parsed.path
|
|
|
|
|
// Gitea pattern: gitea.pockle.world/user/repo@branch
|
|
|
|
|
if (hostAndPath.includes('gitea.')) {
|
|
|
|
|
return 'https://' + hostAndPath + '/archive/' + parsed.version + '.zip'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GitHub pattern: github.com/user/repo@tag
|
|
|
|
|
if (hostAndPath.includes('github.com')) {
|
|
|
|
|
return 'https://' + hostAndPath + '/archive/refs/tags/' + parsed.version + '.zip'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fallback to original locator if no pattern matches
|
|
|
|
|
return locator
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add a dependency
|
|
|
|
|
@@ -311,38 +282,17 @@ Shop.get_api_url = function(locator) {
|
|
|
|
|
var parsed = Shop.parse_locator(locator)
|
|
|
|
|
if (!parsed) return null
|
|
|
|
|
|
|
|
|
|
var hostAndPath = parsed.path
|
|
|
|
|
if (locator.startsWith('https://')) {
|
|
|
|
|
hostAndPath = locator.substring(8).split('@')[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var parts = hostAndPath.split('/')
|
|
|
|
|
|
|
|
|
|
var parts = parsed.path.split('/')
|
|
|
|
|
// Gitea pattern: gitea.pockle.world/user/repo@branch
|
|
|
|
|
if (hostAndPath.includes('gitea.')) {
|
|
|
|
|
if (parsed.path.includes('gitea.')) {
|
|
|
|
|
var host = parts[0]
|
|
|
|
|
var user = parts[1]
|
|
|
|
|
var repo = parts[2]
|
|
|
|
|
return 'https://' + host + '/api/v1/repos/' + user + '/' + repo + '/branches/' + parsed.version
|
|
|
|
|
var url = 'https://' + host + '/api/v1/repos/' + user + '/' + repo + '/branches/'
|
|
|
|
|
if (parsed.version) url += parsed.version
|
|
|
|
|
return url
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GitHub pattern: github.com/user/repo@tag or @branch
|
|
|
|
|
if (hostAndPath.includes('github.com')) {
|
|
|
|
|
var user = parts[1]
|
|
|
|
|
var repo = parts[2]
|
|
|
|
|
// Try branch first, then tag
|
|
|
|
|
return 'https://api.github.com/repos/' + user + '/' + repo + '/branches/' + parsed.version
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GitLab pattern: gitlab.com/user/repo@tag
|
|
|
|
|
if (hostAndPath.includes('gitlab.')) {
|
|
|
|
|
var user = parts[1]
|
|
|
|
|
var repo = parts[2]
|
|
|
|
|
var projectId = encodeURIComponent(user + '/' + repo)
|
|
|
|
|
return 'https://' + parts[0] + '/api/v4/projects/' + projectId + '/repository/branches/' + parsed.version
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fallback - return null if no API pattern matches
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -350,23 +300,12 @@ Shop.get_api_url = function(locator) {
|
|
|
|
|
Shop.extract_commit_hash = function(locator, response) {
|
|
|
|
|
if (!response) return null
|
|
|
|
|
|
|
|
|
|
var data
|
|
|
|
|
try {
|
|
|
|
|
data = json.decode(response)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log.console("Failed to parse API response: " + e)
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
var data = json.decode(response)
|
|
|
|
|
|
|
|
|
|
// Handle different git hosting response formats
|
|
|
|
|
if (locator.includes('gitea.')) {
|
|
|
|
|
// Gitea: response.commit.id
|
|
|
|
|
return data.commit && data.commit.id
|
|
|
|
|
} else if (locator.includes('github.com')) {
|
|
|
|
|
// GitHub: response.commit.sha
|
|
|
|
|
return data.commit && data.commit.sha
|
|
|
|
|
} else if (locator.includes('gitlab.')) {
|
|
|
|
|
// GitLab: response.commit.id
|
|
|
|
|
if (Array.isArray(data))
|
|
|
|
|
data = data[0]
|
|
|
|
|
return data.commit && data.commit.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -449,7 +388,7 @@ Shop.install = function(alias) {
|
|
|
|
|
|
|
|
|
|
var count = zip.count()
|
|
|
|
|
for (var i = 0; i < count; i++) {
|
|
|
|
|
if (zip.is_directory(i)) continue
|
|
|
|
|
if (zip.is_dir(i)) continue
|
|
|
|
|
|
|
|
|
|
var filename = zip.get_filename(i)
|
|
|
|
|
// Strip top-level directory
|
|
|
|
|
@@ -467,19 +406,9 @@ Shop.install = function(alias) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. Update Lock
|
|
|
|
|
if (commit_hash) {
|
|
|
|
|
var lock = Shop.load_lock()
|
|
|
|
|
lock[alias] = {
|
|
|
|
|
locator: locator,
|
|
|
|
|
commit: commit_hash,
|
|
|
|
|
updated: time.number()
|
|
|
|
|
}
|
|
|
|
|
Shop.save_lock(lock)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. Update Lock (only for root package)
|
|
|
|
|
log.console("Installed " + alias)
|
|
|
|
|
return true
|
|
|
|
|
return { commit: commit_hash, locator: locator }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify dependencies
|
|
|
|
|
@@ -620,36 +549,309 @@ Shop.use = function(path, package_context) {
|
|
|
|
|
|
|
|
|
|
Shop.resolve_locator = resolve_locator
|
|
|
|
|
|
|
|
|
|
// Check for updates
|
|
|
|
|
Shop.update = function() {
|
|
|
|
|
var config = Shop.load_config()
|
|
|
|
|
if (!config || !config.dependencies) return
|
|
|
|
|
// Install a package and all its transitive dependencies
|
|
|
|
|
// This is the internal workhorse - installs from a specific package context
|
|
|
|
|
function install_package_deps(canonical_name, installed) {
|
|
|
|
|
installed = installed || {}
|
|
|
|
|
|
|
|
|
|
var lock = Shop.load_lock()
|
|
|
|
|
// Load the package's config to find its dependencies
|
|
|
|
|
var pkg_config = Shop.load_config(canonical_name)
|
|
|
|
|
if (!pkg_config || !pkg_config.dependencies) return installed
|
|
|
|
|
|
|
|
|
|
for (var alias in config.dependencies) {
|
|
|
|
|
var locator = config.dependencies[alias]
|
|
|
|
|
for (var alias in pkg_config.dependencies) {
|
|
|
|
|
var locator = pkg_config.dependencies[alias]
|
|
|
|
|
var parsed = Shop.parse_locator(locator)
|
|
|
|
|
var dep_canonical = parsed.path
|
|
|
|
|
|
|
|
|
|
// Skip if already installed in this run
|
|
|
|
|
if (installed[dep_canonical]) continue
|
|
|
|
|
|
|
|
|
|
// Check if already exists on disk
|
|
|
|
|
var target_dir = '.cell/modules/' + dep_canonical
|
|
|
|
|
if (fd.is_dir(target_dir)) {
|
|
|
|
|
log.console(" " + alias + " already installed")
|
|
|
|
|
installed[dep_canonical] = true
|
|
|
|
|
// Still recurse into its deps
|
|
|
|
|
install_package_deps(dep_canonical, installed)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Install this dependency
|
|
|
|
|
log.console(" Installing transitive dependency: " + alias + " (" + locator + ")")
|
|
|
|
|
var result = install_from_locator(locator)
|
|
|
|
|
if (result) {
|
|
|
|
|
installed[dep_canonical] = true
|
|
|
|
|
// Recurse into this package's dependencies
|
|
|
|
|
install_package_deps(dep_canonical, installed)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return installed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Install from a raw locator (not from config)
|
|
|
|
|
function install_from_locator(locator, locked_hash) {
|
|
|
|
|
var parsed = Shop.parse_locator(locator)
|
|
|
|
|
var target_dir = '.cell/modules/' + parsed.path
|
|
|
|
|
|
|
|
|
|
// 1. Get Commit Hash - use locked hash if provided, otherwise fetch
|
|
|
|
|
var commit_hash = locked_hash
|
|
|
|
|
if (!commit_hash) {
|
|
|
|
|
var api_url = Shop.get_api_url(locator)
|
|
|
|
|
|
|
|
|
|
if (api_url) {
|
|
|
|
|
try {
|
|
|
|
|
var resp = http.fetch(api_url)
|
|
|
|
|
var resp_text = text(resp)
|
|
|
|
|
var remote_hash = Shop.extract_commit_hash(locator, resp_text)
|
|
|
|
|
|
|
|
|
|
var local_hash = lock[alias] ? lock[alias].commit : null
|
|
|
|
|
|
|
|
|
|
if (remote_hash && remote_hash != local_hash) {
|
|
|
|
|
log.console("Update available for " + alias + ": " + local_hash + " -> " + remote_hash)
|
|
|
|
|
Shop.install(alias)
|
|
|
|
|
commit_hash = Shop.extract_commit_hash(locator, resp_text)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log.console("Warning: Failed to fetch API info: " + e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log.console(alias + " is up to date.")
|
|
|
|
|
log.console("Using locked commit: " + commit_hash)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Download Zip
|
|
|
|
|
var download_url = Shop.get_download_url(locator, commit_hash)
|
|
|
|
|
if (!download_url) {
|
|
|
|
|
log.error("Could not determine download URL for " + locator)
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.console("Downloading from " + download_url)
|
|
|
|
|
var zip_blob
|
|
|
|
|
try {
|
|
|
|
|
zip_blob = http.fetch(download_url)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log.error("Download failed: " + e)
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Unpack
|
|
|
|
|
log.console("Unpacking to " + target_dir)
|
|
|
|
|
ensure_dir(target_dir)
|
|
|
|
|
|
|
|
|
|
var zip = miniz.read(zip_blob)
|
|
|
|
|
if (!zip)
|
|
|
|
|
throw new Error("Failed to read zip archive")
|
|
|
|
|
|
|
|
|
|
var count = zip.count()
|
|
|
|
|
log.console(`zip contains ${count} entries`)
|
|
|
|
|
for (var i = 0; i < count; i++) {
|
|
|
|
|
if (zip.is_dir(i)) continue
|
|
|
|
|
|
|
|
|
|
var filename = zip.get_filename(i)
|
|
|
|
|
log.console(filename)
|
|
|
|
|
var parts = filename.split('/')
|
|
|
|
|
if (parts.length > 1) {
|
|
|
|
|
parts.shift()
|
|
|
|
|
var rel_path = parts.join('/')
|
|
|
|
|
|
|
|
|
|
var full_path = target_dir + '/' + rel_path
|
|
|
|
|
var dir_path = full_path.substring(0, full_path.lastIndexOf('/'))
|
|
|
|
|
ensure_dir(dir_path)
|
|
|
|
|
|
|
|
|
|
var content = zip.slurp(filename)
|
|
|
|
|
slurpwrite(full_path, content)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { commit: commit_hash, locator: locator, path: parsed.path }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// High-level: Add a package, install it, and install all transitive dependencies
|
|
|
|
|
// Like `bun add` or `npm install <pkg>`
|
|
|
|
|
Shop.get = function(locator, alias) {
|
|
|
|
|
Shop.init()
|
|
|
|
|
|
|
|
|
|
var parsed = Shop.parse_locator(locator)
|
|
|
|
|
if (!alias) alias = parsed.name
|
|
|
|
|
|
|
|
|
|
log.console("Adding dependency: " + alias + " = " + locator)
|
|
|
|
|
|
|
|
|
|
// Add to config
|
|
|
|
|
var config = Shop.load_config() || { dependencies: {} }
|
|
|
|
|
if (!config.dependencies) config.dependencies = {}
|
|
|
|
|
config.dependencies[alias] = locator
|
|
|
|
|
Shop.save_config(config)
|
|
|
|
|
|
|
|
|
|
// Install the package
|
|
|
|
|
var result = install_from_locator(locator)
|
|
|
|
|
if (!result) {
|
|
|
|
|
log.error("Failed to install " + alias)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update lock file for root
|
|
|
|
|
var lock = Shop.load_lock(null)
|
|
|
|
|
lock[alias] = {
|
|
|
|
|
locator: locator,
|
|
|
|
|
commit: result.commit,
|
|
|
|
|
updated: time.number()
|
|
|
|
|
}
|
|
|
|
|
Shop.save_lock(lock)
|
|
|
|
|
|
|
|
|
|
log.console("Installed " + alias)
|
|
|
|
|
|
|
|
|
|
// Install transitive dependencies
|
|
|
|
|
log.console("Resolving transitive dependencies...")
|
|
|
|
|
install_package_deps(parsed.path, {})
|
|
|
|
|
|
|
|
|
|
log.console("Done.")
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// High-level: Update a specific package or all packages
|
|
|
|
|
// Like `bun update` or `bun update <pkg>`
|
|
|
|
|
Shop.update_all = function(alias) {
|
|
|
|
|
var config = Shop.load_config()
|
|
|
|
|
if (!config || !config.dependencies) {
|
|
|
|
|
log.console("No dependencies to update.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var lock = Shop.load_lock()
|
|
|
|
|
var to_update = alias ? [alias] : Object.keys(config.dependencies)
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < to_update.length; i++) {
|
|
|
|
|
var dep_alias = to_update[i]
|
|
|
|
|
var locator = config.dependencies[dep_alias]
|
|
|
|
|
|
|
|
|
|
if (!locator) {
|
|
|
|
|
log.error("Dependency not found: " + dep_alias)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var api_url = Shop.get_api_url(locator)
|
|
|
|
|
if (!api_url) {
|
|
|
|
|
log.console(dep_alias + ": cannot check for updates (no API URL)")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
var parsed = Shop.parse_locator(locator)
|
|
|
|
|
var target_dir = `.cell/modules/${parsed.path}`
|
|
|
|
|
var resp = http.fetch(api_url)
|
|
|
|
|
var resp_text = text(resp)
|
|
|
|
|
var remote_hash = Shop.extract_commit_hash(locator, resp_text)
|
|
|
|
|
var local_hash = lock[dep_alias] ? lock[dep_alias].commit : null
|
|
|
|
|
if (!fd.is_dir(target_dir) || remote_hash != local_hash) {
|
|
|
|
|
log.console(dep_alias + ": updating " + (local_hash ? local_hash.substring(0,8) : "(new)") + " -> " + remote_hash.substring(0,8))
|
|
|
|
|
|
|
|
|
|
// Remove old directory
|
|
|
|
|
if (fd.is_dir(target_dir))
|
|
|
|
|
fd.rmdir(target_dir)
|
|
|
|
|
|
|
|
|
|
// Reinstall
|
|
|
|
|
var result = install_from_locator(locator, remote_hash)
|
|
|
|
|
if (result) {
|
|
|
|
|
lock[dep_alias] = {
|
|
|
|
|
locator: locator,
|
|
|
|
|
commit: result.commit,
|
|
|
|
|
updated: time.number()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reinstall transitive deps
|
|
|
|
|
install_package_deps(parsed.path, {})
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log.console(dep_alias + ": up to date")
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log.error("Failed to check update for " + alias + ": " + e)
|
|
|
|
|
log.error("Failed to check " + dep_alias)
|
|
|
|
|
log.error(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Shop.save_lock(lock)
|
|
|
|
|
log.console("Update complete.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// High-level: Remove a package and clean up
|
|
|
|
|
// Like `bun remove`
|
|
|
|
|
Shop.remove = function(alias) {
|
|
|
|
|
var config = Shop.load_config()
|
|
|
|
|
if (!config || !config.dependencies || !config.dependencies[alias]) {
|
|
|
|
|
log.error("Dependency not found: " + alias)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var locator = config.dependencies[alias]
|
|
|
|
|
var parsed = Shop.parse_locator(locator)
|
|
|
|
|
var target_dir = '.cell/modules/' + parsed.path
|
|
|
|
|
|
|
|
|
|
// Remove from config
|
|
|
|
|
delete config.dependencies[alias]
|
|
|
|
|
Shop.save_config(config)
|
|
|
|
|
|
|
|
|
|
// Remove from lock
|
|
|
|
|
var lock = Shop.load_lock()
|
|
|
|
|
delete lock[alias]
|
|
|
|
|
Shop.save_lock(lock)
|
|
|
|
|
|
|
|
|
|
// Remove directory
|
|
|
|
|
if (fd.is_dir(target_dir)) {
|
|
|
|
|
log.console("Removing " + target_dir)
|
|
|
|
|
try {
|
|
|
|
|
fd.rmdir(target_dir)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log.error("Failed to remove directory: " + e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.console("Removed " + alias)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Install all dependencies from config (like `bun install`)
|
|
|
|
|
Shop.install_all = function() {
|
|
|
|
|
Shop.init()
|
|
|
|
|
|
|
|
|
|
var config = Shop.load_config()
|
|
|
|
|
if (!config || !config.dependencies) {
|
|
|
|
|
log.console("No dependencies to install.")
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var lock = Shop.load_lock(null)
|
|
|
|
|
var installed = {}
|
|
|
|
|
|
|
|
|
|
for (var alias in config.dependencies) {
|
|
|
|
|
var locator = config.dependencies[alias]
|
|
|
|
|
var parsed = Shop.parse_locator(locator)
|
|
|
|
|
var target_dir = '.cell/modules/' + parsed.path
|
|
|
|
|
|
|
|
|
|
// Check if already installed
|
|
|
|
|
if (fd.is_dir(target_dir)) {
|
|
|
|
|
log.console(alias + ": already installed")
|
|
|
|
|
installed[parsed.path] = true
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.console("Installing " + alias + "...")
|
|
|
|
|
var locked_hash = lock[alias] ? lock[alias].commit : null
|
|
|
|
|
var result = install_from_locator(locator, locked_hash)
|
|
|
|
|
if (result) {
|
|
|
|
|
installed[parsed.path] = true
|
|
|
|
|
lock[alias] = {
|
|
|
|
|
locator: locator,
|
|
|
|
|
commit: result.commit,
|
|
|
|
|
updated: time.number()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now install transitive dependencies for all root deps
|
|
|
|
|
log.console("Resolving transitive dependencies...")
|
|
|
|
|
for (var alias in config.dependencies) {
|
|
|
|
|
var locator = config.dependencies[alias]
|
|
|
|
|
var parsed = Shop.parse_locator(locator)
|
|
|
|
|
install_package_deps(parsed.path, installed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Shop.save_lock(lock)
|
|
|
|
|
log.console("Done.")
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compile a module
|
|
|
|
|
|