Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion src/bundler/Graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::BundledAst as JSAst;
use bun_alloc::Arena as ThreadLocalArena;
use bun_alloc::{AstAlloc, AstVec};
use bun_ast::server_component_boundary;
use bun_collections::MultiArrayList;
use bun_collections::{DynamicBitSetUnmanaged, MultiArrayList, index_sort};
use enum_map::EnumMap;

use crate::IndexStringMap::IndexStringMap;
Expand Down Expand Up @@ -99,6 +99,16 @@ pub struct HtmlImports {
pub(crate) server_source_indices: Vec<IndexInt>,
/// Source index of the HTML file being imported
pub(crate) html_source_indices: Vec<IndexInt>,
/// The browser files that the scan added to `entry_points` for these imports, in the order it found them in.
pub(crate) pages: Vec<IndexInt>,
}

/// See [`Graph::server_imported_pages`].
pub(crate) struct ServerImportedPages {
/// For [`HtmlImports::html_source_indices`].
pub(crate) html_source_indices: Vec<IndexInt>,
/// [`HtmlImports::pages`] in import order.
pub(crate) entry_points: Vec<Index>,
}

pub struct InputFile {
Expand Down Expand Up @@ -221,6 +231,55 @@ impl<'a> Graph<'a> {
&mut self.build_graphs[target]
}

/// Takes [`HtmlImports::pages`] out of `entry_points`: `find_reachable_files` adds them again in import order.
pub(crate) fn remove_server_imported_pages_from_entry_points(
&mut self,
) -> Result<(), bun_alloc::AllocError> {
if self.html_imports.pages.is_empty() {
return Ok(());
}
let mut is_page = DynamicBitSetUnmanaged::init_empty(self.input_files.len())?;
for &page in &self.html_imports.pages {
is_page.set(page as usize);
}
self.entry_points
.retain(|entry_point| !is_page.is_set(entry_point.get() as usize));
Ok(())
}

/// [`HtmlImports::pages`] in the order that `reachable` comes to them or to their manifest modules, then by display path.
pub(crate) fn server_imported_pages(&self, reachable: &[Index]) -> ServerImportedPages {
let sources = self.input_files.items_source();
let manifest_modules = self.html_imports.server_source_indices.as_slice();
let browser_files = &self.build_graphs[options::Target::Browser];
let html_source_indices: Vec<IndexInt> = (manifest_modules.iter())
.map(|&manifest_module| {
browser_files
.get(sources[manifest_module as usize].path.text)
.expect("the scan made a browser file for each page that server code imports")
})
.collect();

let mut position = vec![u32::MAX; sources.len()];
for (at, source_index) in reachable.iter().enumerate() {
position[source_index.get() as usize] = at as u32;
}
for (&manifest_module, &page) in manifest_modules.iter().zip(&html_source_indices) {
position[page as usize] =
position[page as usize].min(position[manifest_module as usize]);
}
let mut pages = self.html_imports.pages.clone();
index_sort::sort_indices(&mut pages, &mut |a, b| {
let (a, b) = (a as usize, b as usize);
(position[a].cmp(&position[b]))
Comment thread
robobun marked this conversation as resolved.
Resolved
.then_with(|| sources[a].path.pretty.cmp(sources[b].path.pretty))
});
ServerImportedPages {
html_source_indices,
entry_points: pages.into_iter().map(Index::init).collect(),
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
}

/// Schedule a task to be run on the JS thread which resolves the promise of
/// each `.defer()` called in an onLoad plugin.
///
Expand Down
44 changes: 6 additions & 38 deletions src/bundler/LinkerContext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ impl<'a> LinkerContext<'a> {
///
/// The returned borrow is tied to `&self`. Callers that need to hold a
/// `&Graph` across a `&mut self` borrow (split-borrow patterns — e.g.
/// `process_html_import_files`, TLA-check column caching, or
/// TLA-check column caching, or
/// `generate_isolated_hash`) must continue to deref the raw
/// `self.parse_graph` field directly.
#[inline]
Expand Down Expand Up @@ -728,46 +728,14 @@ impl<'a> LinkerContext<'a> {
}

fn process_html_import_files(&mut self) {
// SAFETY: `parse_graph` is a backref to `BundleV2.graph`, a sibling
// field of `BundleV2.linker` (= `*self`). The two are disjoint, and no
// other `&`/`&mut` to `BundleV2.graph` is live for this scope —
// `self.graph` below is `LinkerGraph`, a distinct allocation.
// Note: go through raw pointers and reborrow per use to avoid holding
// overlapping `&`/`&mut` into `parse_graph.html_imports` and
// `parse_graph.input_files`.
let parse_graph: *mut Graph<'a> = self.parse_graph;
// SAFETY: see above; sole accessor of `html_imports` for this scope.
let server_len = unsafe { (*parse_graph).html_imports.server_source_indices.len() };
let html_imports = &self.parse_graph().html_imports;
let server_len = html_imports.server_source_indices.len();
debug_assert_eq!(html_imports.html_source_indices.len(), server_len);
if server_len > 0 {
let actual_ref = self.graph.runtime_function(b"__jsonParse");

for i in 0..server_len as usize {
// SAFETY: `server_source_indices` is a stable Vec; index
// bounded by `server_len`.
let html_import: u32 =
unsafe { (*parse_graph).html_imports.server_source_indices.slice()[i] };
// SAFETY: `input_files` SoA is append-only; read-only here.
let path_text = unsafe {
&(*parse_graph).input_files.items_source()[html_import as usize]
.path
.text
};
// SAFETY: sole `&mut` into the per-target map for this lookup.
let source_index: u32 = unsafe {
(*parse_graph).path_to_source_index_map(Target::Browser)
}
.get(path_text)
.unwrap_or_else(|| {
panic!("Assertion failed: HTML import file not found in pathToSourceIndexMap");
});

// SAFETY: sole `&mut` into `html_source_indices` for this push.
unsafe {
(*parse_graph)
.html_imports
.html_source_indices
.push(source_index)
};
for i in 0..server_len {
let html_import: u32 = self.parse_graph().html_imports.server_source_indices[i];

// S.LazyExport is a call to __jsonParse. Each accessor returns
// `Option`; `.unwrap()` panics on shape mismatch.
Expand Down
39 changes: 31 additions & 8 deletions src/bundler/bundle_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2353,6 +2353,9 @@ pub mod bv2_impl {
for entry_point in self.graph.entry_points.iter() {
additional_files_needed_outside_css.set(entry_point.get() as usize);
}
// The scan finds pages in an order that changes from build to build, and entry points are numbered by position.
self.graph
.remove_server_imported_pages_from_entry_points()?;
let mut additional_files_imported_by_css_and_inlined =
DynamicBitSetUnmanaged::init_empty(self.graph.input_files.len())?;

Expand Down Expand Up @@ -2401,15 +2404,28 @@ pub mod bv2_impl {
// imported and weird things will happen
visitor.visit::<false>(Index::RUNTIME, false);

if self.transpiler.options.code_splitting {
for entry_point in self.graph.entry_points.iter().copied() {
visitor.visit::<true>(entry_point, false);
}
} else {
for entry_point in self.graph.entry_points.iter().copied() {
visitor.visit::<false>(entry_point, false);
let code_splitting = self.transpiler.options.code_splitting;
let visit_entry_points = |visitor: &mut ReachableFileVisitor,
entry_points: &[Index]| {
for &entry_point in entry_points {
if code_splitting {
visitor.visit::<true>(entry_point, false);
} else {
visitor.visit::<false>(entry_point, false);
}
}
}
};
visit_entry_points(&mut visitor, &self.graph.entry_points);

let html_imports = &self.graph.html_imports;
let pages =
if html_imports.server_source_indices.is_empty() && html_imports.pages.is_empty() {
None
} else {
let pages = self.graph.server_imported_pages(&visitor.reachable);
visit_entry_points(&mut visitor, &pages.entry_points);
Some(pages)
};

if bun_core::env::IS_DEBUG && ReachableFiles.is_visible() {
bun_core::scoped_log!(
Expand Down Expand Up @@ -2461,6 +2477,12 @@ pub mod bv2_impl {
}
}

if let Some(pages) = pages {
debug_assert_eq!(self.graph.pending_items, 0, "the scan is over");
self.graph.html_imports.html_source_indices = pages.html_source_indices;
self.graph.entry_points.extend(pages.entry_points);
Comment thread
robobun marked this conversation as resolved.
Resolved
}

Ok(reachable.into_boxed_slice())
}

Expand Down Expand Up @@ -7462,6 +7484,7 @@ pub mod bv2_impl {
self.graph
.entry_points
.push(bun_ast::Index(new_source_index));
self.graph.html_imports.pages.push(new_source_index);
}

if self.enqueue_on_load_plugin_if_needed(new_task) {
Expand Down
Loading
Loading