PowerShell PNPM 安装

来源:Tauri 教程 / 时间:2026-11-02

打开 PowerShell

iwr https://get.pnpm.io/install.ps1 -useb | iex

install.ps1

#!/usr/bin/env pwsh

# Stop executing script on any error
$ErrorActionPreference = 'Stop'
# Do not show download progress
$ProgressPreference = 'SilentlyContinue'

# Taken from https://stackoverflow.com/a/34559554/6537420
function New-TemporaryDirectory {
  $parent = [System.IO.Path]::GetTempPath()
  [string] $name = [System.Guid]::NewGuid()
  New-Item -ItemType Directory -Path (Join-Path $parent $name)
}

$platform = $null
$architecture = $null
$pnpmName = $null
$libcSuffix = ''

# Detect the OS portion of the target triplet using `process.platform`-style
# names (`linux`, `darwin`, `win32`) - the scheme pnpm's own platform packages
# and release assets use from v11.0.0-rc.3 onward.

# PowerShell versions before 6.* were only for Windows OS
if ($PSVersionTable.PSVersion.Major -eq 5) {
  $platform = 'win32'
}

if ($PSVersionTable.PSVersion.Major -ge 6) {
  if ($PSVersionTable.Platform -eq 'Unix') {
    switch -Wildcard ($PSVersionTable.OS) {
      'Darwin*' {
        $platform = 'darwin'
      }
      # PowerShell 7.6+ (built on .NET 10) reports a friendly name like
      # "macOS 26.4" instead of the Darwin kernel string.
      'macOS*' {
        $platform = 'darwin'
      }
      'Linux*' {
        $platform = 'linux'
      }
      'Ubuntu*' {
        $platform = 'linux'
      }
    }

    # PowerShell does not seem to have normal cmdlets for retrieving system information, so we use UNAME(1) for this.
    $arch = uname -m
    switch -Wildcard ($arch) {
      'x86_64' { $architecture = 'x64'; Break }
      'amd64' { $architecture = 'x64'; Break }
      'armv*' { $architecture = 'arm'; Break }
      'arm64' { $architecture = 'arm64'; Break }
      'aarch64' { $architecture = 'arm64'; Break }
    }

    # 'uname -m' in some cases mis-reports 32-bit OS as 64-bit, so double check
    if ([System.Environment]::Is64BitOperatingSystem -eq $false) {
      if ($architecture -eq 'x64') {
        $architecture = 'i686'
      }

      if ($architecture -eq 'arm64') {
        $architecture = 'arm'
      }
    }

    # Detect musl on Linux hosts. getconf prints GLIBC info on glibc systems;
    # no output (or a failure) indicates musl or similar.
    if ($platform -eq 'linux') {
      $glibcCheck = $null
      try { $glibcCheck = getconf GNU_LIBC_VERSION 2>$null } catch {}
      if (-not $glibcCheck) {
        try { $glibcCheck = (ldd --version 2>&1 | Select-String 'GLIBC|GNU libc').Matches.Count } catch {}
      }
      if (-not $glibcCheck) {
        $libcSuffix = '-musl'
      }
    }

    $pnpmName = "pnpm"
  }

  if ($PSVersionTable.Platform -eq 'Win32NT') {
    $platform = 'win32'
  }
}

if ($platform -eq 'win32') {
  if ([System.Environment]::Is64BitOperatingSystem -eq $true) {
    $architecture = 'x64'
  }

  if ([System.Environment]::Is64BitOperatingSystem -eq $false) {
    $architecture = 'i686'
  }

  $pnpmName = "pnpm.exe"
}

if ($null -eq $platform) {
  Write-Error "Platform could not be determined! Only Windows, Linux and MacOS are supported."
}

switch ($architecture) {
  'x64' { ; Break }
  'arm64' { ; Break }
  Default {
    Write-Error "Sorry! pnpm currently only provides pre-built binaries for x86_64/arm64 architectures."
  }
}

# The asset renaming shipped in pnpm v11.0.0-rc.3. Anything older than that
# release still has only the legacy asset names on its GitHub release page
# (`pnpm-macos-*`, `pnpm-win-*`, `pnpm-linuxstatic-*`), so the installer needs
# to know when to request which.
function Use-LegacyAssets {
  param([string]$Version)
  $major = [int]($Version -split '\.')[0]
  if ($major -lt 11) { return $true }
  # Only v11.0.0-rc.1 and v11.0.0-rc.2 were published before the rename.
  if ($Version -eq '11.0.0-rc.1' -or $Version -eq '11.0.0-rc.2') { return $true }
  return $false
}

# Whether pnpm $Version is release $Minimum (major.minor.patch) or newer. A
# prerelease of $Minimum is older. Same rule as install.sh.
function Test-VersionAtLeast {
  param([string]$Version, [string]$Minimum)
  $core = ($Version -split '[-+]')[0]
  if ($core -eq $Minimum -and $core -ne $Version) { return $false }
  return [version]$core -ge [version]$Minimum
}

# The arguments that run the built-in `pnpm setup` of pnpm $Version. From pnpm
# 11, a `setup` script in the current project replaces the built-in unless the
# `pm` prefix forces it. pnpm 12.0.0 was released without the prefix.
function Get-SetupArgs {
  param([string]$Version)
  $major = [int]($Version -split '\.')[0]
  if ((Test-VersionAtLeast $Version '12.1.0') -or ($major -eq 11 -and (Test-VersionAtLeast $Version '11.0.0'))) {
    return @('pm', 'setup')
  }
  return @('setup')
}

# Map the new-scheme target back to the legacy asset basename used by
# pre-rename pnpm releases. Arch is unchanged.
function Get-LegacyAssetBasename {
  param(
    [string]$Platform,
    [string]$Arch,
    [string]$LibcSuffix
  )
  if ($Platform -eq 'darwin' -and -not $LibcSuffix) {
    return "pnpm-macos-$Arch"
  }
  if ($Platform -eq 'win32' -and -not $LibcSuffix) {
    return "pnpm-win-$Arch"
  }
  if ($Platform -eq 'linux' -and $LibcSuffix -eq '-musl') {
    return "pnpm-linuxstatic-$Arch"
  }
  return "pnpm-$Platform-$Arch$LibcSuffix"
}

function Get-AssetBasename {
  param(
    [string]$Version,
    [string]$Platform,
    [string]$Arch,
    [string]$LibcSuffix
  )
  if (Use-LegacyAssets -Version $Version) {
    return (Get-LegacyAssetBasename -Platform $Platform -Arch $Arch -LibcSuffix $LibcSuffix)
  }
  return "pnpm-$Platform-$Arch$LibcSuffix"
}

$NpmRegistry = 'https://registry.npmjs.org'

# A reset connection or a 502 from a load balancer in front of the registry is
# transient, and should not fail the whole install, least of all in CI. The
# `get-pnpm` package and install.sh retry the same failures the same way.
$DownloadAttempts = 3

$DownloadOptions = @{ UseBasicParsing = $true }
# PowerShell 7.4+ can abandon a connection that stopped sending, so that a
# registry that stalled is retried instead of hanging the install. Earlier
# versions have no such timeout.
if ((Get-Command Invoke-WebRequest).Parameters.ContainsKey('OperationTimeoutSeconds')) {
  $DownloadOptions.OperationTimeoutSeconds = 30
}

# Invoke-WebRequest, retrying a transient failure. A 4xx is not retried: it
# means the request itself is wrong (a missing package, a bad version), except
# for a request timeout and rate limiting, which say "not now" rather than "no".
# -OutFile is rewritten from the start on each attempt.
function Invoke-Download {
  param(
    [string]$Uri,
    [string]$OutFile
  )
  $options = @{ Uri = $Uri } + $DownloadOptions
  if ($OutFile) {
    $options.OutFile = $OutFile
  }
  for ($attempt = 1; ; $attempt++) {
    try {
      return Invoke-WebRequest @options
    } catch {
      # No response at all, or one lost partway through, leaves no status.
      $response = $_.Exception.Response
      $status = if ($null -ne $response) { [int]$response.StatusCode } else { 0 }
      $retryable = $status -eq 0 -or $status -eq 408 -or $status -eq 429 -or $status -ge 500
      if (-not $retryable -or $attempt -ge $DownloadAttempts) {
        throw
      }
      Write-Host "Retrying $Uri" -ForegroundColor Yellow
      Start-Sleep -Seconds $attempt
    }
  }
}

# npm's registry signing key, mirrored from
# https://registry.npmjs.org/-/npm/v1/keys. See the matching comment in
# install.sh for why the key is pinned rather than fetched.
$NpmSigningKeyId = 'SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U'
$NpmSigningKey = 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY6Ya7W++7aUPzvMTrezH6Ycx3c+HOKYCcNGybJZSCJq/fd7Qa8uuAKtdIkUQtQiEKERhAmE5lMMJhP8OkDOa2g=='

# $true / $false, or $null when this runtime cannot check ECDSA signatures.
# Windows PowerShell 5.1 has no ImportSubjectPublicKeyInfo, so it falls back to
# the checksum alone.
function Test-NpmSignature {
  param(
    [string]$Message,
    [string]$Signature
  )
  if ($PSVersionTable.PSVersion.Major -lt 7) {
    return $null
  }
  try {
    $ecdsa = [System.Security.Cryptography.ECDsa]::Create()
    $bytesRead = 0
    $ecdsa.ImportSubjectPublicKeyInfo([Convert]::FromBase64String($NpmSigningKey), [ref]$bytesRead)
    return $ecdsa.VerifyData(
      [Text.Encoding]::UTF8.GetBytes($Message),
      [Convert]::FromBase64String($Signature),
      [System.Security.Cryptography.HashAlgorithmName]::SHA256,
      [System.Security.Cryptography.DSASignatureFormat]::Rfc3279DerSequence)
  } catch {
    # Past the version check the runtime can verify, so a throw here means the
    # signature itself did not parse. Treating that as "cannot check" would let
    # a malformed signature buy a downgrade to the checksum alone.
    return $false
  }
}

function Get-FileIntegrity {
  param([string]$Path)
  $sha512 = [System.Security.Cryptography.SHA512]::Create()
  $stream = [IO.File]::OpenRead($Path)
  try {
    return 'sha512-' + [Convert]::ToBase64String($sha512.ComputeHash($stream))
  } finally {
    $stream.Dispose()
  }
}

# Download <package>@<version> from the npm registry into $Destination and
# unpack it, refusing to go on unless the registry signature and the tarball
# checksum both check out.
function Get-VerifiedPackage {
  param(
    [string]$Package,
    [string]$Version,
    [string]$Destination
  )
  $meta = (Invoke-Download "$NpmRegistry/$Package/$Version").Content | ConvertFrom-Json
  $integrity = $meta.dist.integrity
  $signature = $meta.dist.signatures | Select-Object -First 1
  if (-not $integrity) {
    throw "The npm registry published no checksum for $Package@$Version."
  }
  if (-not $signature) {
    throw "$Package@$Version carries no npm registry signature, so it cannot be verified."
  }
  if ($signature.keyid -ne $NpmSigningKeyId) {
    throw "$Package@$Version is signed with an unexpected npm key ($($signature.keyid)). If npm has rotated its signing key, this installer needs updating."
  }

  $valid = Test-NpmSignature -Message "$Package@$Version`:$integrity" -Signature $signature.sig
  if ($valid -eq $false) {
    throw "The npm registry signature for $Package@$Version is not valid. Refusing to install."
  } elseif ($null -eq $valid) {
    Write-Host "PowerShell 7 is needed to check the npm signature - verifying the checksum only.`n" -ForegroundColor Yellow
  }

  $archive = Join-Path $Destination 'package.tgz'
  Invoke-Download $meta.dist.tarball -OutFile $archive
  if ((Get-FileIntegrity -Path $archive) -ne $integrity) {
    throw "$Package@$Version does not match the checksum the npm registry published for it. Refusing to install."
  }

  $unpacked = Join-Path $Destination 'unpacked'
  if (Test-Path $unpacked) {
    Remove-Item $unpacked -Recurse -Force
  }
  New-Item -ItemType Directory -Path $unpacked | Out-Null
  tar -xzf $archive -C $unpacked
  # $ErrorActionPreference does not cover the exit code of a native command, so
  # a failed unpack would otherwise surface later as a missing-path error.
  if ($LASTEXITCODE -ne 0) {
    throw "Could not unpack $Package@$Version."
  }
  Remove-Item $archive -Force
  return (Join-Path $unpacked 'package')
}

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

# Tags come from `pnpm`: it is published for every release, whereas
# `@pnpm/exe` is on its way out and would go stale.
$pkgInfo = Invoke-Download "$NpmRegistry/pnpm"
$versionJson = $pkgInfo.Content | ConvertFrom-Json
$versions = Get-Member -InputObject $versionJson.versions -Type NoteProperty | Select-Object -ExpandProperty Name
$distTags = Get-Member -InputObject $versionJson.'dist-tags' -Type NoteProperty | Select-Object -ExpandProperty Name

$version = $null
$preferredVersion = "latest"

if ($null -ne $env:PNPM_VERSION -and $env:PNPM_VERSION -ne "") {
  $preferredVersion = $env:PNPM_VERSION
}

if ($null -eq $version -and $preferredVersion -in $distTags) {
  $version = $versionJson.'dist-tags' | Select-Object -ExpandProperty $preferredVersion
}

if ($null -eq $version -and $preferredVersion -in $versions) {
  $version = $preferredVersion
}

# A bare major (12, v12) asks for that major's current release, which pnpm
# publishes as `latest-<major>` once the major is stable and only as
# `next-<major>` until then. Same rule as install.sh and the `get-pnpm` package.
if ($null -eq $version -and $preferredVersion -match '^v?(\d+)$') {
  $major = $Matches[1]
  foreach ($tag in "latest-$major", "next-$major") {
    if ($null -eq $version -and $tag -in $distTags) {
      $version = $versionJson.'dist-tags' | Select-Object -ExpandProperty $tag
    }
  }
}

if ($null -eq $version) {
  Write-Host "Current tags:" -ForegroundColor Yellow -NoNewline
  $versionJson.'dist-tags' | Format-List

  Write-Host "Versions:" -ForegroundColor Yellow -NoNewline
  $versionJson.versions | Get-Member -Type NoteProperty | Format-Wide -Property Name -AutoSize

  Write-Error "Sorry! pnpm '$preferredVersion' version could not be found. Use a major, or one of the tags or published versions from the provided list"
}

# Setting PNPM_NO_MODIFY_PATH to anything but "", "0", or "false" leaves the
# shell config and the registry alone.
$modifyPath = $env:PNPM_NO_MODIFY_PATH -in @($null, '', '0', 'false')
if (-not $modifyPath -and -not (Test-VersionAtLeast $version '12.16.0')) {
  throw "pnpm $version cannot be installed without adding it to PATH: PNPM_NO_MODIFY_PATH needs pnpm 12.16.0 or newer. Install a newer pnpm, or unset PNPM_NO_MODIFY_PATH."
}

$tempFileFolder = New-TemporaryDirectory
$majorVersion = [int]($version -split '\.')[0]

if ($majorVersion -ge 12) {
  # v12+ is published to the npm registry as well as to the GitHub release
  # page, with the same executable byte for byte. Only the registry copy comes
  # with a signature over its checksum, so that is the one worth downloading.
  Write-Host "Downloading pnpm $version...`n" -ForegroundColor Green

  $executable = if ($platform -eq 'win32') { 'pnpm.exe' } else { 'pnpm' }
  $platformPackage = "@pnpm/exe.$platform-$architecture$libcSuffix"

  $unpacked = Get-VerifiedPackage -Package $platformPackage -Version $version -Destination $tempFileFolder.FullName
  $tempFile = Join-Path $tempFileFolder.FullName $executable
  Move-Item (Join-Path $unpacked $executable) $tempFile

  # The executable expects the `dist/` tree next to it. `pnpm` is where the
  # registry publishes it, verified the same way. (`@pnpm/exe` currently
  # carries identical content, but only `pnpm` is published going forward.)
  $unpacked = Get-VerifiedPackage -Package 'pnpm' -Version $version -Destination $tempFileFolder.FullName
  Move-Item (Join-Path $unpacked 'dist') (Join-Path $tempFileFolder.FullName 'dist')
} elseif ($majorVersion -ge 11) {
  # v11: distributed as tarballs containing the binary and dist/ directory
  Write-Host "Downloading pnpm from GitHub...`n" -ForegroundColor Green
  $assetBase = Get-AssetBasename -Version $version -Platform $platform -Arch $architecture -LibcSuffix $libcSuffix
  if ($platform -eq 'win32') {
    $archiveUrl = "https://github.com/pnpm/pnpm/releases/download/v$version/$assetBase.zip"
    $tempArchive = Join-Path $tempFileFolder.FullName "pnpm.zip"
    Invoke-Download $archiveUrl -OutFile $tempArchive
    Expand-Archive -Path $tempArchive -DestinationPath $tempFileFolder.FullName -Force
    $tempFile = Join-Path $tempFileFolder.FullName "pnpm.exe"
  } else {
    $archiveUrl = "https://github.com/pnpm/pnpm/releases/download/v$version/$assetBase.tar.gz"
    $tempArchive = Join-Path $tempFileFolder.FullName "pnpm.tar.gz"
    Invoke-Download $archiveUrl -OutFile $tempArchive
    tar -xzf $tempArchive -C $tempFileFolder.FullName
    $tempFile = Join-Path $tempFileFolder.FullName "pnpm"
  }
} else {
  # older versions: distributed as a single executable binary
  Write-Host "Downloading pnpm from GitHub...`n" -ForegroundColor Green
  $assetBase = Get-AssetBasename -Version $version -Platform $platform -Arch $architecture -LibcSuffix $libcSuffix
  $archiveUrl = "https://github.com/pnpm/pnpm/releases/download/v$version/$assetBase"
  if ($platform -eq 'win32') {
    $archiveUrl = "$archiveUrl.exe"
  }
  $tempFile = Join-Path $tempFileFolder.FullName $pnpmName
  Invoke-Download $archiveUrl -OutFile $tempFile
}

Write-Host "Running setup...`n" -ForegroundColor Green

if ($platform -ne 'win32') {
  chmod +x $tempFile
}

$setupArgs = @(Get-SetupArgs $version)
if (-not $modifyPath) {
  $setupArgs += '--no-modify-path'
}

$setup = Start-Process -FilePath $tempFile -ArgumentList $setupArgs -NoNewWindow -Wait -PassThru -ErrorAction Continue

Remove-Item $tempFileFolder -Recurse -Force

if ($setup.ExitCode -ne 0) {
  throw "pnpm setup failed with exit code $($setup.ExitCode)."
}

# Where `pnpm setup` installs: PNPM_HOME, or pnpm's default for the platform,
# resolved the way pnpm resolves it.
function Get-PnpmHomeDir {
  if ($env:PNPM_HOME) { return $env:PNPM_HOME }
  if ($env:XDG_DATA_HOME) { return Join-Path $env:XDG_DATA_HOME 'pnpm' }
  if ($platform -eq 'win32') {
    if ($env:LOCALAPPDATA) { return Join-Path $env:LOCALAPPDATA 'pnpm' }
    return Join-Path $HOME '.pnpm'
  }
  if ($platform -eq 'darwin') { return Join-Path $HOME 'Library/pnpm' }
  return Join-Path $HOME '.local/share/pnpm'
}

# The directory with every symlink resolved, or the directory itself when it
# does not exist yet.
function Resolve-RealPath([string] $Path) {
  try {
    $item = Get-Item -LiteralPath $Path -ErrorAction Stop
    # `ResolvedTarget` follows symlinks on PowerShell 7; Windows PowerShell has no such property.
    if ($item.PSObject.Properties['ResolvedTarget'] -and $item.ResolvedTarget) { return $item.ResolvedTarget }
    return $item.FullName
  } catch {
    return $Path
  }
}

# Whether $Dir is a directory `pnpm setup` links pnpm into: `$PNPM_HOME\bin`, or
# `$PNPM_HOME` itself, which the v10 layout linked into directly.
function Test-PnpmHomeBin([string] $Dir, [string] $PnpmHome) {
  $ownDirs = @((Join-Path $PnpmHome 'bin'), $PnpmHome)
  foreach ($own in $ownDirs) {
    if ($Dir.TrimEnd('\', '/') -eq $own.TrimEnd('\', '/')) { return $true }
    if ((Resolve-RealPath $Dir).TrimEnd('\', '/') -eq (Resolve-RealPath $own).TrimEnd('\', '/')) { return $true }
  }
  return $false
}

# How the `pnpm` at $Executable got there, as far as its location and, for a
# shim script, its contents tell: npm, homebrew, corepack, volta, scoop, or
# unknown.
function Get-InstallOrigin([string] $Executable) {
  foreach ($candidate in @((Resolve-RealPath $Executable), $Executable)) {
    $parts = ($candidate -split '[\\/]') | ForEach-Object { $_.ToLowerInvariant() }
    for ($i = 0; $i -lt $parts.Length; $i++) {
      if ($parts[$i] -eq 'cellar' -and $parts[$i + 1] -eq 'pnpm') { return 'homebrew' }
    }
    if ($parts -contains 'corepack') { return 'corepack' }
    if ($parts -contains '.volta') { return 'volta' }
    if ($parts -contains 'scoop') { return 'scoop' }
    for ($i = 0; $i -lt $parts.Length; $i++) {
      if ($parts[$i] -eq 'node_modules' -and $parts[$i + 1] -eq 'pnpm') { return 'npm' }
    }
  }
  # npm's and Corepack's shims name their target inside; a real executable is
  # far larger than any shim.
  try {
    if ((Get-Item -LiteralPath $Executable).Length -le 65536) {
      $script = Get-Content -LiteralPath $Executable -Raw
      if ($script -match 'corepack') { return 'corepack' }
      if ($script -match 'node_modules[\\/]pnpm[\\/]') { return 'npm' }
    }
  } catch {}
  return 'unknown'
}

# Say so when the pnpm just installed is not the one PATH resolves, or the
# success above is the last thing the person reads before `pnpm --version`
# prints the old version again. A `pnpm` from another installer (npm,
# Homebrew, Corepack, Volta, Scoop) that sits ahead of `$PNPM_HOME\bin` on PATH
# keeps running, so every version this script installs looks like it never took.
function Write-ShadowingPnpmWarning {
  $pnpmHome = Get-PnpmHomeDir
  $bin = Join-Path $pnpmHome 'bin'
  $found = @(Get-Command pnpm -All -CommandType Application -ErrorAction SilentlyContinue | ForEach-Object { $_.Source })
  if ($found.Length -eq 0) { return }
  $executable = $found[0]
  if (Test-PnpmHomeBin (Split-Path $executable -Parent) $pnpmHome) { return }
  $binOnPath = $false
  foreach ($other in ($found | Select-Object -Skip 1)) {
    if (Test-PnpmHomeBin (Split-Path $other -Parent) $pnpmHome) { $binOnPath = $true; break }
  }
  $origin = Get-InstallOrigin $executable
  $described = @{
    npm = 'installed with npm'; homebrew = 'installed with Homebrew'; corepack = 'a Corepack shim'
    volta = 'installed with Volta'; scoop = 'installed with Scoop'; unknown = 'not installed by pnpm'
  }[$origin]
  $removal = @{
    npm = 'npm uninstall -g pnpm'; homebrew = 'brew uninstall pnpm'; corepack = 'corepack disable pnpm'
    volta = 'volta uninstall pnpm'; scoop = 'scoop uninstall pnpm'
  }[$origin]
  $reorder = "move $bin ahead of $(Split-Path $executable -Parent) in PATH"
  $fix = if ($removal) { "run `"$removal`" or $reorder" } else { $reorder }
  if ($binOnPath) {
    Write-Host "Warning: `"pnpm`" on PATH is $executable ($described), which comes before $bin." -ForegroundColor Yellow
    Write-Host "Your shell keeps running that pnpm, not the one pnpm installed to $bin." -ForegroundColor Yellow
    Write-Host "To finish switching, $fix." -ForegroundColor Yellow
  } else {
    Write-Host "Warning: `"pnpm`" on PATH is $executable ($described), and $bin is not on PATH yet." -ForegroundColor Yellow
    $when = if ($modifyPath) { 'Once a new shell adds it' } else { 'When you add it' }
    Write-Host "$when, it has to come first: $fix." -ForegroundColor Yellow
  }
}

# How to take back what `pnpm setup` just did. Setup names the shell config
# file it changed and prints the lines it added, so this points back at that
# output rather than guessing the file again. On Windows, setup edits the user
# environment instead.
function Write-UndoHint {
  $pnpmHome = Get-PnpmHomeDir
  if (-not $modifyPath) {
    Write-Host "To uninstall, delete $pnpmHome."
  } elseif ($platform -eq 'win32') {
    Write-Host "To uninstall, delete $pnpmHome, then remove the PNPM_HOME variable and the Path entry pnpm setup added from your user environment variables."
  } else {
    Write-Host "To uninstall, delete $pnpmHome and the lines pnpm setup added to the shell config file named above."
  }
  Write-Host "See https://pnpm.io/uninstall"
}

Write-UndoHint
Write-ShadowingPnpmWarning

标签: PowerShell

上一篇: tauri windows环境设置

下一篇: tauri 桌面项目

客服