use core::mem::{align_of, size_of};
use bun_install_types::resolver_hooks as hooks;
use bun_semver::{SlicedString, String as SemverString};
use crate::dependency::{self, DependencyExt as _};
use crate::lockfile::{self, Package};
use crate::package_manager::package_manager_directories as directories;
use crate::package_manager::package_manager_enqueue as enqueue;
use crate::package_manager::package_manager_lifecycle as lifecycle;
use crate::package_manager::package_manager_resolution as pm_resolution;
use crate::resolution;
use crate::{DependencyID, Features, PackageID, PackageManager, PreinstallState};
const _: () = assert!(size_of::<resolution::Resolution>() == size_of::<hooks::Resolution>());
const _: () = assert!(align_of::<resolution::Resolution>() == align_of::<hooks::Resolution>());
const _: () = {
assert!(resolution::Tag::Uninitialized.0 == hooks::ResolutionTag::Uninitialized as u8);
assert!(resolution::Tag::Root.0 == hooks::ResolutionTag::Root as u8);
assert!(resolution::Tag::Npm.0 == hooks::ResolutionTag::Npm as u8);
assert!(resolution::Tag::Folder.0 == hooks::ResolutionTag::Folder as u8);
assert!(resolution::Tag::LocalTarball.0 == hooks::ResolutionTag::LocalTarball as u8);
assert!(resolution::Tag::Github.0 == hooks::ResolutionTag::Github as u8);
assert!(resolution::Tag::Git.0 == hooks::ResolutionTag::Git as u8);
assert!(resolution::Tag::Symlink.0 == hooks::ResolutionTag::Symlink as u8);
assert!(resolution::Tag::Workspace.0 == hooks::ResolutionTag::Workspace as u8);
assert!(resolution::Tag::RemoteTarball.0 == hooks::ResolutionTag::RemoteTarball as u8);
assert!(resolution::Tag::SingleFileModule.0 == hooks::ResolutionTag::SingleFileModule as u8);
};
#[inline]
fn tag_to_hooks(t: resolution::Tag) -> hooks::ResolutionTag {
match t {
resolution::Tag::Uninitialized => hooks::ResolutionTag::Uninitialized,
resolution::Tag::Root => hooks::ResolutionTag::Root,
resolution::Tag::Npm => hooks::ResolutionTag::Npm,
resolution::Tag::Folder => hooks::ResolutionTag::Folder,
resolution::Tag::LocalTarball => hooks::ResolutionTag::LocalTarball,
resolution::Tag::Github => hooks::ResolutionTag::Github,
resolution::Tag::Git => hooks::ResolutionTag::Git,
resolution::Tag::Symlink => hooks::ResolutionTag::Symlink,
resolution::Tag::Workspace => hooks::ResolutionTag::Workspace,
resolution::Tag::RemoteTarball => hooks::ResolutionTag::RemoteTarball,
resolution::Tag::SingleFileModule => hooks::ResolutionTag::SingleFileModule,
unknown => {
debug_assert!(
false,
"unknown resolution::Tag({}) crossing hooks boundary",
unknown.0
);
hooks::ResolutionTag::Uninitialized
}
}
}
#[inline]
fn resolution_to_hooks(r: &resolution::Resolution) -> hooks::Resolution {
hooks::Resolution {
tag: tag_to_hooks(r.tag),
_padding: r._padding,
value: r.value,
}
}
#[inline]
fn resolution_from_hooks(r: &hooks::Resolution) -> resolution::Resolution {
resolution::Resolution {
tag: resolution::Tag(r.tag as u8),
_padding: r._padding,
value: r.value,
}
}
impl hooks::AutoInstaller for PackageManager {
fn lockfile_packages_len(&self) -> usize {
self.lockfile.packages.len()
}
fn lockfile_package_dependencies(&self, id: PackageID) -> hooks::DependencySlice {
let s = self.lockfile.packages.get(id as usize).dependencies;
hooks::DependencySlice::new(s.off, s.len)
}
fn lockfile_package_resolutions(&self, id: PackageID) -> hooks::ResolutionSlice {
let s = self.lockfile.packages.get(id as usize).resolutions;
hooks::ResolutionSlice::new(s.off, s.len)
}
fn lockfile_package_resolution(&self, id: PackageID) -> hooks::Resolution {
resolution_to_hooks(&self.lockfile.packages.get(id as usize).resolution)
}
fn lockfile_dependencies_buf(&self) -> &[hooks::Dependency] {
self.lockfile.buffers.dependencies.as_slice()
}
fn lockfile_resolutions_buf(&self) -> &[PackageID] {
self.lockfile.buffers.resolutions.as_slice()
}
fn lockfile_string_bytes(&self) -> &[u8] {
self.lockfile.buffers.string_bytes.as_slice()
}
fn lockfile_resolve(
&self,
name: &[u8],
version: &hooks::DependencyVersion,
) -> Option<PackageID> {
self.lockfile
.resolve_package_from_name_and_version(name, version)
}
fn lockfile_legacy_package_to_dependency_id(
&self,
package_id: PackageID,
) -> Result<DependencyID, bun_core::Error> {
self.lockfile
.buffers
.legacy_package_to_dependency_id(None, package_id)
}
fn lockfile_str<'a>(&'a self, s: &'a SemverString) -> &'a [u8] {
self.lockfile.str(s)
}
fn lockfile_append_from_package_json(
&mut self,
package_json: &dyn hooks::PackageJsonView,
features: Features,
) -> Result<PackageID, bun_core::Error> {
let pm: *mut PackageManager = self;
let lockfile: &mut lockfile::Lockfile = unsafe { &mut *(*pm).lockfile };
let mut package = Package::default();
let mut string_builder = crate::string_builder!(lockfile);
let mut total_dependencies_count: u32 = 0;
string_builder.count(package_json.name());
string_builder.count(package_json.version());
let source_buf = package_json.dependency_source_buf();
for (_, dep) in package_json.dependency_iter() {
if dep.behavior.is_enabled(features) {
dep.count(source_buf, &mut string_builder);
total_dependencies_count += 1;
}
}
string_builder.allocate()?;
let dependencies_list = &mut lockfile.buffers.dependencies;
let resolutions_list = &mut lockfile.buffers.resolutions;
dependencies_list.reserve(total_dependencies_count as usize);
resolutions_list.reserve(total_dependencies_count as usize);
let package_name: bun_semver::ExternalString =
string_builder.append::<bun_semver::ExternalString>(package_json.name());
package.name_hash = package_name.hash;
package.name = package_name.value;
package.resolution = resolution::Resolution::init(resolution::TaggedValue::Root);
let dep_start = dependencies_list.len();
debug_assert!(dependencies_list.len() == resolutions_list.len());
let mut dependencies: &mut [dependency::Dependency] =
bun_core::vec::grow_default(dependencies_list, total_dependencies_count as usize);
for (_, dep) in package_json.dependency_iter() {
if !dep.behavior.is_enabled(features) {
continue;
}
let pm_ref: &mut PackageManager = unsafe { &mut *pm };
match dep.clone_in(pm_ref, source_buf, &mut string_builder) {
Ok(cloned) => dependencies[0] = cloned,
Err(e) => {
dependencies_list.truncate(dep_start);
string_builder.clamp();
return Err(e);
}
}
dependencies = &mut dependencies[1..];
if dependencies.is_empty() {
break;
}
}
let remaining = dependencies.len() as u32;
package.meta.arch = package_json.arch();
package.meta.os = package_json.os();
package.meta.set_has_install_script(false);
package.dependencies = crate::lockfile::DependencySlice::new(
dep_start as u32,
total_dependencies_count - remaining,
);
package.resolutions = crate::lockfile::PackageIDSlice::new(
package.dependencies.off,
package.dependencies.len,
);
let new_length = package.dependencies.len as usize + dep_start;
dependencies_list.truncate(new_length);
resolutions_list.resize(new_length, crate::INVALID_PACKAGE_ID);
string_builder.clamp();
let appended = lockfile.append_package(&package)?;
Ok(appended.meta.id)
}
fn lockfile_append_root_stub(&mut self) -> Result<PackageID, bun_core::Error> {
let pkg = Package {
resolution: resolution::Resolution::init(resolution::TaggedValue::Root),
..Default::default()
};
let appended = self.lockfile.append_package(&pkg)?;
Ok(appended.meta.id)
}
fn set_on_wake(&mut self, handler: hooks::WakeHandler) {
self.on_wake = handler;
}
fn path_for_resolution<'b>(
&mut self,
package_id: PackageID,
resolution: &hooks::Resolution,
buf: &'b mut [u8],
) -> Result<&'b [u8], bun_core::Error> {
debug_assert!(buf.len() >= bun_paths::MAX_PATH_BYTES);
let path_buf: &mut bun_paths::PathBuffer =
unsafe { &mut *buf.as_mut_ptr().cast::<bun_paths::PathBuffer>() };
let r = resolution_from_hooks(resolution);
let out = directories::path_for_resolution(self, package_id, &r, path_buf)?;
Ok(&*out)
}
fn get_preinstall_state(&self, package_id: PackageID) -> PreinstallState {
lifecycle::get_preinstall_state(self, package_id)
}
fn enqueue_package_for_download(
&mut self,
name: &[u8],
dependency_id: DependencyID,
package_id: PackageID,
resolution: &hooks::Resolution,
ctx: hooks::TaskCallbackContext,
patch_name_and_version_hash: Option<u64>,
) -> Result<(), bun_core::Error> {
let r = resolution_from_hooks(resolution);
let npm = *r.npm();
let url = self.lockfile.str(&npm.url).to_vec();
enqueue::enqueue_package_for_download(
self,
name,
dependency_id,
package_id,
npm.version,
&url,
crate::TaskCallbackContext::RootRequestId(ctx.root_request_id),
patch_name_and_version_hash,
)
.map_err(Into::into)
}
fn resolve_from_disk_cache(
&mut self,
name: &[u8],
version: &hooks::DependencyVersion,
) -> Option<PackageID> {
pm_resolution::resolve_from_disk_cache(self, name, version)
}
fn enqueue_dependency_to_root(
&mut self,
name: &[u8],
version: &hooks::DependencyVersion,
version_buf: &[u8],
behavior: hooks::Behavior,
) -> hooks::EnqueueResult {
match enqueue::enqueue_dependency_to_root(self, name, version, version_buf, behavior) {
enqueue::DependencyToEnqueue::Resolution {
package_id,
resolution,
} => hooks::EnqueueResult::Resolution {
package_id,
resolution: resolution_to_hooks(&resolution),
},
enqueue::DependencyToEnqueue::Pending(id) => hooks::EnqueueResult::Pending(id),
enqueue::DependencyToEnqueue::NotFound => hooks::EnqueueResult::NotFound,
enqueue::DependencyToEnqueue::Failure(e) => hooks::EnqueueResult::Failure(e),
}
}
fn parse_dependency(
&mut self,
name: SemverString,
name_hash: Option<u64>,
version: &[u8],
sliced: &SlicedString,
log: Option<&mut bun_ast::Log>,
) -> Option<hooks::DependencyVersion> {
dependency::parse(name, name_hash, version, sliced, log, Some(self))
}
fn parse_dependency_with_tag(
&mut self,
name: SemverString,
name_hash: u64,
version: &[u8],
tag: hooks::DependencyVersionTag,
sliced: &SlicedString,
log: Option<&mut bun_ast::Log>,
) -> Option<hooks::DependencyVersion> {
dependency::parse_with_tag(
name,
Some(name_hash),
version,
tag,
sliced,
log,
Some(self as &mut dyn dependency::NpmAliasRegistry),
)
}
fn infer_dependency_tag(&self, dep: &[u8]) -> hooks::DependencyVersionTag {
<dependency::Tag as dependency::TagExt>::infer(dep)
}
}
#[unsafe(no_mangle)]
pub(crate) unsafe fn __bun_resolver_init_package_manager(
mut log: core::ptr::NonNull<bun_ast::Log>,
install: Option<core::ptr::NonNull<crate::bun_schema::api::BunInstall>>,
mut env: core::ptr::NonNull<bun_dotenv::Loader<'static>>,
) -> core::ptr::NonNull<dyn hooks::AutoInstaller> {
bun_http::http_thread::init(&Default::default());
let bun_install: Option<&crate::bun_schema::api::BunInstall> =
install.map(|p| unsafe { p.as_ref() });
let (log_ref, env_ref): (&mut bun_ast::Log, &mut bun_dotenv::Loader<'static>) =
unsafe { (log.as_mut(), env.as_mut()) };
let pm: *mut PackageManager = crate::package_manager::init_with_runtime(
log_ref,
bun_install,
crate::package_manager::CommandLineArguments::default(),
env_ref,
);
core::ptr::NonNull::new(pm as *mut dyn hooks::AutoInstaller)
.expect("init_with_runtime returns the holder::RAW_PTR singleton")
}