<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://pnpm.io/zh/blog</id>
    <title>pnpm Blog</title>
    <updated>2026-05-27T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://pnpm.io/zh/blog"/>
    <subtitle>pnpm Blog</subtitle>
    <icon>https://pnpm.io/zh/img/favicon.png</icon>
    <entry>
        <title type="html"><![CDATA[pnpm 11.4]]></title>
        <id>https://pnpm.io/zh/blog/releases/11.4</id>
        <link href="https://pnpm.io/zh/blog/releases/11.4"/>
        <updated>2026-05-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 11.4 closes a cluster of supply-chain holes around lockfile integrity, credential scoping, git resolutions, patch files, and dependency aliases, makes tarball-integrity mismatches a hard install failure by default (with a narrowly-scoped --update-checksums opt-in), and changes pnpm runtime set to write to devEngines.runtime instead of engines.runtime by default.]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 11.4 closes a cluster of supply-chain holes around lockfile integrity, credential scoping, git resolutions, patch files, and dependency aliases, makes tarball-integrity mismatches a hard install failure by default (with a narrowly-scoped <code>--update-checksums</code> opt-in), and changes <code>pnpm runtime set</code> to write to <code>devEngines.runtime</code> instead of <code>engines.runtime</code> by default.</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/11.4#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="tarball-integrity-mismatches-are-now-a-hard-failure">Tarball-integrity mismatches are now a hard failure<a href="https://pnpm.io/zh/blog/releases/11.4#tarball-integrity-mismatches-are-now-a-hard-failure" class="hash-link" aria-label="直接链接到 Tarball-integrity mismatches are now a hard failure" title="直接链接到 Tarball-integrity mismatches are now a hard failure" translate="no">​</a></h3>
<p>Previously, <code>pnpm install</code> (non-frozen) would log <code>ERR_PNPM_TARBALL_INTEGRITY</code> when a downloaded tarball's hash didn't match the lockfile, silently re-resolve from the registry, and overwrite the locked integrity. A compromised registry, proxy, or republished version could therefore substitute attacker-controlled content on a clean machine even though the project shipped a committed lockfile.</p>
<p><code>pnpm install</code> now exits with <code>ERR_PNPM_TARBALL_INTEGRITY</code> and a hint pointing at the new opt-in flag.</p>
<p>The only opt-in is <strong><code>pnpm install --update-checksums</code></strong> — narrowly scoped to refreshing the locked integrity values from what the registry currently serves. It mirrors yarn's flag of the same name. A warning still prints when the bypass takes effect so the operation is auditable.</p>
<p><code>--force</code> and <code>pnpm update</code> deliberately do <strong>not</strong> bypass the integrity check. They are routine refresh operations; silently overwriting a locked integrity in those flows would erase the protection a committed lockfile is supposed to provide. <code>--frozen-lockfile</code> behavior is unchanged. <code>--fix-lockfile</code> keeps its documented purpose (filling in missing lockfile entries) and is also not a bypass.</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-runtime-set-writes-to-devenginesruntime-by-default"><code>pnpm runtime set</code> writes to <code>devEngines.runtime</code> by default<a href="https://pnpm.io/zh/blog/releases/11.4#pnpm-runtime-set-writes-to-devenginesruntime-by-default" class="hash-link" aria-label="直接链接到 pnpm-runtime-set-writes-to-devenginesruntime-by-default" title="直接链接到 pnpm-runtime-set-writes-to-devenginesruntime-by-default" translate="no">​</a></h3>
<p><a href="https://pnpm.io/zh/cli/runtime"><code>pnpm runtime set &lt;name&gt; &lt;version&gt;</code></a> now saves the runtime to <code>devEngines.runtime</code> by default instead of <code>engines.runtime</code>. Pass <code>--save-prod</code> (or <code>-P</code>) to save it to <code>engines.runtime</code> instead. See <a href="https://github.com/pnpm/pnpm/issues/11948" target="_blank" rel="noopener noreferrer">#11948</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/11.4#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="security-unscoped-credentials-no-longer-leak-across-registries">Security: unscoped credentials no longer leak across registries<a href="https://pnpm.io/zh/blog/releases/11.4#security-unscoped-credentials-no-longer-leak-across-registries" class="hash-link" aria-label="直接链接到 Security: unscoped credentials no longer leak across registries" title="直接链接到 Security: unscoped credentials no longer leak across registries" translate="no">​</a></h3>
<p>An unscoped <code>_authToken</code> (or <code>_auth</code>, or <code>username</code> + <code>_password</code>, or <code>tokenHelper</code>) defined in one source — <code>~/.npmrc</code>, <code>~/.config/pnpm/auth.ini</code>, a workspace <code>.npmrc</code>, CLI flags, etc. — would be sent as an <code>Authorization</code> header to whichever registry a different (potentially untrusted) source named. The same exposure extended to client TLS credentials (<code>cert</code>, <code>key</code>).</p>
<p>pnpm now rewrites each unscoped per-registry setting (<code>_authToken</code>, <code>_auth</code>, <code>username</code>, <code>_password</code>, <code>tokenHelper</code>, <code>cert</code>, <code>key</code>) to its URL-scoped form at load time, using the <code>registry=</code> value declared in the same source (or the npmjs default registry if the source declares none). A later layer overriding <code>registry=</code> therefore cannot pull an unscoped credential along, because it is already pinned to the URL its author intended. <code>ca</code> / <code>cafile</code> are intentionally not rescoped — they're trust anchors, not credentials, and corporate MITM-proxy setups rely on them applying globally.</p>
<p>Every rescope emits a deprecation warning telling the user where the setting was pinned and how to write it directly. npm has rejected unscoped credentials outright since <code>npm@9</code>, and pnpm intends to remove support in a future major release. To target a specific registry, write the setting URL-scoped:</p>
<div class="language-ini codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_KdeV">.npmrc</div><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-ini codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key attr-name" style="color:#00a4db">//registry.example.com/:_authToken</span><span class="token punctuation" style="color:#393A34">=</span><span class="token value attr-value" style="color:#e3116c">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key attr-name" style="color:#00a4db">//registry.example.com/:cert</span><span class="token punctuation" style="color:#393A34">=</span><span class="token value attr-value" style="color:#e3116c">...</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="security-lockfile-entries-without-integrity-are-rejected">Security: lockfile entries without <code>integrity</code> are rejected<a href="https://pnpm.io/zh/blog/releases/11.4#security-lockfile-entries-without-integrity-are-rejected" class="hash-link" aria-label="直接链接到 security-lockfile-entries-without-integrity-are-rejected" title="直接链接到 security-lockfile-entries-without-integrity-are-rejected" translate="no">​</a></h3>
<p>Previously, the worker that extracts a downloaded tarball skipped hash verification when no integrity was supplied and minted a fresh one from the unverified bytes. An attacker who could both alter the lockfile (e.g. via a pull request that strips <code>integrity:</code>) and serve modified content at the referenced tarball URL could install a tampered package without any error — including under <code>--frozen-lockfile</code>.</p>
<p>pnpm now fails closed at lockfile-read time with <code>ERR_PNPM_MISSING_TARBALL_INTEGRITY</code>. Git-hosted tarballs (<code>gitHosted: true</code> or a URL on <code>codeload.github.com</code> / <code>bitbucket.org</code> / <code>gitlab.com</code>) and <code>file:</code> tarballs are exempt — the commit SHA in a git-host URL and the user-controlled local path already anchor the bytes.</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="security-git-resolutions-reject-non-sha-commit-fields">Security: git resolutions reject non-SHA <code>commit</code> fields<a href="https://pnpm.io/zh/blog/releases/11.4#security-git-resolutions-reject-non-sha-commit-fields" class="hash-link" aria-label="直接链接到 security-git-resolutions-reject-non-sha-commit-fields" title="直接链接到 security-git-resolutions-reject-non-sha-commit-fields" translate="no">​</a></h3>
<p>Git resolutions whose <code>commit</code> field is not a 40-character hexadecimal SHA are rejected before <code>git</code> is invoked. A malicious lockfile could otherwise smuggle a value such as <code>--upload-pack=&lt;command&gt;</code> through <code>git fetch</code> / <code>git checkout</code>, which on SSH or local-file transports executes the supplied command.</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="security-patch-files-writing-outside-the-package-directory-are-rejected">Security: patch files writing outside the package directory are rejected<a href="https://pnpm.io/zh/blog/releases/11.4#security-patch-files-writing-outside-the-package-directory-are-rejected" class="hash-link" aria-label="直接链接到 Security: patch files writing outside the package directory are rejected" title="直接链接到 Security: patch files writing outside the package directory are rejected" translate="no">​</a></h3>
<p>Patch files whose <code>diff --git</code> headers reference paths outside the patched package directory are now rejected. Previously a malicious <code>.patch</code> file added via a pull request could write, delete, or rename arbitrary files reachable by the user running <code>pnpm install</code>.</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="security-dependency-aliases-with-path-traversal-segments-are-rejected">Security: dependency aliases with path-traversal segments are rejected<a href="https://pnpm.io/zh/blog/releases/11.4#security-dependency-aliases-with-path-traversal-segments-are-rejected" class="hash-link" aria-label="直接链接到 Security: dependency aliases with path-traversal segments are rejected" title="直接链接到 Security: dependency aliases with path-traversal segments are rejected" translate="no">​</a></h3>
<p>Dependency aliases that contain path-traversal segments (such as <code>@x/../../../../../.git/hooks</code>) are rejected when read from a package manifest or symlinked into <code>node_modules</code>. A malicious registry package could otherwise use a transitive dependency key to make <code>pnpm install</code> create symlinks at attacker-chosen paths outside the intended <code>node_modules</code> directory.</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="trusted-publisher-metadata-now-requires-provenance">Trusted-publisher metadata now requires provenance<a href="https://pnpm.io/zh/blog/releases/11.4#trusted-publisher-metadata-now-requires-provenance" class="hash-link" aria-label="直接链接到 Trusted-publisher metadata now requires provenance" title="直接链接到 Trusted-publisher metadata now requires provenance" translate="no">​</a></h3>
<p>Trusted publisher metadata is only treated as the strongest trust evidence when provenance is also present.</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="other-fixes">Other fixes<a href="https://pnpm.io/zh/blog/releases/11.4#other-fixes" class="hash-link" aria-label="直接链接到 Other fixes" title="直接链接到 Other fixes" translate="no">​</a></h3>
<ul>
<li>Fix <code>pnpm deploy</code> crashing with <code>ENOENT: ... lstat '&lt;deployDir&gt;/node_modules'</code> when <code>configDependencies</code> declares pacquet (<code>pacquet</code> or <code>@pnpm/pacquet</code>). The deploy directory never installs config dependencies, so the install engine they designate isn't on disk to invoke; the nested install now skips them.</li>
<li>Limit concurrent project manifest reads while listing large workspaces to avoid <code>EMFILE</code> errors.</li>
<li>Validate <code>devEngines.runtime</code> and <code>engines.runtime</code> version ranges for <code>node</code>, <code>deno</code>, and <code>bun</code> when <code>onFail</code> is set to <code>error</code> or <code>warn</code>. Previously these settings only had an effect with <code>onFail: 'download'</code> — the <code>error</code> and <code>warn</code> modes silently did nothing <a href="https://github.com/pnpm/pnpm/issues/11818" target="_blank" rel="noopener noreferrer">#11818</a>. Violations now throw <code>ERR_PNPM_BAD_RUNTIME_VERSION</code>.</li>
<li>Improve the log message that pnpm prints after auto-adding entries to <code>minimumReleaseAgeExclude</code> when <code>minimumReleaseAge</code> is set without <code>minimumReleaseAgeStrict</code>. The message previously referred to the internal "loose mode" terminology, which wasn't searchable in the docs; it now tells the user to set <code>minimumReleaseAgeStrict</code> to <code>true</code> if they want these updates gated behind a prompt instead <a href="https://github.com/pnpm/pnpm/issues/11747" target="_blank" rel="noopener noreferrer">#11747</a>.</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 11.3]]></title>
        <id>https://pnpm.io/zh/blog/releases/11.3</id>
        <link href="https://pnpm.io/zh/blog/releases/11.3"/>
        <updated>2026-05-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 11.3 adds support for npm's staged publishing (pnpm stage), the new trustLockfile setting for skipping the supply-chain verification pass on already-trusted lockfiles, and native implementations of pnpm pkg, pnpm repo, and pnpm set-script. It also adds a --skip-manifest-obfuscation flag for pack / publish and cuts the memory footprint of minimumReleaseAge / trustPolicy verification on large workspaces.]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 11.3 adds support for npm's staged publishing (<code>pnpm stage</code>), the new <a href="https://pnpm.io/zh/settings#trustlockfile"><code>trustLockfile</code></a> setting for skipping the supply-chain verification pass on already-trusted lockfiles, and native implementations of <code>pnpm pkg</code>, <code>pnpm repo</code>, and <code>pnpm set-script</code>. It also adds a <code>--skip-manifest-obfuscation</code> flag for <code>pack</code> / <code>publish</code> and cuts the memory footprint of <code>minimumReleaseAge</code> / <code>trustPolicy</code> verification on large workspaces.</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/11.3#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-stage"><code>pnpm stage</code><a href="https://pnpm.io/zh/blog/releases/11.3#pnpm-stage" class="hash-link" aria-label="直接链接到 pnpm-stage" title="直接链接到 pnpm-stage" translate="no">​</a></h3>
<p>A new <a href="https://pnpm.io/zh/cli/stage"><code>pnpm stage</code></a> command brings npm's <a href="https://docs.npmjs.com/staged-publishing" target="_blank" rel="noopener noreferrer">staged publishing</a> workflow to pnpm. Staged publishing lets you publish a version that's hidden from <code>npm install</code> until you explicitly approve it — useful for verifying release artifacts, smoke-testing CI, or coordinating multi-package releases.</p>
<p>The available subcommands are:</p>
<div class="language-sh codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-sh codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> stage publish    </span><span class="token comment" style="color:#999988;font-style:italic"># publish a version into staging</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> stage list       </span><span class="token comment" style="color:#999988;font-style:italic"># list staged versions</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> stage view       </span><span class="token comment" style="color:#999988;font-style:italic"># view a staged version</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> stage approve    </span><span class="token comment" style="color:#999988;font-style:italic"># promote a staged version to the registry</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> stage reject     </span><span class="token comment" style="color:#999988;font-style:italic"># discard a staged version</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> stage download   </span><span class="token comment" style="color:#999988;font-style:italic"># download a staged tarball</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="trustlockfile"><code>trustLockfile</code><a href="https://pnpm.io/zh/blog/releases/11.3#trustlockfile" class="hash-link" aria-label="直接链接到 trustlockfile" title="直接链接到 trustlockfile" translate="no">​</a></h3>
<p>A new <a href="https://pnpm.io/zh/settings#trustlockfile"><code>trustLockfile</code></a> setting controls whether <code>pnpm install</code> re-applies the <a href="https://pnpm.io/zh/settings#minimumreleaseage"><code>minimumReleaseAge</code></a> / <code>trustPolicy: 'no-downgrade'</code> checks to every entry in the loaded lockfile. When <code>true</code>, the install treats the lockfile as already-trusted and skips the verification pass — useful for closed-source projects where every commit comes from a trusted author. The default is <code>false</code>, so verification stays on by default.</p>
<p>Set it in <code>pnpm-workspace.yaml</code>:</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_KdeV">pnpm-workspace.yaml</div><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">trustLockfile</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><br></span></code></pre></div></div>
<p>This release also cuts the <strong>memory footprint</strong> of the verification pass itself: the per-(registry, name) trust-meta cache previously retained the full packument — dependency graphs, scripts, README, and per-version manifests — for the entire install. On large workspaces (~4k lockfile entries with <code>minimumReleaseAge</code> + <code>trustPolicy: no-downgrade</code> enabled) this could OOM CI runners with a 2 GB heap cap. The cache now stores only the fields the trust check actually reads (<code>time</code>, per-version <code>_npmUser.trustedPublisher</code>, <code>dist.attestations.provenance</code>); the abbreviated-metadata cache is similarly projected to just the package-level <code>modified</code> field and the set of currently-listed version names. 修复 <a href="https://github.com/pnpm/pnpm/issues/11860" target="_blank" rel="noopener noreferrer">#11860</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="native-pnpm-pkg-pnpm-repo-and-pnpm-set-script">Native <code>pnpm pkg</code>, <code>pnpm repo</code>, and <code>pnpm set-script</code><a href="https://pnpm.io/zh/blog/releases/11.3#native-pnpm-pkg-pnpm-repo-and-pnpm-set-script" class="hash-link" aria-label="直接链接到 native-pnpm-pkg-pnpm-repo-and-pnpm-set-script" title="直接链接到 native-pnpm-pkg-pnpm-repo-and-pnpm-set-script" translate="no">​</a></h3>
<p>Three more commands that previously delegated to (or were missing without) npm are now implemented natively, following the npm command conventions:</p>
<ul>
<li><a href="https://pnpm.io/zh/cli/pkg"><strong><code>pnpm pkg</code></strong></a> — get / set / delete fields in <code>package.json</code>.</li>
<li><a href="https://pnpm.io/zh/cli/repo"><strong><code>pnpm repo</code></strong></a> — open the repository URL of a package in the browser.</li>
<li><a href="https://pnpm.io/zh/cli/set-script"><strong><code>pnpm set-script</code></strong></a> (alias <code>ss</code>) — add or update an entry in the <code>scripts</code> field of the project manifest. Supports <code>package.json</code>, <code>package.json5</code>, and <code>package.yaml</code> formats.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="--skip-manifest-obfuscation-for-pack-and-publish"><code>--skip-manifest-obfuscation</code> for <code>pack</code> and <code>publish</code><a href="https://pnpm.io/zh/blog/releases/11.3#--skip-manifest-obfuscation-for-pack-and-publish" class="hash-link" aria-label="直接链接到 --skip-manifest-obfuscation-for-pack-and-publish" title="直接链接到 --skip-manifest-obfuscation-for-pack-and-publish" translate="no">​</a></h3>
<p>A new <a href="https://pnpm.io/zh/cli/publish#--skip-manifest-obfuscation"><code>--skip-manifest-obfuscation</code></a> flag for <a href="https://pnpm.io/zh/cli/pack"><code>pnpm pack</code></a> and <a href="https://pnpm.io/zh/cli/publish"><code>pnpm publish</code></a> keeps the original <code>packageManager</code> field and publish lifecycle scripts in the packed/published manifest instead of stripping them. The pnpm-specific <code>pnpm</code> field continues to be omitted.</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/11.3#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h2>
<ul>
<li>Fixed <code>pnpm dlx</code> failing with <code>ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND</code> when the installed package's CAS slot is missing its <code>package.json</code>. Observed in the wild for <code>pnpm dlx node@runtime:&lt;version&gt;</code> when the GVS slot was populated without the synthesized manifest runtime archives need. <code>dlx</code> now falls back to the scopeless package name when the slot's manifest is unreadable — for single-bin packages (the dlx common case, including every <code>runtime:</code> spec) this matches what <code>manifest.bin</code> would have named.</li>
<li>Fixed non-determinism in <code>pnpm dedupe</code> and <code>pnpm install</code> when a dependency graph contains packages with transitive peer dependencies on each other (e.g. <code>@aws-sdk/client-sts</code> and <code>@aws-sdk/client-sso-oidc</code>) and <code>auto-install-peers</code> is enabled. The lockfile no longer flips between two equally-valid forms across consecutive runs. The root cause was that <code>resolveDependencies</code> pushed onto its <code>pkgAddresses</code> / <code>postponedResolutionsQueue</code> arrays from inside <code>Promise.all</code>-spawned callbacks, so completion-order timing leaked into the array order and downstream cyclic-peer suffix assignment. 修复 <a href="https://github.com/pnpm/pnpm/issues/8155" target="_blank" rel="noopener noreferrer">#8155</a>。</li>
<li>Fixed a regression where <code>pnpm add &lt;github-shorthand&gt;</code> (and any other wanted-dependency whose alias can't be parsed from the user-supplied spec, e.g. tarball URLs or <code>pnpm/test-git-fetch#sha</code>) was silently dropped from the manifest update and from <code>pendingBuilds</code>.</li>
<li>Fixed <code>pnpm add --config</code> leaving orphan entries in <code>pnpm-lock.env.yaml</code> (the optional subdependencies of the previously resolved version of the updated config dependency).</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 11.2]]></title>
        <id>https://pnpm.io/zh/blog/releases/11.2</id>
        <link href="https://pnpm.io/zh/blog/releases/11.2"/>
        <updated>2026-05-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 11.2 引入了实验性的选择加入 pacquet（pnpm 的 Rust 移植版）作为安装后端，扩展了 配置依赖项 以安装一层 optionalDependencies（因此 esbuild/swc 平台二进制模式也适用于配置依赖项），连接了文档中长期存在的 pnpm login --scope 标志，并在 pnpm outdated 和 pnpm update --interactive 中显示运行时入口（Node.js、Deno、Bun）。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 11.2 引入了实验性的选择加入 <a href="https://npmx.dev/package/@pnpm/pacquet" target="_blank" rel="noopener noreferrer">pacquet</a>（pnpm 的 Rust 移植版）作为安装后端，扩展了 <a href="https://pnpm.io/zh/config-dependencies">配置依赖项</a> 以安装一层 <code>optionalDependencies</code>（因此 esbuild/swc 平台二进制模式也适用于配置依赖项），连接了文档中长期存在的 <code>pnpm login --scope</code> 标志，并在 <code>pnpm outdated</code> 和 <code>pnpm update --interactive</code> 中显示运行时入口（Node.js、Deno、Bun）。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/11.2#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="实验性使用-pacquet-作为安装后端">实验性：使用 pacquet 作为安装后端<a href="https://pnpm.io/zh/blog/releases/11.2#%E5%AE%9E%E9%AA%8C%E6%80%A7%E4%BD%BF%E7%94%A8-pacquet-%E4%BD%9C%E4%B8%BA%E5%AE%89%E8%A3%85%E5%90%8E%E7%AB%AF" class="hash-link" aria-label="直接链接到 实验性：使用 pacquet 作为安装后端" title="直接链接到 实验性：使用 pacquet 作为安装后端" translate="no">​</a></h3>
<p>现在，在 <code>pnpm-workspace.yaml</code> 的 <code>configDependencies</code> 中添加 <a href="https://npmx.dev/package/@pnpm/pacquet" target="_blank" rel="noopener noreferrer"><code>@pnpm/pacquet</code></a>（pnpm 的 Rust 移植版），即可将 <code>pnpm install</code> 的清单化阶段委托给 pacquet 二进制文件。 pnpm 仍然负责依赖项解析；pacquet 只从新写入的锁文件中获取和导入。 这是 Rust 安装引擎的可选预览版——参见 <a href="https://github.com/pnpm/pnpm/issues/11723" target="_blank" rel="noopener noreferrer">#11723</a>。</p>
<p>要在项目中配置 pacquet，请运行：</p>
<div class="language-sh codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-sh codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> @pnpm/pacquet </span><span class="token parameter variable" style="color:#36acaa">--config</span><br></span></code></pre></div></div>
<p>你会看到 <code>pnpm-workspace.yaml</code> 和 <code>pnpm-lock.yaml</code> 中需要提交的更改。 如果你在使用 pacquet 时遇到任何问题，请在你创建的 GitHub 议题中告知我们。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="optionaldependencies-用于配置依赖项"><code>optionalDependencies</code> 用于配置依赖项<a href="https://pnpm.io/zh/blog/releases/11.2#optionaldependencies-%E7%94%A8%E4%BA%8E%E9%85%8D%E7%BD%AE%E4%BE%9D%E8%B5%96%E9%A1%B9" class="hash-link" aria-label="直接链接到 optionaldependencies-用于配置依赖项" title="直接链接到 optionaldependencies-用于配置依赖项" translate="no">​</a></h3>
<p><a href="https://pnpm.io/zh/config-dependencies">配置依赖项</a> 现在解析并安装由配置依赖项声明的一级 <code>optionalDependencies</code>，并在安装时应用 <code>os</code> / <code>cpu</code> / <code>libc</code> 平台过滤。 这解锁了 esbuild/swc 风格的模式，其中软件包通过 <code>optionalDependencies</code> 分发特定于平台的二进制文件——现在配置依赖项也可以这样做，并将匹配的二进制文件符号链接到全局虚拟存储中的它旁边，因此配置依赖项内部的 <code>require('pkg-platform-arch')</code> 可以正确解析。</p>
<p>环境锁文件会记录所有平台变体，而不管主机平台如何，因此它可以在不同机器之间保持可移植性。 配置依赖项的 <code>optionalDependencies</code> 中的每个条目都必须声明一个确切的版本——为了保持安装的可重现性，范围和标签将被拒绝。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-login---scope"><code>pnpm login --scope</code><a href="https://pnpm.io/zh/blog/releases/11.2#pnpm-login---scope" class="hash-link" aria-label="直接链接到 pnpm-login---scope" title="直接链接到 pnpm-login---scope" translate="no">​</a></h3>
<p>早已记录在案的 <code>pnpm login --scope &lt;scope&gt;</code> 标志现已实现。 作用域已规范化（如果缺少前导 <code>@</code>，则添加 <code>@</code>；忽略空白值），并且 <code>@&lt;scope&gt;:registry=&lt;registry&gt;</code> 映射与身份验证令牌一起写入 pnpm 身份验证文件。 后续安装的 <code>@&lt;scope&gt;/*</code> 软件包将路由到选定的注册源。 之前此记录标志出现错误，提示 <code>Unknown option: 'scope'</code>。 请参阅 <a href="https://github.com/pnpm/pnpm/issues/11716" target="_blank" rel="noopener noreferrer">#11716</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="outdated-和-update---interactive-中的运行时"><code>outdated</code> 和 <code>update --interactive</code> 中的运行时<a href="https://pnpm.io/zh/blog/releases/11.2#outdated-%E5%92%8C-update---interactive-%E4%B8%AD%E7%9A%84%E8%BF%90%E8%A1%8C%E6%97%B6" class="hash-link" aria-label="直接链接到 outdated-和-update---interactive-中的运行时" title="直接链接到 outdated-和-update---interactive-中的运行时" translate="no">​</a></h3>
<p><a href="https://pnpm.io/zh/cli/outdated"><code>pnpm outdated</code></a> 和 <a href="https://pnpm.io/zh/cli/update"><code>pnpm update --interactive</code></a> 现在报告 Node.js、Deno 和 Bun 运行时已作为项目依赖项安装（<code>runtime:</code> 说明符）。 以前这些都是静默地跳过的。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/11.2#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h2>
<ul>
<li>修复了当从不同的当前工作目录调用 pnpm 时（例如从 CI 包装器或 monorepo 脚本调用 <code>pnpm --dir &lt;project&gt; install</code>），<code>.npmrc</code> 中的 <code>cafile=&lt;relative-path&gt;</code> 从错误的目录读取的问题。 现在路径是根据声明它的 <code>.npmrc</code> 目录来解析的，而不是根据 <code>process.cwd()</code>。 在此修复之前，安装程序在没有配置 CA 的情况下继续运行，用户只会看到针对私有注册源的 TLS 错误，而没有日志行与错误解析的路径关联 <a href="https://github.com/pnpm/pnpm/issues/11624" target="_blank" rel="noopener noreferrer">#11624</a>。</li>
<li>修复了当在 <code>.npmrc</code> 中设置了 <code>registry</code> 并且 <code>pnpm-workspace.yaml</code> 没有提供 <code>registries.default</code> 时，<code>config.registry</code> 会附加尾部斜杠的问题。</li>
<li>修复了全局添加/更新操作，使其能够处理 <code>minimumReleaseAge</code> 策略违规，而不是抛出内部解析器防护错误。</li>
<li>修复了当锁文件被清理（例如通过 <code>turbo prune --docker</code>）时，使用 <code>injectWorkspacePackages: true</code> 会导致的两个崩溃：一个是当对等依赖变体的基础 <code>packages:</code> 条目已被删除时，其注入的快照遇到 <code>Cannot use 'in' operator to search for 'directory' in undefined</code>；另一个是在 <code>prepare</code> / <code>postinstall</code> 重新导入每个注入的工作区包之后，在 <code>node_modules/.bin/&lt;tool&gt;</code> 上出现 <code>ERR_PNPM_ENOENT</code>。</li>
<li>修复了 <code>pnpm login</code> 和 <code>pnpm logout</code> 忽略 <code>pnpm-workspace.yaml</code> 中的 <code>registries.default</code> 的问题 <a href="https://github.com/pnpm/pnpm/issues/10099" target="_blank" rel="noopener noreferrer">#10099</a>。</li>
<li>修复了 <code>minimumReleaseAge</code> (publishedBy) 成熟度快捷方式，使其包含截止时间。 以前，<code>modified</code> 字段等于截止值的缩写元数据会脱离快速路径，并触发完整元数据重新获取（或者在不允许完整元数据时触发 <code>MISSING_TIME</code> 错误）。</li>
<li>发布软件包时遵守 <code>publishConfig.access</code>。</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="1121">11.2.1<a href="https://pnpm.io/zh/blog/releases/11.2#1121" class="hash-link" aria-label="直接链接到 11.2.1" title="直接链接到 11.2.1" translate="no">​</a></h2>
<ul>
<li>在环境锁文件中，使用 <code>optional: true</code> 标记可选的配置依赖项子依赖项快照，以匹配 <code>pnpm-lock.yaml</code> 中其他地方记录可选依赖项的方式。 以前，通过配置依赖项的 <code>optionalDependencies</code> 拉取的平台特定子依赖项的快照会被写为空对象。</li>
<li>Fixed <code>pickRegistryForPackage</code> returning the wrong registry for an unscoped <code>npm:</code> alias under a scoped local name. A manifest entry like <code>"@private/foo": "npm:lodash@^1"</code> was routing the <code>lodash</code> fetch through <code>registries["@private"]</code>, even though <code>lodash</code> is unscoped.</li>
<li>Don't print <code>Installing config dependencies...</code> when config dependencies are already installed and nothing needs to be fetched, re-linked, or removed.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="1122">11.2.2<a href="https://pnpm.io/zh/blog/releases/11.2#1122" class="hash-link" aria-label="直接链接到 11.2.2" title="直接链接到 11.2.2" translate="no">​</a></h2>
<ul>
<li>When the install engine is delegated to pacquet via <code>configDependencies</code>, the user's CLI flags passed to <code>pnpm install</code> (e.g. <code>--no-runtime</code>, <code>--prod</code>, <code>--dev</code>, <code>--no-optional</code>, <code>--node-linker</code>, <code>--cpu</code> / <code>--os</code> / <code>--libc</code>, <code>--offline</code>, <code>--prefer-offline</code>) are now forwarded to pacquet's <code>install</code> subcommand verbatim. Previously pacquet was invoked with a fixed argument list, so flags like <code>--no-runtime</code> were silently dropped. Flag forwarding is gated on the command being <code>install</code> / <code>i</code>; <code>add</code>, <code>update</code>, and <code>dedupe</code> still don't forward (their flag surface doesn't line up with pacquet's <code>install</code>).</li>
<li>Fixed <code>pnpm up</code> (and <code>pnpm add</code> / <code>pnpm remove</code>) failing with <code>pacquet_package_manager::outdated_lockfile</code> when pacquet is declared in <code>configDependencies</code>. pnpm now passes <code>--ignore-manifest-check</code> to pacquet so its <code>--frozen-lockfile</code> check doesn't fire against the (pre-mutation) <code>package.json</code> pnpm hasn't written yet <a href="https://github.com/pnpm/pnpm/issues/11797" target="_blank" rel="noopener noreferrer">#11797</a>.</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 11.1]]></title>
        <id>https://pnpm.io/zh/blog/releases/11.1</id>
        <link href="https://pnpm.io/zh/blog/releases/11.1"/>
        <updated>2026-05-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 11.1 新增了一些命令——pnpm audit signatures、pnpm bugs 和 pnpm owner——同时还支持从任意名称的注册表安装（包括 GitHub Packages npm 注册源的内置别名），能够在 CI 中跳过运行时安装，以及修复了一些问题。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 11.1 新增了一些命令——<a href="https://pnpm.io/zh/cli/audit#signatures"><code>pnpm audit signatures</code></a>、<a href="https://pnpm.io/zh/cli/bugs"><code>pnpm bugs</code></a> 和 <a href="https://pnpm.io/zh/cli/owner"><code>pnpm owner</code></a>——同时还支持从任意名称的注册表安装（包括 GitHub Packages npm 注册源的内置别名），能够在 CI 中跳过运行时安装，以及修复了一些问题。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/11.1#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-audit-signatures"><code>pnpm audit signatures</code><a href="https://pnpm.io/zh/blog/releases/11.1#pnpm-audit-signatures" class="hash-link" aria-label="直接链接到 pnpm-audit-signatures" title="直接链接到 pnpm-audit-signatures" translate="no">​</a></h3>
<p>一个新的 <a href="https://pnpm.io/zh/cli/audit#signatures"><code>pnpm audit signatures</code></a> 子命令会根据 <code>/-/npm/v1/keys</code> <a href="https://github.com/pnpm/pnpm/issues/7909" target="_blank" rel="noopener noreferrer">#7909</a> 上发布的密钥，验证已安装软件包的 ECDSA 注册源签名。 会尊重已定义范围的注册表；不会发布签名密钥的注册表将被跳过。</p>
<div class="language-sh codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-sh codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> audit signatures</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="命名注册表以及内置的-gh-别名">命名注册表（以及内置的 <code>gh:</code> 别名）<a href="https://pnpm.io/zh/blog/releases/11.1#%E5%91%BD%E5%90%8D%E6%B3%A8%E5%86%8C%E8%A1%A8%E4%BB%A5%E5%8F%8A%E5%86%85%E7%BD%AE%E7%9A%84-gh-%E5%88%AB%E5%90%8D" class="hash-link" aria-label="直接链接到 命名注册表以及内置的-gh-别名" title="直接链接到 命名注册表以及内置的-gh-别名" translate="no">​</a></h3>
<p>现在，你可以通过内置的 <code>gh:</code> 前缀从 <a href="https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry" target="_blank" rel="noopener noreferrer">GitHub Packages npm 注册表</a> 安装软件包，更广泛地说，还可以像 <a href="https://docs.vlt.sh/cli/registries" target="_blank" rel="noopener noreferrer">vlt 的命名注册源别名</a> 那样，从任意命名的注册表安装软件包：</p>
<div class="language-sh codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-sh codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> gh:@acme/private</span><br></span></code></pre></div></div>
<p>身份验证来自现有的每个 URL 的 <code>.npmrc</code> 条目（例如 <code>//npm.pkg.github.com/:_authToken=...</code>），因此不需要单独的身份验证机制。</p>
<p>可以在 <code>pnpm-workspace.yaml</code> 文件中的 <code>namedRegistries</code> 下配置其他别名，或者覆盖内置的 <code>gh</code> 别名（例如 GitHub Enterprise Server）：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_KdeV">pnpm-workspace.yaml</div><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">namedRegistries</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">gh</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//npm.pkg.github.example.com/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">work</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//npm.work.example.com/</span><br></span></code></pre></div></div>
<p>这样，<code>work:@corp/lib@^2.0.0</code> 就对应到 <code>https://npm.work.example.com/</code>。 请参阅 <a href="https://github.com/pnpm/pnpm/issues/8941" target="_blank" rel="noopener noreferrer">#8941</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="--sbom-spec-version"><code>--sbom-spec-version</code><a href="https://pnpm.io/zh/blog/releases/11.1#--sbom-spec-version" class="hash-link" aria-label="直接链接到 --sbom-spec-version" title="直接链接到 --sbom-spec-version" translate="no">​</a></h3>
<p><a href="https://pnpm.io/zh/cli/sbom"><code>pnpm sbom</code></a> 现在接受一个 <code>--sbom-spec-version</code> 标志来选择 CycloneDX 规范版本（<code>1.5</code>、<code>1.6</code> 或 <code>1.7</code> — 默认为 <code>1.7</code>）。 该标志仅在使用 <code>--sbom-format cyclonedx</code> 时有效。 请参阅 <a href="https://github.com/pnpm/pnpm/pull/11389" target="_blank" rel="noopener noreferrer">#11389</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="用于-ci-矩阵的---no-runtime">用于 CI 矩阵的 <code>--no-runtime</code><a href="https://pnpm.io/zh/blog/releases/11.1#%E7%94%A8%E4%BA%8E-ci-%E7%9F%A9%E9%98%B5%E7%9A%84---no-runtime" class="hash-link" aria-label="直接链接到 用于-ci-矩阵的---no-runtime" title="直接链接到 用于-ci-矩阵的---no-runtime" translate="no">​</a></h3>
<p>新增的 <code>--no-runtime</code> 标志（配置：<code>runtime=false</code>）会跳过安装运行时条目（例如通过 <code>devEngines.runtime</code> 下载的 Node.js），而不会修改锁文件。 锁文件保留了运行时条目，因此冻结锁文件验证仍然通过；只是跳过了运行时获取和 <code>.bin</code> 链接。 这在 CI 矩阵中很有用，运行时是在外部配置的（例如，通过 <code>pnpm runtime -g set node &lt;version&gt;</code> ），然后再运行 <code>pnpm install</code>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-bugs"><code>pnpm bugs</code><a href="https://pnpm.io/zh/blog/releases/11.1#pnpm-bugs" class="hash-link" aria-label="直接链接到 pnpm-bugs" title="直接链接到 pnpm-bugs" translate="no">​</a></h3>
<p>新的 <a href="https://pnpm.io/zh/cli/bugs"><code>pnpm bugs</code></a> 命令会在浏览器中打开软件包的 bug 跟踪器 URL。 不带任何参数时，它会读取当前项目的 <code>package.json</code>；带一个或多个包名时，它会从注册表中获取每个包的元数据并打开其错误跟踪器。 当缺少 <code>bugs</code> 字段时，它会回退到 <code>&lt;repository&gt;/issues</code>。 请参阅 <a href="https://github.com/pnpm/pnpm/pull/11279" target="_blank" rel="noopener noreferrer">#11279</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-owner"><code>pnpm owner</code><a href="https://pnpm.io/zh/blog/releases/11.1#pnpm-owner" class="hash-link" aria-label="直接链接到 pnpm-owner" title="直接链接到 pnpm-owner" translate="no">​</a></h3>
<p>新的 <a href="https://pnpm.io/zh/cli/owner"><code>pnpm owner</code></a> 命令用于管理注册源中的包所有者：</p>
<div class="language-sh codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-sh codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> owner </span><span class="token function" style="color:#d73a49">ls</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">package</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> owner </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">package</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">user</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> owner </span><span class="token function" style="color:#d73a49">rm</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">package</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">user</span><span class="token operator" style="color:#393A34">&gt;</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/11.1#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h2>
<ul>
<li>
<p><code>pnpm view</code> 现在会在其输出的其余部分旁边打印“由 Y 于 X 年前发布”，同步 <code>npm view</code> 表现。 在与 <code>minimumReleaseAge</code> 进行比较时，这很有用。 例如，<code>pnpm view pnpm</code> 现在显示 <code>published 17 hours ago by GitHub Actions</code>。</p>
</li>
<li>
<p><code>pnpm publish</code> 现在会在基于 Web 的身份验证流程期间轮询注册表的 <code>doneUrl</code> 时，遵循已配置的 HTTP/HTTPS 代理（包括 <code>https_proxy</code> / <code>http_proxy</code> / <code>no_proxy</code> 环境变量）。 之前轮询绕过了代理，导致注册源不同的源 IP 响应 <code>403</code>，登录永远无法完成 <a href="https://github.com/pnpm/pnpm/issues/11561" target="_blank" rel="noopener noreferrer">#11561</a>。</p>
</li>
<li>
<p><code>pnpm add -g</code> 现在默认将每个以空格分隔的包安装到其自己的独立目录中。 要将多个软件包捆绑到同一个隔离的安装中（以便它们共享依赖项并一起删除），请将它们作为逗号分隔的列表传递。 例如：</p>
<ul>
<li><code>pnpm add -g foo bar</code> 将 <code>foo</code> 和 <code>bar</code> 安装为两个独立的全局变量——删除其中一个不会影响另一个。</li>
<li><code>pnpm add -g foo,bar qar</code> 将 <code>foo</code> 和 <code>bar</code> 捆绑到一个独立的安装包中，而 <code>qar</code> 则单独安装。</li>
</ul>
<p>相关：<a href="https://github.com/pnpm/pnpm/issues/11587" target="_blank" rel="noopener noreferrer">#11587</a>。</p>
</li>
<li>
<p><code>pnpm runtime set &lt;name&gt; &lt;version&gt;</code> 在多包工作区的根目录中不再出现 <code>ADDING_TO_ROOT</code> 错误。 安装工作区根目录是运行时的有效目标，因此该命令现在绕过了该安全检查。</p>
</li>
<li>
<p>修复了在打印版本信息后，<code>pnpm --version</code> 在工作池生命周期内一直挂起的问题。 CLI 条目现在从它自己的 <code>finally</code> 中运行 <code>finishWorkers()</code>，因此每个退出路径都会销毁池。</p>
</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 11.0]]></title>
        <id>https://pnpm.io/zh/blog/releases/11.0</id>
        <link href="https://pnpm.io/zh/blog/releases/11.0"/>
        <updated>2026-04-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 11 来了！ 此版本加强了 v10 周期中引入的安全默认设置，放弃了 npm CLI 发布回退，转而采用原生实现，将每个包的 JSON 存储索引替换为单个 SQLite 数据库，并将全局安装隔离，使其不再相互干扰。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 11 来了！ 此版本加强了 v10 周期中引入的安全默认设置，放弃了 npm CLI 发布回退，转而采用原生实现，将每个包的 JSON 存储索引替换为单个 SQLite 数据库，并将全局安装隔离，使其不再相互干扰。</p>
<p>它还需要 Node.js 22 或更高版本——pnpm 本身现在是纯 ESM。</p>
<p>从 v10 升级？ 请参阅 <a href="https://pnpm.io/zh/migration">从 v10 迁移到 v11</a> 指南。 大多数配置更改都是机械性的，可以通过 <code>pnpm-v10-to-v11</code> 代码转换来应用。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="亮点">亮点<a href="https://pnpm.io/zh/blog/releases/11.0#%E4%BA%AE%E7%82%B9" class="hash-link" aria-label="直接链接到 亮点" title="直接链接到 亮点" translate="no">​</a></h2>
<ul>
<li><strong>需要 Node.js 22+。</strong> 已停止支持 Node 18、19、20 和 21。 独立可执行文件需要 glibc 2.27 或更高版本。</li>
<li><strong>供应链保护默认开启。</strong> <a href="https://pnpm.io/zh/settings#minimumreleaseage"><code>minimumReleaseAge</code></a> 默认为 <code>1440</code>（1 天），<a href="https://pnpm.io/zh/settings#blockexoticsubdeps"><code>blockExoticSubdeps</code></a> 默认为 <code>true</code>。</li>
<li><strong><code>allowBuilds</code> 取代了旧版构建依赖项设置。</strong> <code>onlyBuiltDependencies</code>、<code>onlyBuiltDependenciesFile</code>、<code>neverBuiltDependencies</code>、<code>ignoredBuiltDependencies</code> 和 <code>ignoreDepScripts</code> 已移除。</li>
<li><strong>全局安装是隔离的，默认情况下使用全局虚拟存储。</strong> 每个 <code>pnpm add -g</code> 都会获得自己的目录，其中包含自己的 <code>package.json</code>、<code>node_modules</code> 和 lockfile。</li>
<li><strong>新的 SQLite 支持的存储索引</strong>（存储 v11），捆绑了清单和十六进制摘要，减少了系统调用，加快了安装速度。</li>
<li><strong>Native publish flow.</strong> <a href="https://pnpm.io/zh/cli/publish"><code>pnpm publish</code></a>, <a href="https://pnpm.io/zh/cli/login"><code>login</code></a>, <a href="https://pnpm.io/zh/cli/logout"><code>logout</code></a>, <a href="https://pnpm.io/zh/cli/view"><code>view</code></a>, <a href="https://pnpm.io/zh/cli/deprecate"><code>deprecate</code></a>, <a href="https://pnpm.io/zh/cli/unpublish"><code>unpublish</code></a>, <a href="https://pnpm.io/zh/cli/dist-tag"><code>dist-tag</code></a>, and <a href="https://pnpm.io/zh/cli/version"><code>version</code></a> no longer delegate to the npm CLI.</li>
<li><strong><code>.npmrc</code> 仅用于身份验证/注册源。</strong> 其他设置必须放在 <code>pnpm-workspace.yaml</code> 或新的全局 <code>config.yaml</code> 中。 环境变量使用 <code>pnpm_config_*</code> 前缀。</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="破坏性改动">破坏性改动<a href="https://pnpm.io/zh/blog/releases/11.0#%E7%A0%B4%E5%9D%8F%E6%80%A7%E6%94%B9%E5%8A%A8" class="hash-link" aria-label="直接链接到 破坏性改动" title="直接链接到 破坏性改动" translate="no">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="需求">需求<a href="https://pnpm.io/zh/blog/releases/11.0#%E9%9C%80%E6%B1%82" class="hash-link" aria-label="直接链接到 需求" title="直接链接到 需求" translate="no">​</a></h3>
<ul>
<li>不支持 Node.js 18、19、20 和 21。</li>
<li>pnpm 现在以纯 ESM 格式分发。</li>
<li>独立可执行文件需要 glibc 2.27+。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="安全和构建默认值">安全和构建默认值<a href="https://pnpm.io/zh/blog/releases/11.0#%E5%AE%89%E5%85%A8%E5%92%8C%E6%9E%84%E5%BB%BA%E9%BB%98%E8%AE%A4%E5%80%BC" class="hash-link" aria-label="直接链接到 安全和构建默认值" title="直接链接到 安全和构建默认值" translate="no">​</a></h3>
<p>多项默认值已翻转为更安全的值：</p>
<table><thead><tr><th>设置</th><th>新的默认值</th></tr></thead><tbody><tr><td><code>minimumReleaseAge</code></td><td><code>1440</code>（1天）</td></tr><tr><td><code>minimumReleaseAgeStrict</code></td><td><code>false</code></td></tr><tr><td><code>blockExoticSubdeps</code></td><td><code>true</code></td></tr><tr><td><code>strictDepBuilds</code></td><td><code>true</code></td></tr><tr><td><code>optimisticRepeatInstall</code></td><td><code>true</code></td></tr><tr><td><code>verifyDepsBeforeRun</code></td><td><code>install</code></td></tr></tbody></table>
<p>新发布的软件包至少要过 1 天才能被解析。 若要选择退出，在 <code>pnpm-workspace.yaml</code> 中设置 <code>minimumReleaseAge: 0</code>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="allowbuilds-取代了旧的构建设置"><code>allowBuilds</code> 取代了旧的构建设置<a href="https://pnpm.io/zh/blog/releases/11.0#allowbuilds-%E5%8F%96%E4%BB%A3%E4%BA%86%E6%97%A7%E7%9A%84%E6%9E%84%E5%BB%BA%E8%AE%BE%E7%BD%AE" class="hash-link" aria-label="直接链接到 allowbuilds-取代了旧的构建设置" title="直接链接到 allowbuilds-取代了旧的构建设置" translate="no">​</a></h3>
<p><code>onlyBuiltDependencies</code>、<code>onlyBuiltDependenciesFile</code>、<code>neverBuiltDependencies</code>、<code>ignoredBuiltDependencies</code> 和 <code>ignoreDepScripts</code> 都已被移除。 请改用 <a href="https://pnpm.io/zh/settings#allowbuilds"><code>allowBuilds</code></a> — 一个从包名称模式到布尔值的映射：</p>
<p>之前：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">onlyBuiltDependencies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> electron</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">onlyBuiltDependenciesFile</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"allowed-builds.json"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">neverBuiltDependencies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> core</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">ignoredBuiltDependencies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> esbuild</span><br></span></code></pre></div></div>
<p>之后：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">allowBuilds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">electron</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">core-js</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">esbuild</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">false</span><br></span></code></pre></div></div>
<p>同时移除了：<code>allowNonAppliedPatches</code>（使用 <code>allowUnusedPatches</code>）和<code>ignorePatchFailures</code>（补丁失败现在会抛出异常）。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="npmrc-仅用于身份验证注册源"><code>.npmrc</code> 仅用于身份验证/注册源<a href="https://pnpm.io/zh/blog/releases/11.0#npmrc-%E4%BB%85%E7%94%A8%E4%BA%8E%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81%E6%B3%A8%E5%86%8C%E6%BA%90" class="hash-link" aria-label="直接链接到 npmrc-仅用于身份验证注册源" title="直接链接到 npmrc-仅用于身份验证注册源" translate="no">​</a></h3>
<p>pnpm 不再从 <code>.npmrc</code> 读取非身份验证设置。 配置分为两类：</p>
<ul>
<li><strong>注册源和认证设置</strong> — INI 文件（<code>.npmrc</code>, 全局<code>rc</code>文件, <code>~/.config/pnpm/auth.ini</code>）。</li>
<li><strong>pnpm 特定设置</strong> — YAML 文件（<code>pnpm-workspace.yaml</code>，新的全局 <code>~/.config/pnpm/config.yaml</code>）。</li>
</ul>
<p>其他相关变更：</p>
<ul>
<li>
<p>pnpm 不再读取 <code>npm_config_*</code> 环境变量。 请改用 <code>pnpm_config_*</code>（例如 <code>pnpm_config_registry</code>）。</p>
</li>
<li>
<p>pnpm 不再读取 <code>package.json</code> 中的 <code>pnpm</code> 字段。</p>
</li>
<li>
<p>pnpm 不再读取 npm 的全局配置，位于 <code>$PREFIX/etc/npmrc</code>。</p>
</li>
<li>
<p>网络设置（<code>httpProxy</code>、<code>httpsProxy</code>、<code>noProxy</code>、<code>localAddress</code>、<code>strictSsl</code>、<code>gitShallowHosts</code>）现在写入到 <code>config.yaml</code> / <code>pnpm-workspace.yaml</code> 中（仍然从 <code>.npmrc</code> 中读取以平滑迁移）。</p>
</li>
<li>
<p><code>pnpm-workspace.yaml</code> 文件中新增的 <code>registries</code> 设置替换了 <code>@scope:registry=</code> 行：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">registries</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">default</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//registry.npmjs.org/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">"@my-org"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//private.example.com/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">"@internal"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//nexus.corp.com/</span><br></span></code></pre></div></div>
</li>
<li>
<p>每个项目的 <code>.npmrc</code> 文件被 <code>pnpm-workspace.yaml</code> 文件中的 <code>packageConfigs</code> 文件取代：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">packages</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"packages/project-1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"packages/project-2"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">packageConfigs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">"project-1"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">saveExact</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">"project-2"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">savePrefix</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"~"</span><br></span></code></pre></div></div>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="原生发布流程不再使用-npm-cli-回退方案">原生发布流程，不再使用 npm CLI 回退方案<a href="https://pnpm.io/zh/blog/releases/11.0#%E5%8E%9F%E7%94%9F%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B%E4%B8%8D%E5%86%8D%E4%BD%BF%E7%94%A8-npm-cli-%E5%9B%9E%E9%80%80%E6%96%B9%E6%A1%88" class="hash-link" aria-label="直接链接到 原生发布流程，不再使用 npm CLI 回退方案" title="直接链接到 原生发布流程，不再使用 npm CLI 回退方案" translate="no">​</a></h3>
<p>以前通过传递给 <code>npm</code> CLI 实现的命令要么已经<strong>以原生方式重新实现</strong>，要么已经<strong>移除</strong>。</p>
<p>Reimplemented: <a href="https://pnpm.io/zh/cli/publish"><code>publish</code></a>, <a href="https://pnpm.io/zh/cli/view"><code>view</code></a> (<code>info</code>, <code>show</code>, <code>v</code>), <a href="https://pnpm.io/zh/cli/login"><code>login</code></a> (<code>adduser</code>), <a href="https://pnpm.io/zh/cli/logout"><code>logout</code></a>, <a href="https://pnpm.io/zh/cli/deprecate"><code>deprecate</code></a>, <a href="https://pnpm.io/zh/cli/unpublish"><code>unpublish</code></a>, <a href="https://pnpm.io/zh/cli/dist-tag"><code>dist-tag</code></a>, <a href="https://pnpm.io/zh/cli/version"><code>version</code></a>, <a href="https://pnpm.io/zh/cli/search"><code>search</code></a>, <a href="https://pnpm.io/zh/cli/star"><code>star</code>/<code>unstar</code>/<code>stars</code></a>, <a href="https://pnpm.io/zh/cli/whoami"><code>whoami</code></a>, <a href="https://pnpm.io/zh/cli/ping"><code>ping</code></a>, <a href="https://pnpm.io/zh/cli/docs"><code>docs</code>/<code>home</code></a>.</p>
<p>已移除（现在抛出“未实现”）：<code>access</code>、<code>bugs</code>、<code>edit</code>、<code>issues</code>、<code>owner</code>、<code>prefix</code>、<code>profile</code>、<code>pkg</code>、<code>repo</code>、<code>set-script</code>、<code>team</code>、<code>token</code>、<code>xmas</code>。</p>
<p>关于新的原生 <code>pnpm publish</code> 的一些说明：</p>
<ul>
<li>OTP 环境变量现在是 <code>PNPM_CONFIG_OTP</code>（以前是 <code>NPM_CONFIG_OTP</code>）。</li>
<li>如果注册源要求提供 OTP 但未提供，pnpm 将以交互方式提示输入 OTP。</li>
<li>基于网页的身份验证会显示可扫描的二维码和网址。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-audit-使用批量建议端点"><code>pnpm audit</code> 使用批量建议端点<a href="https://pnpm.io/zh/blog/releases/11.0#pnpm-audit-%E4%BD%BF%E7%94%A8%E6%89%B9%E9%87%8F%E5%BB%BA%E8%AE%AE%E7%AB%AF%E7%82%B9" class="hash-link" aria-label="直接链接到 pnpm-audit-使用批量建议端点" title="直接链接到 pnpm-audit-使用批量建议端点" translate="no">​</a></h3>
<p>注册源已停用旧版 <code>/-/npm/v1/security/audits{,/quick}</code> 端点。 <code>pnpm audit</code> 现在调用 <code>/-/npm/v1/security/advisories/bulk</code>，该命令不返回 CVE 标识符——因此，基于 CVE 的过滤已被<strong>基于 GHSA 的过滤</strong>所取代：</p>
<ul>
<li><code>auditConfig.ignoreCves</code> → <code>auditConfig.ignoreGhsas</code></li>
<li><code>pnpm audit --ignore &lt;id&gt;</code> 和 <code>--ignore-unfixable</code> 读取和写入 GHSA</li>
</ul>
<p>要进行迁移，请将 <code>ignoreCves</code> 下的每个 <code>CVE-YYYY-NNNNN</code> 条目替换为匹配的 <code>GHSA-xxxx-xxxx-xxxx</code>（在 <code>pnpm audit</code> 的 <code>More info</code> 列中可见），并将其移动到 <code>ignoreGhsas</code>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="孤立的全局虚拟存储的全局安装">孤立的、全局虚拟存储的全局安装<a href="https://pnpm.io/zh/blog/releases/11.0#%E5%AD%A4%E7%AB%8B%E7%9A%84%E5%85%A8%E5%B1%80%E8%99%9A%E6%8B%9F%E5%AD%98%E5%82%A8%E7%9A%84%E5%85%A8%E5%B1%80%E5%AE%89%E8%A3%85" class="hash-link" aria-label="直接链接到 孤立的、全局虚拟存储的全局安装" title="直接链接到 孤立的、全局虚拟存储的全局安装" translate="no">​</a></h3>
<p><code>pnpm add -g &lt;pkg&gt;</code> 和 <code>pnx</code> 现在使用全局虚拟存储，并且<strong>每个安装都会在 <code>{pnpmHomeDir}/global/v11/{hash}/</code> 获得自己的独立目录</strong>，其中包含自己的 <code>package.json</code>、<code>node_modules/</code> 和锁文件。 这样可以防止全局包之间因对等依赖冲突、提升更改或版本错位而相互干扰。</p>
<ul>
<li><code>pnpm remove -g &lt;pkg&gt;</code> 会删除包含该软件包的整个安装组。</li>
<li><code>pnpm update -g [pkg]</code> 会将软件包重新安装到一个新的隔离目录中。</li>
<li><code>pnpm list -g</code> 扫描隔离目录。</li>
<li><code>pnpm install -g</code>（不带参数）不再受支持——请使用 <code>pnpm add -g &lt;pkg&gt;</code>。</li>
</ul>
<p>全局安装的二进制文件现在位于 <code>PNPM_HOME</code> 的 <code>bin</code> 子目录中（而不是直接位于 <code>PNPM_HOME</code> 中），因此像 <code>global/</code> 和 <code>store/</code> 这样的内部目录不会污染 shell 自动补全。 <strong>升级后运行 <code>pnpm setup</code></strong> 以更新 shell 配置。</p>
<p><code>pnpm link</code> 也进行了收紧：只接受相对路径或绝对路径，移除了 <code>--global</code>（使用 <code>pnpm add -g .</code>），并且移除了不带参数的 <code>pnpm link</code>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="其他移除">其他移除<a href="https://pnpm.io/zh/blog/releases/11.0#%E5%85%B6%E4%BB%96%E7%A7%BB%E9%99%A4" class="hash-link" aria-label="直接链接到 其他移除" title="直接链接到 其他移除" translate="no">​</a></h3>
<ul>
<li>
<p><code>pnpm server</code>.</p>
</li>
<li>
<p><code>useNodeVersion</code> 和 <code>executionEnv.nodeVersion</code> — 使用 <code>devEngines.runtime</code> / <code>engines.runtime</code>。</p>
</li>
<li>
<p><code>hooks.fetchers</code> — 已被 pnpmfile 中的新 <code>fetchers</code> 字段取代。</p>
</li>
<li>
<p><code>managePackageManagerVersions</code>、<code>packageManagerStrict</code> 和 <code>packageManagerStrictVersion</code>。 这些都继承了旧版 <code>packageManager</code> 字段的 <code>onFail</code> 行为；新的 <code>pmOnFail</code> 设置包含了它们：</p>
<table><thead><tr><th>已移除</th><th>替换为</th></tr></thead><tbody><tr><td><code>managePackageManagerVersions: true</code></td><td><code>pmOnFail: download</code>（默认）</td></tr><tr><td><code>managePackageManagerVersions: false</code></td><td><code>pmOnFail: ignore</code></td></tr><tr><td><code>packageManagerStrict: false</code></td><td><code>pmOnFail: warn</code></td></tr><tr><td><code>packageManagerStrictVersion: true</code></td><td><code>pmOnFail: error</code></td></tr><tr><td><code>COREPACK_ENABLE_STRICT=0</code></td><td><code>pmOnFail: warn</code></td></tr></tbody></table>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="新命令">新命令<a href="https://pnpm.io/zh/blog/releases/11.0#%E6%96%B0%E5%91%BD%E4%BB%A4" class="hash-link" aria-label="直接链接到 新命令" title="直接链接到 新命令" translate="no">​</a></h2>
<table><thead><tr><th>命令</th><th>它的作用</th></tr></thead><tbody><tr><td><a href="https://pnpm.io/zh/cli/ci"><code>pnpm ci</code></a></td><td>运行 <code>pnpm clean</code>，然后运行 <code>pnpm install --frozen-lockfile</code>。 别名：<code>clean-install</code>、<code>ic</code>、<code>install-clean</code>.</td></tr><tr><td><a href="https://pnpm.io/zh/cli/clean"><code>pnpm clean</code></a></td><td>从所有工作区项目中移除 <code>node_modules</code>。 <code>--lockfile</code> 也会删除 <code>pnpm-lock.yaml</code>。</td></tr><tr><td><a href="https://pnpm.io/zh/cli/sbom"><code>pnpm sbom</code></a></td><td>生成 CycloneDX 1.7 或 SPDX 2.3 JSON 格式的软件物料清单。</td></tr><tr><td><a href="https://pnpm.io/zh/cli/peers"><code>pnpm peers check</code></a></td><td>报告锁文件中未满足/缺失的对等依赖。</td></tr><tr><td><a href="https://pnpm.io/zh/cli/runtime"><code>pnpm runtime set</code></a></td><td>安装运行时；弃用 <code>pnpm env use</code>。</td></tr><tr><td><a href="https://pnpm.io/zh/cli/docs"><code>pnpm docs</code></a> / <code>home</code></td><td>打开软件包主页。</td></tr><tr><td><a href="https://pnpm.io/zh/cli/ping"><code>pnpm ping</code></a></td><td>向注册源发送 Ping 请求。</td></tr><tr><td><a href="https://pnpm.io/zh/cli/with"><code>pnpm with</code></a></td><td>在单次调用中以特定（或当前）版本运行 pnpm，绕过 <code>packageManager</code> 的版本限制。</td></tr><tr><td><a href="https://pnpm.io/zh/cli/pack-app"><code>pnpm pack-app</code></a></td><td>通过 <a href="https://nodejs.org/api/single-executable-applications.html" target="_blank" rel="noopener noreferrer">Node.js SEA</a> 将 CommonJS 条目打包成一个或多个目标平台的独立可执行文件。</td></tr></tbody></table>
<p>Plus the natively reimplemented commands listed under "Native publish flow" above, and short aliases <a href="https://pnpm.io/zh/cli/pn"><code>pn</code></a> for <code>pnpm</code> and <a href="https://pnpm.io/zh/cli/pnx"><code>pnx</code></a> for <code>pnpx</code>/<code>pnpm dlx</code>.</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-audit---fixupdate"><code>pnpm audit --fix=update</code><a href="https://pnpm.io/zh/blog/releases/11.0#pnpm-audit---fixupdate" class="hash-link" aria-label="直接链接到 pnpm-audit---fixupdate" title="直接链接到 pnpm-audit---fixupdate" translate="no">​</a></h3>
<p>通过<strong>更新锁文件</strong> 中的软件包来修复易受伤害性，而不是添加覆盖。 为了实现更精细的控制，新增的 <code>--interactive</code> / <code>-i</code> 标志允许你选择要修复的警示：</p>
<div class="language-sh codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-sh codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> audit </span><span class="token parameter variable" style="color:#36acaa">--fix</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">update </span><span class="token parameter variable" style="color:#36acaa">--interactive</span><br></span></code></pre></div></div>
<p><code>pnpm audit --fix</code> 还会将每个安全公告的最小补丁版本添加到 <code>pnpm-workspace.yaml</code> 中的 <code>minimumReleaseAgeExclude</code>，这样就可以在不等待 <code>minimumReleaseAge</code> 的情况下安装安全修复程序。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="esm-pnpmfiles">ESM pnpmfiles<a href="https://pnpm.io/zh/blog/releases/11.0#esm-pnpmfiles" class="hash-link" aria-label="直接链接到 ESM pnpmfiles" title="直接链接到 ESM pnpmfiles" translate="no">​</a></h2>
<p>现在可以使用 <code>.mjs</code> 扩展名以 ESM 格式编写 pnpmfiles。 当 <code>.pnpmfile.mjs</code> 存在时，它的优先级高于 <code>.pnpmfile.cjs</code>，并且只会加载其中一个。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="存储-v11">存储 v11<a href="https://pnpm.io/zh/blog/releases/11.0#%E5%AD%98%E5%82%A8-v11" class="hash-link" aria-label="直接链接到 存储 v11" title="直接链接到 存储 v11" translate="no">​</a></h2>
<p>该存储的重建围绕两个理念展开：减少读取，减少系统调用。</p>
<ul>
<li>现在，软件包索引是一个位于 <code>$STORE/index.db</code> 的单个 <strong>SQLite 数据库</strong>（具有 MessagePack 值，WAL 模式用于并发访问），而不是位于 <code>$STORE/index/</code> 下的数百万个 JSON 文件。 新索引中缺失的软件包会根据需要重新获取。</li>
<li><strong>捆绑清单</strong>（名称、版本、bin、引擎、脚本等） 直接存储在软件包索引中，无需在解析和安装过程中从 CAS 读取 <code>package.json</code>。</li>
<li>索引条目存储<strong>十六进制摘要</strong>，而不是完整的完整性字符串（<code>&lt;algo&gt;-&lt;digest&gt;</code>），并且哈希算法每个文件记录一次，而不是每个条目记录一次。 这样可以避免每次 CAS 路径查找时进行 base64 → hex 转换。</li>
<li>启用全局虚拟存储后，不允许构建的软件包（并且不间接依赖于允许构建的软件包）将获得<strong>不包含引擎名称</strong>（平台、架构、Node.js 主版本号）的哈希值。 现在约 95% 的 GVS 包无需重新导入即可在 Node.js 升级和架构变更后继续保留。</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="性能">性能<a href="https://pnpm.io/zh/blog/releases/11.0#%E6%80%A7%E8%83%BD" class="hash-link" aria-label="直接链接到 性能" title="直接链接到 性能" translate="no">​</a></h2>
<p>许多小胜利增加了一个明显更快的安装：</p>
<ul>
<li><strong><code>undici</code> 取代了 <code>node-fetch</code></strong> 用于所有 HTTP 请求，具有 Happy Eyeballs（双栈）、更好的 keep-alive 和优化的全局调度器。</li>
<li>已知大小的 Tarball 下载会<strong>预先分配内存</strong>，以避免重复复制带来的开销。</li>
<li>元数据缓存现在采用 <strong>NDJSON</strong> 格式，并带有 <code>If-Modified-Since</code> 以进行条件获取。</li>
<li><code>minimumReleaseAge</code> 检查使用 <strong>简化的元数据</strong> 端点来获取更少的信息。</li>
<li>CAS 文件直接写入其内容寻址路径，而不是通过临时文件 + 重命名 — 每次冷安装可节省约 30k 次重命名系统调用。</li>
<li>导入到 <code>node_modules</code> 时，暂存目录<strong>消失了</strong>。</li>
<li>CAS 中的热路径字符串操作得到了加强，<code>gunzipSync</code> 运行时使用更大的块大小，以减少 tarball 解压缩期间的缓冲区分配。</li>
<li>在 GVS 热重装过程中，如果没有添加任何软件包，则会跳过冗余的内部链接。</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="更精简的运行时安装">更精简的运行时安装<a href="https://pnpm.io/zh/blog/releases/11.0#%E6%9B%B4%E7%B2%BE%E7%AE%80%E7%9A%84%E8%BF%90%E8%A1%8C%E6%97%B6%E5%AE%89%E8%A3%85" class="hash-link" aria-label="直接链接到 更精简的运行时安装" title="直接链接到 更精简的运行时安装" translate="no">​</a></h2>
<p>通过 <code>node@runtime:&lt;version&gt;</code>（包括 <code>pnpm env use</code> 和 <code>pnpm runtime set node</code>）安装 Node.js 运行时<strong>不再从 Node.js 归档中提取捆绑的 <code>npm</code>、<code>npx</code> 和 <code>corepack</code></strong>。 这大约减少了一半数量的pnpm需要哈希、写入CAS和链接的文件。 如果仍然需要 <code>npm</code>，请将其作为单独的软件包安装。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="其他重要改动">其他重要改动<a href="https://pnpm.io/zh/blog/releases/11.0#%E5%85%B6%E4%BB%96%E9%87%8D%E8%A6%81%E6%94%B9%E5%8A%A8" class="hash-link" aria-label="直接链接到 其他重要改动" title="直接链接到 其他重要改动" translate="no">​</a></h2>
<ul>
<li>
<p><strong>更清晰的脚本输出。</strong> <code>pnpm</code> 现在打印 <code>$ command</code>（到 stderr，因此 stdout 保持管道友好），而不是 <code>&gt; pkg@version stage path\n&gt; command</code>。 只有在不同目录下运行时才会显示项目名称和路径。</p>
</li>
<li>
<p>安装过程中，<strong>对等依赖关系问题</strong>不再以树状结构呈现——pnpm 建议运行 <code>pnpm peers check</code> 来查看它们。</p>
</li>
<li>
<p><strong>生命周期脚本</strong>不再从 pnpm 配置中获取 <code>npm_config_*</code> 环境变量；仅设置众所周知的 <code>npm_*</code> 环境变量，与 Yarn 一致。</p>
</li>
<li>
<p>当启用 <code>init-package-manager</code> 时，<strong><code>pnpm init</code></strong> 会写入 <code>devEngines.packageManager</code> 而不是 <code>packageManager</code>，并且默认的 <code>type</code> 现在是 <code>"module"</code>。</p>
</li>
<li>
<p><strong><code>devEngines.packageManager</code></strong> 现在支持版本范围；解析后的版本存储在 <code>pnpm-lock.yaml</code> 中，如果仍然满足范围，则会被重用。</p>
</li>
<li>
<p><strong><code>dedupePeers</code></strong> 是一个新设置，它使用仅包含版本信息的后缀 (<code>name@version</code>) 而不是完整的依赖路径，从而消除具有许多递归对等项的项目的嵌套后缀，例如 <code>(foo@1.0.0(bar@2.0.0))</code>。</p>
</li>
<li>
<p><strong><code>pnpm approve-builds</code></strong> 现在接受用于非交互式使用的位置参数；在名称前加上 <code>!</code> 即可拒绝。</p>
</li>
<li>
<p><strong>隐藏脚本</strong> — 以 <code>.</code> 开头的脚本只能从其他脚本调用，并且不会显示在 <code>pnpm run</code> 中。</p>
</li>
<li>
<p><strong><code>-F</code></strong> 是 <code>--filter</code> 的新简短别名。</p>
</li>
<li>
<p><strong><code>pnpm add</code> 短标志</strong> — <code>-d</code> 现在是 <code>--save-dev</code>，<code>-p</code> 是 <code>--save-prod</code>，<code>-o</code> 是 <code>--save-optional</code>，<code>-e</code> 是 <code>--save-exact</code>（仅在 <code>pnpm add</code> 内部）。</p>
</li>
<li>
<p><strong><code>virtualStoreOnly</code></strong> 在不创建导入器符号链接、提升、bin 链接或运行生命周期脚本下填充虚拟存储。 在 Nix 构建中预先填充存储很有用。 <code>pnpm fetch</code> 现在内部使用此功能。</p>
</li>
<li>
<p><code>pnpm-workspace.yaml</code> 中的 <code>nodeDownloadMirrors</code> 替换了 <code>.npmrc</code> 中的 <code>node-mirror:&lt;channel&gt;</code> 设置：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">nodeDownloadMirrors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">release</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//my</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">mirror.example.com/download/release/</span><br></span></code></pre></div></div>
</li>
<li>
<p><strong>配置依赖项</strong> 现在已安装到 <code>{storeDir}/links/</code> 中，并且符号链接到 <code>node_modules/.pnpm-config/</code>，因此它们可以在使用同一存储的项目之间共享。 已解析的版本和完整性哈希值已从 <code>pnpm-workspace.yaml</code> 移至 <code>pnpm-lock.yaml</code> 中的单独文档；旧的内联哈希项目会自动迁移。</p>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="升级">升级<a href="https://pnpm.io/zh/blog/releases/11.0#%E5%8D%87%E7%BA%A7" class="hash-link" aria-label="直接链接到 升级" title="直接链接到 升级" translate="no">​</a></h2>
<p>有关代码转换和后续操作的完整指南，请参阅 <a href="https://pnpm.io/zh/migration">从 v10 迁移到 v11</a>。 简短版本：</p>
<ul>
<li>升级前，请将 CI 和开发环境升级到 <strong>Node.js 22+</strong> 版本。</li>
<li>将 pnpm 设置从 <code>.npmrc</code> 移到 <code>pnpm-workspace.yaml</code>（或全局的 <code>~/.config/pnpm/config.yaml</code>）。</li>
<li>将 <code>onlyBuiltDependencies</code> 及其相关功能迁移到 <code>allowBuilds</code>。</li>
<li>将 <code>auditConfig.ignoreCves</code> 迁移到 <code>auditConfig.ignoreGhsas</code>。</li>
<li>安装 v11 后，运行 <code>pnpm setup</code> 来更新你的 shell，以便将新的 <code>bin</code> 子目录添加到 <code>PATH</code> 中。</li>
</ul>
<p>完整的更改列表在 <a href="https://github.com/pnpm/pnpm/blob/main/pnpm/CHANGELOG.md%E4%B8%AD" target="_blank" rel="noopener noreferrer">更改日志</a>。 如果你依赖的某些功能缺失或损坏，请在 <a href="https://github.com/pnpm/pnpm/issues" target="_blank" rel="noopener noreferrer">github.com/pnpm/pnpm/issues</a> 上提交问题。</p><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.32]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.32</id>
        <link href="https://pnpm.io/zh/blog/releases/10.32"/>
        <updated>2026-03-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 10.32 为 pnpm approve-builds 添加了 --all 标志，用于在不显示交互式提示的情况下批准所有待处理的构建。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 10.32 为 <code>pnpm approve-builds</code> 添加了 <code>--all</code> 标志，用于在不显示交互式提示的情况下批准所有待处理的构建。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.32#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="--all-标志用于-pnpm-approve-builds"><code>--all</code> 标志用于 <code>pnpm approve-builds</code><a href="https://pnpm.io/zh/blog/releases/10.32#--all-%E6%A0%87%E5%BF%97%E7%94%A8%E4%BA%8E-pnpm-approve-builds" class="hash-link" aria-label="直接链接到 --all-标志用于-pnpm-approve-builds" title="直接链接到 --all-标志用于-pnpm-approve-builds" translate="no">​</a></h4>
<p>为 <code>pnpm approve-builds</code> 添加了 <code>--all</code> 标志，该标志无需交互式提示即可批准所有待处理的构建 <a href="https://github.com/pnpm/pnpm/issues/10136" target="_blank" rel="noopener noreferrer">#10136</a>。</p>
<div class="language-sh codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-sh codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> approve-builds </span><span class="token parameter variable" style="color:#36acaa">--all</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.32#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>撤销了与显式设置 npm 配置文件路径相关的更改，该更改导致了回归问题。</li>
<li>撤销了与 <code>lockfile-include-tarball-url</code> 相关的修复。 修复 <a href="https://github.com/pnpm/pnpm/issues/10915" target="_blank" rel="noopener noreferrer">#10915</a>。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.31]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.31</id>
        <link href="https://pnpm.io/zh/blog/releases/10.31"/>
        <updated>2026-03-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 10.31 在更新 pnpm-workspace.yaml 时保留注释和格式，并包含大量错误修复。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 10.31 在更新 <code>pnpm-workspace.yaml</code> 时保留注释和格式，并包含大量错误修复。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.31#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="保留-pnpm-workspaceyaml-中的注释">保留 <code>pnpm-workspace.yaml</code> 中的注释<a href="https://pnpm.io/zh/blog/releases/10.31#%E4%BF%9D%E7%95%99-pnpm-workspaceyaml-%E4%B8%AD%E7%9A%84%E6%B3%A8%E9%87%8A" class="hash-link" aria-label="直接链接到 保留-pnpm-workspaceyaml-中的注释" title="直接链接到 保留-pnpm-workspaceyaml-中的注释" translate="no">​</a></h4>
<p>当 pnpm 更新 <code>pnpm-workspace.yaml</code> 时，注释、字符串格式和空格将被保留。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.31#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>在帮助输出中添加了 <code>-F</code> 作为 <code>--filter</code> 选项的简短别名。</li>
<li>处理 <code>pnpm why -r</code> 中未定义的 pkgSnapshot <a href="https://github.com/pnpm/pnpm/issues/10700" target="_blank" rel="noopener noreferrer">#10700</a>。</li>
<li>修复当项目注入自引用 <code>file:</code> 依赖项并在锁文件中解析为 <code>link:</code> 时，未使用无头安装的问题。</li>
<li>修复了多个工作线程同时将同一个包导入全局虚拟存储时出现的竞态条件。 如果另一个线程已经完成了导入，重命名操作现在可以容忍 <code>ENOTEMPTY</code>/<code>EEXIST</code> 错误。</li>
<li>当 <code>lockfile-include-tarball-url</code> 设置为 <code>false</code> 时，tarball URL 现在总是从锁文件中排除。 以前，对于托管在非标准 URL 下的软件包，tarball URL 仍然可能出现 <a href="https://github.com/pnpm/pnpm/issues/6667" target="_blank" rel="noopener noreferrer">#6667</a>。</li>
<li>修复了当 <code>overrides</code>、<code>packageExtensions</code>、<code>ignoredOptionalDependencies</code>、<code>patchedDependencies</code> 或 <code>peersSuffixMaxLength</code> 更改时，<code>optimisticRepeatInstall</code> 跳过安装的问题。</li>
<li>修复了在 HOME 未设置或非标准环境（Docker 容器、CI 系统）中，<code>pnpm patch-commit</code> 失败并出现“无法访问 '/.config/git/attributes': 权限被拒绝”错误的问题 <a href="https://github.com/pnpm/pnpm/issues/6537" target="_blank" rel="noopener noreferrer">#6537</a>。</li>
<li>修复当多个工作区包共享同一依赖项时，<code>pnpm why -r --parseable</code> 缺少依赖项的问题 <a href="https://github.com/pnpm/pnpm/issues/8100" target="_blank" rel="noopener noreferrer">#8100</a>。</li>
<li>修复当请求的版本与工作区包的版本不匹配时，<code>link-workspace-packages=true</code> 错误地链接工作区包的问题 <a href="https://github.com/pnpm/pnpm/issues/10173" target="_blank" rel="noopener noreferrer">#10173</a>。</li>
<li>修复<code>pnpm update --interactive</code> 表格与长版本字符串断开，动态计算列宽度，而不是使用硬代码值 <a href="https://github.com/pnpm/pnpm/issues/10316" target="_blank" rel="noopener noreferrer">#10316</a>。</li>
<li><code>--allow-build</code> 标志设置的参数被写入 <code>allowBuilds</code>。</li>
<li>修复了在 <code>pnpm-workspace.yaml</code> 上指定 <code>filter</code> 会导致 pnpm 检测不到任何项目的 bug。</li>
<li>在没有参数和退出的情况下运行 pnpm dlx` 时打印帮助信息。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.30]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.30</id>
        <link href="https://pnpm.io/zh/blog/releases/10.30"/>
        <updated>2026-02-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 10.30 重新设计了 pnpm why，以显示反向依赖关系树，从而更容易理解为什么安装了一个软件包。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 10.30 重新设计了 <code>pnpm why</code>，以显示反向依赖关系树，从而更容易理解为什么安装了一个软件包。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.30#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-why-中的反向依赖树"><code>pnpm why</code> 中的反向依赖树<a href="https://pnpm.io/zh/blog/releases/10.30#pnpm-why-%E4%B8%AD%E7%9A%84%E5%8F%8D%E5%90%91%E4%BE%9D%E8%B5%96%E6%A0%91" class="hash-link" aria-label="直接链接到 pnpm-why-中的反向依赖树" title="直接链接到 pnpm-why-中的反向依赖树" translate="no">​</a></h4>
<p><code>pnpm why</code> 现在会显示反向依赖关系树。 搜索到的软件包出现在根目录，其依赖者作为分支，一直追溯到工作区根目录。 这取代了之前的前向树输出，之前的输出对于深度嵌套的依赖关系来说噪声很大且难以阅读。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.30#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>通过在所有导入器之间共享依赖关系图和物化缓存，而不是为每个导入器独立重建它们，来优化具有许多导入器的工作区中的 <code>pnpm why</code> 和 <code>pnpm list</code> 的性能 <a href="https://github.com/pnpm/pnpm/pull/10596" target="_blank" rel="noopener noreferrer">#10596</a>。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.29]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.29</id>
        <link href="https://pnpm.io/zh/blog/releases/10.29"/>
        <updated>2026-02-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 10.29 为 pnpm dlx 添加了 catalog 说明符，并修复了几个错误。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 10.29 为 <code>pnpm dlx</code> 添加了 <code>catalog:</code> 协议支持，允许在 <code>pnpm-workspace.yaml</code> 中配置 <code>auditLevel</code>，支持裸 <code>workspace:</code> 说明符，并修复了几个错误。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.29#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-dlx-中的-catalog-协议"><code>pnpm dlx</code> 中的 <code>catalog:</code> 协议<a href="https://pnpm.io/zh/blog/releases/10.29#pnpm-dlx-%E4%B8%AD%E7%9A%84-catalog-%E5%8D%8F%E8%AE%AE" class="hash-link" aria-label="直接链接到 pnpm-dlx-中的-catalog-协议" title="直接链接到 pnpm-dlx-中的-catalog-协议" translate="no">​</a></h4>
<p><code>pnpm dlx</code> / <code>pnpx</code> 命令现在支持 <code>catalog:</code> 协议，允许你引用工作区目录中定义的版本：</p>
<div class="language-sh codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-sh codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> dlx shx@catalog:</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="auditlevel-设置"><code>auditLevel</code> 设置<a href="https://pnpm.io/zh/blog/releases/10.29#auditlevel-%E8%AE%BE%E7%BD%AE" class="hash-link" aria-label="直接链接到 auditlevel-设置" title="直接链接到 auditlevel-设置" translate="no">​</a></h4>
<p>现在可以在 <code>pnpm-workspace.yaml</code> 文件中配置 <code>auditLevel</code>，因此你无需在每次调用 <code>pnpm audit</code> 时都传递 <code>--audit-level</code> 参数 <a href="https://github.com/pnpm/pnpm/pull/10540" target="_blank" rel="noopener noreferrer">#10540</a>：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_KdeV">pnpm-workspace.yaml</div><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">auditLevel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> high</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="裸-workspace-协议">裸 <code>workspace:</code> 协议<a href="https://pnpm.io/zh/blog/releases/10.29#%E8%A3%B8-workspace-%E5%8D%8F%E8%AE%AE" class="hash-link" aria-label="直接链接到 裸-workspace-协议" title="直接链接到 裸-workspace-协议" translate="no">​</a></h4>
<p>现在支持不带版本范围的 <code>workspace:</code> 说明符。 它被视为 <code>workspace:*</code>，并在发布期间解析为具体版本 <a href="https://github.com/pnpm/pnpm/pull/10436" target="_blank" rel="noopener noreferrer">#10436</a>：</p>
<div class="language-json codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-json codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"dependencies"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"foo"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace:"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.29#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>修复了在大型依赖关系图上 <code>pnpm list</code>（以及 <code>pnpm why</code>）的内存不足错误，方法是将递归树构建器替换为两阶段方法：先构建 BFS 依赖关系图，然后进行缓存树实现。 现在输出中已对重复子树进行去重 <a href="https://github.com/pnpm/pnpm/pull/10586" target="_blank" rel="noopener noreferrer">#10586</a>。</li>
<li>修复了通过 <code>.pnpmfile.cjs</code> 设置时 <code>allowBuilds</code> 不起作用的问题 <a href="https://github.com/pnpm/pnpm/issues/10516" target="_blank" rel="noopener noreferrer">#10516</a>。</li>
<li>当设置了 <code>enableGlobalVirtualStore</code>，<code>pnpm deploy</code> 现在会忽略它，并始终在部署目录中创建一个本地化的虚拟存储，以保持其自包含性。</li>
<li>修复了 <code>pnpm dlx</code> 不遵守 <code>minimumReleaseAgeExclude</code> 的问题 <a href="https://github.com/pnpm/pnpm/issues/10338" target="_blank" rel="noopener noreferrer">#10338</a>。</li>
<li>修复了使用全局虚拟存储时 <code>pnpm list --json</code> 返回错误路径的问题 <a href="https://github.com/pnpm/pnpm/issues/10187" target="_blank" rel="noopener noreferrer">#10187</a>。</li>
<li>修复了当 <code>storeDir</code> 为相对路径时，<code>pnpm store path</code> 和 <code>pnpm store status</code> 使用工作区根目录进行路径解析 <a href="https://github.com/pnpm/pnpm/issues/10290" target="_blank" rel="noopener noreferrer">#10290</a>。</li>
<li>修复了在重新添加现有目录依赖项时，<code>catalogMode: strict</code> 将字面字符串 <code>catalog:</code> 写入 <code>pnpm-workspace.yaml</code> 而不是已解析的版本说明符的问题 <a href="https://github.com/pnpm/pnpm/issues/10176" target="_blank" rel="noopener noreferrer">#10176</a>。</li>
<li>在执行 <code>pnpm fetch</code> 期间跳过本地 <code>file:</code> 协议依赖项，修复本地目录依赖项不可用时的 Docker 构建问题 <a href="https://github.com/pnpm/pnpm/issues/10460" target="_blank" rel="noopener noreferrer">#10460</a>。</li>
<li>修复了 <code>pnpm audit --json</code> 以在退出代码和输出过滤器上尊重 <code>--audit-level</code> 设置 <a href="https://github.com/pnpm/pnpm/pull/10540" target="_blank" rel="noopener noreferrer">#10540</a>。</li>
<li>将 <code>tar</code> 更新到 7.5.7 版本，以修复安全漏洞 (<a href="https://www.cve.org/CVERecord?id=CVE-2026-24842" target="_blank" rel="noopener noreferrer">CVE-2026-24842</a>)。</li>
<li>修复了 <code>pnpm audit --fix</code> 将引用覆盖（例如 <code>$foo</code>）替换为具体版本的问题 <a href="https://github.com/pnpm/pnpm/issues/10325" target="_blank" rel="noopener noreferrer">#10325</a>。</li>
<li>修复了通过 <code>.pnpmfile.cjs</code> 中的 <code>updateConfig</code> 设置的 <code>shamefullyHoist</code> 未转换为 <code>publicHoistPattern</code> <a href="https://github.com/pnpm/pnpm/issues/10271" target="_blank" rel="noopener noreferrer">#10271</a>。</li>
<li><code>pnpm help</code> 现在可以正确报告当前运行的 pnpm CLI 是否与 Node.js 捆绑在一起 <a href="https://github.com/pnpm/pnpm/issues/10561" target="_blank" rel="noopener noreferrer">#10561</a>。</li>
<li>添加警告，当当前目录包含 PATH 分隔符字符时，可能会破坏 <code>node_modules/.bin</code> 路径注入 <a href="https://github.com/pnpm/pnpm/issues/10457" target="_blank" rel="noopener noreferrer">#10457</a>。</li>
<li>修复了 <code>pnpm completion --help</code> 中显示的文档 URL，使其指向正确的页面 <a href="https://github.com/pnpm/pnpm/issues/10281" target="_blank" rel="noopener noreferrer">#10281</a>。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.28]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.28</id>
        <link href="https://pnpm.io/zh/blog/releases/10.28"/>
        <updated>2026-01-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 10.28 引入了一个新的 beforePacking 钩子，用于在发布时自定义 package.json，提高了过滤安装的性能，并修复了几个错误。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 10.28 引入了一个新的 <code>beforePacking</code> 钩子，用于在发布时自定义 package.json，提高了过滤安装的性能，并修复了几个错误。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.28#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="beforepacking-钩子"><code>beforePacking</code> 钩子<a href="https://pnpm.io/zh/blog/releases/10.28#beforepacking-%E9%92%A9%E5%AD%90" class="hash-link" aria-label="直接链接到 beforepacking-钩子" title="直接链接到 beforepacking-钩子" translate="no">​</a></h4>
<p>添加了对名为 <code>beforePacking</code> 的新钩子的支持，允许你在发布时自定义 <code>package.json</code> 内容 <a href="https://github.com/pnpm/pnpm/issues/3816" target="_blank" rel="noopener noreferrer">#3816</a>。</p>
<p>在运行 <code>pnpm pack</code> 或 <code>pnpm publish</code> 时，这个钩子就在创建 tarball 之前被调用。 它允许你修改将包含在已发布软件包中的软件包清单，而不会影响你本地的 <code>package.json</code> 文件。</p>
<p><code>.pnpmfile.cjs</code> 中的示例用法：</p>
<div class="language-js codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-js codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">hooks</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">beforePacking</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">pkg</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// 移除仅用于开发的字段</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">delete</span><span class="token plain"> pkg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">devDependencies</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">delete</span><span class="token plain"> pkg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">scripts</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// 添加发布元数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      pkg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">publishedAt</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Date</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">toISOString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> pkg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>有关更多详细信息，请参阅 <a href="https://pnpm.io/zh/pnpmfile#hooksbeforepackingpkg-pkg--promisepkg">.pnpmfile.cjs 文档</a>。</p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="过滤后的安装性能">过滤后的安装性能<a href="https://pnpm.io/zh/blog/releases/10.28#%E8%BF%87%E6%BB%A4%E5%90%8E%E7%9A%84%E5%AE%89%E8%A3%85%E6%80%A7%E8%83%BD" class="hash-link" aria-label="直接链接到 过滤后的安装性能" title="直接链接到 过滤后的安装性能" translate="no">​</a></h4>
<p>在某些情况下，使用过滤参数进行安装（即 <code>pnpm install --filter ...</code>）比不带任何过滤参数运行 <code>pnpm install</code> 的速度要慢。 此性能下降问题现已修复。 过滤后的安装速度应该与完整安装一样快或更快 <a href="https://github.com/pnpm/pnpm/pull/10408" target="_blank" rel="noopener noreferrer">#10408</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.28#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>如果存储位于项目的子目录中，则不要将指向该项目的符号链接添加到存储的项目注册表中 <a href="https://github.com/pnpm/pnpm/issues/10411" target="_blank" rel="noopener noreferrer">#10411</a>。</li>
<li>应该可以在 <code>pnpm-workspace.yaml</code> 中声明 <code>requiredScripts</code> 设置 <a href="https://github.com/pnpm/pnpm/issues/10261" target="_blank" rel="noopener noreferrer">#10261</a>。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.27]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.27</id>
        <link href="https://pnpm.io/zh/blog/releases/10.27"/>
        <updated>2025-12-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 10.27 新增了一个设置，可以忽略旧版本软件包的信任策略检查，引入了一个用于全局虚拟存储修剪的项目注册表，并修复了几个错误。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 10.27 新增了一个设置，可以忽略旧版本软件包的信任策略检查，引入了一个用于全局虚拟存储修剪的项目注册表，并修复了几个错误。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.27#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="trustpolicyignoreafter"><code>trustPolicyIgnoreAfter</code><a href="https://pnpm.io/zh/blog/releases/10.27#trustpolicyignoreafter" class="hash-link" aria-label="直接链接到 trustpolicyignoreafter" title="直接链接到 trustpolicyignoreafter" translate="no">​</a></h4>
<p>添加 <code>trustPolicyIgnoreAfter</code> 可以忽略对超过指定时间发布的软件包的信任策略检查 <a href="https://github.com/pnpm/pnpm/issues/10352" target="_blank" rel="noopener noreferrer">#10352</a>。</p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="全局虚拟存储改进">全局虚拟存储改进<a href="https://pnpm.io/zh/blog/releases/10.27#%E5%85%A8%E5%B1%80%E8%99%9A%E6%8B%9F%E5%AD%98%E5%82%A8%E6%94%B9%E8%BF%9B" class="hash-link" aria-label="直接链接到 全局虚拟存储改进" title="直接链接到 全局虚拟存储改进" translate="no">​</a></h4>
<p>添加了项目注册源，以支持全局虚拟商店清理。</p>
<p>使用该存储的项目现在通过符号链接在 <code>{storeDir}/v10/projects/</code> 中注册。 这使得 <code>pnpm store prune</code> 能够跟踪哪些软件包仍在被活跃项目使用，并安全地从全局虚拟存储中删除未使用的软件包。</p>
<p><strong>半破坏性更改。</strong> 更改了虚拟全局存储中未限定范围的软件包的位置。 为了保持统一的 4 级目录深度，它们现在将被存储在名为 <code>@</code> 的目录下。</p>
<p>为全球虚拟存储添加了标记清除垃圾回收功能。</p>
<p><code>pnpm store prune</code> 现在会从全局虚拟存储的 <code>links/</code> 目录中删除未使用的软件包。 算法：</p>
<ol>
<li>扫描所有已注册项目，查找指向存储的符号链接</li>
<li>遍历传递依赖关系以标记可达包</li>
<li>删除所有未标记为可达的软件包目录</li>
</ol>
<p>这包括对工作区单存储库的支持——扫描项目中的所有 <code>node_modules</code> 目录（包括工作区包中的目录）。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.27#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>如果 <code>tokenHelper</code> 或 <code>&lt;url&gt;:tokenHelper</code> 设置的值包含环境变量，则抛出错误。</li>
<li>带有构建脚本的 Git 依赖项应遵循 <code>dangerouslyAllowAllBuilds</code> 设置 <a href="https://github.com/pnpm/pnpm/issues/10376" target="_blank" rel="noopener noreferrer">#10376</a>。</li>
<li>如果使用 --global 运行程序并且配置了项目包管理器，则跳过包管理器检查，并发出跳过检查的警告。</li>
<li>如果 dlx 缓存目录包含文件（而不仅仅是目录），则 <code>pnpm store prune</code> 不应失败 <a href="https://github.com/pnpm/pnpm/pull/10384" target="_blank" rel="noopener noreferrer">#10384</a></li>
<li>修复了一个 bug (<a href="https://github.com/pnpm/pnpm/issues/9759" target="_blank" rel="noopener noreferrer">#9759</a>)，其中<code>pnpm add</code> 错误地将一个 <code>pnpm-workspace.yaml</code> 目录修改为精确版本。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[🚀 2025年的 pnpm]]></title>
        <id>https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025</id>
        <link href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025"/>
        <updated>2025-12-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[2025 年对于 pnpm 来说是具有变革意义的一年。 虽然我们的主要重点是重新定义软件包管理的安全模型，但我们也显著提高了性能和开发者体验。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>2025 年对于 pnpm 来说是具有变革意义的一年。 虽然我们的主要重点是重新定义软件包管理的安全模型，但我们也显著提高了性能和开发者体验。</p>
<p>从默认阻止生命周期脚本到引入全局虚拟存储，以下回顾一下 2025 年发布的主要功能。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="使用方法">使用方法<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95" class="hash-link" aria-label="直接链接到 使用方法" title="直接链接到 使用方法" translate="no">​</a></h2>
<p>根据 <a href="https://npm-stat.com/charts.html?package=pnpm&amp;from=2016-12-01&amp;to=2025-12-29" target="_blank" rel="noopener noreferrer">下载统计</a> pnpm 的下载量是 2024 年的 2 倍！</p>
<p><img decoding="async" loading="lazy" src="https://pnpm.io/zh/assets/images/download-stats-2025-bc5a6c1e5b99cc9ed041229899d563f7.png" width="1990" height="758" class="img_M8jV"></p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="首页重新设计">首页重新设计<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E9%A6%96%E9%A1%B5%E9%87%8D%E6%96%B0%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="直接链接到 首页重新设计" title="直接链接到 首页重新设计" translate="no">​</a></h2>
<p>你可能已经注意到，我们重新设计了主页！ 此次重新设计得益于我们最主要的赞助商 <a href="https://bit.cloud/" target="_blank" rel="noopener noreferrer">Bit.cloud</a>。</p>
<p>新的主页现在使用 <a href="https://bit.cloud/pnpm/website" target="_blank" rel="noopener noreferrer">Bit 组件</a> 构建，其中很多工作都是使用 Bit 的 AI 代理 <a href="https://bit.cloud/products/hope-ai" target="_blank" rel="noopener noreferrer">Hope AI</a> 完成的。<a href="https://bit.cloud/pnpm/website" target="_blank" rel="noopener noreferrer">https://bit.cloud/pnpm/website</a> 我们现在甚至有了自己的<a href="https://bit.cloud/pnpm/design" target="_blank" rel="noopener noreferrer">设计系统</a>。</p>
<div class="theme-admonition theme-admonition-info admonition_g_IF alert alert--info"><div class="admonitionHeading_Ll6V"><span class="admonitionIcon_pyky"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>提示</div><div class="admonitionContent_e01q"><p>我在 Bit 公司全职从事依赖管理工作。 在底层，Bit <a href="https://github.com/teambit/bit/blob/9de9a2bce5183d79ee805c4fba3c3386e9384eac/workspace.jsonc#L52-L80" target="_blank" rel="noopener noreferrer">使用 pnpm 进行安装</a>。</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="在-jsnation-上的演讲">在 JSNation 上的演讲<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E5%9C%A8-jsnation-%E4%B8%8A%E7%9A%84%E6%BC%94%E8%AE%B2" class="hash-link" aria-label="直接链接到 在 JSNation 上的演讲" title="直接链接到 在 JSNation 上的演讲" translate="no">​</a></h2>
<p>今年对我个人而言是一个巨大的里程碑，因为我第一次在大型国际会议——6 月在阿姆斯特丹举行的 JSNation 会议上进行了现场演讲。 我要感谢 JSNation 团队给予我这个绝佳的机会！</p>
<p><img decoding="async" loading="lazy" src="https://pnpm.io/zh/assets/images/jsnation-2025-e3a119ab673869a6dc5a2e2cd172487e.jpg" width="2048" height="1366" class="img_M8jV"></p>
<p>令我惊喜的是，pnpm 在社区中如此知名，而且有那么多人在工作中使用它！</p>
<p>我的演讲内容是关于<a href="https://pnpm.io/zh/config-dependencies">配置依赖项</a> ，你可以<a href="https://gitnation.com/contents/configurational-dependencies-in-pnpm" target="_blank" rel="noopener noreferrer">在这里</a>观看录像。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="功能亮点">功能亮点<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E5%8A%9F%E8%83%BD%E4%BA%AE%E7%82%B9" class="hash-link" aria-label="直接链接到 功能亮点" title="直接链接到 功能亮点" translate="no">​</a></h2>
<p>现在，让我们深入了解一下 pnpm v10 在 2025 年期间发布的最重大变化。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="默认安全">默认安全<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E9%BB%98%E8%AE%A4%E5%AE%89%E5%85%A8" class="hash-link" aria-label="直接链接到 默认安全" title="直接链接到 默认安全" translate="no">​</a></h3>
<p>今年最重要的变化是 pnpm 转向“默认安全”。 在 pnpm v10.0 中，我们不再隐式信任已安装的软件包。</p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="阻止生命周期脚本-v100">阻止生命周期脚本 (<a href="https://github.com/pnpm/pnpm/releases/tag/v10.0.0" target="_blank" rel="noopener noreferrer">v10.0</a>)<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E9%98%BB%E6%AD%A2%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E8%84%9A%E6%9C%AC-v100" class="hash-link" aria-label="直接链接到 阻止生命周期脚本-v100" title="直接链接到 阻止生命周期脚本-v100" translate="no">​</a></h4>
<p>多年来，<code>pnpm install</code> 意味着信任整个依赖树来执行任意代码。 在 v10 版本中，我们关闭了此功能。 pnpm 默认情况下不再运行 <code>preinstall</code> 或 <code>postinstall</code> 脚本，从而消除了一类巨大的供应链攻击途径。</p>
<p>为了完善此控制，我们在 <a href="https://pnpm.io/zh/blog/releases/10.26">v10.26</a> 中引入了 <a href="https://pnpm.io/zh/settings#allowbuilds"><code>allowBuilds</code></a>，用更灵活的配置取代了之前的 <code>onlyBuiltDependencies</code>：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">allowBuilds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">esbuild</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># 仅允许特定版本</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">nx@21.6.4</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="纵深防御v1016-和-v1021">纵深防御（<a href="https://pnpm.io/zh/blog/releases/10.16">v10.16</a> 和 <a href="https://pnpm.io/zh/blog/releases/10.21">v10.21</a>）<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E7%BA%B5%E6%B7%B1%E9%98%B2%E5%BE%A1v1016-%E5%92%8C-v1021" class="hash-link" aria-label="直接链接到 纵深防御v1016-和-v1021" title="直接链接到 纵深防御v1016-和-v1021" translate="no">​</a></h4>
<p>我们并没有止步于剧本。 我们增加了多层防御措施，在恶意软件到达你的磁盘之前就将其拦截：</p>
<ul>
<li><strong><a href="https://pnpm.io/zh/settings#minimumreleaseage"><code>minimumReleaseAge</code></a></strong>: 阻止“零日”版本（例如，发布时间不足 24 小时的软件包），让社区有时间标记恶意更新。</li>
<li><strong><a href="https://pnpm.io/zh/settings#trustpolicy"><code>trustPolicy: no-downgrade</code></a></strong>: 防止安装来源不如以前版本可靠的更新（例如，未经 CI/CD 验证发布的版本）。</li>
<li><strong><a href="https://pnpm.io/zh/settings#blockexoticsubdeps"><code>blockExoticSubdeps</code></a></strong>: 防止受信任的依赖项从不受信任的源拉取传递依赖项。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="全局虚拟存储-v1012">全局虚拟存储 (<a href="https://github.com/pnpm/pnpm/releases/tag/v10.12.1" target="_blank" rel="noopener noreferrer">v10.12</a>)<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E5%85%A8%E5%B1%80%E8%99%9A%E6%8B%9F%E5%AD%98%E5%82%A8-v1012" class="hash-link" aria-label="直接链接到 全局虚拟存储-v1012" title="直接链接到 全局虚拟存储-v1012" translate="no">​</a></h3>
<p>pnpm 的一项原创创新是内容寻址存储，它通过文件去重来节省磁盘空间。 在 v10.12中，我们使用<strong>全局虚拟存储</strong>向前迈出了一步。</p>
<p>以前，项目都有自己的 <code>node_modules</code> 结构。 启用 <code>enableGlobalVirtualStore: true</code> 后，pnpm 现在可以将磁盘上中央位置的依赖项直接链接到您的项目中。 这意味着：</p>
<ol>
<li><strong>大幅节省磁盘空间</strong>：项目之间共享相同的依赖关系图。</li>
<li><strong>更快的安装速度</strong>：如果您有 10 个项目使用 <code>react@19</code>，pnpm 只需要全局链接一次。</li>
</ol>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="原生-jsr-支持v109">原生 JSR 支持（<a href="https://github.com/pnpm/pnpm/releases/tag/v10.9.0" target="_blank" rel="noopener noreferrer">v10.9</a>）<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E5%8E%9F%E7%94%9F-jsr-%E6%94%AF%E6%8C%81v109" class="hash-link" aria-label="直接链接到 原生-jsr-支持v109" title="直接链接到 原生-jsr-支持v109" translate="no">​</a></h3>
<p>我们采用了具有原生支持的新 JSR 注册表。 现在可以使用 <code>jsr:</code> 协议直接从 <a href="https://pnpm.io/zh/package-sources#jsr-registry">JSR</a> 安装软件包：</p>
<div class="language-bash codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-bash codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> jsr:@std/collections</span><br></span></code></pre></div></div>
<p>这样就能在 <code>package.json</code> 中正确映射，并能与 npm 依赖项无缝地处理 JSR 包的独特解析规则。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="配置依赖项v100">配置依赖项（v10.0）<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E9%85%8D%E7%BD%AE%E4%BE%9D%E8%B5%96%E9%A1%B9v100" class="hash-link" aria-label="直接链接到 配置依赖项（v10.0）" title="直接链接到 配置依赖项（v10.0）" translate="no">​</a></h3>
<p>对于单体仓库和复杂的设置，我们引入了**<a href="https://pnpm.io/zh/config-dependencies">配置依赖项</a>**。 此功能允许你在多个项目中共享和集中管理 pnpm 配置（例如钩子、补丁和构建权限）。</p>
<p>配置依赖项在解析主依赖关系图之前安装到 <code>node_modules/.pnpm-config</code> 中。 这意味着你可以用它们来：</p>
<ul>
<li>在不同仓库之间共享 <code>.pnpmfile.cjs</code> 钩子。</li>
<li>集中管理 <code>patchedDependencies</code> 的补丁文件。</li>
<li>维护一个共享的软件包列表，允许这些软件包为 <code>allowBuilds</code> 执行构建脚本。</li>
</ul>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_KdeV">pnpm-workspace.yaml</div><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">configDependencies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">pnpm-plugin-my-company</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1.0.0+sha512-..."</span><br></span></code></pre></div></div>
<p>这样可以确保你的 pnpm 配置具有版本控制、一致性，并且在包管理器需要时可用。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="自动-javascript-运行时管理v1014-和-v1021">自动 JavaScript 运行时管理（v10.14 和 v10.21）<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E8%87%AA%E5%8A%A8-javascript-%E8%BF%90%E8%A1%8C%E6%97%B6%E7%AE%A1%E7%90%86v1014-%E5%92%8C-v1021" class="hash-link" aria-label="直接链接到 自动 JavaScript 运行时管理（v10.14 和 v10.21）" title="直接链接到 自动 JavaScript 运行时管理（v10.14 和 v10.21）" translate="no">​</a></h3>
<p>我们已经支持 Node.js 运行时管理一段时间了。 2025年，我们将其扩展到支持其他运行时环境，例如 Deno 和 Bun。</p>
<p>现在您可以通过在 <code>package.json</code> 中通过 <a href="https://pnpm.io/zh/package_json#devenginesruntime"><code>devEngines.runtime</code></a> 指定所需的运行时：</p>
<div class="language-json codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_KdeV">package.json</div><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-json codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"devEngines"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"runtime"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"node"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"version"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"24.6.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>pnpm 将自动下载并使用该特定版本的运行时来运行该项目中的脚本。 这使得“在我的机器上运行正常”成为过去式——团队中的每个人都使用完全相同的运行时，完全由 pnpm 管理。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="展望未来">展望未来<a href="https://pnpm.io/zh/blog/2025/12/29/pnpm-in-2025#%E5%B1%95%E6%9C%9B%E6%9C%AA%E6%9D%A5" class="hash-link" aria-label="直接链接到 展望未来" title="直接链接到 展望未来" translate="no">​</a></h2>
<p>我们已经开始着手开发 pnpm v11.0，该版本在性能方面有一些明显的改进。 全局虚拟存储默认情况下尚未启用。 我们将致力于修复漏洞和完善缺失的功能，以便在未来的主要版本中默认启用该功能。</p><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="recap" term="recap"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.26]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.26</id>
        <link href="https://pnpm.io/zh/blog/releases/10.26"/>
        <updated>2025-12-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 10.26 为 git 托管的依赖项引入了更严格的安全默认值，添加了 allowBuilds 以实现细粒度的脚本权限，并包含了一个新设置来阻止特殊的传递依赖项。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 10.26 为 git 托管的依赖项引入了更严格的安全默认值，添加了 <code>allowBuilds</code> 以实现细粒度的脚本权限，并包含了一个新设置来阻止特殊的传递依赖项。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.26#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="更严格的-git-依赖安全">更严格的 Git 依赖安全<a href="https://pnpm.io/zh/blog/releases/10.26#%E6%9B%B4%E4%B8%A5%E6%A0%BC%E7%9A%84-git-%E4%BE%9D%E8%B5%96%E5%AE%89%E5%85%A8" class="hash-link" aria-label="直接链接到 更严格的 Git 依赖安全" title="直接链接到 更严格的 Git 依赖安全" translate="no">​</a></h4>
<p><strong>半破坏性更改。</strong> 现在，除非在 <code>onlyBuiltDependencies</code>（或 <code>allowBuilds</code>）中明确允许，否则 Git 托管的依赖项将无法在安装期间运行 <code>prepare</code> 脚本 <a href="https://github.com/pnpm/pnpm/pull/10288" target="_blank" rel="noopener noreferrer">#10288</a>。 此项更改可防止从不受信任的 Git 仓库执行恶意代码。</p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="allowbuilds"><code>allowBuilds</code><a href="https://pnpm.io/zh/blog/releases/10.26#allowbuilds" class="hash-link" aria-label="直接链接到 allowbuilds" title="直接链接到 allowbuilds" translate="no">​</a></h4>
<p>新增了一个名为 <code>allowBuilds</code> 的新设置，提供了灵活管理构建脚本的方式。 它接受一个包匹配器映射，以明确允许（<code>true</code>）或禁止（<code>false</code>）脚本执行。 这将取代 <code>onlyBuiltDependencies</code> 和 <code>ignoredBuiltDependencies</code>，成为首选配置方法 <a href="https://github.com/pnpm/pnpm/pull/10311" target="_blank" rel="noopener noreferrer">#10311</a>。</p>
<p>示例：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">allowBuilds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">esbuild</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">core-js</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">nx@21.6.4 || 21.6.5</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="blockexoticsubdeps"><code>blockExoticSubdeps</code><a href="https://pnpm.io/zh/blog/releases/10.26#blockexoticsubdeps" class="hash-link" aria-label="直接链接到 blockexoticsubdeps" title="直接链接到 blockexoticsubdeps" translate="no">​</a></h4>
<p>新增了 <code>blockExoticSubdeps</code> 设置，以提高供应链安全性。 当设置为 <code>true</code> 时，它会阻止在过渡依赖中解析异常协议 (如 <code>git+ssh:</code> 或直接的 <code>https://:</code> tarball)。 只有直接依赖项才允许使用特殊来源 <a href="https://github.com/pnpm/pnpm/pull/10265" target="_blank" rel="noopener noreferrer">#10265</a>。</p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="http-tarball-的完整性哈希">HTTP Tarball 的完整性哈希<a href="https://pnpm.io/zh/blog/releases/10.26#http-tarball-%E7%9A%84%E5%AE%8C%E6%95%B4%E6%80%A7%E5%93%88%E5%B8%8C" class="hash-link" aria-label="直接链接到 HTTP Tarball 的完整性哈希" title="直接链接到 HTTP Tarball 的完整性哈希" translate="no">​</a></h4>
<p><strong>半破坏性更新。</strong> pnpm 现在会在获取 HTTP tarball 依赖项时计算其完整性哈希值，并将其存储在 lockfile 中。 这将确保服务器在随后安装时无法在没有检测到的情况下提供被更改的内容 <a href="https://github.com/pnpm/pnpm/pull/10287" target="_blank" rel="noopener noreferrer">#10287</a>。</p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-pack---dry-run"><code>pnpm pack --dry-run</code><a href="https://pnpm.io/zh/blog/releases/10.26#pnpm-pack---dry-run" class="hash-link" aria-label="直接链接到 pnpm-pack---dry-run" title="直接链接到 pnpm-pack---dry-run" translate="no">​</a></h4>
<p>为 <code>pack</code> 命令添加了对 <code>--dry-run</code> 的支持。 这样，你就可以在不实际创建 tarball 的情况下验证哪些文件将包含在 tarball 中 <a href="https://github.com/pnpm/pnpm/issues/10301" target="_blank" rel="noopener noreferrer">#10301</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.26#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>当最新版本被弃用时，在表格/列表格式中显示弃用提示 <a href="https://github.com/pnpm/pnpm/issues/8658" target="_blank" rel="noopener noreferrer">#8658</a>。</li>
<li>从 <code>deploy</code> 命令的锁文件中删除 <code>injectWorkspacePackages</code> 设置 <a href="https://github.com/pnpm/pnpm/pull/10294" target="_blank" rel="noopener noreferrer">#10294</a>。</li>
<li>在将 tarball URL 保存到锁文件之前对其进行规范化 <a href="https://github.com/pnpm/pnpm/pull/10273" target="_blank" rel="noopener noreferrer">#10273</a> 。</li>
<li>修复重定向不可变依赖项的 URL 规范化 <a href="https://github.com/pnpm/pnpm/pull/10197" target="_blank" rel="noopener noreferrer">#10197</a>。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.25]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.25</id>
        <link href="https://pnpm.io/zh/blog/releases/10.25"/>
        <updated>2025-12-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 10.25 改进了证书处理，添加了裸 pnpm init，并修复了一些影响用户体验的问题。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 10.25 改进了证书处理，添加了裸 <code>pnpm init</code>，并修复了一些影响用户体验的问题。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.25#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="基于注册源的证书">基于注册源的证书<a href="https://pnpm.io/zh/blog/releases/10.25#%E5%9F%BA%E4%BA%8E%E6%B3%A8%E5%86%8C%E6%BA%90%E7%9A%84%E8%AF%81%E4%B9%A6" class="hash-link" aria-label="直接链接到 基于注册源的证书" title="直接链接到 基于注册源的证书" translate="no">​</a></h4>
<p>现在你可以从特定注册表 URL 的 <code>cert</code>、<code>ca</code> 和 <code>key</code> 设置中加载内联证书（例如，<code>//registry.example.com/:ca=-----BEGIN CERTIFICATE-----...</code>）。 以前，pnpm 只考虑 <code>certfile</code>、<code>cafile</code> 和 <code>keyfile</code> 条目。 这使得 pnpm 与 npm 的 <code>.npmrc</code> 行为保持一致 <a href="https://github.com/pnpm/pnpm/pull/10230" target="_blank" rel="noopener noreferrer">#10230</a>。</p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-init---bare"><code>pnpm init --bare</code><a href="https://pnpm.io/zh/blog/releases/10.25#pnpm-init---bare" class="hash-link" aria-label="直接链接到 pnpm-init---bare" title="直接链接到 pnpm-init---bare" translate="no">​</a></h4>
<p>为 <code>pnpm init</code> 添加了 <code>--bare</code> 标志，用于创建仅包含必需字段的 <code>package.json</code> <a href="https://github.com/pnpm/pnpm/issues/10226" target="_blank" rel="noopener noreferrer">#10226</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.25#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>改进了对忽略的依赖脚本的报告 <a href="https://github.com/pnpm/pnpm/pull/10276" target="_blank" rel="noopener noreferrer">#10276</a>。</li>
<li><code>pnpm install</code> 现在会构建添加到 <code>onlyBuiltDependencies</code> 但尚未运行构建的任何依赖项 <a href="https://github.com/pnpm/pnpm/pull/10256" target="_blank" rel="noopener noreferrer">#10256</a>。</li>
<li><code>pnpm publish -r --force</code> 即使版本已存在于注册表中也会发布，符合标志的意图 <a href="https://github.com/pnpm/pnpm/issues/10272" target="_blank" rel="noopener noreferrer">#10272</a> 。</li>
<li>当从信任策略检查中排除的软件包的元数据中缺少 <code>time</code> 字段时，避免出现<code>ERR_PNPM_MISSING_TIME</code> 错误。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[我们如何保护新闻编辑部免受npm供应链攻击]]></title>
        <id>https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security</id>
        <link href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security"/>
        <updated>2025-12-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[我们很幸运地遇到了 Shai-Hulud 2.0。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>我们很幸运地遇到了 Shai-Hulud 2.0。</p>
<p>2025 年 11 月，一个自我复制的 npm 蠕虫<a href="https://securitylabs.datadoghq.com/articles/shai-hulud-2.0-npm-worm/" target="_blank" rel="noopener noreferrer">感染了 796 个软件包</a>，每月下载量达 1.32 亿次。 该攻击利用预安装脚本窃取凭据、安装持久后门，并在某些情况下清除整个开发人员环境。 我们没有受到影响——不是因为我们有强大的防御措施，而是因为在攻击期间我们没有运行 <code>npm install</code> 或 <code>npm update</code>。</p>
<p>运气不是安全策略。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="我们是谁">我们是谁<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E6%88%91%E4%BB%AC%E6%98%AF%E8%B0%81" class="hash-link" aria-label="直接链接到 我们是谁" title="直接链接到 我们是谁" translate="no">​</a></h2>
<p>我是 Ryan Sobol，西雅图时报的首席软件工程师。 多年来，我们一直使用 npm 作为默认的包管理器，虽然也曾短暂地尝试过 Yarn，但始终没有流行起来。 现在我们正在试用 pnpm，专门用于其客户端安全控制，以补充 npm 一直在推出的注册表级改进。</p>
<p>对于新闻机构而言，信任至关重要，尤其是在当今时代。 供应链被攻破可能会暴露客户数据、员工凭证、生产基础设施和源代码——这些可能需要数周才能恢复，甚至需要向读者发出泄露通知。 我们明白这些事件会造成多么巨大的时间和金钱损失。 我们不想走上那条路。</p>
<p>尽管坚持 npm 存在组织惯性，但我们认为 pnpm 在这里确实有机会。 它的真的直接替换——相同的命令、相同的工作流程、相同的注册源。 这使得过度成为可能，而之前的替代方案则无法做到这一点。</p>
<p>这并非一份精心打磨的案例研究。 这是来自一个刚刚开始研究供应链安全的团队的真实数据点。 我们遇到的挑战以及我们思考这些控制措施的方式，或许对你自己实施这些措施有所帮助。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="为什么客户端控制很重要">为什么客户端控制很重要<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E4%B8%BA%E4%BB%80%E4%B9%88%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A7%E5%88%B6%E5%BE%88%E9%87%8D%E8%A6%81" class="hash-link" aria-label="直接链接到 为什么客户端控制很重要" title="直接链接到 为什么客户端控制很重要" translate="no">​</a></h2>
<p>npm 在供应链安全方面取得了巨大进展。 <a href="https://docs.npmjs.com/trusted-publishers/" target="_blank" rel="noopener noreferrer">可信发布</a>、<a href="https://docs.npmjs.com/generating-provenance-statements/" target="_blank" rel="noopener noreferrer">来源证明</a>和<a href="https://docs.npmjs.com/about-access-tokens/" target="_blank" rel="noopener noreferrer">细粒度访问令牌</a>都是重大改进，使得在维护者帐户被攻破后发布恶意软件包变得更加困难。</p>
<p>但问题在于：这些注册表改进保护的是_<strong>发布</strong>_方面。 它们并不能阻止<strong>消费</strong>*恶意包。</p>
<p>当你运行 <code>npm install</code> 或 <code>npm update</code> 时，生命周期脚本（例如 preinstall 和 postinstall）会在软件包经过安全评估之前，以完整的开发者权限从互联网执行任意代码。 这些脚本可以访问您的凭证（npm、GitHub、AWS、数据库）、源代码、云基础设施和整个文件系统。</p>
<p>这是 Shai-Hulud 等攻击所利用的根本漏洞。 即使有了这些注册表改进，如果合法维护者的帐户被盗用，攻击者仍然可以发布一个带有恶意生命周期脚本的版本，这些脚本会在安装后立即执行——在社区检测到被盗用之前。</p>
<p>这就是为什么我们觉得需要在两方面都进行防御：npm 的改进使得发布恶意软件包更加困难；pnpm 的客户端控制使得使用恶意软件包更加困难。 这些方法是互补的，而不是竞争的。 pnpm 使用 npm 的注册表，并受益于 npm 的所有安全改进，同时在客户端增加了一层额外的保护。</p>
<p>这是深入的防御。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="我们正在使用的三层控制">我们正在使用的三层控制<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E6%88%91%E4%BB%AC%E6%AD%A3%E5%9C%A8%E4%BD%BF%E7%94%A8%E7%9A%84%E4%B8%89%E5%B1%82%E6%8E%A7%E5%88%B6" class="hash-link" aria-label="直接链接到 我们正在使用的三层控制" title="直接链接到 我们正在使用的三层控制" translate="no">​</a></h2>
<p>在我们的试点项目中，我们使用了三个协同工作的 pnpm 安全控制措施。 每个控制措施都针对不同的攻击途径，并且每个控制措施都为合法的例外情况提供了逃生通道。 我们知道，我们需要这些例外——真正的世界是混乱的。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="控制-1生命周期脚本管理">控制 1：生命周期脚本管理<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E6%8E%A7%E5%88%B6-1%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E8%84%9A%E6%9C%AC%E7%AE%A1%E7%90%86" class="hash-link" aria-label="直接链接到 控制 1：生命周期脚本管理" title="直接链接到 控制 1：生命周期脚本管理" translate="no">​</a></h3>
<p>我们考虑 pnpm 的主要原因之一是了解到它<strong>默认情况下会阻止生命周期脚本</strong>。 与其他包管理器不同，它不会隐式地信任和执行包中的任意代码。</p>
<p>实际上，当一个软件包有 preinstall 或 postinstall 脚本时，pnpm 会阻止它们，但安装会继续进行并发出警告。 这已经提供了重要的保护——如果您不明确允许，恶意脚本将不会被执行。 但是，我们担心警告很容易被忽略，尤其是在安装似乎成功的情况下。 我们希望通过设置 <code>strictDepBuilds: true</code> 来获得更严格的控制：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_KdeV">pnpm-workspace.yaml</div><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">strictDepBuilds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">onlyBuiltDependencies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> package</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">with</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">necessary</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">build</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">scripts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">ignoredBuiltDependencies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> package</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">with</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">unnecessary</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">build</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">scripts</span><br></span></code></pre></div></div>
<p>所谓“必要的”，指的是那些真正需要其生命周期脚本才能运行的软件包——例如从源代码编译的本地扩展程序，或者链接到特定于平台的库的数据库驱动程序。脚本 所谓“不必要的”，指的是那些可选的优化脚本或设置步骤，它们不会影响软件包在我们的使用场景中是否能正常运行。</p>
<p>如果设置 <code>strictDepBuilds: true</code>，则安装程序在遇到生命周期脚本时会立即失败，迫使我们：</p>
<ol>
<li>确定哪些软件包包含生命周期脚本——pnpm 会准确地告诉你哪些软件包包含生命周期脚本</li>
<li>研究每个脚本的功能，这可以很简单，只需将独立的预安装或后安装脚本输入到生成式人工智能中进行解读即可</li>
<li>运用人的判断力，做出有意识的、有记录的决定，决定是否允许或阻止它</li>
</ol>
<p>对于我们的团队来说，这确保我们能够提前做出深思熟虑的选择，而不是在之后才发现问题。</p>
<p><strong>注意：</strong> pnpm 团队正在考虑将 <code>strictDepBuilds: true</code> 设置为 v11 中的默认行为，并且正在根据在实践中实施这些控制的团队的反馈，探索更清晰的允许/拒绝语法命名。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="控制-2发布冷却">控制 2：发布冷却<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E6%8E%A7%E5%88%B6-2%E5%8F%91%E5%B8%83%E5%86%B7%E5%8D%B4" class="hash-link" aria-label="直接链接到 控制 2：发布冷却" title="直接链接到 控制 2：发布冷却" translate="no">​</a></h3>
<p>此控制阻止安装在冷却期内发布的软件包。 这样做的目的是给社区时间在恶意软件到达你的环境之前检测并清除它们。</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_KdeV">pnpm-workspace.yaml</div><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">minimumReleaseAge</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> &lt;duration</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">in</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">minutes</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">minimumReleaseAgeExclude</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> package</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">with</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">critical</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">hotfix@1.2.3</span><br></span></code></pre></div></div>
<p><strong>我们的思维转变：</strong> 我们必须重新训练自己，停止认为“最新的就是最好的”。 我们从供应链安全角度了解到，情况并非总是如此——稍微老一些的产品往往更安全。 一个软件包如果已经可用一段时间，就能给社区和安全研究人员时间来发现潜在的问题。</p>
<p>从最近的攻击事件来看，恶意软件包的检测和清除所需时间各不相同。 [2025年9月npm供应链攻击]（<a href="https://www.wiz.io/blog/widespread-npm-supply-chain-attack-breaking-down-impact-scope-across-debug-chalk%EF%BC%89%E8%A2%AB%E6%94%BB%E7%A0%B4%E7%9A%84" target="_blank" rel="noopener noreferrer">https://www.wiz.io/blog/widespread-npm-supply-chain-attack-breaking-down-impact-scope-across-debug-chalk）被攻破的</a> debug 、chalk 及其他16个软件包在大约2.5小时内被移除，而 [Shai-Hulud 2.0]（<a href="https://securitylabs.datadoghq.com/articles/shai-hulud-2.0-npm-worm/%EF%BC%89%EF%BC%882025%E5%B9%B411%E6%9C%88%EF%BC%89" target="_blank" rel="noopener noreferrer">https://securitylabs.datadoghq.com/articles/shai-hulud-2.0-npm-worm/）（2025年11月）</a> 大约花了12个小时。 每次攻击的情况都不同，每次恢复时间也会有所不同，但适当的冷却期取决于你组织的风险承受能力——它可以以小时、天或周来衡量。 不管怎样，冷却期都会阻止这些袭击。</p>
<p><strong>我们接受的折衷：</strong> 考虑到我们组织的规模和我们的优先事项，我们并非总是在最新的软件包上，尽管我们做出了最大的努力。 因此，这种冷却政策更符合我们的实际情况，而不是扰乱它。 当我们确实需要更新版本（关键安全补丁、重大漏洞）时，经过审查后，我们可以暂时豁免。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="控制-3信任策略">控制 3：信任策略<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E6%8E%A7%E5%88%B6-3%E4%BF%A1%E4%BB%BB%E7%AD%96%E7%95%A5" class="hash-link" aria-label="直接链接到 控制 3：信任策略" title="直接链接到 控制 3：信任策略" translate="no">​</a></h3>
<p>当软件包版本的身份验证强度低于先前发布的版本时，此控制措施会阻止安装——这通常表明攻击者窃取了维护者的凭据，并从他们自己的机器而不是官方的 CI/CD 管道发布了软件包。</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_KdeV">pnpm-workspace.yaml</div><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">trustPolicy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> no</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">downgrade</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">trustPolicyExclude</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> package</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">that</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">migrated</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cicd@1.2.3</span><br></span></code></pre></div></div>
<p><strong>工作原理：</strong> npm 会跟踪已发布软件包的三个信任级别（从最强到最弱）：</p>
<ol>
<li>**可信发布者：**通过 GitHub Actions 发布，带有 OIDC 令牌和 npm 来源</li>
<li>**来源：**来自 CI/CD 系统的签名证明</li>
<li>**无信任凭据：**发布时使用用户名/密码或令牌进行身份验证</li>
</ol>
<p>如果新版本的身份验证强度低于旧版本，则安装会失败。 例如，如果 v1.0.0 是通过 Trusted Publisher 发布的，而 v1.0.1 是通过基本身份验证发布的，则 pnpm 会阻止 v1.0.1。</p>
<p>在 2025 年 8 月的 <a href="https://www.wiz.io/blog/s1ngularity-supply-chain-attack" target="_blank" rel="noopener noreferrer">s1ngularity 攻击</a> 中，攻击者窃取了维护者的凭证，并从他们自己的机器上发布了恶意版本。 由于他们没有 CI/CD 访问权限，恶意版本没有来源信息——这明显降低了信任度。 这种控制会阻止安装。</p>
<p><strong>以下情况可能合理地导致信任降级：</strong> 新的维护者尚未设置溯源，CI/CD 系统迁移，CI/CD 系统宕机期间手动发布了紧急热修复程序。 在这些情况下，我们会调查信任级别下降的原因，验证其安全性，然后将其添加到 <code>trustPolicyExclude</code> 中。</p>
<p>**注意：**此功能于 2025 年 11 月添加到 pnpm 中，相当新。 我们仍然在了解在实践中正当的信任下降的频率。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="它们如何协同工作react-示例">它们如何协同工作：React 示例<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E5%AE%83%E4%BB%AC%E5%A6%82%E4%BD%95%E5%8D%8F%E5%90%8C%E5%B7%A5%E4%BD%9Creact-%E7%A4%BA%E4%BE%8B" class="hash-link" aria-label="直接链接到 它们如何协同工作：React 示例" title="直接链接到 它们如何协同工作：React 示例" translate="no">​</a></h2>
<p>我们认为这些控制措施都不是万全之策。 它们就像多层防御——当我们不得不对其中一层控制措施做出例外处理时，其他层控制措施仍然会继续保护我们。</p>
<p>让我们来看一个真实的场景：2025 年 12 月披露的 <a href="https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components" target="_blank" rel="noopener noreferrer">严重 React 漏洞</a>。</p>
<p>这是一个严重的安全问题，需要立即修复。 通常情况下，我们的版本发布冷却期会阻止我们安装最近发布的软件包版本。 但这是一个至关重要的安全补丁——我们不能再等了。</p>
<p>在这种情况下，多层防御机制将如何运作：</p>
<p><strong>你做了什么：</strong> 在审查了脆弱性披露并核实补丁是合法的后，将特定的React版本添加到 <code>minimumReleaseAgeExclude</code>。</p>
<p><strong>哪些因素仍然能保护你：</strong></p>
<ul>
<li><strong>生命周期脚本管理</strong>仍然处于活动状态——如果攻击者将恶意生命周期脚本注入到 React 补丁中，这些脚本将被阻止（React 通常没有生命周期脚本，因此任何脚本都会立即引起怀疑）</li>
<li><strong>信任策略</strong>仍然有效——如果攻击者窃取了 React 的发布凭据，并从其自身机器推送了恶意“补丁”，则信任降级将被阻止</li>
</ul>
<p>这就是为什么我们认为例外情况是可以接受的。 你出于正当理由，有意识地、有记录地决定绕过一项控制措施，但你仍然受到其他层面的强大保护。 没有单一故障点。</p>
<p>这就是我们实际运用纵深防御的方式。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="我们的试点体验">我们的试点体验<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E6%88%91%E4%BB%AC%E7%9A%84%E8%AF%95%E7%82%B9%E4%BD%93%E9%AA%8C" class="hash-link" aria-label="直接链接到 我们的试点体验" title="直接链接到 我们的试点体验" translate="no">​</a></h2>
<p>我们在我们的后端服务之一实施了所有三项安全控制，作为概念验证。 总设置时间：几小时研究、理解和定义我们的方法。</p>
<p>在设置过程中，pnpm 识别了三个带有生命周期脚本的软件包：</p>
<ul>
<li><strong>esbuild：</strong> 优化 CLI 工具启动时间（以毫秒为单位）——由于我们仅使用 JavaScript API，因此不需要此功能</li>
<li><strong>@firebase/util:</strong> 自动配置客户端 SDK——由于我们只使用服务器 SDK，因此不需要此配置</li>
<li><strong>protobufjs：</strong> 检查版本模式兼容性——由于它是传递依赖项，因此不需要此检查</li>
</ul>
<p>我们研究了每个脚本的功能（阅读文档并将脚本输入人工智能进行解释），确定没有一个脚本对我们的用例是必要的，因此我们阻止了它们。 对功能没有影响。</p>
<p>仅此而已。 只需几个小时的初始投入，即可持续抵御 Shai-Hulud 式的攻击。</p>
<p><strong>摩擦感是怎样的：</strong> 这些控件的设计本身就带有摩擦感——对我们来说，这是一个特性，而不是一个缺陷。 这种摩擦迫使我们有意识地决定在我们的环境中运行哪些代码，而不是盲目地信任一切。 当新增依赖项包含脚本时，我们预计审查和记录该决定大约需要 15 分钟。</p>
<p>我们期望，在我们更熟悉这一进程时，摩擦将变得更加直觉。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="我们正在学习什么">我们正在学习什么<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E6%88%91%E4%BB%AC%E6%AD%A3%E5%9C%A8%E5%AD%A6%E4%B9%A0%E4%BB%80%E4%B9%88" class="hash-link" aria-label="直接链接到 我们正在学习什么" title="直接链接到 我们正在学习什么" translate="no">​</a></h2>
<p>我们从我们的试点学到了一些东西：</p>
<p><strong>纵深防御模型确实有效。</strong> 在客户端设置多层防御——再加上 npm 发布端改进带来的好处——意味着我们可以务实地处理例外情况。 当我们出于正当理由需要绕过某个控制措施时，其他控制措施仍然会保护我们。 这样就消除了做出例外处理的焦虑——它们不是安全漏洞，而是系统按设计运行的结果。</p>
<p><strong>这种思维模式的形成需要时间。</strong> 从“便利性优先”转变为“安全优先”需要一个学习过程。 但一旦人们理解了这种心理模型——稍微旧一点的包更安全，明确的决定比隐性的信任更好——工作流程就会感觉很自然。</p>
<p><strong>这些控制对中型团队是实用的。</strong> 我们不是一个拥有专职安全团队的大型科技公司。 我们是一个中等规模的新闻媒体组织，工程资源有限。 如果我们能够成功实施这些控制措施，那么大多数团队都可以使用这些措施。</p>
<p><strong>我们仍在学习。</strong> 威胁正在演变，我们的方法也将会改变。 信任策略功能推出仅几周，我们还不知道在实践中合法的信任降级会发生多频繁。 我们计划在不久的将来将这些控制措施扩展到其他代码库，这将为我们提供更多关于它们如何随着具有不同依赖关系图的应用程序而扩展的数据。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="对于其他正在考虑此方案的团队">对于其他正在考虑此方案的团队<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E5%AF%B9%E4%BA%8E%E5%85%B6%E4%BB%96%E6%AD%A3%E5%9C%A8%E8%80%83%E8%99%91%E6%AD%A4%E6%96%B9%E6%A1%88%E7%9A%84%E5%9B%A2%E9%98%9F" class="hash-link" aria-label="直接链接到 对于其他正在考虑此方案的团队" title="直接链接到 对于其他正在考虑此方案的团队" translate="no">​</a></h2>
<p>如果你正在考虑使用 pnpm 的安全控制措施，以下是我们总结出的有效方法：</p>
<p>**从一个项目开始。**先在一个代码库上进行试点，可以让我们熟悉工作流程，了解痛点，并在考虑更广泛推广之前建立信心。</p>
<p><strong>提前做好例外情况的规划。</strong> 要做好心理准备，你需要为生命周期脚本（需要编译的软件包）、发布冷却时间（关键安全补丁）和信任降级（CI/CD 迁移）设置例外情况。 这不是失败——这是系统设计的工作方式。</p>
<p><strong>从一开始就使用 <code>strictDepBuilds: true</code>。</strong> 我们认为依赖警告风险太大。 我们希望安装立即失败，从而迫使他们做出决定。 这可以防止软件包在以后出现潜在的故障，并确保经过深思熟虑的选择。</p>
<p><strong>记录所有例外情况。</strong> 写下你允许生命周期脚本运行或豁免某个软件包的原因。 这样可以创建审计跟踪，帮助未来的团队成员理解原因，并使以后清理异常情况变得容易。</p>
<p><strong>相信多层防护。</strong> 当你为其中一个控件破例时，请记住其他两个控件仍然在保护你。 纵深防御模型给了你务实的空间。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="分享您的体验">分享您的体验<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E5%88%86%E4%BA%AB%E6%82%A8%E7%9A%84%E4%BD%93%E9%AA%8C" class="hash-link" aria-label="直接链接到 分享您的体验" title="直接链接到 分享您的体验" translate="no">​</a></h2>
<p>我们很想听听其他团队是如何实施或考虑实施这些控制措施的。 哪些方法有效？ 挑战是什么？ 你学到了什么？ 加入 <a href="https://github.com/orgs/pnpm/discussions" target="_blank" rel="noopener noreferrer">pnpm GitHub Discussions</a> 的讨论，或在社交媒体上分享你的经验——我们都在共同学习。</p>
<h2 class="anchor anchorWithStickyNavbar_VHH3" id="谢谢">谢谢<a href="https://pnpm.io/zh/blog/2025/12/05/newsroom-npm-supply-chain-security#%E8%B0%A2%E8%B0%A2" class="hash-link" aria-label="直接链接到 谢谢" title="直接链接到 谢谢" translate="no">​</a></h2>
<p>感谢 pnpm 团队构建了这些控件，并感谢他们以周到的方式使它们既强大又实用。 感谢邀请我们分享我们的故事。</p>
<p>你正在做的工作很重要。 这些控制措施提供了真正的保护，是对 npm 注册表改进的补充。 它们共同为像我们这样的团队提供了对抗日益复杂的供应链攻击的机会。</p>
<hr>
<p><em>Ryan Sobol 是《西雅图时报》的首席软件工程师，他负责移动和 Web 开发、云基础设施和开发者工具。 本文表达的观点仅代表作者个人意见，并基于《西雅图时报》对 pnpm 安全控制措施的试点实施情况。</em></p><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Ryan Sobol</name>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.24]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.24</id>
        <link href="https://pnpm.io/zh/blog/releases/10.24"/>
        <updated>2025-11-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[pnpm 现在可以在高核心机器上自动扩展网络并发性，并发布了多项可靠性修复。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>pnpm 现在可以在高核心机器上自动扩展网络并发性，并发布了多项可靠性修复。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.24#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="自适应网络并发性">自适应网络并发性<a href="https://pnpm.io/zh/blog/releases/10.24#%E8%87%AA%E9%80%82%E5%BA%94%E7%BD%91%E7%BB%9C%E5%B9%B6%E5%8F%91%E6%80%A7" class="hash-link" aria-label="直接链接到 自适应网络并发性" title="直接链接到 自适应网络并发性" translate="no">​</a></h4>
<p>网络并发数现在会根据 pnpm 工作进程数（工作进程数 × 3）在 16 到 64 之间自动调整。 这会增加许多CPU核心的机器输送量，同时使资源使用在较小的设置上保持可预见<a href="https://github.com/pnpm/pnpm/issues/10068" target="_blank" rel="noopener noreferrer">#10068</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.24#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>当你安装非预发布版本时，<code>trustPolicy</code> 现在忽略了预发布版本中的信任证据， 所以可信的预发布不能阻止安装缺少信任证据的稳定发布。</li>
<li>处理由 <code>fs.linkSync()</code> 抛出的 <code>ENOENT</code> 错误，该错误可能发生在容器化环境 (OverlayFS) 中，而不是 <code>EXDEV</code> 中。 pnpm 现在在这些情况下会优雅地回退到 <code>fs.copyFileSync()</code> <a href="https://github.com/pnpm/pnpm/issues/10217" target="_blank" rel="noopener noreferrer">#10217</a>。</li>
<li>已还原：<code>pnpm self-update</code> 从配置的 npm 注册表下载 pnpm <a href="https://github.com/pnpm/pnpm/pull/10205" target="_blank" rel="noopener noreferrer">#10205</a>。</li>
<li>没有 <code>package.json</code> 文件的软件包（例如 Node.js）在每次安装时不再从存储库中重新导入。 pnpm 现在会检查一个额外的文件来验证 <code>node_modules</code> 中的包。</li>
<li>正确读取包含下划线的 URL 的身份验证令牌 <a href="https://github.com/pnpm/npm-conf/pull/17" target="_blank" rel="noopener noreferrer">#17</a>。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.23]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.23</id>
        <link href="https://pnpm.io/zh/blog/releases/10.23"/>
        <updated>2025-11-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[为 pnpm list 添加了 --lockfile-only 选项，并对 pnpm self-update 进行了各种改进。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>为 <code>pnpm list</code> 添加了 <code>--lockfile-only</code> 选项，并对 <code>pnpm self-update</code> 进行了各种改进。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.23#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-list---lockfile-only"><code>pnpm list --lockfile-only</code><a href="https://pnpm.io/zh/blog/releases/10.23#pnpm-list---lockfile-only" class="hash-link" aria-label="直接链接到 pnpm-list---lockfile-only" title="直接链接到 pnpm-list---lockfile-only" translate="no">​</a></h4>
<p>为 <code>pnpm list</code> 添加了 <code>--lockfile-only</code> 选项 <a href="https://github.com/pnpm/pnpm/issues/10020" target="_blank" rel="noopener noreferrer">#10020</a>。</p>
<p>如果指定了 <code>pnpm list</code>，则会从锁文件中读取包信息，而不是检查实际的 <code>node_modules</code> 目录。 这有助于快速检查需要安装的内容，而无需进行完整的安装。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.23#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li><code>pnpm self-update</code> 应该从配置的 npm 注册源下载 pnpm <a href="https://github.com/pnpm/pnpm/pull/10205" target="_blank" rel="noopener noreferrer">#10205</a>。</li>
<li>安装 v11 或更高版本时，<code>pnpm self-update</code> 应该始终安装不可执行的 pnpm 包（注册表中的 pnpm），而永远不要安装 <code>@pnpm/exe</code> 包。 我们目前无法发布 <code>@pnpm/exe</code>，因为 <code>pkg</code> 不适用于 ESM <a href="https://github.com/pnpm/pnpm/pull/10190" target="_blank" rel="noopener noreferrer">#10190</a>。</li>
<li>如果在 <code>package.json</code> 中声明了 <code>engines.runtime</code> 设置，则 <code>pnpm add</code> 时不会将 Node.js 运行时添加到 "dependencies" 中 <a href="https://github.com/pnpm/pnpm/issues/10209" target="_blank" rel="noopener noreferrer">#10209</a>。</li>
<li>如果由于信任策略检查失败而无法安装可选依赖项，则安装应失败 <a href="https://github.com/pnpm/pnpm/issues/10208" target="_blank" rel="noopener noreferrer">#10208</a>。</li>
<li><code>pnpm list</code> 和 <code>pnpm why</code> 现在显示别名包的 npm: 协议（例如，<code>foo npm:is-odd@3.0.1</code>）<a href="https://github.com/pnpm/pnpm/issues/8660" target="_blank" rel="noopener noreferrer">#8660</a>。</li>
<li>不要在 Node.js 镜像 URL 中添加额外的斜杠 <a href="https://github.com/pnpm/pnpm/pull/10204" target="_blank" rel="noopener noreferrer">#10204</a>。</li>
<li>如果存储包含 Node.js 包，则 <code>pnpm store prune</code> 不应该失败 <a href="https://github.com/pnpm/pnpm/issues/10131" target="_blank" rel="noopener noreferrer">#10131</a>。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.22]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.22</id>
        <link href="https://pnpm.io/zh/blog/releases/10.22"/>
        <updated>2025-11-12T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[增加了对从信任策略中排除软件包以及在发布时覆盖 engines 字段的支持。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>增加了对从信任策略中排除软件包以及在发布时覆盖 <code>engines</code> 字段的支持。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.22#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="信任政策例外">信任政策例外<a href="https://pnpm.io/zh/blog/releases/10.22#%E4%BF%A1%E4%BB%BB%E6%94%BF%E7%AD%96%E4%BE%8B%E5%A4%96" class="hash-link" aria-label="直接链接到 信任政策例外" title="直接链接到 信任政策例外" translate="no">​</a></h4>
<p>添加了对 <a href="https://pnpm.io/zh/settings#trustpolicyexclude"><code>trustPolicyExclude</code></a> 的支持。</p>
<p>现在你可以列出一个或多个 pnpm 应该允许安装的特定软件包或版本，即使这些软件包不满足信任策略要求。 例如：</p>
<div class="language-yaml codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-yaml codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">trustPolicy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> no</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">downgrade</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">trustPolicyExclude</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> chokidar@4.0.3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> webpack@4.47.0 </span><span class="token punctuation" style="color:#393A34">|</span><span class="token punctuation" style="color:#393A34">|</span><span class="token plain"> 5.102.1</span><br></span></code></pre></div></div>
<p>相关问题: <a href="https://github.com/pnpm/pnpm/issues/10164" target="_blank" rel="noopener noreferrer">#10164</a></p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="在发布时覆盖-engine-字段">在发布时覆盖 engine 字段<a href="https://pnpm.io/zh/blog/releases/10.22#%E5%9C%A8%E5%8F%91%E5%B8%83%E6%97%B6%E8%A6%86%E7%9B%96-engine-%E5%AD%97%E6%AE%B5" class="hash-link" aria-label="直接链接到 在发布时覆盖 engine 字段" title="直接链接到 在发布时覆盖 engine 字段" translate="no">​</a></h4>
<p>允许在发布时通过 <code>publishConfig.engines</code> 字段覆盖 <code>engines</code> 字段。</p>
<p>这允许你为发布的软件包指定与开发时不同的引擎需求。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.22#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>当两个 pnpm 进程同时将目录内容硬链接到同一目标时，不要崩溃 <a href="https://github.com/pnpm/pnpm/issues/10179" target="_blank" rel="noopener noreferrer">#10179</a>。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.21]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.21</id>
        <link href="https://pnpm.io/zh/blog/releases/10.21"/>
        <updated>2025-11-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[增加了为依赖项安装 Node.js 运行时的支持，以及配置信任策略的设置。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>增加了为依赖项安装 Node.js 运行时的支持，以及配置信任策略的设置。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.21#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="为依赖项安装-nodejs-运行时">为依赖项安装 Node.js 运行时<a href="https://pnpm.io/zh/blog/releases/10.21#%E4%B8%BA%E4%BE%9D%E8%B5%96%E9%A1%B9%E5%AE%89%E8%A3%85-nodejs-%E8%BF%90%E8%A1%8C%E6%97%B6" class="hash-link" aria-label="直接链接到 为依赖项安装 Node.js 运行时" title="直接链接到 为依赖项安装 Node.js 运行时" translate="no">​</a></h4>
<p>增加了为依赖项自动安装 Node.js 运行时的支持。 如果依赖项在 <a href="https://pnpm.io/zh/package_json#enginesruntime"><code>engines.runtime</code></a> 字段中声明了 Node.js 运行时，pnpm 现在将安装依赖项所需的 Node.js 版本。 例如：</p>
<div class="language-json codeBlockContainer_pbKI theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_cWnm"><pre tabindex="0" class="prism-code language-json codeBlock_dUJW thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_wqoa"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"engines"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"runtime"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"node"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"version"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"^24.11.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"onFail"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"download"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>如果依赖 Node.js 运行时的包是一个 CLI 应用程序，pnpm 会将该 CLI 应用程序绑定到所需的 Node.js 版本。 这样可以确保，无论全局安装的 Node.js 实例是什么，CLI 都将使用兼容版本的 Node.js。</p>
<p>如果软件包包含 <code>postinstall</code> 脚本，则该脚本将使用指定的 Node.js 版本执行。</p>
<p>相关 PR：<a href="https://github.com/pnpm/pnpm/pull/10141" target="_blank" rel="noopener noreferrer">#10141</a></p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="信任政策">信任政策<a href="https://pnpm.io/zh/blog/releases/10.21#%E4%BF%A1%E4%BB%BB%E6%94%BF%E7%AD%96" class="hash-link" aria-label="直接链接到 信任政策" title="直接链接到 信任政策" translate="no">​</a></h4>
<p>添加了一个新设置：<a href="https://pnpm.io/zh/settings#trustpolicy"><code>trustPolicy</code></a>。</p>
<p>当设置为 <code>no-downgrade</code> 时，如果软件包的信任级别与以前的版本相比有所下降，pnpm 将失败。 例如，如果一个软件包之前是由受信任的发布者发布的，但现在只有来源信息而没有信任证明，则安装将会失败。 这有助于防止安装可能已被入侵的版本。</p>
<p>相关问题: <a href="https://github.com/pnpm/pnpm/issues/8889" target="_blank" rel="noopener noreferrer">#8889</a>。</p>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="其他功能">其他功能<a href="https://pnpm.io/zh/blog/releases/10.21#%E5%85%B6%E4%BB%96%E5%8A%9F%E8%83%BD" class="hash-link" aria-label="直接链接到 其他功能" title="直接链接到 其他功能" translate="no">​</a></h4>
<ul>
<li>添加了对 <code>pnpm config get globalconfig</code> 的支持，用于检索全局配置文件路径 <a href="https://github.com/pnpm/pnpm/issues/9977" target="_blank" rel="noopener noreferrer">#9977</a>。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.21#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>当用户对未直接列在 <code>package.json</code> 中的依赖项运行 <code>pnpm update</code> 时，任何直接依赖项都不应该更新 <a href="https://github.com/pnpm/pnpm/pull/10155" target="_blank" rel="noopener noreferrer">#10155</a>。</li>
<li>当两个 pnpm 进程同时将目录内容硬链接到同一目标时，不要崩溃 <a href="https://github.com/pnpm/pnpm/pull/10160" target="_blank" rel="noopener noreferrer">#10160</a>。</li>
<li>通过 <code>pnpm-workspace.yaml</code> 设置 <code>gitBranchLockfile</code> 和相关设置应该可以工作 <a href="https://github.com/phttps://github.com/pnpm/pnpm/issues/9651npm/pnpm/issues/9651" target="_blank" rel="noopener noreferrer">#9651</a>。</li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[pnpm 10.20]]></title>
        <id>https://pnpm.io/zh/blog/releases/10.20</id>
        <link href="https://pnpm.io/zh/blog/releases/10.20"/>
        <updated>2025-10-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[此版本为 pnpm help 命令添加了 --all 标志，用于打印所有命令。]]></summary>
        <content type="html"><![CDATA[<div id="bsa-custom-01" class="bsa-standard"></div><p>此版本为 <code>pnpm help</code> 命令添加了 <code>--all</code> 标志，用于打印所有命令。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="次要更改">次要更改<a href="https://pnpm.io/zh/blog/releases/10.20#%E6%AC%A1%E8%A6%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 次要更改" title="直接链接到 次要更改" translate="no">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_VHH3" id="pnpm-help---all"><code>pnpm help --all</code><a href="https://pnpm.io/zh/blog/releases/10.20#pnpm-help---all" class="hash-link" aria-label="直接链接到 pnpm-help---all" title="直接链接到 pnpm-help---all" translate="no">​</a></h4>
<p>在 <code>pnpm help</code> 中添加了对 <code>--all</code> 选项的支持，以列出所有命令 <a href="https://github.com/pnpm/pnpm/pull/8628" target="_blank" rel="noopener noreferrer">#8628</a>。</p>
<h3 class="anchor anchorWithStickyNavbar_VHH3" id="补丁更改">补丁更改<a href="https://pnpm.io/zh/blog/releases/10.20#%E8%A1%A5%E4%B8%81%E6%9B%B4%E6%94%B9" class="hash-link" aria-label="直接链接到 补丁更改" title="直接链接到 补丁更改" translate="no">​</a></h3>
<ul>
<li>当 <code>latest</code> 版本不满足 <code>minimumReleaseAge</code> 配置的成熟度要求时，选择足够成熟的最高版本，即使它具有不同的主版本号 <a href="https://github.com/pnpm/pnpm/issues/10100" target="_blank" rel="noopener noreferrer">#10100</a>。</li>
<li><code>create</code> 命令不应该验证补丁信息。</li>
<li>切换到不同版本的 pnpm CLI 时，将 <code>managePackageManagerVersions</code> 设置为 <code>false</code>，以避免后续切换 [#10063]（<a href="https://github.com/pnpm/pnpm/issues/10063)%E3%80%82" target="_blank" rel="noopener noreferrer">https://github.com/pnpm/pnpm/issues/10063)。</a></li>
</ul><script src="//m.servedby-buysellads.com/monetization.custom.js"></script><script>"undefined"!=typeof _bsa&&_bsa&&_bsa.init("custom","CWYI4K7E","placement:pnpmio",{target:"#bsa-custom-01",template:`
<a href="##link##" class="native-banner" style="background: ##backgroundColor##" rel="sponsored noopener" target="_blank" title="##company## — ##tagline##">
<img class="native-img" width="125" src="##logo##" />
<div class="native-main">
  <div class="native-details" style="
      color: ##textColor##;
      border-left: solid 1px ##textColor##;
    ">
    <span class="native-desc">##description##</span>
  </div>
  <span class="native-cta" style="
      color: ##ctaTextColor##;
      background-color: ##ctaBackgroundColor##;
    ">##callToAction##</span>
</div>
</a>
`})</script>]]></content>
        <author>
            <name>Zoltan Kochan</name>
            <uri>https://x.com/ZoltanKochan</uri>
        </author>
        <category label="release" term="release"/>
    </entry>
</feed>