use core::ptr::NonNull;
use std::io::Write as _;
use ::bun_ast::import_record as ast;
use ::bun_install_types::resolver_hooks as Install;
use bun_alloc as allocators;
use bun_ast::Msg;
use bun_core::MutableString;
use bun_paths::SEP_STR;
use bun_paths::strings;
use bun_sys::Fd as FD;
use crate::dir_info::DirInfoRef;
use crate::fs as Fs;
use crate::options;
use crate::package_json::PackageJSON;
use crate::resolver::Dependency;
type Path = crate::fs::Path<'static>;
pub struct SideEffectsData {
pub source: Option<NonNull<bun_ast::Source>>, pub range: bun_ast::Range,
pub is_side_effects_array_in_json: bool,
}
pub struct PathPair {
pub primary: Path,
pub secondary: Option<Path>,
}
impl Default for PathPair {
fn default() -> Self {
Self {
primary: Path::empty(),
secondary: None,
}
}
}
pub(crate) struct PathPairIter<'a> {
index: u8, ctx: &'a mut PathPair,
}
impl<'a> PathPairIter<'a> {
pub(crate) fn next(&mut self) -> Option<&mut Path> {
if let Some(path_) = self.next_() {
let p: *mut Path = path_;
unsafe {
if (*p).is_disabled {
return self.next();
}
return Some(&mut *p);
}
}
None
}
fn next_(&mut self) -> Option<&mut Path> {
let ind = self.index;
self.index = self.index.saturating_add(1);
match ind {
0 => Some(&mut self.ctx.primary),
1 => self.ctx.secondary.as_mut(),
_ => None,
}
}
}
impl PathPair {
pub fn iter(&mut self) -> PathPairIter<'_> {
PathPairIter {
ctx: self,
index: 0,
}
}
}
use bun_ast::SideEffects;
pub struct Result {
pub path_pair: PathPair,
pub jsx: options::jsx::Pragma,
pub package_json: Option<*const PackageJSON>,
pub diff_case: Option<Fs::file_system::entry::lookup::DifferentCase<'static>>,
pub primary_side_effects_data: SideEffects,
pub module_type: options::ModuleType,
pub debug_meta: Option<DebugMeta>,
pub dirname_fd: FD,
pub file_fd: FD,
pub import_kind: ast::ImportKind,
pub flags: ResultFlags,
}
impl Default for Result {
fn default() -> Self {
Self {
path_pair: PathPair::default(),
jsx: options::jsx::Pragma::default(),
package_json: None,
diff_case: None,
primary_side_effects_data: SideEffects::HasSideEffects,
module_type: options::ModuleType::Unknown,
debug_meta: None,
dirname_fd: FD::INVALID,
file_fd: FD::INVALID,
import_kind: ast::ImportKind::Stmt, flags: ResultFlags::default(),
}
}
}
bitflags::bitflags! {
#[derive(Default, Clone, Copy)]
pub struct ResultFlags: u8 {
const IS_EXTERNAL = 1 << 0;
const IS_EXTERNAL_AND_REWRITE_IMPORT_PATH = 1 << 1;
const IS_STANDALONE_MODULE = 1 << 2;
const IS_FROM_NODE_MODULES = 1 << 3;
const PRESERVE_UNUSED_IMPORTS_TS = 1 << 4;
const EMIT_DECORATOR_METADATA = 1 << 5;
const EXPERIMENTAL_DECORATORS = 1 << 6;
}
}
impl ResultFlags {
#[inline]
pub fn is_external(self) -> bool {
self.contains(Self::IS_EXTERNAL)
}
#[inline]
pub fn set_is_external(&mut self, v: bool) {
self.set(Self::IS_EXTERNAL, v)
}
#[inline]
pub fn is_external_and_rewrite_import_path(self) -> bool {
self.contains(Self::IS_EXTERNAL_AND_REWRITE_IMPORT_PATH)
}
#[inline]
pub fn set_is_external_and_rewrite_import_path(&mut self, v: bool) {
self.set(Self::IS_EXTERNAL_AND_REWRITE_IMPORT_PATH, v)
}
#[inline]
pub fn is_standalone_module(self) -> bool {
self.contains(Self::IS_STANDALONE_MODULE)
}
#[inline]
pub fn is_from_node_modules(self) -> bool {
self.contains(Self::IS_FROM_NODE_MODULES)
}
#[inline]
pub fn set_is_from_node_modules(&mut self, v: bool) {
self.set(Self::IS_FROM_NODE_MODULES, v)
}
#[inline]
pub fn emit_decorator_metadata(self) -> bool {
self.contains(Self::EMIT_DECORATOR_METADATA)
}
#[inline]
pub fn set_emit_decorator_metadata(&mut self, v: bool) {
self.set(Self::EMIT_DECORATOR_METADATA, v)
}
#[inline]
pub fn experimental_decorators(self) -> bool {
self.contains(Self::EXPERIMENTAL_DECORATORS)
}
#[inline]
pub fn set_experimental_decorators(&mut self, v: bool) {
self.set(Self::EXPERIMENTAL_DECORATORS, v)
}
}
pub enum ResultUnion {
Success(Result),
Failure(bun_core::Error),
Pending(PendingResolution),
NotFound,
}
impl Result {
#[inline]
pub fn package_json_ref(&self) -> Option<&'static PackageJSON> {
Self::deref_package_json(self.package_json)
}
#[inline]
pub fn deref_package_json(ptr: Option<*const PackageJSON>) -> Option<&'static PackageJSON> {
ptr.map(|p| unsafe { &*p })
}
pub fn path(&mut self) -> Option<&mut Path> {
if !self.path_pair.primary.is_disabled {
return Some(&mut self.path_pair.primary);
}
if let Some(second) = self.path_pair.secondary.as_mut() {
if !second.is_disabled {
return Some(second);
}
}
None
}
pub fn path_const(&self) -> Option<&Path> {
if !self.path_pair.primary.is_disabled {
return Some(&self.path_pair.primary);
}
if let Some(second) = self.path_pair.secondary.as_ref() {
if !second.is_disabled {
return Some(second);
}
}
None
}
pub fn is_likely_node_module(&self) -> bool {
let Some(path_) = self.path_const() else {
return false;
};
self.flags.is_from_node_modules()
|| strings::index_of(path_.text(), b"/node_modules/").is_some()
}
pub fn should_assume_common_js(&self, kind: ast::ImportKind) -> bool {
match self.module_type {
options::ModuleType::Esm => false,
options::ModuleType::Cjs => true,
_ => {
if kind == ast::ImportKind::Require || kind == ast::ImportKind::RequireResolve {
return true;
}
self.is_likely_node_module()
}
}
}
pub fn hash(&self, _: &[u8], _: options::Loader) -> u32 {
let module = self.path_pair.primary.text();
let node_module_root = const_format::concatcp!(SEP_STR, "node_modules", SEP_STR).as_bytes();
if let Some(end_) = strings::last_index_of(module, node_module_root) {
let end: usize = end_ + node_module_root.len();
return bun_wyhash::hash(&module[end..]) as u32;
}
bun_wyhash::hash(self.path_pair.primary.text()) as u32
}
}
pub struct DebugMeta {
pub notes: Vec<bun_ast::Data>,
pub suggestion_text: &'static [u8],
pub suggestion_message: &'static [u8],
pub suggestion_range: SuggestionRange,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum SuggestionRange {
Full,
End,
}
impl DebugMeta {
pub fn init() -> DebugMeta {
DebugMeta {
notes: Vec::new(),
suggestion_text: b"",
suggestion_message: b"",
suggestion_range: SuggestionRange::Full,
}
}
pub fn log_error_msg(
&mut self,
log: &mut bun_ast::Log,
source: Option<&bun_ast::Source>,
r: bun_ast::Range,
args: core::fmt::Arguments<'_>,
) -> core::result::Result<(), bun_core::Error> {
if source.is_some() && !self.suggestion_message.is_empty() {
let suggestion_range = if self.suggestion_range == SuggestionRange::End {
bun_ast::Range {
loc: bun_ast::Loc {
start: r.end_i() as i32 - 1,
},
..Default::default()
}
} else {
r
};
let data = bun_ast::range_data(source, suggestion_range, self.suggestion_message);
let _ = &self.suggestion_text;
self.notes.push(data);
}
let mut msg_text = Vec::new();
write!(&mut msg_text, "{}", args).ok();
log.add_msg(Msg {
kind: bun_ast::Kind::Err,
data: bun_ast::range_data(source, r, msg_text),
notes: core::mem::take(&mut self.notes).into_boxed_slice(),
..Default::default()
});
Ok(())
}
}
pub struct DirEntryResolveQueueItem {
pub result: allocators::Result,
pub unsafe_path: bun_ptr::RawSlice<u8>,
pub safe_path: bun_ptr::RawSlice<u8>,
pub fd: FD,
}
impl Default for DirEntryResolveQueueItem {
fn default() -> Self {
Self {
result: allocators::Result {
hash: 0,
index: allocators::NOT_FOUND,
status: allocators::ItemStatus::Unknown,
},
unsafe_path: bun_ptr::RawSlice::EMPTY,
safe_path: bun_ptr::RawSlice::EMPTY,
fd: FD::INVALID,
}
}
}
impl Clone for DirEntryResolveQueueItem {
fn clone(&self) -> Self {
Self {
result: allocators::Result {
hash: self.result.hash,
index: self.result.index,
status: self.result.status,
},
unsafe_path: self.unsafe_path,
safe_path: self.safe_path,
fd: self.fd,
}
}
}
pub struct DebugLogs {
pub what: Vec<u8>,
pub indent: MutableString,
pub notes: Vec<bun_ast::Data>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum FlushMode {
Fail,
Success,
}
impl DebugLogs {
pub fn init() -> core::result::Result<DebugLogs, bun_alloc::AllocError> {
let mutable = MutableString::init(0)?;
Ok(DebugLogs {
what: Vec::new(),
indent: mutable,
notes: Vec::new(),
})
}
#[cold]
pub fn increase_indent(&mut self) {
self.indent.append(b" ").expect("unreachable");
}
#[cold]
pub fn decrease_indent(&mut self) {
let new_len = self.indent.list.len() - 1;
self.indent.list.truncate(new_len);
}
#[cold]
pub fn add_note(&mut self, text: Vec<u8>) {
let len = self.indent.len();
let final_text = if len > 0 {
let mut __text = Vec::with_capacity(text.len() + len);
__text.extend_from_slice(self.indent.list.as_slice());
__text.extend_from_slice(&text);
__text
} else {
text
};
self.notes
.push(bun_ast::range_data(None, bun_ast::Range::NONE, final_text));
}
#[cold]
pub fn add_note_fmt(&mut self, args: core::fmt::Arguments<'_>) {
let mut buf = Vec::new();
write!(&mut buf, "{}", args).expect("unreachable");
self.add_note(buf);
}
}
pub struct MatchResult {
pub path_pair: PathPair,
pub dirname_fd: FD,
pub file_fd: FD,
pub is_node_module: bool,
pub package_json: Option<*const PackageJSON>,
pub diff_case: Option<Fs::file_system::entry::lookup::DifferentCase<'static>>,
pub dir_info: Option<DirInfoRef>,
pub module_type: options::ModuleType,
pub is_external: bool,
}
impl Default for MatchResult {
fn default() -> Self {
Self {
path_pair: PathPair::default(),
dirname_fd: FD::INVALID,
file_fd: FD::INVALID,
is_node_module: false,
package_json: None,
diff_case: None,
dir_info: None,
module_type: options::ModuleType::Unknown,
is_external: false,
}
}
}
pub enum MatchStatus {
NotFound,
Success,
Pending(Box<PendingResolution>),
Failure(bun_core::Error),
}
impl MatchStatus {
#[inline]
pub fn is_success(&self) -> bool {
matches!(self, MatchStatus::Success)
}
}
pub struct PendingResolution {
pub esm: crate::package_json::PackageExternal,
pub dependency: Dependency::Version,
pub resolution_id: Install::PackageID,
pub root_dependency_id: Install::DependencyID,
pub import_record_id: u32,
pub string_buf: Vec<u8>,
pub tag: PendingResolutionTag,
}
impl Default for PendingResolution {
fn default() -> Self {
Self {
esm: Default::default(),
dependency: Default::default(),
resolution_id: Install::INVALID_PACKAGE_ID,
root_dependency_id: Install::INVALID_PACKAGE_ID,
import_record_id: u32::MAX,
string_buf: Vec::new(),
tag: PendingResolutionTag::Download,
}
}
}
impl PendingResolution {
pub fn init(
esm: crate::package_json::Package<'_>,
dependency: Dependency::Version,
resolution_id: Install::PackageID,
) -> core::result::Result<PendingResolution, bun_core::Error> {
let (esm, string_buf) = esm.copy()?;
Ok(PendingResolution {
esm,
dependency,
resolution_id,
string_buf,
..PendingResolution::default()
})
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum PendingResolutionTag {
Download,
Resolve,
Done,
}
pub struct LoadResult {
pub path: &'static [u8], pub diff_case: Option<Fs::file_system::entry::lookup::DifferentCase<'static>>,
pub dirname_fd: FD,
pub file_fd: FD,
pub dir_info: Option<DirInfoRef>,
}