@pnpm/engine.pm.commands 1000.0.0 → 1100.0.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.
@@ -2,12 +2,16 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import util from 'node:util';
4
4
  import { linkBins } from '@pnpm/bins.linker';
5
+ import { createAllowBuildFunction } from '@pnpm/building.policy';
5
6
  import { getCurrentPackageName } from '@pnpm/cli.meta';
6
7
  import { iterateHashedGraphNodes, iteratePkgMeta, lockfileToDepGraph, } from '@pnpm/deps.graph-hasher';
7
8
  import { installGlobalPackages } from '@pnpm/global.commands';
8
9
  import { cleanOrphanedInstallDirs, createGlobalCacheKey, createInstallDir, findGlobalPackage, getHashLink, } from '@pnpm/global.packages';
9
10
  import { headlessInstall } from '@pnpm/installing.deps-restorer';
10
- import symlinkDir from 'symlink-dir';
11
+ import { symlinkDir } from 'symlink-dir';
12
+ // @pnpm/exe has platform-specific binaries, so its GVS hash must
13
+ // include ENGINE_NAME for correct per-platform resolution.
14
+ const PNPM_ALLOW_BUILDS = { '@pnpm/exe': true };
11
15
  /**
12
16
  * Installs pnpm to the global packages directory (for self-update).
13
17
  * Creates an entry in globalPkgDir that is visible to `pnpm ls -g`.
@@ -34,7 +38,7 @@ export async function installPnpmToStore(pnpmVersion, opts) {
34
38
  const wantedLockfile = buildLockfileFromEnvLockfile(opts.envLockfile, currentPkgName, pnpmVersion);
35
39
  const globalVirtualStoreDir = path.join(opts.storeDir, 'links');
36
40
  // Compute the GVS hash for the pnpm package to find its path
37
- const pnpmGvsPath = findPnpmGvsPath(wantedLockfile, currentPkgName, globalVirtualStoreDir);
41
+ const pnpmGvsPath = findPnpmGvsPath(wantedLockfile, currentPkgName, globalVirtualStoreDir, PNPM_ALLOW_BUILDS);
38
42
  const pnpmPkgDir = path.join(pnpmGvsPath, 'node_modules', currentPkgName);
39
43
  const binDir = path.join(pnpmGvsPath, 'bin');
40
44
  // Check if already installed in the GVS
@@ -51,6 +55,7 @@ export async function installPnpmToStore(pnpmVersion, opts) {
51
55
  try {
52
56
  await installFromLockfile(tmpInstallDir, binDir, {
53
57
  wantedLockfile,
58
+ allowBuilds: PNPM_ALLOW_BUILDS,
54
59
  storeController: opts.storeController,
55
60
  storeDir: opts.storeDir,
56
61
  registries: opts.registries,
@@ -70,10 +75,11 @@ export async function installPnpmToStore(pnpmVersion, opts) {
70
75
  }
71
76
  }
72
77
  function noop(_message) { }
73
- function findPnpmGvsPath(lockfile, pkgName, globalVirtualStoreDir) {
78
+ function findPnpmGvsPath(lockfile, pkgName, globalVirtualStoreDir, allowBuilds) {
74
79
  const graph = lockfileToDepGraph(lockfile);
75
80
  const pkgMetaIterator = iteratePkgMeta(lockfile, graph);
76
- for (const { hash, pkgMeta } of iterateHashedGraphNodes(graph, pkgMetaIterator)) {
81
+ const allowBuild = createAllowBuildFunction({ allowBuilds });
82
+ for (const { hash, pkgMeta } of iterateHashedGraphNodes(graph, pkgMetaIterator, allowBuild)) {
77
83
  if (pkgMeta.name === pkgName) {
78
84
  return path.join(globalVirtualStoreDir, hash);
79
85
  }
@@ -110,6 +116,7 @@ async function installPnpmToGlobalDir(opts, pkgName, version, wantedLockfile) {
110
116
  if (wantedLockfile != null && opts.storeController != null && opts.storeDir != null) {
111
117
  await installFromLockfile(installDir, binDir, {
112
118
  wantedLockfile,
119
+ allowBuilds: PNPM_ALLOW_BUILDS,
113
120
  storeController: opts.storeController,
114
121
  storeDir: opts.storeDir,
115
122
  registries: opts.registries,
@@ -150,8 +157,8 @@ async function installFromLockfile(installDir, binDir, opts) {
150
157
  registries: opts.registries,
151
158
  enableGlobalVirtualStore: true,
152
159
  globalVirtualStoreDir: path.join(opts.storeDir, 'links'),
160
+ allowBuilds: opts.allowBuilds,
153
161
  ignoreScripts: true,
154
- ignoreDepScripts: true,
155
162
  force: false,
156
163
  engineStrict: false,
157
164
  currentEngine: {
@@ -177,7 +184,7 @@ async function installFromLockfile(installDir, binDir, opts) {
177
184
  virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
178
185
  sideEffectsCacheRead: false,
179
186
  sideEffectsCacheWrite: false,
180
- rawConfig: {},
187
+ configByUri: {},
181
188
  unsafePerm: false,
182
189
  userAgent: '',
183
190
  packageManager: opts.packageManager ?? { name: 'pnpm', version: '' },
@@ -238,6 +245,18 @@ export function linkExePlatformBinary(installDir) {
238
245
  if (!fs.existsSync(src))
239
246
  return;
240
247
  const dest = path.join(exePkgDir, executable);
248
+ forceLink(src, dest);
249
+ if (platform === 'win') {
250
+ const exePkgJsonPath = path.join(exePkgDir, 'package.json');
251
+ const exePkg = JSON.parse(fs.readFileSync(exePkgJsonPath, 'utf8'));
252
+ exePkg.bin.pnpm = 'pnpm.exe';
253
+ exePkg.bin.pn = 'pn.cmd';
254
+ exePkg.bin.pnpx = 'pnpx.cmd';
255
+ exePkg.bin.pnx = 'pnx.cmd';
256
+ fs.writeFileSync(exePkgJsonPath, JSON.stringify(exePkg, null, 2));
257
+ }
258
+ }
259
+ function forceLink(src, dest) {
241
260
  try {
242
261
  fs.unlinkSync(dest);
243
262
  }
@@ -248,13 +267,6 @@ export function linkExePlatformBinary(installDir) {
248
267
  }
249
268
  fs.linkSync(src, dest);
250
269
  fs.chmodSync(dest, 0o755);
251
- if (platform === 'win') {
252
- const exePkgJsonPath = path.join(exePkgDir, 'package.json');
253
- const exePkg = JSON.parse(fs.readFileSync(exePkgJsonPath, 'utf8'));
254
- fs.writeFileSync(path.join(exePkgDir, 'pnpm'), 'This file intentionally left blank');
255
- exePkg.bin.pnpm = 'pnpm.exe';
256
- fs.writeFileSync(exePkgJsonPath, JSON.stringify(exePkg, null, 2));
257
- }
258
270
  }
259
271
  function buildLockfileFromEnvLockfile(envLockfile, pkgName, version) {
260
272
  const dependencies = {};
@@ -1,9 +1,9 @@
1
- import { type Config } from '@pnpm/config.reader';
1
+ import { type Config, type ConfigContext } from '@pnpm/config.reader';
2
2
  import { type CreateStoreControllerOptions } from '@pnpm/store.connection-manager';
3
3
  export declare function rcOptionsTypes(): Record<string, unknown>;
4
4
  export declare function cliOptionsTypes(): Record<string, unknown>;
5
5
  export declare const commandNames: string[];
6
6
  export declare const skipPackageManagerCheck = true;
7
7
  export declare function help(): string;
8
- export type SelfUpdateCommandOptions = CreateStoreControllerOptions & Pick<Config, 'globalPkgDir' | 'lockfileDir' | 'managePackageManagerVersions' | 'modulesDir' | 'pnpmHomeDir' | 'rootProjectManifestDir' | 'wantedPackageManager'>;
8
+ export type SelfUpdateCommandOptions = CreateStoreControllerOptions & Pick<Config, 'globalPkgDir' | 'lockfileDir' | 'managePackageManagerVersions' | 'modulesDir' | 'pnpmHomeDir'> & Pick<ConfigContext, 'rootProjectManifestDir' | 'wantedPackageManager'>;
9
9
  export declare function handler(opts: SelfUpdateCommandOptions, params: string[]): Promise<undefined | string>;
@@ -41,7 +41,7 @@ export async function handler(opts, params) {
41
41
  if (isExecutedByCorepack()) {
42
42
  throw new PnpmError('CANT_SELF_UPDATE_IN_COREPACK', 'You should update pnpm with corepack');
43
43
  }
44
- const { resolve } = createResolver({ ...opts, authConfig: opts.rawConfig });
44
+ const { resolve } = createResolver({ ...opts, configByUri: opts.configByUri });
45
45
  const pkgName = 'pnpm';
46
46
  const bareSpecifier = params[0] ?? 'latest';
47
47
  const resolution = await resolve({ alias: pkgName, bareSpecifier }, {
@@ -108,8 +108,8 @@ export async function handler(opts, params) {
108
108
  storeController: store.ctrl,
109
109
  storeDir: store.dir,
110
110
  });
111
- // Link bins to pnpmHomeDir so the updated pnpm is the active global binary
112
- await linkBins(path.join(baseDir, 'node_modules'), opts.pnpmHomeDir, { warn: globalWarn });
111
+ // Link bins to pnpmHomeDir/bin so the updated pnpm is the active global binary
112
+ await linkBins(path.join(baseDir, 'node_modules'), path.join(opts.pnpmHomeDir, 'bin'), { warn: globalWarn });
113
113
  if (alreadyExisted) {
114
114
  return `The ${bareSpecifier} version, v${resolution.manifest.version}, is already present on the system. It was activated by linking it from ${baseDir}.`;
115
115
  }
@@ -2,6 +2,7 @@ export declare const rcOptionsTypes: () => Record<string, unknown>;
2
2
  export declare const cliOptionsTypes: () => Record<string, unknown>;
3
3
  export declare const shorthands: {};
4
4
  export declare const commandNames: string[];
5
+ export declare const overridableByScript = true;
5
6
  export declare function help(): string;
6
7
  export declare function handler(opts: {
7
8
  force?: boolean;
@@ -5,6 +5,7 @@ import { detectIfCurrentPkgIsExecutable, packageManager } from '@pnpm/cli.meta';
5
5
  import { docsUrl } from '@pnpm/cli.utils';
6
6
  import { logger } from '@pnpm/logger';
7
7
  import { addDirToEnvPath, } from '@pnpm/os.env.path-extender';
8
+ import PATH from 'path-name';
8
9
  import { renderHelp } from 'render-help';
9
10
  export const rcOptionsTypes = () => ({});
10
11
  export const cliOptionsTypes = () => ({
@@ -12,6 +13,7 @@ export const cliOptionsTypes = () => ({
12
13
  });
13
14
  export const shorthands = {};
14
15
  export const commandNames = ['setup'];
16
+ export const overridableByScript = true;
15
17
  export function help() {
16
18
  return renderHelp({
17
19
  description: 'Sets up pnpm',
@@ -55,7 +57,7 @@ function installCliGlobally(execPath, pnpmHomeDir) {
55
57
  fs.writeFileSync(pkgJsonPath, JSON.stringify({
56
58
  name: '@pnpm/exe',
57
59
  version: packageManager.version,
58
- bin: { pnpm: execName },
60
+ bin: { pnpm: execName, pn: execName },
59
61
  }));
60
62
  createdPkgJson = true;
61
63
  }
@@ -64,11 +66,13 @@ function installCliGlobally(execPath, pnpmHomeDir) {
64
66
  prefix: process.cwd(),
65
67
  });
66
68
  try {
69
+ const binDir = path.join(pnpmHomeDir, 'bin');
67
70
  const { status, error } = spawnSync(execPath, ['add', '-g', `file:${execDir}`], {
68
71
  stdio: 'inherit',
69
72
  env: {
70
73
  ...process.env,
71
74
  PNPM_HOME: pnpmHomeDir,
75
+ [PATH]: `${binDir}${path.delimiter}${process.env[PATH] ?? ''}`,
72
76
  },
73
77
  });
74
78
  if (error)
@@ -83,7 +87,7 @@ function installCliGlobally(execPath, pnpmHomeDir) {
83
87
  }
84
88
  }
85
89
  }
86
- function createPnpxScripts(targetDir) {
90
+ function createAliasScripts(targetDir) {
87
91
  // Why script files instead of aliases?
88
92
  // 1. Aliases wouldn't work on all platform, such as Windows Command Prompt or POSIX `sh`.
89
93
  // 2. Aliases wouldn't work on all environments, such as non-interactive shells and CI environments.
@@ -91,32 +95,31 @@ function createPnpxScripts(targetDir) {
91
95
  // 4. Aliases cannot be located with the `which` or `where` command.
92
96
  // 5. Editing rc files is more error-prone than just write new files to the filesystem.
93
97
  fs.mkdirSync(targetDir, { recursive: true });
98
+ createShellScript(targetDir, 'pn', 'pnpm');
99
+ createShellScript(targetDir, 'pnpx', 'pnpm dlx');
100
+ createShellScript(targetDir, 'pnx', 'pnpm dlx');
101
+ }
102
+ function createShellScript(targetDir, name, command) {
94
103
  // windows can also use shell script via mingw or cygwin so no filter
95
- const shellScript = [
96
- '#!/bin/sh',
97
- 'exec pnpm dlx "$@"',
98
- ].join('\n');
99
- fs.writeFileSync(path.join(targetDir, 'pnpx'), shellScript, { mode: 0o755 });
104
+ const shellScript = `#!/bin/sh\nexec ${command} "$@"\n`;
105
+ fs.writeFileSync(path.join(targetDir, name), shellScript, { mode: 0o755 });
100
106
  if (process.platform === 'win32') {
101
- const batchScript = [
102
- '@echo off',
103
- 'pnpm dlx %*',
104
- ].join('\n');
105
- fs.writeFileSync(path.join(targetDir, 'pnpx.cmd'), batchScript);
106
- const powershellScript = 'pnpm dlx @args';
107
- fs.writeFileSync(path.join(targetDir, 'pnpx.ps1'), powershellScript);
107
+ fs.writeFileSync(path.join(targetDir, `${name}.cmd`), `@echo off\n${command} %*\n`);
108
+ fs.writeFileSync(path.join(targetDir, `${name}.ps1`), `${command} @args\n`);
108
109
  }
109
110
  }
110
111
  export async function handler(opts) {
111
112
  const execPath = getExecPath();
113
+ const binDir = path.join(opts.pnpmHomeDir, 'bin');
112
114
  if (execPath.match(/\.[cm]?js$/) == null) {
113
115
  installCliGlobally(execPath, opts.pnpmHomeDir);
114
- createPnpxScripts(opts.pnpmHomeDir);
116
+ createAliasScripts(binDir);
115
117
  }
116
118
  try {
117
119
  const report = await addDirToEnvPath(opts.pnpmHomeDir, {
118
120
  configSectionName: 'pnpm',
119
121
  proxyVarName: 'PNPM_HOME',
122
+ proxyVarSubDir: 'bin',
120
123
  overwrite: opts.force,
121
124
  position: 'start',
122
125
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/engine.pm.commands",
3
- "version": "1000.0.0",
3
+ "version": "1100.0.0",
4
4
  "description": "pnpm commands for self-updating and setting up pnpm",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -24,44 +24,46 @@
24
24
  "!*.map"
25
25
  ],
26
26
  "dependencies": {
27
- "@pnpm/os.env.path-extender": "^2.0.3",
27
+ "@pnpm/os.env.path-extender": "^3.0.0",
28
+ "path-name": "^1.0.0",
28
29
  "ramda": "npm:@pnpm/ramda@0.28.1",
29
30
  "render-help": "^2.0.0",
30
31
  "semver": "^7.7.2",
31
- "symlink-dir": "^7.0.0",
32
- "@pnpm/bins.linker": "1000.2.6",
33
- "@pnpm/cli.meta": "1000.0.11",
34
- "@pnpm/deps.graph-hasher": "1002.0.8",
35
- "@pnpm/cli.utils": "1001.2.8",
36
- "@pnpm/config.reader": "1004.4.2",
37
- "@pnpm/error": "1000.0.5",
38
- "@pnpm/global.packages": "1000.0.0-0",
39
- "@pnpm/installing.client": "1001.1.4",
40
- "@pnpm/global.commands": "1000.0.0-0",
41
- "@pnpm/installing.deps-restorer": "1006.0.0",
42
- "@pnpm/lockfile.types": "1002.0.2",
43
- "@pnpm/resolving.npm-resolver": "1004.4.1",
44
- "@pnpm/installing.env-installer": "1000.0.19",
45
- "@pnpm/store.connection-manager": "1002.2.4",
46
- "@pnpm/store.controller": "1004.0.0",
47
- "@pnpm/types": "1000.9.0",
48
- "@pnpm/workspace.project-manifest-reader": "1001.1.4"
32
+ "symlink-dir": "^10.0.1",
33
+ "@pnpm/bins.linker": "1100.0.0",
34
+ "@pnpm/building.policy": "1100.0.0",
35
+ "@pnpm/cli.meta": "1100.0.0",
36
+ "@pnpm/cli.utils": "1100.0.0",
37
+ "@pnpm/config.reader": "1100.0.0",
38
+ "@pnpm/deps.graph-hasher": "1100.0.0",
39
+ "@pnpm/global.commands": "1100.0.0",
40
+ "@pnpm/error": "1100.0.0",
41
+ "@pnpm/installing.client": "1100.0.0",
42
+ "@pnpm/installing.deps-restorer": "1100.0.0",
43
+ "@pnpm/installing.env-installer": "1100.0.0",
44
+ "@pnpm/global.packages": "1100.0.0",
45
+ "@pnpm/lockfile.types": "1100.0.0",
46
+ "@pnpm/store.connection-manager": "1100.0.0",
47
+ "@pnpm/types": "1100.0.0",
48
+ "@pnpm/store.controller": "1100.0.0",
49
+ "@pnpm/workspace.project-manifest-reader": "1100.0.0",
50
+ "@pnpm/resolving.npm-resolver": "1100.0.0"
49
51
  },
50
52
  "peerDependencies": {
51
53
  "@pnpm/logger": ">=1001.0.0 <1002.0.0"
52
54
  },
53
55
  "devDependencies": {
54
- "@jest/globals": "30.0.5",
56
+ "@jest/globals": "30.3.0",
55
57
  "@types/cross-spawn": "^6.0.6",
56
- "@types/ramda": "0.29.12",
58
+ "@types/ramda": "0.31.1",
57
59
  "@types/semver": "7.7.1",
58
60
  "cross-spawn": "^7.0.6",
59
- "nock": "13.3.4",
60
- "@pnpm/engine.pm.commands": "1000.0.0",
61
- "@pnpm/error": "1000.0.5",
62
- "@pnpm/logger": "1001.0.1",
63
- "@pnpm/prepare": "1000.0.4",
64
- "@pnpm/shell.path": "1000.0.0"
61
+ "@pnpm/logger": "1100.0.0",
62
+ "@pnpm/engine.pm.commands": "1100.0.0",
63
+ "@pnpm/prepare": "1100.0.0",
64
+ "@pnpm/error": "1100.0.0",
65
+ "@pnpm/shell.path": "1100.0.0",
66
+ "@pnpm/testing.mock-agent": "1100.0.0"
65
67
  },
66
68
  "engines": {
67
69
  "node": ">=22.13"
@@ -71,8 +73,8 @@
71
73
  },
72
74
  "scripts": {
73
75
  "lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
74
- "test": "pnpm run compile && pnpm run _test",
75
- "compile": "tsgo --build && pnpm run lint --fix",
76
- "_test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest"
76
+ "test": "pn compile && pn .test",
77
+ "compile": "tsgo --build && pn lint --fix",
78
+ ".test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest"
77
79
  }
78
80
  }