@pnpm/installing.commands 1100.7.3 → 1100.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -230,7 +230,7 @@ function selectProjectByDir(projects, searchedDir) {
230
230
  const project = projects.find(({ rootDir }) => path.relative(rootDir, searchedDir) === '');
231
231
  if (project == null)
232
232
  return undefined;
233
- return { [searchedDir]: { dependencies: [], package: project } };
233
+ return { [project.rootDir]: { dependencies: [], package: project } };
234
234
  }
235
235
  function getYarnLockfileType(lockFileContents) {
236
236
  return lockFileContents.includes('__metadata')
package/lib/install.d.ts CHANGED
@@ -7,7 +7,7 @@ export declare const shorthands: Record<string, string>;
7
7
  export declare const commandNames: string[];
8
8
  export declare const recursiveByDefault = true;
9
9
  export declare function help(): string;
10
- export type InstallCommandOptions = Pick<Config, 'autoInstallPeers' | 'bail' | 'bin' | 'catalogs' | 'configDependencies' | 'dedupeInjectedDeps' | 'dedupeDirectDeps' | 'dedupePeerDependents' | 'dedupePeers' | 'deployAllFiles' | 'depth' | 'dev' | 'enableGlobalVirtualStore' | 'engineStrict' | 'excludeLinksFromLockfile' | 'frozenLockfile' | 'global' | 'globalPnpmfile' | 'hoistPattern' | 'hoistingLimits' | 'publicHoistPattern' | 'ignorePnpmfile' | 'ignoreScripts' | 'injectWorkspacePackages' | 'linkWorkspacePackages' | 'lockfileDir' | 'lockfileOnly' | 'modulesDir' | 'nodeLinker' | 'patchedDependencies' | 'preferFrozenLockfile' | 'preferWorkspacePackages' | 'production' | 'registries' | 'save' | 'saveDev' | 'saveExact' | 'saveOptional' | 'savePeer' | 'savePrefix' | 'saveProd' | 'saveCatalogName' | 'saveWorkspaceProtocol' | 'lockfileIncludeTarballUrl' | 'sideEffectsCache' | 'sideEffectsCacheReadonly' | 'sort' | 'sharedWorkspaceLockfile' | 'tag' | 'trustLockfile' | 'allowBuilds' | 'optional' | 'virtualStoreDir' | 'workspaceConcurrency' | 'workspaceDir' | 'workspacePackagePatterns' | 'extraEnv' | 'resolutionMode' | 'ignoreWorkspaceCycles' | 'disallowWorkspaceCycles' | 'updateConfig' | 'overrides' | 'packageExtensions' | 'pnprServer' | 'supportedArchitectures' | 'packageConfigs'> & Pick<ConfigContext, 'allProjects' | 'cliOptions' | 'hooks' | 'rootProjectManifest' | 'rootProjectManifestDir' | 'allProjectsGraph' | 'selectedProjectsGraph'> & CreateStoreControllerOptions & Partial<Pick<Config, 'globalPkgDir'>> & {
10
+ export type InstallCommandOptions = Pick<Config, 'autoInstallPeers' | 'bail' | 'bin' | 'catalogs' | 'configDependencies' | 'dedupeInjectedDeps' | 'dedupeDirectDeps' | 'dedupePeerDependents' | 'dedupePeers' | 'deployAllFiles' | 'depth' | 'dev' | 'enableGlobalVirtualStore' | 'engineStrict' | 'excludeLinksFromLockfile' | 'frozenLockfile' | 'global' | 'globalPnpmfile' | 'hoistPattern' | 'hoistingLimits' | 'publicHoistPattern' | 'ignorePnpmfile' | 'ignoreScripts' | 'injectWorkspacePackages' | 'linkWorkspacePackages' | 'lockfileDir' | 'lockfileOnly' | 'optimisticRepeatInstall' | 'modulesDir' | 'nodeLinker' | 'patchedDependencies' | 'preferFrozenLockfile' | 'preferWorkspacePackages' | 'production' | 'registries' | 'save' | 'saveDev' | 'saveExact' | 'saveOptional' | 'savePeer' | 'savePrefix' | 'saveProd' | 'saveCatalogName' | 'saveWorkspaceProtocol' | 'lockfileIncludeTarballUrl' | 'sideEffectsCache' | 'sideEffectsCacheReadonly' | 'sort' | 'sharedWorkspaceLockfile' | 'tag' | 'trustLockfile' | 'allowBuilds' | 'optional' | 'virtualStoreDir' | 'workspaceConcurrency' | 'workspaceDir' | 'workspacePackagePatterns' | 'extraEnv' | 'resolutionMode' | 'ignoreWorkspaceCycles' | 'disallowWorkspaceCycles' | 'updateConfig' | 'overrides' | 'packageExtensions' | 'pnprServer' | 'supportedArchitectures' | 'packageConfigs'> & Pick<ConfigContext, 'allProjects' | 'cliOptions' | 'hooks' | 'rootProjectManifest' | 'rootProjectManifestDir' | 'allProjectsGraph' | 'selectedProjectsGraph'> & CreateStoreControllerOptions & Partial<Pick<Config, 'globalPkgDir'>> & {
11
11
  argv: {
12
12
  cooked?: string[];
13
13
  original: string[];
package/lib/install.js CHANGED
@@ -71,6 +71,7 @@ export function rcOptionsTypes() {
71
71
  'optional',
72
72
  'unsafe-perm',
73
73
  'verify-store-integrity',
74
+ 'frozen-store',
74
75
  'virtual-store-dir',
75
76
  'virtual-store-only',
76
77
  ], allTypes);
@@ -211,6 +212,10 @@ by any dependencies, so it is an emulation of a flat node_modules',
211
212
  description: 'If false, skips store integrity checks. These checks detect accidental corruption, not tampering by untrusted users with write access to the store',
212
213
  name: '--[no-]verify-store-integrity',
213
214
  },
215
+ {
216
+ description: 'Open the package store read-only (immutable) and skip all store writes. For installs against a store on a read-only filesystem (e.g. a Nix store); pair with --offline --frozen-lockfile. Incompatible with --force',
217
+ name: '--frozen-store',
218
+ },
214
219
  {
215
220
  description: 'Fail on missing or invalid peer dependencies',
216
221
  name: '--strict-peer-dependencies',
@@ -4,7 +4,7 @@ import { type UpdateMatchingFunction } from '@pnpm/installing.deps-installer';
4
4
  import type { LockfileObject } from '@pnpm/lockfile.types';
5
5
  import { type CreateStoreControllerOptions } from '@pnpm/store.connection-manager';
6
6
  import type { IncludedDependencies, PackageVulnerabilityAudit } from '@pnpm/types';
7
- export type InstallDepsOptions = Pick<Config, 'autoInstallPeers' | 'bail' | 'bin' | 'catalogs' | 'catalogMode' | 'cleanupUnusedCatalogs' | 'dedupePeerDependents' | 'dedupePeers' | 'depth' | 'dev' | 'enableGlobalVirtualStore' | 'virtualStoreOnly' | 'engineStrict' | 'excludeLinksFromLockfile' | 'global' | 'globalPnpmfile' | 'ignoreCurrentSpecifiers' | 'ignorePnpmfile' | 'ignoreScripts' | 'optimisticRepeatInstall' | 'linkWorkspacePackages' | 'lockfileDir' | 'lockfileOnly' | 'pnprServer' | 'production' | 'preferWorkspacePackages' | 'registries' | 'runtime' | 'runtimeOnFail' | 'save' | 'saveDev' | 'saveExact' | 'saveOptional' | 'savePeer' | 'savePrefix' | 'saveProd' | 'saveWorkspaceProtocol' | 'lockfileIncludeTarballUrl' | 'scriptsPrependNodePath' | 'scriptShell' | 'sideEffectsCache' | 'sideEffectsCacheReadonly' | 'sort' | 'sharedWorkspaceLockfile' | 'shellEmulator' | 'tag' | 'trustLockfile' | 'allowBuilds' | 'optional' | 'workspaceConcurrency' | 'workspaceDir' | 'workspacePackagePatterns' | 'extraEnv' | 'ignoreWorkspaceCycles' | 'disallowWorkspaceCycles' | 'configDependencies' | 'packageExtensions' | 'updateConfig'> & Pick<ConfigContext, 'allProjects' | 'allProjectsGraph' | 'cliOptions' | 'hooks' | 'rootProjectManifestDir' | 'rootProjectManifest' | 'selectedProjectsGraph'> & Partial<Pick<Config, 'ci'>> & CreateStoreControllerOptions & {
7
+ export type InstallDepsOptions = Pick<Config, 'autoInstallPeers' | 'bail' | 'bin' | 'catalogs' | 'catalogMode' | 'cleanupUnusedCatalogs' | 'dedupePeerDependents' | 'dedupePeers' | 'depth' | 'dev' | 'enableGlobalVirtualStore' | 'virtualStoreOnly' | 'engineStrict' | 'excludeLinksFromLockfile' | 'global' | 'globalPnpmfile' | 'ignoreCurrentSpecifiers' | 'ignorePnpmfile' | 'ignoreScripts' | 'optimisticRepeatInstall' | 'linkWorkspacePackages' | 'lockfileDir' | 'lockfileOnly' | 'pnprServer' | 'production' | 'preferWorkspacePackages' | 'registries' | 'runtime' | 'runtimeOnFail' | 'save' | 'saveDev' | 'saveExact' | 'saveOptional' | 'savePeer' | 'savePrefix' | 'saveProd' | 'saveWorkspaceProtocol' | 'lockfileIncludeTarballUrl' | 'scriptsPrependNodePath' | 'scriptShell' | 'sideEffectsCache' | 'sideEffectsCacheReadonly' | 'sort' | 'sharedWorkspaceLockfile' | 'shellEmulator' | 'tag' | 'trustLockfile' | 'allowBuilds' | 'optional' | 'workspaceConcurrency' | 'workspaceDir' | 'workspacePackagePatterns' | 'extraEnv' | 'ignoreWorkspaceCycles' | 'disallowWorkspaceCycles' | 'configDependencies' | 'packageExtensions' | 'updateConfig' | 'virtualStoreDirMaxLength'> & Pick<ConfigContext, 'allProjects' | 'allProjectsGraph' | 'cliOptions' | 'hooks' | 'rootProjectManifestDir' | 'rootProjectManifest' | 'selectedProjectsGraph'> & Partial<Pick<Config, 'ci'>> & CreateStoreControllerOptions & {
8
8
  argv: {
9
9
  cooked?: string[];
10
10
  original: string[];
@@ -48,5 +48,5 @@ export type InstallDepsOptions = Pick<Config, 'autoInstallPeers' | 'bail' | 'bin
48
48
  * subcommand — see `runPacquet.ts`'s `noRuntime` opt.
49
49
  */
50
50
  isInstallCommand?: boolean;
51
- } & Partial<Pick<Config, 'pnpmHomeDir' | 'strictDepBuilds'>>;
51
+ } & Partial<Pick<Config, 'pnpmHomeDir' | 'strictDepBuilds' | 'useLockfile' | 'useGitBranchLockfile'>>;
52
52
  export declare function installDeps(opts: InstallDepsOptions, params: string[]): Promise<void>;
@@ -5,6 +5,7 @@ import { checkDepsStatus } from '@pnpm/deps.status';
5
5
  import { PnpmError } from '@pnpm/error';
6
6
  import { arrayOfWorkspacePackagesToMap } from '@pnpm/installing.context';
7
7
  import { install, mutateModulesInSingleProject, } from '@pnpm/installing.deps-installer';
8
+ import { writeWantedLockfile } from '@pnpm/lockfile.fs';
8
9
  import { globalInfo, logger } from '@pnpm/logger';
9
10
  import { applyRuntimeOnFailOverride, filterDependenciesByType } from '@pnpm/pkg-manifest.utils';
10
11
  import { createStoreController } from '@pnpm/store.connection-manager';
@@ -28,11 +29,11 @@ const OVERWRITE_UPDATE_OPTIONS = {
28
29
  };
29
30
  export async function installDeps(opts, params) {
30
31
  if (!opts.update && !opts.dedupe && params.length === 0 && opts.optimisticRepeatInstall) {
31
- const { upToDate } = await checkDepsStatus({
32
+ const { upToDate, wantedLockfileToRestore } = await checkDepsStatus({
32
33
  ...opts,
33
34
  ignoreFilteredInstallCache: true,
34
35
  });
35
- if (upToDate) {
36
+ if (upToDate && await restoreWantedLockfileIfMissing(wantedLockfileToRestore, opts)) {
36
37
  if (opts.hooks?.customResolvers?.some(r => r.shouldRefreshResolution)) {
37
38
  logger.warn({
38
39
  message: 'shouldRefreshResolution hooks were skipped because optimisticRepeatInstall is enabled.',
@@ -94,6 +95,7 @@ export async function installDeps(opts, params) {
94
95
  packageName: pacquetConfigDepName,
95
96
  argv: { original: opts.argv.original, remain: opts.argv.remain ?? [] },
96
97
  isInstallCommand: opts.isInstallCommand === true,
98
+ virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
97
99
  })
98
100
  : undefined;
99
101
  const includeDirect = opts.includeDirect ?? {
@@ -351,7 +353,7 @@ function selectProjectByDir(projects, searchedDir) {
351
353
  const project = projects.find(({ rootDir }) => path.relative(rootDir, searchedDir) === '');
352
354
  if (project == null)
353
355
  return undefined;
354
- return { [searchedDir]: { dependencies: [], package: project } };
356
+ return { [project.rootDir]: { dependencies: [], package: project } };
355
357
  }
356
358
  async function recursiveInstallThenUpdateWorkspaceState(allProjects, params, opts, cmdFullName) {
357
359
  const recursiveResult = await recursive(allProjects, params, opts, cmdFullName);
@@ -416,4 +418,26 @@ function preferNonvulnerablePackageVersions(packageVulnerabilityAudit) {
416
418
  }
417
419
  return preferredVersions;
418
420
  }
421
+ /**
422
+ * Restore a missing `pnpm-lock.yaml` from the current lockfile before the
423
+ * optimistic repeat-install short-circuit reports "Already up to date", so
424
+ * the fast path leaves the same on-disk contract a full install would.
425
+ * Returns `true` when the short-circuit may proceed: nothing to restore,
426
+ * lockfile writing is disabled (`useLockfile: false`), or the restore
427
+ * succeeded. A failed write returns `false` so the caller falls through to
428
+ * the full install instead of reporting up to date while `pnpm-lock.yaml`
429
+ * stays missing.
430
+ */
431
+ async function restoreWantedLockfileIfMissing(wantedLockfileToRestore, opts) {
432
+ if (wantedLockfileToRestore == null || opts.useLockfile === false)
433
+ return true;
434
+ try {
435
+ await writeWantedLockfile(wantedLockfileToRestore.lockfileDir, wantedLockfileToRestore.lockfile);
436
+ return true;
437
+ }
438
+ catch (error) {
439
+ logger.debug({ msg: 'Failed to restore pnpm-lock.yaml from the current lockfile', error });
440
+ return false;
441
+ }
442
+ }
419
443
  //# sourceMappingURL=installDeps.js.map
package/lib/link.js CHANGED
@@ -31,6 +31,7 @@ export function cliOptionsTypes() {
31
31
  'save-exact',
32
32
  'save-optional',
33
33
  'save-prefix',
34
+ 'trust-lockfile',
34
35
  'unsafe-perm',
35
36
  ], allTypes);
36
37
  }
@@ -32,11 +32,17 @@ export type RecursiveOptions = CreateStoreControllerOptions & Pick<Config, 'bail
32
32
  pnpmfile: string[];
33
33
  /**
34
34
  * Alternative install engine (today: pacquet) the deps-installer
35
- * delegates the materialization phase to. Built in `installDeps`
36
- * when `configDependencies.pacquet` is declared, threaded through
37
- * here so the recursive workspace path picks it up too.
35
+ * delegates the install to. Built in `installDeps` when
36
+ * `configDependencies.pacquet` is declared, threaded through here so
37
+ * the recursive workspace path picks it up too.
38
38
  */
39
- runPacquet?: () => Promise<void>;
39
+ runPacquet?: {
40
+ supportsResolution: boolean;
41
+ run: (opts?: {
42
+ filterResolvedProgress?: boolean;
43
+ resolve?: boolean;
44
+ }) => Promise<void>;
45
+ };
40
46
  } & Partial<Pick<Config, 'ci' | 'sort' | 'strictDepBuilds' | 'workspaceConcurrency'>> & Required<Pick<Config, 'workspaceDir'>>;
41
47
  export type CommandFullName = 'install' | 'add' | 'remove' | 'update' | 'import';
42
48
  export declare function recursive(allProjects: Project[], params: string[], opts: RecursiveOptions, cmdFullName: CommandFullName): Promise<boolean | string>;
@@ -1,5 +1,12 @@
1
1
  export interface MakeRunPacquetOpts {
2
2
  lockfileDir: string;
3
+ /**
4
+ * Effective pnpm config value. Forwarded through `PNPM_CONFIG_*` so
5
+ * pacquet writes the same `.modules.yaml` and virtual-store paths as
6
+ * the pnpm process that delegated to it, including Windows' shorter
7
+ * default.
8
+ */
9
+ virtualStoreDirMaxLength: number;
3
10
  /**
4
11
  * Which `configDependencies` entry installed pacquet: either the
5
12
  * original unscoped `pacquet` or the official scoped
@@ -32,21 +39,6 @@ export interface MakeRunPacquetOpts {
32
39
  */
33
40
  isInstallCommand: boolean;
34
41
  }
35
- /**
36
- * Build the install-engine callback `mutateModules` invokes when
37
- * `configDependencies` declares pacquet.
38
- *
39
- * The callback spawns the pacquet binary installed under
40
- * `node_modules/.pnpm-config/pacquet`. From `pnpm install`/`pnpm i` it
41
- * forwards the user's own pnpm CLI flags to pacquet's `install`
42
- * subcommand; from `add`/`update`/`dedupe` it doesn't forward (warning
43
- * instead). Pacquet's NDJSON stderr is parsed line-by-line and the
44
- * valid JSON records are re-emitted on pnpm's global `streamParser` so
45
- * `@pnpm/cli.default-reporter` renders pacquet's events the same way it
46
- * renders pnpm's own. Non-JSON stderr lines (panic backtraces,
47
- * unexpected diagnostics) are forwarded to the real stderr verbatim so
48
- * they reach the user.
49
- */
50
42
  /** Args the deps-installer passes per pacquet invocation. */
51
43
  export interface RunPacquetCallOpts {
52
44
  /**
@@ -60,5 +52,51 @@ export interface RunPacquetCallOpts {
60
52
  * source.
61
53
  */
62
54
  filterResolvedProgress?: boolean;
55
+ /**
56
+ * `true` to let pacquet perform the resolution itself rather than
57
+ * materialize an already-resolved lockfile. Drops the injected
58
+ * `--frozen-lockfile`, so pacquet resolves the manifests, writes
59
+ * `pnpm-lock.yaml`, and materializes in a single pass. Only valid
60
+ * when {@link PacquetEngine.supportsResolution} is `true` (pacquet
61
+ * >= 0.11.7).
62
+ */
63
+ resolve?: boolean;
63
64
  }
64
- export declare function makeRunPacquet(opts: MakeRunPacquetOpts): (callOpts?: RunPacquetCallOpts) => Promise<void>;
65
+ /**
66
+ * Handle to the pacquet install engine: its capabilities plus the
67
+ * callback `mutateModules` invokes to run it.
68
+ */
69
+ export interface PacquetEngine {
70
+ /**
71
+ * `true` when the installed pacquet is new enough (>= 0.11.7) to
72
+ * perform dependency resolution itself. When `false`, pacquet can
73
+ * only materialize an already-resolved lockfile, so the deps-installer
74
+ * runs its own resolve pass first and hands the written lockfile to
75
+ * pacquet.
76
+ */
77
+ supportsResolution: boolean;
78
+ run: (callOpts?: RunPacquetCallOpts) => Promise<void>;
79
+ }
80
+ /**
81
+ * Build the pacquet install engine `mutateModules` delegates to when
82
+ * `configDependencies` declares pacquet.
83
+ *
84
+ * `run` spawns the pacquet binary installed under
85
+ * `node_modules/.pnpm-config/pacquet`. From `pnpm install`/`pnpm i` it
86
+ * forwards the user's own pnpm CLI flags to pacquet's `install`
87
+ * subcommand; from `add`/`update`/`dedupe` it doesn't forward (warning
88
+ * instead). Pacquet's NDJSON stderr is parsed line-by-line and the
89
+ * valid JSON records are re-emitted on pnpm's global `streamParser` so
90
+ * `@pnpm/cli.default-reporter` renders pacquet's events the same way it
91
+ * renders pnpm's own. Non-JSON stderr lines (panic backtraces,
92
+ * unexpected diagnostics) are forwarded to the real stderr verbatim so
93
+ * they reach the user.
94
+ */
95
+ export declare function makeRunPacquet(opts: MakeRunPacquetOpts): PacquetEngine;
96
+ /**
97
+ * Name of the `@pacquet/<platform>-<arch>[-musl]` package that holds the
98
+ * native pacquet binary for the host. On linux the binary packages are
99
+ * split by libc and only the matching one is installed, so spawning and
100
+ * signature verification must agree on this exact name.
101
+ */
102
+ export declare function pacquetPlatformPkgName(): string;
package/lib/runPacquet.js CHANGED
@@ -6,12 +6,34 @@ import readline from 'node:readline';
6
6
  import { PnpmError } from '@pnpm/error';
7
7
  import { logger, streamParser } from '@pnpm/logger';
8
8
  import chalk from 'chalk';
9
+ import { familySync as getLibcFamilySync, MUSL } from 'detect-libc';
9
10
  // The runtime `streamParser` is a `Transform` stream (split2 + JSON.parse).
10
11
  // Its public typing only exposes `on`/`removeListener`, so we narrow to the
11
12
  // writable side here to feed pacquet's NDJSON lines back through the same
12
13
  // parser that `@pnpm/cli.default-reporter` listens on.
13
14
  const streamParserWritable = streamParser;
15
+ /**
16
+ * Build the pacquet install engine `mutateModules` delegates to when
17
+ * `configDependencies` declares pacquet.
18
+ *
19
+ * `run` spawns the pacquet binary installed under
20
+ * `node_modules/.pnpm-config/pacquet`. From `pnpm install`/`pnpm i` it
21
+ * forwards the user's own pnpm CLI flags to pacquet's `install`
22
+ * subcommand; from `add`/`update`/`dedupe` it doesn't forward (warning
23
+ * instead). Pacquet's NDJSON stderr is parsed line-by-line and the
24
+ * valid JSON records are re-emitted on pnpm's global `streamParser` so
25
+ * `@pnpm/cli.default-reporter` renders pacquet's events the same way it
26
+ * renders pnpm's own. Non-JSON stderr lines (panic backtraces,
27
+ * unexpected diagnostics) are forwarded to the real stderr verbatim so
28
+ * they reach the user.
29
+ */
14
30
  export function makeRunPacquet(opts) {
31
+ return {
32
+ supportsResolution: pacquetSupportsResolution(resolvePacquetVersion(opts.lockfileDir, opts.packageName)),
33
+ run: makeRun(opts),
34
+ };
35
+ }
36
+ function makeRun(opts) {
15
37
  return async (callOpts) => {
16
38
  const pacquetBin = resolvePacquetBin(opts.lockfileDir, opts.packageName);
17
39
  // From `pnpm install`/`pnpm i` we forward the user's flags through to
@@ -19,21 +41,23 @@ export function makeRunPacquet(opts) {
19
41
  // surface closely enough on that command that they're safe to pass
20
42
  // along. From `add`/`update`/`dedupe` we don't forward anything: those
21
43
  // commands carry flags pacquet's `install` doesn't recognize
22
- // (`--save-dev`, `--save-peer`, etc.) which clap would reject. Either
23
- // way pacquet picks up the settings users care about from
24
- // `pnpm-workspace.yaml` / `.npmrc` on its own, so a non-install
25
- // delegation isn't broken by the omission.
44
+ // (`--save-dev`, `--save-peer`, etc.) which clap would reject.
26
45
  const forwardedFlags = opts.isInstallCommand ? collectForwardedFlags(opts.argv) : [];
27
- // `--ignore-manifest-check` tells pacquet to skip its per-importer
28
- // `package.json` `pnpm-lock.yaml` freshness gate. pnpm just
29
- // resolved and wrote the lockfile itself; on `pnpm up` / `add` /
30
- // `remove` the manifest on disk is still the pre-mutation copy
31
- // (pnpm writes it after `mutateModules` returns), so pacquet's own
32
- // check would always fire here. See
46
+ // In resolve mode pacquet does the resolution itself, so it must not
47
+ // be pinned to the existing lockfile — drop both injected flags.
48
+ //
49
+ // Otherwise (frozen materialization) inject `--frozen-lockfile` plus
50
+ // `--ignore-manifest-check`. The latter tells pacquet to skip its
51
+ // per-importer `package.json` `pnpm-lock.yaml` freshness gate:
52
+ // pnpm just resolved and wrote the lockfile itself; on `pnpm up` /
53
+ // `add` / `remove` the manifest on disk is still the pre-mutation
54
+ // copy (pnpm writes it after `mutateModules` returns), so pacquet's
55
+ // own check would always fire here. See
33
56
  // https://github.com/pnpm/pnpm/issues/11797. The flag is narrow
34
57
  // (only the manifest check); settings drift like `overrides` is
35
58
  // still enforced and was already re-validated by pnpm.
36
- const args = ['--reporter=ndjson', 'install', '--frozen-lockfile', '--ignore-manifest-check', ...forwardedFlags];
59
+ const frozenArgs = callOpts?.resolve === true ? [] : ['--frozen-lockfile', '--ignore-manifest-check'];
60
+ const args = ['--reporter=ndjson', 'install', ...frozenArgs, ...forwardedFlags];
37
61
  const droppedFlags = opts.isInstallCommand ? [] : collectDroppedFlags(opts.argv);
38
62
  if (droppedFlags.length > 0) {
39
63
  logger.warn({
@@ -53,6 +77,7 @@ export function makeRunPacquet(opts) {
53
77
  logger.info({ message: banner, prefix: opts.lockfileDir });
54
78
  const child = spawn(pacquetBin, args, {
55
79
  cwd: opts.lockfileDir,
80
+ env: makePacquetEnv(opts),
56
81
  stdio: ['ignore', 'inherit', 'pipe'],
57
82
  });
58
83
  const filterResolved = callOpts?.filterResolvedProgress === true;
@@ -89,6 +114,16 @@ export function makeRunPacquet(opts) {
89
114
  });
90
115
  };
91
116
  }
117
+ function makePacquetEnv(opts) {
118
+ const env = { ...process.env };
119
+ for (const key of Object.keys(env)) {
120
+ if (key.toLowerCase() === 'pnpm_config_virtual_store_dir_max_length') {
121
+ delete env[key];
122
+ }
123
+ }
124
+ env.PNPM_CONFIG_VIRTUAL_STORE_DIR_MAX_LENGTH = String(opts.virtualStoreDirMaxLength);
125
+ return env;
126
+ }
92
127
  /**
93
128
  * Path of the platform-specific native pacquet binary for the host. The
94
129
  * pacquet npm package ships a Node wrapper at `bin/pacquet` that uses
@@ -108,7 +143,47 @@ export function makeRunPacquet(opts) {
108
143
  function resolvePacquetBin(lockfileDir, packageName) {
109
144
  const ext = process.platform === 'win32' ? '.exe' : '';
110
145
  const pacquetPkg = fs.realpathSync(path.join(lockfileDir, 'node_modules/.pnpm-config', packageName, 'package.json'));
111
- return createRequire(pacquetPkg).resolve(`@pacquet/${process.platform}-${process.arch}/pacquet${ext}`);
146
+ return createRequire(pacquetPkg).resolve(`${pacquetPlatformPkgName()}/pacquet${ext}`);
147
+ }
148
+ /**
149
+ * Name of the `@pacquet/<platform>-<arch>[-musl]` package that holds the
150
+ * native pacquet binary for the host. On linux the binary packages are
151
+ * split by libc and only the matching one is installed, so spawning and
152
+ * signature verification must agree on this exact name.
153
+ */
154
+ export function pacquetPlatformPkgName() {
155
+ const libc = process.platform === 'linux' && getLibcFamilySync() === MUSL ? '-musl' : '';
156
+ return `@pacquet/${process.platform}-${process.arch}${libc}`;
157
+ }
158
+ /**
159
+ * Read the installed pacquet's version from its `package.json` under
160
+ * `node_modules/.pnpm-config/<packageName>`. Returns `undefined` if it
161
+ * can't be read — callers treat that as "assume the older,
162
+ * materialization-only pacquet" so a missing/garbled manifest degrades
163
+ * to the safe path rather than failing the install.
164
+ */
165
+ function resolvePacquetVersion(lockfileDir, packageName) {
166
+ try {
167
+ const pacquetPkg = fs.realpathSync(path.join(lockfileDir, 'node_modules/.pnpm-config', packageName, 'package.json'));
168
+ const { version } = JSON.parse(fs.readFileSync(pacquetPkg, 'utf8'));
169
+ return version;
170
+ }
171
+ catch {
172
+ return undefined;
173
+ }
174
+ }
175
+ /**
176
+ * pacquet gained full resolving installs in 0.11.7; earlier releases
177
+ * stay on pnpm's resolve-then-materialize path. Pre-release builds of
178
+ * 0.11.7 (e.g. `0.11.7-rc.1`) count as supporting it.
179
+ */
180
+ function pacquetSupportsResolution(version) {
181
+ if (version == null)
182
+ return false;
183
+ const [major, minor, patch] = version.split('.', 3).map((part) => parseInt(part, 10));
184
+ if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch))
185
+ return false;
186
+ return major > 0 || (major === 0 && (minor > 11 || (minor === 11 && patch >= 7)));
112
187
  }
113
188
  /**
114
189
  * From `pnpm install`/`pnpm i`, return everything in argv that should
@@ -205,6 +205,11 @@ async function interactiveUpdate(input, opts, rebuildHandler) {
205
205
  flatChoices.push({
206
206
  name: choice.message,
207
207
  value: choice.value,
208
+ // `name` is the rendered table row (label + versions + workspace + url)
209
+ // that lays out a single choice during selection. After submission
210
+ // @inquirer/prompts comma-joins each choice's `short`, which without
211
+ // this defaults to `name` and dumps the whole table back to stdout.
212
+ short: choice.value,
208
213
  });
209
214
  }
210
215
  }
@@ -4,6 +4,7 @@ import { PnpmError } from '@pnpm/error';
4
4
  import { readEnvLockfile } from '@pnpm/lockfile.fs';
5
5
  import { logger } from '@pnpm/logger';
6
6
  import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header';
7
+ import { pacquetPlatformPkgName } from './runPacquet.js';
7
8
  /**
8
9
  * Decides whether pnpm may spawn the pacquet binary installed under
9
10
  * `node_modules/.pnpm-config/<packageName>` as an install engine.
@@ -60,7 +61,7 @@ async function collectPacquetPackagesToVerify(packageName, rootDir, registries)
60
61
  return undefined;
61
62
  // Only the host's platform binary is ever spawned, so that's the one whose
62
63
  // identity matters. If it isn't in the lockfile, pacquet couldn't run here.
63
- const platformPkgName = `@pacquet/${process.platform}-${process.arch}`;
64
+ const platformPkgName = pacquetPlatformPkgName();
64
65
  const platformVersion = envLockfile.snapshots[shimKey]?.optionalDependencies?.[platformPkgName];
65
66
  if (platformVersion == null)
66
67
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/installing.commands",
3
- "version": "1100.7.3",
3
+ "version": "1100.9.0",
4
4
  "description": "Commands for installation",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -30,14 +30,15 @@
30
30
  "@inquirer/prompts": "^8.4.3",
31
31
  "@pnpm/colorize-semver-diff": "^2.0.0",
32
32
  "@pnpm/semver-diff": "^2.0.0",
33
- "@pnpm/util.lex-comparator": "^3.0.2",
34
- "@yarnpkg/core": "4.5.0",
33
+ "@pnpm/util.lex-comparator": "^4.0.1",
34
+ "@yarnpkg/core": "4.8.0",
35
35
  "@yarnpkg/lockfile": "^1.1.0",
36
36
  "@yarnpkg/parsers": "3.0.3",
37
37
  "@zkochan/rimraf": "^4.0.0",
38
38
  "@zkochan/table": "^2.0.1",
39
39
  "chalk": "^5.6.2",
40
40
  "ci-info": "^4.4.0",
41
+ "detect-libc": "^2.1.2",
41
42
  "get-npm-tarball-url": "^2.1.0",
42
43
  "is-subdir": "^2.0.0",
43
44
  "load-json-file": "^7.0.1",
@@ -47,62 +48,62 @@
47
48
  "ramda": "npm:@pnpm/ramda@0.28.1",
48
49
  "render-help": "^2.0.0",
49
50
  "version-selector-type": "^3.0.0",
50
- "@pnpm/building.policy": "1100.0.9",
51
- "@pnpm/cli.command": "1100.0.1",
51
+ "@pnpm/building.after-install": "1102.0.0",
52
+ "@pnpm/building.policy": "1100.0.10",
52
53
  "@pnpm/catalogs.types": "1100.0.0",
54
+ "@pnpm/cli.utils": "1101.0.12",
53
55
  "@pnpm/cli.common-cli-options-help": "1100.0.2",
54
- "@pnpm/cli.utils": "1101.0.10",
56
+ "@pnpm/cli.command": "1100.0.1",
55
57
  "@pnpm/config.matcher": "1100.0.1",
56
- "@pnpm/config.pick-registry-for-package": "1100.0.8",
57
- "@pnpm/config.writer": "1100.0.12",
58
+ "@pnpm/config.pick-registry-for-package": "1100.0.9",
59
+ "@pnpm/config.reader": "1101.9.0",
60
+ "@pnpm/config.writer": "1100.0.13",
58
61
  "@pnpm/constants": "1100.0.0",
59
- "@pnpm/config.reader": "1101.7.0",
60
- "@pnpm/deps.inspection.outdated": "1100.1.6",
61
- "@pnpm/deps.status": "1100.0.23",
62
- "@pnpm/deps.path": "1100.0.7",
63
- "@pnpm/deps.security.signatures": "1101.2.0",
62
+ "@pnpm/deps.inspection.outdated": "1100.1.8",
63
+ "@pnpm/deps.path": "1100.0.8",
64
+ "@pnpm/deps.security.signatures": "1101.2.2",
65
+ "@pnpm/deps.status": "1100.1.1",
64
66
  "@pnpm/error": "1100.0.0",
65
- "@pnpm/fs.graceful-fs": "1100.1.0",
66
67
  "@pnpm/fs.read-modules-dir": "1100.0.1",
67
- "@pnpm/global.commands": "1100.0.26",
68
- "@pnpm/hooks.pnpmfile": "1100.0.13",
69
- "@pnpm/installing.context": "1100.0.16",
70
- "@pnpm/installing.dedupe.check": "1100.0.10",
71
- "@pnpm/building.after-install": "1101.0.20",
72
- "@pnpm/installing.deps-installer": "1101.8.0",
73
- "@pnpm/lockfile.fs": "1100.1.4",
74
- "@pnpm/installing.env-installer": "1101.1.7",
75
- "@pnpm/lockfile.types": "1100.0.10",
76
- "@pnpm/network.fetch": "1100.1.1",
77
- "@pnpm/network.auth-header": "1101.1.1",
78
- "@pnpm/resolving.npm-resolver": "1101.5.1",
68
+ "@pnpm/fs.graceful-fs": "1100.1.0",
69
+ "@pnpm/global.commands": "1100.0.28",
70
+ "@pnpm/hooks.pnpmfile": "1100.0.15",
71
+ "@pnpm/installing.dedupe.check": "1100.0.11",
72
+ "@pnpm/installing.context": "1100.0.18",
73
+ "@pnpm/installing.env-installer": "1102.0.0",
74
+ "@pnpm/lockfile.types": "1100.0.11",
75
+ "@pnpm/installing.deps-installer": "1102.0.0",
76
+ "@pnpm/network.fetch": "1100.1.3",
77
+ "@pnpm/lockfile.fs": "1100.1.5",
78
+ "@pnpm/network.auth-header": "1101.1.2",
79
+ "@pnpm/pkg-manifest.reader": "1100.0.8",
80
+ "@pnpm/resolving.npm-resolver": "1102.0.0",
79
81
  "@pnpm/resolving.parse-wanted-dependency": "1100.0.1",
80
- "@pnpm/pkg-manifest.utils": "1100.2.3",
81
- "@pnpm/resolving.resolver-base": "1100.4.1",
82
- "@pnpm/store.controller": "1101.0.12",
83
- "@pnpm/types": "1101.3.1",
84
- "@pnpm/store.connection-manager": "1100.2.7",
85
- "@pnpm/workspace.project-manifest-reader": "1100.0.11",
86
- "@pnpm/workspace.projects-filter": "1100.0.19",
87
- "@pnpm/workspace.project-manifest-writer": "1100.0.7",
88
- "@pnpm/workspace.projects-reader": "1101.0.10",
89
- "@pnpm/workspace.projects-sorter": "1100.0.6",
90
- "@pnpm/workspace.projects-graph": "1100.0.16",
91
- "@pnpm/workspace.state": "1100.0.20",
92
- "@pnpm/workspace.workspace-manifest-writer": "1100.0.12",
82
+ "@pnpm/pkg-manifest.utils": "1100.2.5",
83
+ "@pnpm/store.connection-manager": "1100.3.0",
84
+ "@pnpm/resolving.resolver-base": "1100.4.2",
85
+ "@pnpm/store.controller": "1102.0.0",
86
+ "@pnpm/types": "1101.3.2",
87
+ "@pnpm/workspace.project-manifest-reader": "1100.0.13",
88
+ "@pnpm/workspace.project-manifest-writer": "1100.0.8",
89
+ "@pnpm/workspace.projects-filter": "1100.0.21",
90
+ "@pnpm/workspace.projects-reader": "1101.0.12",
91
+ "@pnpm/workspace.projects-graph": "1100.0.18",
92
+ "@pnpm/workspace.projects-sorter": "1100.0.7",
93
93
  "@pnpm/workspace.root-finder": "1100.0.1",
94
- "@pnpm/pkg-manifest.reader": "1100.0.7"
94
+ "@pnpm/workspace.state": "1100.0.22",
95
+ "@pnpm/workspace.workspace-manifest-writer": "1100.0.13"
95
96
  },
96
97
  "peerDependencies": {
97
- "@pnpm/logger": "^1001.0.1"
98
+ "@pnpm/logger": "^1100.0.0"
98
99
  },
99
100
  "devDependencies": {
100
- "@jest/globals": "30.3.0",
101
+ "@jest/globals": "30.4.1",
101
102
  "@types/normalize-path": "^3.0.2",
102
103
  "@types/proxyquire": "^1.3.31",
103
104
  "@types/ramda": "0.31.1",
104
105
  "@types/yarnpkg__lockfile": "^1.1.9",
105
- "@types/zkochan__table": "npm:@types/table@6.0.0",
106
+ "@types/zkochan__table": "npm:@types/table@6.3.2",
106
107
  "delay": "^7.0.0",
107
108
  "jest-diff": "^30.4.1",
108
109
  "path-name": "^1.0.0",
@@ -113,19 +114,19 @@
113
114
  "write-json-file": "^7.0.0",
114
115
  "write-package": "7.2.0",
115
116
  "write-yaml-file": "^6.0.0",
116
- "@pnpm/installing.commands": "1100.7.3",
117
- "@pnpm/installing.modules-yaml": "1100.0.8",
117
+ "@pnpm/installing.commands": "1100.9.0",
118
+ "@pnpm/assert-project": "1100.0.16",
119
+ "@pnpm/installing.modules-yaml": "1100.0.9",
118
120
  "@pnpm/logger": "1100.0.0",
119
- "@pnpm/prepare": "1100.0.14",
120
- "@pnpm/assert-project": "1100.0.14",
121
- "@pnpm/store.index": "1100.1.0",
121
+ "@pnpm/prepare": "1100.0.16",
122
122
  "@pnpm/test-fixtures": "1100.0.0",
123
- "@pnpm/testing.command-defaults": "1100.0.4",
124
- "@pnpm/testing.registry-mock": "1100.0.4",
125
- "@pnpm/testing.mock-agent": "1101.0.1",
126
123
  "@pnpm/test-ipc-server": "1100.0.0",
127
- "@pnpm/workspace.projects-filter": "1100.0.19",
128
- "@pnpm/worker": "1100.1.10"
124
+ "@pnpm/testing.command-defaults": "1100.0.6",
125
+ "@pnpm/testing.mock-agent": "1101.0.3",
126
+ "@pnpm/testing.registry-mock": "1100.0.6",
127
+ "@pnpm/store.index": "1100.2.0",
128
+ "@pnpm/worker": "1100.2.0",
129
+ "@pnpm/workspace.projects-filter": "1100.0.21"
129
130
  },
130
131
  "engines": {
131
132
  "node": ">=22.13"