@@ -56,7 +56,8 @@ WARNING: `update_pnpm_lock` attribute in `npm_translate_lock(name = "{rctx_name}
5656 _init_patched_dependencies_labels (priv , rctx , attr , label_store )
5757
5858 # May depend on lockfile state
59- _init_root_package (priv , rctx , attr , label_store )
59+ _init_root_package (priv , attr , label_store )
60+ _init_workspace (priv , rctx , attr , label_store , is_windows )
6061
6162 if _should_update_pnpm_lock (priv ):
6263 _init_importer_labels (priv , label_store )
@@ -191,7 +192,7 @@ def _init_external_repository_action_cache(priv, attr):
191192 priv ["external_repository_action_cache" ] = attr .external_repository_action_cache if attr .external_repository_action_cache else utils .default_external_repository_action_cache ()
192193
193194################################################################################
194- def _init_root_package (priv , rctx , attr , label_store ):
195+ def _init_root_package (priv , attr , label_store ):
195196 pnpm_lock_label = label_store .label ("pnpm_lock" )
196197
197198 # use the directory of the pnpm_lock file as the root_package unless overridden by the root_package attribute
@@ -208,19 +209,51 @@ def _init_root_package(priv, rctx, attr, label_store):
208209 fail ("root_package cannot be overridden if there are pnpm workspace packages specified" )
209210 priv ["root_package" ] = attr .root_package
210211
211- # Load a declared root package.json
212+ def _init_workspace (priv , rctx , attr , label_store , is_windows ):
213+ root_package_json = {}
214+
215+ # Load pnpm settings from root package.json for pnpm <= v9.
212216 if label_store .has ("package_json_root" ):
217+ # Load a declared root package.json
213218 root_package_json_path = label_store .path ("package_json_root" )
214- priv [ " root_package_json" ] = json .decode (rctx .read (root_package_json_path ))
219+ root_package_json = json .decode (rctx .read (root_package_json_path ))
215220 elif "." in priv ["importers" ].keys ():
216221 # Load an undeclared package.json derived from the root importer in the lockfile
217222 label_store .add_sibling ("lock" , "package_json_root" , PACKAGE_JSON_FILENAME )
218223 root_package_json_path = label_store .path ("package_json_root" )
219- priv ["root_package_json" ] = json .decode (rctx .read (root_package_json_path ))
220- else :
221- # if there is no root importer that means there is no root package.json to read; pnpm allows
222- # you to just have a pnpm-workspaces.yaml at the root and no package.json at that location
223- priv ["root_package_json" ] = {}
224+ root_package_json = json .decode (rctx .read (root_package_json_path ))
225+
226+ priv ["pnpm_settings" ] = root_package_json .get ("pnpm" , {})
227+
228+ # Read settings from pnpm-workspace.yaml for pnpm v10+ (NOTE: pnpm 9-10+ has lockfile version 9).
229+ # Support scenario where pnpm-lock.yaml was never parsed and "lock_version" is not set.
230+ if priv .get ("lock_version" , 0 ) >= 9.0 and label_store .has ("pnpm_workspace" ):
231+ pnpm_workspace_path = label_store .path ("pnpm_workspace" )
232+ if is_windows or utils .exists (rctx , pnpm_workspace_path ):
233+ pnpm_workspace_json , workspace_parse_err = _yaml_to_json (rctx , attr , str (pnpm_workspace_path ), is_windows )
234+
235+ if workspace_parse_err == None :
236+ pnpm_workspace_settings , workspace_parse_err = pnpm .parse_pnpm_workspace_json (pnpm_workspace_json )
237+
238+ if pnpm_workspace_settings :
239+ priv ["pnpm_settings" ] = priv ["pnpm_settings" ] | pnpm_workspace_settings
240+
241+ if workspace_parse_err != None :
242+ should_update = _should_update_pnpm_lock (priv )
243+ msg = """
244+ {type}: pnpm-workspace.yaml parse error {error}`.
245+ """ .format (type = "WARNING" if should_update else "ERROR" , error = workspace_parse_err )
246+
247+ if should_update :
248+ # buildifier: disable=print
249+ print (msg )
250+ else :
251+ fail (msg )
252+
253+ # Default to empty settings if none found such as no pnpm-workspace.yaml or no package.json alongside the lockfile
254+ # TODO(3.0): pnpm-workspace should always be available
255+ if priv ["pnpm_settings" ] == None :
256+ priv ["pnpm_settings" ] = {}
224257
225258################################################################################
226259def _init_npmrc (priv , rctx , attr , label_store , is_windows ):
@@ -473,24 +506,33 @@ WARNING: Cannot determine home directory in order to load home `.npmrc` file in
473506 _load_npmrc (priv , rctx , home_npmrc_path )
474507
475508################################################################################
476- def _load_lockfile (priv , rctx , attr , pnpm_lock_path , is_windows ):
477- importers = {}
478- packages = {}
479- patched_dependencies = {}
480- lock_version = None
481- lock_parse_err = None
482-
509+ def _yaml_to_json (rctx , attr , yaml_path , is_windows ):
483510 host_yq = Label ("@{}_{}//:yq{}" .format (attr .yq_toolchain_prefix , repo_utils .platform (rctx ), ".exe" if is_windows else "" ))
484511 yq_args = [
485512 str (rctx .path (host_yq )),
486- str (pnpm_lock_path ),
513+ str (yaml_path ),
487514 "-o=json" ,
488515 ]
489516 result = rctx .execute (yq_args )
490517 if result .return_code :
491- lock_parse_err = "failed to parse pnpm lock file with yq. '{}' exited with {}: \n STDOUT:\n {}\n STDERR:\n {}" .format (" " .join (yq_args ), result .return_code , result .stdout , result .stderr )
492- else :
493- importers , packages , patched_dependencies , lock_version , lock_parse_err = pnpm .parse_pnpm_lock_json (result .stdout if result .stdout != "null" else None ) # NB: yq will return the string "null" if the yaml file is empty
518+ return None , "failed to parse {} with yq. '{}' exited with {}: \n STDOUT:\n {}\n STDERR:\n {}" .format (" " .join (yq_args ), yaml_path , result .return_code , result .stdout , result .stderr )
519+
520+ # NB: yq will return the string "null" if the yaml file is empty
521+ if result .stdout != "null" :
522+ return result .stdout , None
523+
524+ return None , None
525+
526+ def _load_lockfile (priv , rctx , attr , pnpm_lock_path , is_windows ):
527+ importers = {}
528+ packages = {}
529+ patched_dependencies = {}
530+ lock_version = None
531+ lock_parse_err = None
532+
533+ lockfile_content , lock_parse_err = _yaml_to_json (rctx , attr , str (pnpm_lock_path ), is_windows )
534+ if lock_parse_err == None :
535+ importers , packages , patched_dependencies , lock_version , lock_parse_err = pnpm .parse_pnpm_lock_json (lockfile_content )
494536
495537 priv ["lock_version" ] = lock_version
496538 priv ["importers" ] = importers
@@ -538,7 +580,7 @@ def _patched_dependencies(priv):
538580 return priv ["patched_dependencies" ]
539581
540582def _only_built_dependencies (priv ):
541- return _root_package_json (priv ). get ( "pnpm" , {} ).get ("onlyBuiltDependencies" , None )
583+ return _pnpm_settings (priv ).get ("onlyBuiltDependencies" , None )
542584
543585def _num_patches (priv ):
544586 return priv ["num_patches" ]
@@ -552,8 +594,8 @@ def _npm_auth(priv):
552594def _root_package (priv ):
553595 return priv ["root_package" ]
554596
555- def _root_package_json (priv ):
556- return priv ["root_package_json " ]
597+ def _pnpm_settings (priv ):
598+ return priv ["pnpm_settings " ]
557599
558600################################################################################
559601def _new (rctx_name , rctx , attr , bzlmod ):
@@ -576,7 +618,7 @@ def _new(rctx_name, rctx, attr, bzlmod):
576618 "npm_registries" : {},
577619 "packages" : {},
578620 "root_package" : attr .root_package ,
579- "root_package_json " : {},
621+ "pnpm_settings " : {},
580622 "patched_dependencies" : {},
581623 "should_update_pnpm_lock" : should_update_pnpm_lock ,
582624 }
0 commit comments