fix(engine-pm): record a satisfied range pin in the lockfile (#13932)
* fix(engine-pm): record a satisfied range pin in the lockfile The comment in pre_command says a pin the running pnpm already satisfies still has to reach the lockfile — the switch would have written it on the way to the wanted version — but package_manager_to_sync could only produce a version for an exact pin (or a source checkout), so a range pin like `^12.0.0-rc.6` written by hand, or by any tool other than `pnpm add` / `pnpm self-update`, never landed in packageManagerDependencies and the project went without the shared resolution the pin exists to provide. When the running pnpm satisfies the range, its version is the one the project actually uses, so that is what gets recorded. An exact pin still records itself, and a source checkout still answers first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: keep the range-pin comments to what the code cannot say The comment recorded how the code used to behave and the test's doc repeated its own name. What is left is the part neither the name nor the body carries: why the range is built from PNPM_VERSION. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d278429ba9
commit
12ea6ca0bb
3 files changed
+54
-4
No files matched your search
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"pacquet": patch
|
||||
---
|
||||
|
||||
A `devEngines.packageManager` range pin on pnpm is now recorded in `pnpm-lock.yaml`'s `packageManagerDependencies` when the running pnpm already satisfies it, using the running version. Previously only an exact pin — or a range resolved on the way through a version switch — reached the lockfile, so a range pin written by hand (or by any tool other than `pnpm add` / `pnpm self-update`) left the project without the shared resolution the pin exists to provide.
|
||||
@@ -1,5 +1,5 @@
|
||||
use miette::IntoDiagnostic;
|
||||
use pnpm_config::PmOnFail;
|
||||
use pnpm_config::{PNPM_VERSION, PmOnFail};
|
||||
use pnpm_env_installer::is_package_manager_resolved;
|
||||
use pnpm_lockfile::EnvLockfile;
|
||||
use pnpm_package_manifest::{
|
||||
@@ -57,9 +57,17 @@ pub(crate) fn package_manager_to_sync(
|
||||
{
|
||||
return Some(PackageManagerToSync { specifier: wanted_version.to_string(), version });
|
||||
}
|
||||
exact_version(wanted_version)
|
||||
.filter(|version| version_satisfies(version, wanted_version))
|
||||
.map(|version| PackageManagerToSync { specifier: wanted_version.to_string(), version })
|
||||
if let Some(version) =
|
||||
exact_version(wanted_version).filter(|version| version_satisfies(version, wanted_version))
|
||||
{
|
||||
return Some(PackageManagerToSync { specifier: wanted_version.to_string(), version });
|
||||
}
|
||||
// A range pin names no exact version, so the running pnpm's version is
|
||||
// the one the project actually uses.
|
||||
version_satisfies(PNPM_VERSION, wanted_version).then(|| PackageManagerToSync {
|
||||
specifier: wanted_version.to_string(),
|
||||
version: PNPM_VERSION.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether the pnpm version the manifest at `root_dir` pins still has to be
|
||||
|
||||
@@ -725,6 +725,43 @@ fn package_manager_to_sync_preserves_dev_engine_specifier() {
|
||||
);
|
||||
}
|
||||
|
||||
/// The range is built from `PNPM_VERSION` so the source checkout's version
|
||||
/// (a different major) can never satisfy it and answer first.
|
||||
#[test]
|
||||
fn package_manager_to_sync_records_the_running_version_for_a_satisfied_range_pin() {
|
||||
let root = TempDir::new().expect("tmp dir");
|
||||
let manifest_path = root.path().join("package.json");
|
||||
let range = format!("^{}", pnpm_config::PNPM_VERSION);
|
||||
std::fs::write(
|
||||
&manifest_path,
|
||||
format!(
|
||||
r#"{{"devEngines":{{"packageManager":{{"name":"pnpm","version":"{range}","onFail":"download"}}}}}}"#,
|
||||
),
|
||||
)
|
||||
.expect("write manifest");
|
||||
|
||||
let manifest = read_manifest_json(&manifest_path).expect("read manifest").expect("manifest");
|
||||
let package_manager =
|
||||
package_manager_to_sync(&manifest, root.path(), None).expect("sync package manager");
|
||||
|
||||
assert_eq!(package_manager.specifier, range);
|
||||
assert_eq!(package_manager.version, pnpm_config::PNPM_VERSION);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn package_manager_to_sync_records_nothing_for_a_pin_nothing_satisfies() {
|
||||
let root = TempDir::new().expect("tmp dir");
|
||||
let manifest_path = root.path().join("package.json");
|
||||
std::fs::write(
|
||||
&manifest_path,
|
||||
r#"{"devEngines":{"packageManager":{"name":"pnpm","version":"^999.0.0","onFail":"download"}}}"#,
|
||||
)
|
||||
.expect("write manifest");
|
||||
|
||||
let manifest = read_manifest_json(&manifest_path).expect("read manifest").expect("manifest");
|
||||
assert_eq!(package_manager_to_sync(&manifest, root.path(), None), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_bool_override_tri_state() {
|
||||
// force_on wins, force_off wins over a config `true`, and an unset
|
||||
|
||||
Reference in new issue
Block a user