#![feature(allocator_api)]
#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
#![warn(unused_must_use)]
use bun_collections::VecExt;
#[path = "Chunk.rs"]
pub mod chunk;
#[path = "InternalSourceMap.rs"]
pub mod internal_source_map;
#[path = "LineOffsetTable.rs"]
pub mod line_offset_table;
#[path = "Mapping.rs"]
pub mod mapping;
#[path = "ParsedSourceMap.rs"]
pub mod parsed_source_map;
pub use bun_base64::vlq;
pub use vlq::{VLQ, encode as encode_vlq};
use vlq::{decode as decode_vlq, decode_assume_valid as decode_vlq_assume_valid};
pub use line_offset_table::{LineOffsetTable, LineOffsetTableColumns};
pub use mapping::{Lookup as MappingLookup, Mapping};
pub use parsed_source_map::{ParsedSourceMap, SourceContentPtr};
unsafe impl Send for ParsedSourceMap {}
unsafe impl Sync for ParsedSourceMap {}
pub use bun_core::Ordinal;
pub use chunk::Chunk;
pub use internal_source_map::InternalSourceMap;
bun_opaque::opaque_ffi! { pub struct BakeSourceProvider; }
unsafe extern "C" {
fn BakeSourceProvider__getSourceSlice(this: *const BakeSourceProvider) -> bun_core::String;
}
unsafe extern "Rust" {
static __BUN_BAKE_EXTERNAL_SOURCEMAP: fn(source_filename: &[u8]) -> Option<*const [u8]>;
}
impl BakeSourceProvider {
#[inline]
pub fn get_source_slice(&self) -> bun_core::String {
unsafe { BakeSourceProvider__getSourceSlice(self) }
}
pub fn to_source_content_ptr(&self) -> SourceContentPtr {
SourceContentPtr::from_bake_provider(self._p.get().cast::<Self>())
}
pub fn get_external_data(&self, source_filename: &[u8]) -> Option<&[u8]> {
let slice = unsafe { __BUN_BAKE_EXTERNAL_SOURCEMAP }(source_filename)?;
Some(unsafe { &*slice })
}
pub fn get_source_map(
&self,
source_filename: &[u8],
load_hint: SourceMapLoadHint,
result: ParseUrlResultHint,
) -> Option<ParseUrl> {
get_source_map_impl(self, source_filename, load_hint, result)
}
}
impl SourceProvider for BakeSourceProvider {
const HAS_EXTERNAL_DATA: bool = true;
fn get_source_slice(&self) -> bun_core::String {
Self::get_source_slice(self)
}
fn to_source_content_ptr(&self) -> SourceContentPtr {
Self::to_source_content_ptr(self)
}
fn get_external_data(&self, source_filename: &[u8]) -> Option<&[u8]> {
Self::get_external_data(self, source_filename)
}
}
#[derive(Default, Clone, Copy)]
pub struct SourceMapState {
pub generated_line: i32,
pub generated_column: i32,
pub source_index: i32,
pub original_line: i32,
pub original_column: i32,
}
#[derive(Default)]
pub struct SourceMap {
pub sources: Vec<Box<[u8]>>,
pub sources_content: Vec<Box<[u8]>>,
pub mapping: mapping::List,
}
impl SourceMap {
pub fn find(&self, line: Ordinal, column: Ordinal) -> Option<Mapping> {
self.mapping.find(line, column)
}
}
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum SourceContentHandling {
NoSourceContents,
SourceContents,
}
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum SourceMapLoadHint {
#[default]
None,
IsInlineMap,
IsExternalMap,
}
#[derive(Clone, Copy)]
pub enum ParseUrlResultHint {
MappingsOnly,
SourceOnly(u32),
All {
line: i32,
column: i32,
include_names: bool,
},
}
#[derive(Default)]
pub struct ParseUrl {
pub map: Option<std::sync::Arc<ParsedSourceMap>>,
pub mapping: Option<Mapping>,
pub source_contents: Option<Box<[u8]>>,
}
pub type ParseResult = core::result::Result<ParsedSourceMap, ParseFail>;
pub struct ParseFail {
pub loc: bun_ast::Loc,
pub err: bun_core::Error,
}
#[derive(Clone, Copy)]
pub struct LineColumnOffset {
pub lines: Ordinal,
pub columns: Ordinal,
}
impl Default for LineColumnOffset {
#[inline]
fn default() -> Self {
Self {
lines: Ordinal::START,
columns: Ordinal::START,
}
}
}
impl LineColumnOffset {
pub fn add(&mut self, b: LineColumnOffset) {
if b.lines.zero_based() == 0 {
self.columns = self.columns.add(b.columns);
} else {
self.lines = self.lines.add(b.lines);
self.columns = b.columns;
}
}
pub fn comes_before(a: LineColumnOffset, b: LineColumnOffset) -> bool {
a.lines.zero_based() < b.lines.zero_based()
|| (a.lines.zero_based() == b.lines.zero_based()
&& a.columns.zero_based() < b.columns.zero_based())
}
pub fn cmp(_ctx: (), a: LineColumnOffset, b: LineColumnOffset) -> core::cmp::Ordering {
if a.lines.zero_based() != b.lines.zero_based() {
return a.lines.zero_based().cmp(&b.lines.zero_based());
}
a.columns.zero_based().cmp(&b.columns.zero_based())
}
pub fn advance(&mut self, input: &[u8]) {
let this_ptr = self;
use bun_core::strings;
let mut this = *this_ptr;
let mut offset: u32 = 0;
while let Some(i) = strings::index_of_newline_or_non_ascii(input, offset) {
debug_assert!(i >= offset);
debug_assert!((i as usize) < input.len());
let iter = strings::CodepointIterator::init_offset(input, i as usize);
let mut cursor = strings::Cursor {
i,
..Default::default()
};
let _ = iter.next(&mut cursor);
if cursor.width == 0 {
this.columns = this.columns.add_scalar(1);
offset = i + 1;
continue;
}
offset = i + cursor.width as u32;
match cursor.c {
0x0D | 0x0A | 0x2028 | 0x2029 => {
if cursor.c == 0x0D
&& input.len() > (i as usize) + 1
&& input[(i as usize) + 1] == b'\n'
{
this.columns = this.columns.add_scalar(1);
continue;
}
this.lines = this.lines.add_scalar(1);
this.columns = Ordinal::START;
}
c => {
this.columns = this.columns.add_scalar(if c > 0xFFFF { 2 } else { 1 });
}
}
}
let remain = &input[offset as usize..];
if cfg!(debug_assertions) {
debug_assert!(strings::is_all_ascii(remain));
debug_assert!(strings::index_of_char(remain, b'\n').is_none());
debug_assert!(strings::index_of_char(remain, b'\r').is_none());
}
this.columns = this
.columns
.add_scalar(i32::try_from(remain.len()).expect("int cast"));
*this_ptr = this;
}
}
impl LineColumnOffsetOptional {
pub fn advance(&mut self, input: &[u8]) {
match self {
Self::Null => {}
Self::Value(v) => v.advance(input),
}
}
pub fn reset(&mut self) {
match self {
Self::Null => {}
Self::Value(_) => *self = Self::Value(LineColumnOffset::default()),
}
}
}
#[derive(Clone, Copy)]
pub enum LineColumnOffsetOptional {
Null,
Value(LineColumnOffset),
}
#[derive(Clone, Copy)]
pub struct SourceMapShifts {
pub before: LineColumnOffset,
pub after: LineColumnOffset,
}
#[derive(Default)]
pub struct SourceMapPieces {
pub prefix: Vec<u8>,
pub mappings: Vec<u8>,
pub suffix: Vec<u8>,
}
pub(crate) fn append_mapping_to_buffer(
buffer: &mut bun_core::MutableString,
last_byte: u8,
prev_state: SourceMapState,
current_state: SourceMapState,
) {
let needs_comma = last_byte != 0 && last_byte != b';' && last_byte != b'"';
let vlqs: [VLQ; 4] = [
VLQ::encode(
current_state
.generated_column
.saturating_sub(prev_state.generated_column),
),
VLQ::encode(
current_state
.source_index
.saturating_sub(prev_state.source_index),
),
VLQ::encode(
current_state
.original_line
.saturating_sub(prev_state.original_line),
),
VLQ::encode(
current_state
.original_column
.saturating_sub(prev_state.original_column),
),
];
let total_len =
vlqs[0].len as usize + vlqs[1].len as usize + vlqs[2].len as usize + vlqs[3].len as usize;
let mut writable = buffer
.writable_n_bytes(total_len + needs_comma as usize)
.expect("unreachable");
if needs_comma {
writable[0] = b',';
writable = &mut writable[1..];
}
for item in &vlqs {
let n = item.len as usize;
writable[..n].copy_from_slice(item.slice());
writable = &mut writable[n..];
}
}
#[derive(Default, Clone, Copy)]
pub struct DebugIDFormatter {
pub id: u64,
}
impl core::fmt::Display for DebugIDFormatter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:016X}64756E2164756E21", self.id)
}
}
bun_opaque::opaque_ffi! { pub struct SourceProviderMap; }
unsafe extern "C" {
safe fn ZigSourceProvider__getSourceSlice(this: &SourceProviderMap) -> bun_core::String;
}
impl SourceProviderMap {
pub fn get_source_slice(&self) -> bun_core::String {
ZigSourceProvider__getSourceSlice(self)
}
pub fn to_source_content_ptr(&self) -> SourceContentPtr {
SourceContentPtr::from_provider(self)
}
pub fn get_source_map(
&self,
source_filename: &[u8],
load_hint: SourceMapLoadHint,
result: ParseUrlResultHint,
) -> Option<ParseUrl> {
get_source_map_impl(self, source_filename, load_hint, result)
}
}
impl SourceProvider for SourceProviderMap {
fn get_source_slice(&self) -> bun_core::String {
SourceProviderMap::get_source_slice(self)
}
fn to_source_content_ptr(&self) -> SourceContentPtr {
SourceProviderMap::to_source_content_ptr(self)
}
}
bun_opaque::opaque_ffi! { pub struct DevServerSourceProvider; }
#[repr(C)]
pub struct DevServerSourceMapData {
pub ptr: *const u8,
pub length: usize,
}
unsafe extern "C" {
fn DevServerSourceProvider__getSourceSlice(
this: *const DevServerSourceProvider,
) -> bun_core::String;
fn DevServerSourceProvider__getSourceMapJSON(
this: *const DevServerSourceProvider,
) -> DevServerSourceMapData;
}
impl DevServerSourceProvider {
pub fn get_source_slice(&self) -> bun_core::String {
unsafe { DevServerSourceProvider__getSourceSlice(self) }
}
pub fn get_source_map_json_raw(&self) -> DevServerSourceMapData {
unsafe { DevServerSourceProvider__getSourceMapJSON(self) }
}
pub fn to_source_content_ptr(&self) -> SourceContentPtr {
SourceContentPtr::from_dev_server_provider(self)
}
pub fn get_source_map(
&self,
source_filename: &[u8],
load_hint: SourceMapLoadHint,
result: ParseUrlResultHint,
) -> Option<ParseUrl> {
get_source_map_impl(self, source_filename, load_hint, result)
}
}
impl SourceProvider for DevServerSourceProvider {
const IS_DEV_SERVER: bool = true;
fn get_source_slice(&self) -> bun_core::String {
DevServerSourceProvider::get_source_slice(self)
}
fn to_source_content_ptr(&self) -> SourceContentPtr {
DevServerSourceProvider::to_source_content_ptr(self)
}
fn get_source_map_json(&self) -> Option<&[u8]> {
let d = self.get_source_map_json_raw();
if d.length == 0 {
return None;
}
Some(unsafe { core::slice::from_raw_parts(d.ptr, d.length) })
}
}
pub trait SourceProvider {
fn get_source_slice(&self) -> bun_core::String;
fn to_source_content_ptr(&self) -> SourceContentPtr;
fn get_source_map_json(&self) -> Option<&[u8]> {
None
}
fn get_external_data(&self, _source_filename: &[u8]) -> Option<&[u8]> {
None
}
const IS_DEV_SERVER: bool = false;
const HAS_EXTERNAL_DATA: bool = false;
}
pub fn get_source_map_impl<P: SourceProvider + ?Sized>(
provider: &P,
source_filename: &[u8],
load_hint: SourceMapLoadHint,
result: ParseUrlResultHint,
) -> Option<ParseUrl> {
let arena = bun_alloc::Arena::new();
let (new_load_hint, mut parsed): (SourceMapLoadHint, ParseUrl) = 'parsed: {
let mut inline_err: Option<bun_core::Error> = None;
if load_hint != SourceMapLoadHint::IsExternalMap {
'try_inline: {
let source = provider.get_source_slice();
debug_assert!(source.tag() == bun_core::Tag::ZigString);
let maybe_found_url = if source.is_8bit() {
find_source_mapping_url_u8(source.latin1())
} else {
find_source_mapping_url_u16(source.utf16())
};
let Some(found_url) = maybe_found_url else {
break 'try_inline;
};
match parse_url(&arena, found_url.slice(), result) {
Ok(parsed) => break 'parsed (SourceMapLoadHint::IsInlineMap, parsed),
Err(err) => {
inline_err = Some(err);
break 'try_inline;
}
}
}
}
if load_hint != SourceMapLoadHint::IsInlineMap {
'try_external: {
if P::IS_DEV_SERVER {
let Some(json_slice) = provider.get_source_map_json() else {
break 'try_external;
};
match parse_json(&arena, json_slice, result) {
Ok(parsed) => {
break 'parsed (SourceMapLoadHint::IsExternalMap, parsed);
}
Err(err) => {
bun_core::Output::warn(format_args!(
"Could not decode sourcemap in dev server runtime: {} - {}",
::bstr::BStr::new(source_filename),
::bstr::BStr::new(err.name()),
));
crate::SavedSourceMap::MissingSourceMapNoteInfo::set_seen_invalid(true);
return None;
}
}
}
if P::HAS_EXTERNAL_DATA {
'fallback_to_normal: {
let Some(data) = provider.get_external_data(source_filename) else {
break 'fallback_to_normal;
};
match parse_json(&arena, data, result) {
Ok(parsed) => {
break 'parsed (SourceMapLoadHint::IsExternalMap, parsed);
}
Err(err) => {
bun_core::Output::warn(format_args!(
"Could not decode sourcemap in '{}': {}",
::bstr::BStr::new(source_filename),
::bstr::BStr::new(err.name()),
));
crate::SavedSourceMap::MissingSourceMapNoteInfo::set_seen_invalid(
true,
);
return None;
}
}
}
}
let mut load_path_buf = bun_paths::path_buffer_pool::get();
if source_filename.len() + 5 > load_path_buf.len() {
break 'try_external;
}
load_path_buf[..source_filename.len()].copy_from_slice(source_filename);
load_path_buf[source_filename.len()..source_filename.len() + 4]
.copy_from_slice(b".map");
load_path_buf[source_filename.len() + 4] = 0;
let load_path =
bun_core::ZStr::from_buf(&load_path_buf[..], source_filename.len() + 4);
let data = match bun_sys::File::read_from(bun_core::Fd::cwd(), load_path) {
Ok(data) => data,
Err(_) => break 'try_external,
};
match parse_json(&arena, &data, result) {
Ok(parsed) => break 'parsed (SourceMapLoadHint::IsExternalMap, parsed),
Err(err) => {
bun_core::Output::warn(format_args!(
"Could not decode sourcemap in '{}': {}",
::bstr::BStr::new(source_filename),
::bstr::BStr::new(err.name()),
));
crate::SavedSourceMap::MissingSourceMapNoteInfo::set_seen_invalid(true);
return None;
}
}
}
}
if let Some(err) = inline_err {
bun_core::Output::warn(format_args!(
"Could not decode sourcemap in '{}': {}",
::bstr::BStr::new(source_filename),
::bstr::BStr::new(err.name()),
));
crate::SavedSourceMap::MissingSourceMapNoteInfo::set_seen_invalid(true);
return None;
}
return None;
};
if let Some(ptr) = parsed.map.as_mut() {
if let Some(map) = std::sync::Arc::get_mut(ptr) {
map.underlying_provider = provider.to_source_content_ptr();
map.underlying_provider.set_load_hint(new_load_hint);
} else {
debug_assert!(false, "ParsedSourceMap Arc should be unique here");
}
}
Some(parsed)
}
#[allow(non_snake_case)]
pub mod SavedSourceMap {
pub mod MissingSourceMapNoteInfo {
use core::sync::atomic::{AtomicBool, Ordering};
static SEEN_INVALID: AtomicBool = AtomicBool::new(false);
static PATH: bun_core::Mutex<Option<Box<[u8]>>> = bun_core::Mutex::new(None);
#[inline]
pub fn set_seen_invalid(v: bool) {
SEEN_INVALID.store(v, Ordering::Relaxed);
}
#[inline]
pub fn seen_invalid() -> bool {
SEEN_INVALID.load(Ordering::Relaxed)
}
pub fn set_path(path: &[u8]) {
*PATH.lock() = Some(path.to_vec().into_boxed_slice());
}
pub fn print() {
if SEEN_INVALID.load(Ordering::Relaxed) {
return;
}
if let Some(note) = PATH.lock().as_deref() {
bun_core::Output::note(format_args!(
"missing sourcemaps for {}",
::bstr::BStr::new(note),
));
bun_core::Output::note(
"consider bundling with '--sourcemap' to get unminified traces",
);
}
}
}
}
#[allow(non_snake_case)]
pub mod SerializedSourceMap {
use bun_core::StringPointer;
use core::mem::size_of;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Header {
pub source_files_count: u32,
pub map_bytes_length: u32,
}
#[derive(Clone, Copy)]
pub struct SerializedSourceMap {
pub bytes: &'static [u8],
}
impl SerializedSourceMap {
#[inline]
pub(crate) fn header(self) -> Header {
unsafe { core::ptr::read_unaligned(self.bytes.as_ptr().cast::<Header>()) }
}
fn compressed_source_file_at(self, index: usize) -> StringPointer {
let count = self.header().source_files_count as usize;
unsafe {
core::ptr::read_unaligned(
self.bytes
.as_ptr()
.add(size_of::<Header>() + (count + index) * size_of::<StringPointer>())
.cast::<StringPointer>(),
)
}
}
}
pub struct Loaded {
pub map: SerializedSourceMap,
pub decompressed_files: Box<[Option<Vec<u8>>]>,
}
impl Loaded {
pub(crate) fn source_file_contents(&mut self, index: usize) -> Option<&[u8]> {
if self.decompressed_files[index].is_none() {
let sp = self.map.compressed_source_file_at(index);
let compressed_file = sp.slice(self.map.bytes);
let size = bun_zstd::get_decompressed_size(compressed_file);
let mut bytes = vec![0u8; size];
self.decompressed_files[index] =
Some(match bun_zstd::decompress(&mut bytes, compressed_file) {
bun_zstd::Result::Err(err) => {
bun_core::Output::warn(format_args!(
"Source map decompression error: {}",
::bstr::BStr::new(err.as_bytes()),
));
Vec::new()
}
bun_zstd::Result::Success(n) => {
bytes.truncate(n);
bytes
}
});
}
let decompressed = self.decompressed_files[index].as_deref().unwrap();
if decompressed.is_empty() {
None
} else {
Some(decompressed)
}
}
}
}
impl SourceMapPieces {
pub fn init() -> SourceMapPieces {
SourceMapPieces::default()
}
pub fn has_content(&self) -> bool {
(self.prefix.len() + self.mappings.len() + self.suffix.len()) > 0
}
pub fn finalize(
&mut self,
shifts_: &[SourceMapShifts],
) -> Result<Box<[u8]>, bun_alloc::AllocError> {
if self.mappings.is_empty() && self.suffix.is_empty() {
debug_assert!(self.prefix.first() == Some(&b'{')); return Ok(core::mem::take(&mut self.prefix).into_boxed_slice());
}
let mut shifts = shifts_;
let mut start_of_run: usize = 0;
let mut current: usize = 0;
let mut generated = LineColumnOffset::default();
let mut prev_shift_column_delta: i32 = 0;
let mut j = bun_core::string_joiner::StringJoiner::default();
j.push_static(&self.prefix);
let mappings = &self.mappings;
while current < mappings.len() {
if mappings[current] == b';' {
generated.lines = generated.lines.add_scalar(1);
generated.columns = Ordinal::START;
prev_shift_column_delta = 0;
current += 1;
continue;
}
let potential_end_of_run = current;
let decode_result = decode_vlq(mappings, current);
generated.columns = generated.columns.add_scalar(decode_result.value);
current = decode_result.start;
let potential_start_of_run = current;
current = decode_vlq_assume_valid(mappings, current).start;
current = decode_vlq_assume_valid(mappings, current).start;
current = decode_vlq_assume_valid(mappings, current).start;
if current < mappings.len() {
let c = mappings[current];
if c != b',' && c != b';' {
current = decode_vlq_assume_valid(mappings, current).start;
}
}
if current < mappings.len() && mappings[current] == b',' {
current += 1;
}
let mut did_cross_boundary = false;
if shifts.len() > 1 && LineColumnOffset::comes_before(shifts[1].before, generated) {
shifts = &shifts[1..];
did_cross_boundary = true;
}
if !did_cross_boundary {
continue;
}
let shift = shifts[0];
if shift.after.lines.zero_based() != generated.lines.zero_based() {
continue;
}
j.push_static(&mappings[start_of_run..potential_end_of_run]);
debug_assert!(shift.before.lines.zero_based() == shift.after.lines.zero_based());
let shift_column_delta =
shift.after.columns.zero_based() - shift.before.columns.zero_based();
let vlq_value = decode_result.value + shift_column_delta - prev_shift_column_delta;
let encode = VLQ::encode(vlq_value);
j.push_cloned(encode.slice());
prev_shift_column_delta = shift_column_delta;
start_of_run = potential_start_of_run;
}
j.push_static(&mappings[start_of_run..]);
let str = j.done_with_end(&self.suffix)?;
debug_assert!(str[0] == b'{'); Ok(str)
}
}
pub fn parse_url(
arena: &bun_alloc::Arena,
source: &[u8],
hint: ParseUrlResultHint,
) -> Result<ParseUrl, bun_core::Error> {
let json_bytes: &[u8] = 'json_bytes: {
const DATA_PREFIX: &[u8] = b"data:application/json";
'try_data_url: {
if source.starts_with(DATA_PREFIX) && source.len() > DATA_PREFIX.len() + 1 {
match source[DATA_PREFIX.len()] {
b';' => {
let after = &source[DATA_PREFIX.len() + 1..];
let Some(comma) = after.iter().position(|&b| b == b',') else {
break 'try_data_url;
};
if &after[..comma] != b"base64" {
break 'try_data_url;
}
let base64_data = &after[comma + 1..];
let len = bun_base64::decode_len(base64_data);
let bytes = arena.alloc_slice_fill_default::<u8>(len);
let decoded = bun_base64::decode(bytes, base64_data);
if !decoded.is_successful() {
return Err(bun_core::err!("InvalidBase64"));
}
break 'json_bytes &bytes[..decoded.count];
}
b',' => break 'json_bytes &source[DATA_PREFIX.len() + 1..],
_ => break 'try_data_url,
}
}
}
return Err(bun_core::err!("UnsupportedFormat"));
};
parse_json(arena, json_bytes, hint)
}
pub fn parse_json(
arena: &bun_alloc::Arena,
source: &[u8],
hint: ParseUrlResultHint,
) -> Result<ParseUrl, bun_core::Error> {
use crate::mapping::SourceMap as SourceMapLog;
use bun_ast::StoreResetGuard as DataStoreScope;
use std::sync::Arc;
let json_src = bun_ast::Source::init_path_string_owned("sourcemap.json", source.to_vec());
let mut log = bun_ast::Log::init();
let _store_scope = DataStoreScope::new();
bun_core::scoped_log!(SourceMapLog, "parse (JSON, {} bytes)", source.len());
let json = match bun_parsers::json::parse::<false>(&json_src, &mut log, arena) {
Ok(j) => j,
Err(_) => return Err(bun_core::err!("InvalidJSON")),
};
if let Some(version) = json.get(b"version") {
match version.data.as_e_number() {
Some(n) if n.value == 3.0 => {}
_ => return Err(bun_core::err!("UnsupportedVersion")),
}
}
let Some(mappings_str) = json.get(b"mappings") else {
return Err(bun_core::err!("UnsupportedVersion"));
};
if !mappings_str.data.is_e_string() {
return Err(bun_core::err!("InvalidSourceMap"));
}
let sources_content = match json
.get(b"sourcesContent")
.ok_or_else(|| bun_core::err!("InvalidSourceMap"))?
.data
.as_e_array()
{
Some(arr) => arr,
None => return Err(bun_core::err!("InvalidSourceMap")),
};
let sources_paths = match json
.get(b"sources")
.ok_or_else(|| bun_core::err!("InvalidSourceMap"))?
.data
.as_e_array()
{
Some(arr) => arr,
None => return Err(bun_core::err!("InvalidSourceMap")),
};
if sources_content.items.len_u32() != sources_paths.items.len_u32() {
return Err(bun_core::err!("InvalidSourceMap"));
}
let source_only = matches!(hint, ParseUrlResultHint::SourceOnly(_));
let source_paths_slice: Option<Vec<Box<[u8]>>> = if !source_only {
let mut v: Vec<Box<[u8]>> = Vec::with_capacity(sources_content.items.len_u32() as usize);
for item in sources_paths.items.slice() {
let Some(s) = item.data.as_e_string() else {
return Err(bun_core::err!("InvalidSourceMap"));
};
let s = s.string(arena)?;
v.push(Box::<[u8]>::from(s));
}
Some(v)
} else {
None
};
let map: Option<Arc<ParsedSourceMap>> = if !source_only {
let mut map_data = match mapping::parse(
mappings_str.data.as_e_string().unwrap().slice(arena),
None,
i32::MAX,
i32::MAX as usize,
mapping::ParseOptions {
allow_names: matches!(
hint,
ParseUrlResultHint::All {
include_names: true,
..
}
),
sort: true,
},
) {
Ok(x) => x,
Err(fail) => return Err(fail.err),
};
if let ParseUrlResultHint::All {
include_names: true,
..
} = hint
{
if matches!(map_data.mappings.r#impl, mapping::ListValue::WithNames(_)) {
if let Some(names) = json.get(b"names") {
if let Some(arr) = names.data.as_e_array() {
let mut names_list: Vec<bun_semver::String> =
Vec::with_capacity(arr.items.len_u32() as usize);
let mut names_buffer: Vec<u8> = Vec::new();
for item in arr.items.slice() {
let Some(estr) = item.data.as_e_string() else {
return Err(bun_core::err!("InvalidSourceMap"));
};
let str = estr.string(arena)?;
names_list.push(bun_semver::String::init_append_if_needed(
&mut names_buffer,
str,
)?);
}
map_data.mappings.names = names_list.into_boxed_slice();
map_data.mappings.names_buffer = names_buffer;
}
}
}
}
let mut psm = map_data;
psm.external_source_names = source_paths_slice.unwrap();
Some(Arc::new(psm))
} else {
None
};
let (found_mapping, source_index): (Option<Mapping>, Option<u32>) = match hint {
ParseUrlResultHint::SourceOnly(index) => (None, Some(index)),
ParseUrlResultHint::All { line, column, .. } => 'brk: {
let Some(m) = map.as_ref().unwrap().find_mapping(
Ordinal::from_zero_based(line),
Ordinal::from_zero_based(column),
) else {
break 'brk (None, None);
};
let idx = u32::try_from(m.source_index).ok();
(Some(m), idx)
}
ParseUrlResultHint::MappingsOnly => (None, None),
};
let content_slice: Option<Box<[u8]>> = match source_index {
Some(idx)
if !matches!(hint, ParseUrlResultHint::MappingsOnly)
&& (idx as usize) < sources_content.items.len_u32() as usize =>
'content: {
let item = &sources_content.items.slice()[idx as usize];
let Some(estr) = item.data.as_e_string() else {
break 'content None;
};
let str = estr.string(arena).expect("OOM");
if str.is_empty() {
break 'content None;
}
Some(Box::<[u8]>::from(str))
}
_ => None,
};
Ok(ParseUrl {
map,
mapping: found_mapping,
source_contents: content_slice,
})
}
pub fn append_source_map_chunk<'a>(
j: &mut bun_core::string_joiner::StringJoiner<'a>,
prev_end_state_: SourceMapState,
start_state_: SourceMapState,
source_map_: &'a [u8],
) -> Result<(), bun_core::Error> {
let mut prev_end_state = prev_end_state_;
let mut start_state = start_state_;
if start_state.generated_line != 0 {
j.push_owned(bun_core::strings::repeating_alloc(
usize::try_from(start_state.generated_line).expect("int cast"),
b';',
)?);
prev_end_state.generated_column = 0;
}
let mut source_map = source_map_;
if let Some(semicolons) = bun_core::strings::index_of_not_char(source_map, b';') {
let semicolons = semicolons as usize;
if semicolons > 0 {
j.push_static(&source_map[..semicolons]);
source_map = &source_map[semicolons..];
prev_end_state.generated_column = 0;
start_state.generated_column = 0;
}
}
let mut i: usize = 0;
let generated_column = decode_vlq_assume_valid(source_map, i);
i = generated_column.start;
let source_index = decode_vlq_assume_valid(source_map, i);
i = source_index.start;
let original_line = decode_vlq_assume_valid(source_map, i);
i = original_line.start;
let original_column = decode_vlq_assume_valid(source_map, i);
i = original_column.start;
source_map = &source_map[i..];
start_state.source_index += source_index.value;
start_state.generated_column += generated_column.value;
start_state.original_line += original_line.value;
start_state.original_column += original_column.value;
let mut str = bun_core::MutableString::init_empty();
append_mapping_to_buffer(&mut str, j.last_byte(), prev_end_state, start_state);
j.push_owned(str.to_owned_slice());
j.push_static(source_map);
Ok(())
}
fn find_source_mapping_url_u8(source: &[u8]) -> Option<bun_core::zig_string::Slice> {
const NEEDLE: &[u8] = b"\n//# sourceMappingURL=";
let found = bun_core::strings::last_index_of(source, NEEDLE)?;
let start = found + NEEDLE.len();
let end = source[start..]
.iter()
.position(|&b| b == b'\n')
.map(|p| start + p)
.unwrap_or(source.len());
let url = bun_core::strings::trim_right(&source[start..end], b" \r");
Some(bun_core::zig_string::Slice::from_utf8_never_free(url))
}
fn find_source_mapping_url_u16(source: &[u16]) -> Option<bun_core::zig_string::Slice> {
let needle: &[u16] = bun_core::w!("\n//# sourceMappingURL=");
let found = bun_core::strings::last_index_of_t(source, needle)?;
let start = found + needle.len();
let end = source[start..]
.iter()
.position(|&c| c == b'\n' as u16)
.map(|p| start + p)
.unwrap_or(source.len());
let mut url = &source[start..end];
while let Some(&last) = url.last() {
if last == b' ' as u16 || last == b'\r' as u16 {
url = &url[..url.len() - 1];
} else {
break;
}
}
Some(bun_core::zig_string::Slice::init_owned(
bun_core::strings::to_utf8_alloc(url),
))
}
pub fn append_source_mapping_url_remote<W: bun_io::Write + ?Sized>(
origin: &bun_url::URL<'_>,
source: &bun_ast::Source,
asset_prefix_path: &[u8],
writer: &mut W,
) -> bun_io::Result<()> {
writer.write_all(b"\n//# sourceMappingURL=")?;
writer.write_all(bun_core::strings::without_trailing_slash(origin.href))?;
if !asset_prefix_path.is_empty() {
writer.write_all(asset_prefix_path)?;
}
if !source.path.pretty.is_empty() && source.path.pretty[0] != b'/' {
writer.write_all(b"/")?;
}
writer.write_all(source.path.pretty)?;
writer.write_all(b".map")?;
Ok(())
}