@@ -6,7 +6,9 @@ load("//npm/private:pnpm_extension.bzl", "DEFAULT_PNPM_REPO_NAME", "resolve_pnpm
66load ("//npm/private:pnpm_repository.bzl" , "DEFAULT_PNPM_VERSION" , "LATEST_PNPM_VERSION" )
77load ("//npm/private:versions.bzl" , "PNPM_VERSIONS" )
88
9- def _fake_pnpm_tag (version = None , name = DEFAULT_PNPM_REPO_NAME , integrity = None , pnpm_version_from = None , include_npm = False , patches = [], patch_args = ["-p1" ]):
9+ # NB: version defaults to the empty sentinel, mirroring the real tag attribute, so a
10+ # bare _fake_pnpm_tag() is equivalent to the default registration from rules_js itself.
11+ def _fake_pnpm_tag (version = "" , name = DEFAULT_PNPM_REPO_NAME , integrity = None , pnpm_version_from = None , include_npm = False , patches = [], patch_args = ["-p1" ]):
1012 return struct (
1113 name = name ,
1214 pnpm_version = version ,
@@ -129,6 +131,21 @@ def _include_npm(ctx):
129131 ],
130132 )
131133
134+ def _include_npm_other_version_wins (ctx ):
135+ # include_npm is a per-repo setting, not per-version: a version-less tag asking to
136+ # bundle npm must be honored even when another module's explicit version is the one
137+ # selected. Otherwise the npm request would be silently dropped.
138+ return _resolve_test (
139+ ctx ,
140+ repositories = {"pnpm" : {"version" : "9.1.0" , "integrity" : "sha512-Z/WHmRapKT5c8FnCOFPVcb6vT3U8cH9AyyK+1fsVeMaq07bEEHzLO6CzW+AD62IaFkcayDbIe+tT+dVLtGEnJA==" , "include_npm" : True , "patches" : [], "patch_args" : ["-p1" ]}},
141+ modules = [
142+ # Root brings the repo into scope and wants npm, but expresses no version.
143+ _fake_mod (True , _fake_pnpm_tag (include_npm = True )),
144+ # A dependency pins the version that ends up selected.
145+ _fake_mod (False , _fake_pnpm_tag (version = "9.1.0" )),
146+ ],
147+ )
148+
132149def _custom_name (ctx ):
133150 return _resolve_test (
134151 ctx ,
@@ -148,6 +165,85 @@ def _custom_name(ctx):
148165 ],
149166 )
150167
168+ def _dep_version_with_default_registration (ctx ):
169+ # A non-root module explicitly requests a pnpm version while another non-root
170+ # module (rules_js itself, which always registers `pnpm.pnpm(name = "pnpm")`)
171+ # carries the default version.
172+ #
173+ # The rules_js registration comes first in BFS order ("aspect_rules_js" sorts
174+ # before nearly all module names) and must not mask the explicit request,
175+ # even when the explicit request is for a lower version than the default.
176+ # See https://github.com/aspect-build/rules_js/pull/2349#discussion_r3362093839
177+ return _resolve_test (
178+ ctx ,
179+ repositories = {"pnpm" : {"version" : "9.1.0" , "integrity" : "sha512-Z/WHmRapKT5c8FnCOFPVcb6vT3U8cH9AyyK+1fsVeMaq07bEEHzLO6CzW+AD62IaFkcayDbIe+tT+dVLtGEnJA==" , "include_npm" : False , "patches" : [], "patch_args" : ["-p1" ]}},
180+ modules = [
181+ _fake_mod (True ),
182+ # rules_js itself: a bare pnpm.pnpm(name = "pnpm") tag carrying the default version
183+ _fake_mod (False , _fake_pnpm_tag ()),
184+ _fake_mod (False , _fake_pnpm_tag (version = "9.1.0" )),
185+ ],
186+ )
187+
188+ def _dep_versions_mvs (ctx ):
189+ # Multiple non-root modules explicitly request different pnpm versions:
190+ # the highest wins, with a note. The default registration from rules_js
191+ # does not participate in the selection.
192+ return _resolve_test (
193+ ctx ,
194+ repositories = {"pnpm" : {"version" : "9.2.0" , "integrity" : "sha512-mKgP0RwucJZ0d2IwQQZDKz3cZ9z1S1qMAck/aKLNXgXmghhJUioG+3YoTUGiZg1eM08u47vykYO/LnObHa+ncQ==" , "include_npm" : False , "patches" : [], "patch_args" : ["-p1" ]}},
195+ notes = ["""NOTE: repo 'pnpm' has multiple versions ["9.1.0", "9.2.0"]; selected 9.2.0""" ],
196+ modules = [
197+ _fake_mod (True ),
198+ _fake_mod (False , _fake_pnpm_tag ()),
199+ _fake_mod (False , _fake_pnpm_tag (version = "9.1.0" )),
200+ _fake_mod (False , _fake_pnpm_tag (version = "9.2.0" )),
201+ ],
202+ )
203+
204+ def _root_default_dep_version (ctx ):
205+ # The root module registers a bare tag (no version preference, e.g. just to
206+ # bring the repo into scope). A dep's explicit version wins over the default
207+ # version carried by the root's bare tag.
208+ return _resolve_test (
209+ ctx ,
210+ repositories = {"pnpm" : {"version" : "9.1.0" , "integrity" : "sha512-Z/WHmRapKT5c8FnCOFPVcb6vT3U8cH9AyyK+1fsVeMaq07bEEHzLO6CzW+AD62IaFkcayDbIe+tT+dVLtGEnJA==" , "include_npm" : False , "patches" : [], "patch_args" : ["-p1" ]}},
211+ modules = [
212+ _fake_mod (True , _fake_pnpm_tag ()),
213+ _fake_mod (False , _fake_pnpm_tag (version = "9.1.0" )),
214+ ],
215+ )
216+
217+ def _root_explicit_default_beats_dep (ctx ):
218+ # Explicitly requesting the default version is distinct from not requesting a
219+ # version at all: it is a real request from the root module and therefore wins
220+ # over a (higher) version requested by a non-root module.
221+ # See https://github.com/aspect-build/rules_js/pull/2883#discussion_r3367024654
222+ return _resolve_test (
223+ ctx ,
224+ repositories = {"pnpm" : {"version" : DEFAULT_PNPM_VERSION , "integrity" : PNPM_VERSIONS [DEFAULT_PNPM_VERSION ], "include_npm" : False , "patches" : [], "patch_args" : ["-p1" ]}},
225+ modules = [
226+ _fake_mod (True , _fake_pnpm_tag (version = DEFAULT_PNPM_VERSION )),
227+ _fake_mod (False , _fake_pnpm_tag (version = "11.0.9" )),
228+ ],
229+ )
230+
231+ def _root_lower_than_default (ctx ):
232+ # The realistic override case: the root module pins the default "pnpm" repo to
233+ # a version *lower* than DEFAULT_PNPM_VERSION, while rules_js itself carries the
234+ # default registration. The root's explicit (lower) pin must win; a naive
235+ # "Minimal Version Selection across all registrations" would wrongly keep the
236+ # higher default and silently ignore the user's pin.
237+ return _resolve_test (
238+ ctx ,
239+ repositories = {"pnpm" : {"version" : "9.1.0" , "integrity" : "sha512-Z/WHmRapKT5c8FnCOFPVcb6vT3U8cH9AyyK+1fsVeMaq07bEEHzLO6CzW+AD62IaFkcayDbIe+tT+dVLtGEnJA==" , "include_npm" : False , "patches" : [], "patch_args" : ["-p1" ]}},
240+ modules = [
241+ _fake_mod (True , _fake_pnpm_tag (version = "9.1.0" )),
242+ # rules_js itself: a bare pnpm.pnpm(name = "pnpm") tag carrying DEFAULT_PNPM_VERSION
243+ _fake_mod (False , _fake_pnpm_tag ()),
244+ ],
245+ )
246+
151247def _integrity_conflict (ctx ):
152248 # What happens if two modules define the same version with conflicting integrity parameters.
153249 # Currently we print nothing to indicate this, we trust whichever integrity wins.
@@ -228,9 +324,15 @@ def _os_cpu_constraints(ctx):
228324
229325basic_test = unittest .make (_basic )
230326override_test = unittest .make (_override )
327+ dep_version_with_default_registration_test = unittest .make (_dep_version_with_default_registration )
328+ dep_versions_mvs_test = unittest .make (_dep_versions_mvs )
329+ root_default_dep_version_test = unittest .make (_root_default_dep_version )
330+ root_explicit_default_beats_dep_test = unittest .make (_root_explicit_default_beats_dep )
331+ root_lower_than_default_test = unittest .make (_root_lower_than_default )
231332latest_test = unittest .make (_latest )
232333custom_name_test = unittest .make (_custom_name )
233334include_npm_test = unittest .make (_include_npm )
335+ include_npm_other_version_wins_test = unittest .make (_include_npm_other_version_wins )
234336integrity_conflict_test = unittest .make (_integrity_conflict )
235337from_package_json_simple_test = unittest .make (_from_package_json_simple )
236338from_package_json_with_hash_test = unittest .make (_from_package_json_with_hash )
@@ -245,9 +347,15 @@ def pnpm_tests(name):
245347 name ,
246348 basic_test ,
247349 override_test ,
350+ dep_version_with_default_registration_test ,
351+ dep_versions_mvs_test ,
352+ root_default_dep_version_test ,
353+ root_explicit_default_beats_dep_test ,
354+ root_lower_than_default_test ,
248355 latest_test ,
249356 custom_name_test ,
250357 include_npm_test ,
358+ include_npm_other_version_wins_test ,
251359 integrity_conflict_test ,
252360 from_package_json_simple_test ,
253361 from_package_json_with_hash_test ,
0 commit comments