@@ -29,6 +29,7 @@ pub struct PackageGraphBuilder<'a, T> {
2929 package_jsons : Option < HashMap < AbsoluteSystemPathBuf , PackageJson > > ,
3030 lockfile : Option < Box < dyn Lockfile > > ,
3131 package_discovery : T ,
32+ package_manager : Option < PackageManager > ,
3233}
3334
3435#[ derive( Debug , Diagnostic , thiserror:: Error ) ]
@@ -88,6 +89,7 @@ impl<'a> PackageGraphBuilder<'a, LocalPackageDiscoveryBuilder> {
8889 is_single_package : false ,
8990 package_jsons : None ,
9091 lockfile : None ,
92+ package_manager : None ,
9193 }
9294 }
9395
@@ -98,6 +100,7 @@ impl<'a> PackageGraphBuilder<'a, LocalPackageDiscoveryBuilder> {
98100 }
99101
100102 pub fn with_package_manager ( mut self , package_manager : PackageManager ) -> Self {
103+ self . package_manager = Some ( package_manager. clone ( ) ) ;
101104 self . package_discovery
102105 . with_package_manager ( Some ( package_manager) ) ;
103106 self
@@ -137,6 +140,7 @@ impl<'a, P> PackageGraphBuilder<'a, P> {
137140 package_jsons : self . package_jsons ,
138141 lockfile : self . lockfile ,
139142 package_discovery : discovery,
143+ package_manager : self . package_manager ,
140144 }
141145 }
142146}
@@ -149,14 +153,49 @@ where
149153{
150154 /// Build the `PackageGraph`.
151155 #[ tracing:: instrument( skip( self ) ) ]
152- pub async fn build ( self ) -> Result < PackageGraph , Error > {
156+ pub async fn build ( mut self ) -> Result < PackageGraph , Error > {
153157 let is_single_package = self . is_single_package ;
158+
159+ // If no pre-supplied lockfile, start reading it on a blocking thread
160+ // concurrently with package discovery + JSON parsing.
161+ let known_pm = self . package_manager . take ( ) . or_else ( || {
162+ PackageManager :: get_package_manager ( self . repo_root , & self . root_package_json ) . ok ( )
163+ } ) ;
164+ let lockfile_future = if !is_single_package && self . lockfile . is_none ( ) {
165+ if let Some ( pm) = known_pm {
166+ let repo_root = self . repo_root . to_owned ( ) ;
167+ let root_package_json = self . root_package_json . clone ( ) ;
168+ Some ( tokio:: task:: spawn_blocking (
169+ move || -> Option < Box < dyn Lockfile > > {
170+ pm. read_lockfile ( & repo_root, & root_package_json) . ok ( )
171+ } ,
172+ ) )
173+ } else {
174+ None
175+ }
176+ } else {
177+ None
178+ } ;
179+
154180 let state = BuildState :: new ( self ) ?;
155181
156182 match is_single_package {
157183 true => Ok ( state. build_single_package_graph ( ) . await ?) ,
158184 false => {
159185 let state = state. parse_package_jsons ( ) . await ?;
186+
187+ // If we started a lockfile read, collect the result before
188+ // entering resolve_lockfile so it becomes a cache hit.
189+ let state = if let Some ( handle) = lockfile_future {
190+ if let Ok ( Some ( lockfile) ) = handle. await {
191+ state. with_lockfile ( lockfile)
192+ } else {
193+ state
194+ }
195+ } else {
196+ state
197+ } ;
198+
160199 let state = state. resolve_lockfile ( ) . await ?;
161200 Ok ( state. build_inner ( ) . await ?)
162201 }
@@ -220,6 +259,7 @@ where
220259 package_jsons,
221260 lockfile,
222261 package_discovery,
262+ package_manager : _,
223263 } = builder;
224264 let mut workspaces = HashMap :: new ( ) ;
225265 workspaces. insert (
@@ -388,6 +428,11 @@ impl<'a, T: PackageDiscovery> BuildState<'a, ResolvedPackageManager, T> {
388428}
389429
390430impl < ' a , T : PackageDiscovery > BuildState < ' a , ResolvedWorkspaces , T > {
431+ fn with_lockfile ( mut self , lockfile : Box < dyn Lockfile > ) -> Self {
432+ self . lockfile = Some ( lockfile) ;
433+ self
434+ }
435+
391436 #[ tracing:: instrument( skip( self ) ) ]
392437 fn connect_internal_dependencies (
393438 & mut self ,
0 commit comments