Subchapter 9.13
references/features-multi-ecosystem.mdMarkdown6 KBView on GitHub
pnpm v12 can resolve and install Python and Rust/Cargo dependencies in the same pnpm install as npm packages. Both graphs share pnpm’s connection budget, artifact verification, and content-addressable store; each keeps its own lockfile and requirement semantics. Settings and on-disk layout may still change.
Enable in pnpm-workspace.yaml:
python:
enabled: truepnpm reads pyproject.toml, writes pylock.toml, and builds a .venv per project (a symlink into python-envs in the store; swapped atomically). It never touches a hand-made .venv.
pnpm add pypi:httpx # -> httpx==<latest> in pyproject.toml
pnpm add pypi:httpx@0.28.1 # exact pin
pnpm add pypi:'httpx>=0.28' # PEP 508 spec kept as written
pnpm add -D pypi:pytestpnpm outdated/pnpm update also cover Python deps (selectors pypi:<name>); requirements keep their operator and precision on update.pnpm run/pnpm exec put .venv/bin (.venv/Scripts on Windows) on PATH, so scripts call pytest/ruff without activation.--lockfile-only, --frozen-lockfile, --offline apply.[tool.uv.sources] ({ workspace = true } or { path, editable }); a project with [build-system] is installed editable.allowBuilds with Package URL keys: 'pkg:pypi/hatchling': true.registries with ecosystem: pypi and route package names with packages patterns; python.indexUrl is no longer supported. Credentials go in .npmrc, matched by origin.supportedArchitectures + python.versions: ['3.12','3.13'] locks wheels for each platform×interpreter.requires-python (prefers .python-version); downloads a python-build-standalone build if none fits (runtimeOnFail controls). Set python.executable to force one.python.enabled, python.executable, python.extras, python.groups (default ['dev']), python.versions, python.overrides, python.constraints. Per-project overrides in [tool.pnpm.python]. shared-environment = true under [tool.uv.workspace] shares one .venv across uv-workspace members.Enable in pnpm-workspace.yaml:
cargo:
enabled: truepnpm add crate:serde
pnpm add crate:serde@^1.0.200
pnpm add --save-build crate:cc # as a Cargo build dependencypnpm add crate:/rust@ fail with --recursive/--filter; run them in the project. pnpm outdated/update also cover crates (crate:<name> selectors).
Cargo.toml, writes a deterministic Cargo.lock, verifies each .crate against its checksum, unpacks into the store, and links a Cargo [directory source] under .pnpm/crates/crates-io. It writes the source replacement to .pnpm/crates/config.toml and adds an optional [[include]] of it to .cargo/config.toml between # >>> pnpm-managed cargo sources >>> markers (your content outside is untouched; write your own includes as [[include]] tables; Cargo 1.95+ reads it). In a fresh clone without .pnpm, Cargo skips the include and fetches normally. Already-unpacked crates are linked without re-verifying; pnpm install --force unpacks again.cargo build then compiles offline against vendored sources; pnpm compiles nothing.Cargo.lock, never pnpm-lock.yaml. --lockfile-only/--frozen-lockfile/--offline apply.registries with ecosystem: cargo (default crates.io); only one index per workspace. cargo.indexUrl is no longer supported. Auth reuses URL-scoped credentials plus CARGO_REGISTRY_TOKEN/$CARGO_HOME/credentials.toml.[patch]/[replace] overrides are honored and vendored for offline builds (git packages in .pnpm/crates/git). Such workspaces resolve via the cargo of the workspace’s Rust toolchain and require the default crates.io index; otherwise commit a Cargo.lock generated by Cargo. Only unstable.bindeps and resolver.incompatible-rust-versions are read from the checkout’s .cargo/config.toml.registry = "..." is rejected (one index per workspace). With unstable.build-std, the toolchain needs the rust-src component.pnpm pipeline and tasks.<name>.cargoTargetDir.pnpm install installs the toolchain named by the nearest rust-toolchain.toml (or rust-toolchain), read like rustup. pnpm verifies it against Rust’s signing key, stores it once per machine, and links it into .pnpm/rust; pnpm run/exec and pnpm’s own cargo calls use it.
[toolchain]
channel = "1.95.0" # version, stable, beta, nightly, nightly-YYYY-MM-DD
components = ["rust-src"]
targets = ["wasm32-unknown-unknown"]pnpm add rust@1.95 writes the file; pnpm add -g rust@stable installs globally; pnpm shim add rust delegates to rustup instead; pnx --package=rust@<channel> cargo ... runs one release.stable) are checked daily; offline uses the newest cached one. Files with path, no channel, linked toolchains, or exotic profiles are left to rustup.runtimeOnFail: error|warn|ignore turns it off; tools.rust.mirror sets a mirror.With pnprServer set, the server resolves the Python/Cargo graph so pnpm needn’t download wheels/index files to discover requirements; it falls back to local resolution when the server doesn’t answer for that ecosystem.