From 6e1aa5f6c4ac45b24f1784be90a8fceafb69f963 Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Fri, 31 Jan 2025 09:38:45 +1100 Subject: [PATCH 1/9] Add asset_graph.has_edge --- crates/atlaspack_core/src/asset_graph/asset_graph.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/atlaspack_core/src/asset_graph/asset_graph.rs b/crates/atlaspack_core/src/asset_graph/asset_graph.rs index a7179e6812..53cd88abb0 100644 --- a/crates/atlaspack_core/src/asset_graph/asset_graph.rs +++ b/crates/atlaspack_core/src/asset_graph/asset_graph.rs @@ -175,7 +175,6 @@ impl AssetGraph { pub fn add_entry_dependency(&mut self, dependency: Dependency) -> NodeIndex { let is_library = dependency.env.is_library; let dependency_idx = self.add_dependency(dependency); - self.add_edge(&self.root_node_index.clone(), &dependency_idx); if is_library { if let Some(dependency_node) = self.get_dependency_node_mut(&dependency_idx) { @@ -186,6 +185,10 @@ impl AssetGraph { dependency_idx } + pub fn has_edge(&mut self, from_idx: &NodeIndex, to_idx: &NodeIndex) -> bool { + self.graph.contains_edge(*from_idx, *to_idx) + } + pub fn add_edge(&mut self, from_idx: &NodeIndex, to_idx: &NodeIndex) { self.graph.add_edge(*from_idx, *to_idx, ()); } From 2886019913857e0dea17d71c3a7b5b4c66dd3d57 Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Fri, 31 Jan 2025 09:50:18 +1100 Subject: [PATCH 2/9] Add entries to graph after traversal in sorter order --- .../src/requests/asset_graph_request.rs | 31 ++++++++++++++----- .../src/atlaspack/serialize_asset_graph.rs | 1 + 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/crates/atlaspack/src/requests/asset_graph_request.rs b/crates/atlaspack/src/requests/asset_graph_request.rs index cf0e1dfe3e..82db4a6ab9 100644 --- a/crates/atlaspack/src/requests/asset_graph_request.rs +++ b/crates/atlaspack/src/requests/asset_graph_request.rs @@ -55,6 +55,7 @@ struct AssetGraphBuilder { receiver: ResultReceiver, asset_request_to_asset_idx: HashMap, waiting_asset_requests: HashMap>, + entry_dependencies: Vec<(String, NodeIndex)>, } impl AssetGraphBuilder { @@ -71,6 +72,7 @@ impl AssetGraphBuilder { receiver, asset_request_to_asset_idx: HashMap::new(), waiting_asset_requests: HashMap::new(), + entry_dependencies: Vec::new(), } } @@ -131,6 +133,15 @@ impl AssetGraphBuilder { } } + // Connect the entries to the root node in the graph. We do this in + // alphabetical order so it's consistent between builds. + self + .entry_dependencies + .sort_by_key(|(entry, _)| entry.clone()); + for (_, node_index) in self.entry_dependencies.iter() { + self.graph.add_edge(&self.graph.root_node(), node_index); + } + Ok(ResultAndInvalidations { result: RequestResult::AssetGraph(AssetGraphRequestOutput { graph: self.graph }), invalidations: vec![], @@ -192,10 +203,12 @@ impl AssetGraphBuilder { .request_context .queue_request(asset_request, self.sender.clone()); } else if let Some(asset_node_index) = self.asset_request_to_asset_idx.get(&id) { - // We have already completed this AssetRequest so we can connect the - // Dependency to the Asset immediately - self.graph.add_edge(&dependency_idx, asset_node_index); - self.propagate_requested_symbols(*asset_node_index, dependency_idx); + if !self.graph.has_edge(&dependency_idx, asset_node_index) { + // We have already completed this AssetRequest so we can connect the + // Dependency to the Asset immediately + self.graph.add_edge(&dependency_idx, asset_node_index); + self.propagate_requested_symbols(*asset_node_index, dependency_idx); + } } else { // The AssetRequest has already been kicked off but is yet to // complete. Register this Dependency to be connected once it @@ -282,8 +295,10 @@ impl AssetGraphBuilder { // for this AssetNode to be created if let Some(waiting) = self.waiting_asset_requests.remove(&request_id) { for dep in waiting { - self.graph.add_edge(&dep, &asset_idx); - self.propagate_requested_symbols(asset_idx, dep); + if !self.graph.has_edge(&dep, &asset_idx) { + self.graph.add_edge(&dep, &asset_idx); + self.propagate_requested_symbols(asset_idx, dep); + } } } } @@ -405,10 +420,12 @@ impl AssetGraphBuilder { for target in targets { let entry = diff_paths(&entry, &self.request_context.project_root).unwrap_or_else(|| entry.clone()); + let entry = entry.to_str().unwrap().to_string(); - let dependency = Dependency::entry(entry.to_str().unwrap().to_string(), target); + let dependency = Dependency::entry(entry.clone(), target); let dep_node = self.graph.add_entry_dependency(dependency.clone()); + self.entry_dependencies.push((entry, dep_node)); let request = PathRequest { dependency: Arc::new(dependency), diff --git a/crates/node-bindings/src/atlaspack/serialize_asset_graph.rs b/crates/node-bindings/src/atlaspack/serialize_asset_graph.rs index 3afe94c5c2..2148472334 100644 --- a/crates/node-bindings/src/atlaspack/serialize_asset_graph.rs +++ b/crates/node-bindings/src/atlaspack/serialize_asset_graph.rs @@ -12,6 +12,7 @@ use atlaspack_core::types::{Asset, Dependency}; /// nodes: Array, /// } /// ``` +#[tracing::instrument(level = "info", skip_all)] pub fn serialize_asset_graph(env: &Env, asset_graph: &AssetGraph) -> anyhow::Result { // Serialize graph nodes in parallel let nodes = asset_graph From 5662284a1b3641fb716a461e1a40929bf11928bf Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Fri, 31 Jan 2025 11:04:31 +1100 Subject: [PATCH 3/9] Add determinism test --- .../core/integration-tests/test/bundler.js | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/packages/core/integration-tests/test/bundler.js b/packages/core/integration-tests/test/bundler.js index 99f217e103..8238065302 100644 --- a/packages/core/integration-tests/test/bundler.js +++ b/packages/core/integration-tests/test/bundler.js @@ -2341,4 +2341,60 @@ describe('bundler', function () { await run(splitBundle); }, ); + + it('should produce deterministic builds with multiple entries', async () => { + await fsFixture(overlayFS, __dirname)` + deterministic-builds + shared-one.js: + export const one = 'one'; + shared-two.js: + export const two = 'two'; + one.js: + import {one} from './shared-one'; + import {two} from './shared-two'; + sideEffectNoop(one + two); + entry-one.html: +