pnpm 12.15.0 → 12.16.1

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +213 -0
  2. package/README.md +4 -4
  3. package/bin/pnpm.mjs +11 -233
  4. package/dist/node_modules/.pnpm-workspace-state-v1.json +97 -269
  5. package/dist/node_modules/confbox/LICENSE +27 -0
  6. package/dist/node_modules/confbox/dist/_chunks/_format.d.mts +22 -0
  7. package/dist/node_modules/confbox/dist/_chunks/_format.mjs +1 -0
  8. package/dist/node_modules/confbox/dist/_chunks/libs/detect-indent.mjs +1 -0
  9. package/dist/node_modules/confbox/dist/_chunks/libs/ini.mjs +3 -0
  10. package/dist/node_modules/confbox/dist/_chunks/libs/js-yaml.d.mts +33 -0
  11. package/dist/node_modules/confbox/dist/_chunks/libs/js-yaml.mjs +38 -0
  12. package/dist/node_modules/confbox/dist/_chunks/libs/json5.mjs +14 -0
  13. package/dist/node_modules/confbox/dist/_chunks/libs/smol-toml.mjs +11 -0
  14. package/dist/node_modules/confbox/dist/_chunks/libs/strip-json-comments.mjs +4 -0
  15. package/dist/node_modules/confbox/dist/_chunks/rolldown-runtime.mjs +1 -0
  16. package/dist/node_modules/confbox/dist/index.d.mts +7 -0
  17. package/dist/node_modules/confbox/dist/index.mjs +1 -0
  18. package/dist/node_modules/confbox/dist/ini.d.mts +59 -0
  19. package/dist/node_modules/confbox/dist/ini.mjs +1 -0
  20. package/dist/node_modules/confbox/dist/json.d.mts +25 -0
  21. package/dist/node_modules/confbox/dist/json.mjs +1 -0
  22. package/dist/node_modules/confbox/dist/json5.d.mts +55 -0
  23. package/dist/node_modules/confbox/dist/json5.mjs +1 -0
  24. package/dist/node_modules/confbox/dist/jsonc.d.mts +29 -0
  25. package/dist/node_modules/confbox/dist/jsonc.mjs +1 -0
  26. package/dist/node_modules/confbox/dist/toml.d.mts +20 -0
  27. package/dist/node_modules/confbox/dist/toml.mjs +1 -0
  28. package/dist/node_modules/confbox/dist/yaml.d.mts +101 -0
  29. package/dist/node_modules/confbox/dist/yaml.mjs +1 -0
  30. package/dist/node_modules/confbox/package.json +68 -0
  31. package/dist/node_modules/get-pnpm/lib/corepack.js +79 -0
  32. package/dist/node_modules/get-pnpm/lib/downloadExecutable.js +10 -4
  33. package/dist/node_modules/get-pnpm/lib/extractTarballMember.js +45 -35
  34. package/dist/node_modules/get-pnpm/lib/index.js +3 -219
  35. package/dist/node_modules/get-pnpm/lib/install.js +293 -0
  36. package/dist/node_modules/get-pnpm/lib/npmConfig.js +280 -0
  37. package/dist/node_modules/get-pnpm/lib/npmSigningKeys.js +2 -6
  38. package/dist/node_modules/get-pnpm/lib/platformPackageName.js +11 -7
  39. package/dist/node_modules/get-pnpm/lib/pnpmConfig.js +297 -0
  40. package/dist/node_modules/get-pnpm/lib/projectPin.js +201 -0
  41. package/dist/node_modules/get-pnpm/lib/registry.js +265 -41
  42. package/dist/node_modules/get-pnpm/lib/resolveVersion.js +19 -2
  43. package/dist/node_modules/get-pnpm/lib/sameFileContents.js +31 -27
  44. package/dist/node_modules/get-pnpm/lib/shadowingPnpm.js +174 -0
  45. package/dist/node_modules/get-pnpm/lib/undoHint.js +12 -0
  46. package/dist/node_modules/get-pnpm/package.json +20 -14
  47. package/package.json +15 -15
@@ -0,0 +1 @@
1
+ import{getFormat as e,storeFormat as t,stripBOM as n}from"./_chunks/_format.mjs";import{YAMLException as r,dump as i,load as a}from"./_chunks/libs/js-yaml.mjs";function o(e,r){let i=a(n(e),r);return t(e,i,r),i}function s(t,n){let r=e(t,{preserveIndentation:!1}),a=typeof r.indent==`string`?r.indent.length:r.indent,o=i(t,{indent:a,...n});return r.whitespace.start+o.trim()+r.whitespace.end}export{r as YAMLException,o as parseYAML,s as stringifyYAML};
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "confbox",
3
+ "version": "0.3.1",
4
+ "description": "Compact YAML, TOML, JSONC, JSON5 and INI parser and serializer",
5
+ "keywords": [
6
+ "config",
7
+ "ini",
8
+ "json5",
9
+ "jsonc",
10
+ "toml",
11
+ "unjs",
12
+ "yaml"
13
+ ],
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "unjs/confbox"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "type": "module",
23
+ "sideEffects": false,
24
+ "types": "./dist/index.d.mts",
25
+ "exports": {
26
+ ".": "./dist/index.mjs",
27
+ "./ini": "./dist/ini.mjs",
28
+ "./json": "./dist/json.mjs",
29
+ "./json5": "./dist/json5.mjs",
30
+ "./jsonc": "./dist/jsonc.mjs",
31
+ "./toml": "./dist/toml.mjs",
32
+ "./yaml": "./dist/yaml.mjs"
33
+ },
34
+ "devDependencies": {
35
+ "@types/ini": "^4.1.1",
36
+ "@types/node": "^26",
37
+ "@vitest/coverage-v8": "^4.1.11",
38
+ "automd": "^0.4.3",
39
+ "changelogen": "^0.6.2",
40
+ "detect-indent": "^7.0.2",
41
+ "ini": "^7.0.0",
42
+ "jiti": "^2.7.0",
43
+ "js-toml": "^2.0.1",
44
+ "js-yaml": "^5.4.1",
45
+ "json5": "^2.2.3",
46
+ "jsonc-parser": "^3.3.1",
47
+ "mitata": "^1.0.34",
48
+ "obuild": "^0.4.39",
49
+ "oxfmt": ">=0.66.0",
50
+ "oxlint": "^1.81.0",
51
+ "smol-toml": "^1.8.0",
52
+ "strip-json-comments": "^5.0.1",
53
+ "toml": "^5.0.0",
54
+ "typescript": "^7",
55
+ "vitest": "^4.1.11",
56
+ "yaml": "^2.9.0"
57
+ },
58
+ "scripts": {
59
+ "build": "obuild",
60
+ "dev": "vitest dev --coverage",
61
+ "bench": "pnpm build && node test/bench.mjs",
62
+ "lint": "oxlint && oxfmt --check src test",
63
+ "lint:fix": "oxlint --fix && oxfmt src test",
64
+ "release": "pnpm test && pnpm build && changelogen --release && pnpm publish && git push --follow-tags",
65
+ "test": "pnpm lint && pnpm test:types && vitest run --coverage",
66
+ "test:types": "tsc --noEmit --skipLibCheck"
67
+ }
68
+ }
@@ -0,0 +1,79 @@
1
+ import { pnpmProxyConfig, pnpmRegistryConfig } from './pnpmConfig.js';
2
+ import { DEFAULT_REGISTRY } from './registry.js';
3
+ /**
4
+ * The options for `downloadPnpmExecutable` that download pnpm where
5
+ * Corepack would, verified the way Corepack verifies it.
6
+ *
7
+ * - `COREPACK_NPM_REGISTRY` names the registry. Otherwise `pnpmRegistryConfig`
8
+ * reads pnpm's config.
9
+ * - `COREPACK_NPM_TOKEN`, or `COREPACK_NPM_USERNAME` with
10
+ * `COREPACK_NPM_PASSWORD`, are the credentials for Corepack's registry, and
11
+ * for the npm registry when pnpm's config holds none for it.
12
+ * - `COREPACK_INTEGRITY_KEYS` follows Corepack. Unset, npm's own keys are
13
+ * trusted. A key set trusts its `npm` keys. `0` or empty skips the signature
14
+ * check, which a registry that re-publishes packages without npm's
15
+ * signatures already needs for Corepack to have installed pnpm from it.
16
+ * - The project `.npmrc` is read only while the signature is checked; see
17
+ * `PnpmRegistryConfigOptions.readProjectNpmrc`.
18
+ * - `pnpmProxyConfig` reads the proxy from pnpm's config, whichever registry
19
+ * is used.
20
+ *
21
+ * @throws if `COREPACK_ENABLE_NETWORK` is `0`, or `COREPACK_INTEGRITY_KEYS`
22
+ * holds no `npm` key set.
23
+ */
24
+ export function corepackDownloadOptions(opts = {}) {
25
+ const env = opts.env ?? process.env;
26
+ if (env.COREPACK_ENABLE_NETWORK === '0') {
27
+ throw new Error('Network access is disabled by the environment, so the pnpm binary cannot be downloaded.');
28
+ }
29
+ const signature = signaturePolicy(env.COREPACK_INTEGRITY_KEYS);
30
+ return {
31
+ ...registryAccess({ ...opts, env, readProjectNpmrc: signature.verifySignature !== false }),
32
+ proxy: pnpmProxyConfig({ env, homeDir: opts.homeDir }),
33
+ ...signature,
34
+ };
35
+ }
36
+ function registryAccess(opts) {
37
+ const corepackRegistry = opts.env.COREPACK_NPM_REGISTRY;
38
+ if (corepackRegistry) {
39
+ return withHeaders({ registry: corepackRegistry }, corepackHeaders(opts.env));
40
+ }
41
+ const config = pnpmRegistryConfig(opts);
42
+ if (config.headers == null && new URL(config.registry).origin === new URL(DEFAULT_REGISTRY).origin) {
43
+ return withHeaders(config, corepackHeaders(opts.env));
44
+ }
45
+ return config;
46
+ }
47
+ function withHeaders(config, headers) {
48
+ return headers == null ? config : { ...config, headers };
49
+ }
50
+ function corepackHeaders(env) {
51
+ const { COREPACK_NPM_TOKEN, COREPACK_NPM_USERNAME, COREPACK_NPM_PASSWORD } = env;
52
+ if (COREPACK_NPM_TOKEN) {
53
+ return { authorization: `Bearer ${COREPACK_NPM_TOKEN}` };
54
+ }
55
+ if (COREPACK_NPM_USERNAME && COREPACK_NPM_PASSWORD) {
56
+ return { authorization: `Basic ${Buffer.from(`${COREPACK_NPM_USERNAME}:${COREPACK_NPM_PASSWORD}`).toString('base64')}` };
57
+ }
58
+ return undefined;
59
+ }
60
+ function signaturePolicy(configured) {
61
+ if (configured == null)
62
+ return {};
63
+ if (configured === '' || configured === '0')
64
+ return { verifySignature: false };
65
+ let keys;
66
+ try {
67
+ keys = JSON.parse(configured)?.npm;
68
+ }
69
+ catch (err) {
70
+ throw new Error(`COREPACK_INTEGRITY_KEYS is not readable as JSON: ${err.message}`, { cause: err });
71
+ }
72
+ // Passing no keys would fall back to npm's own, the opposite of what setting
73
+ // the variable asked for. An empty set names no key to trust, and Corepack
74
+ // reads it the same way, so every download is refused.
75
+ if (!Array.isArray(keys)) {
76
+ throw new Error('COREPACK_INTEGRITY_KEYS holds no "npm" key set to verify the pnpm binary against.');
77
+ }
78
+ return { keys: keys };
79
+ }
@@ -3,7 +3,7 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { extractTarballMember } from './extractTarballMember.js';
5
5
  import { isMusl, platformPackageName } from './platformPackageName.js';
6
- import { downloadTarball, fetchVersionMeta, normalizeRegistry, useProxyFromEnv } from './registry.js';
6
+ import { downloadTarball, fetchSignatures, fetchVersionMeta, normalizeRegistry, useCertificateAuthorities, useProxyFromEnv } from './registry.js';
7
7
  import { majorVersion } from './resolveVersion.js';
8
8
  import { sameFileContents } from './sameFileContents.js';
9
9
  import { verifyRegistrySignature } from './verifySignature.js';
@@ -38,9 +38,15 @@ export async function downloadPnpmExecutable(opts) {
38
38
  arch: process.arch,
39
39
  musl: isMusl(),
40
40
  });
41
- const restoreProxy = useProxyFromEnv();
41
+ const restoreProxy = useProxyFromEnv(opts.proxy);
42
42
  try {
43
- return await fetchExecutable({ ...opts, registry, packageName });
43
+ const restoreCertificates = useCertificateAuthorities(opts.ca);
44
+ try {
45
+ return await fetchExecutable({ ...opts, registry, packageName });
46
+ }
47
+ finally {
48
+ restoreCertificates();
49
+ }
44
50
  }
45
51
  finally {
46
52
  restoreProxy();
@@ -57,7 +63,7 @@ async function fetchExecutable(opts) {
57
63
  name: packageName,
58
64
  version,
59
65
  integrity: meta.dist.integrity,
60
- signatures: meta.dist.signatures,
66
+ signatures: await fetchSignatures(meta, { registry, pkgName: packageName, version, headers: opts.headers }),
61
67
  keys: opts.keys,
62
68
  });
63
69
  }
@@ -27,28 +27,38 @@ export async function extractTarballMember(tarball, memberPath, dest, mode = 0o6
27
27
  let found = false;
28
28
  await pipeline(createReadStream(tarball), createGunzip(), async function (source) {
29
29
  const reader = new BlockReader(source);
30
- while (true) {
31
- const header = await reader.read(BLOCK_SIZE);
32
- // The archive ends with zero-filled blocks; one is enough to stop.
33
- if (header == null || header[0] === 0)
34
- return;
35
- const entry = parseHeader(header);
36
- if (FILE_TYPES.has(entry.type) && entry.path === memberPath) {
37
- const written = await reader.pipe(entry.size, createWriteStream(dest, { flags: 'wx', mode }));
38
- if (written !== entry.size) {
39
- throw new Error(`${tarball} ends after ${written} of the ${entry.size} bytes it declares for ${memberPath}.`);
40
- }
41
- found = true;
42
- // The rest of the archive holds nothing this caller asked for, and
43
- // the checksum that vouches for it was checked before any of it was
44
- // read.
45
- return;
46
- }
47
- await reader.skip(Math.ceil(entry.size / BLOCK_SIZE) * BLOCK_SIZE);
30
+ const entry = await seekMember(reader, memberPath);
31
+ if (entry == null)
32
+ return;
33
+ const written = await reader.pipe(entry.size, createWriteStream(dest, { flags: 'wx', mode }));
34
+ if (written !== entry.size) {
35
+ throw new Error(`${tarball} ends after ${written} of the ${entry.size} bytes it declares for ${memberPath}.`);
48
36
  }
37
+ found = true;
38
+ // The rest of the archive holds nothing this caller asked for, and the
39
+ // checksum that vouches for it was checked before any of it was read.
49
40
  });
50
41
  return found;
51
42
  }
43
+ /**
44
+ * Reads up to the content of the regular file at `memberPath`.
45
+ *
46
+ * @returns its header, or `null` when the archive has no such file.
47
+ */
48
+ async function seekMember(reader, memberPath) {
49
+ while (true) {
50
+ // eslint-disable-next-line no-await-in-loop -- a tar archive can only be walked one entry after another
51
+ const header = await reader.read(BLOCK_SIZE);
52
+ // The archive ends with zero-filled blocks; one is enough to stop.
53
+ if (header == null || header[0] === 0)
54
+ return null;
55
+ const entry = parseHeader(header);
56
+ if (FILE_TYPES.has(entry.type) && entry.path === memberPath)
57
+ return entry;
58
+ // eslint-disable-next-line no-await-in-loop -- a tar archive can only be walked one entry after another
59
+ await reader.skip(Math.ceil(entry.size / BLOCK_SIZE) * BLOCK_SIZE);
60
+ }
61
+ }
52
62
  function parseHeader(header) {
53
63
  const name = readString(header, 0, 100);
54
64
  const prefix = readString(header, 345, 155);
@@ -78,11 +88,8 @@ class BlockReader {
78
88
  return this.#take(size);
79
89
  }
80
90
  async skip(size) {
81
- let left = size;
82
- while (left > 0) {
83
- if (!await this.#fill(1))
84
- return;
85
- left -= this.#take(Math.min(left, this.#buffedBytes)).length;
91
+ for await (const _chunk of this.#chunks({ left: size })) {
92
+ // Discarded.
86
93
  }
87
94
  }
88
95
  /**
@@ -92,22 +99,25 @@ class BlockReader {
92
99
  * the stream ended early.
93
100
  */
94
101
  async pipe(size, destination) {
95
- const self = this;
96
- let left = size;
97
- await pipeline(async function* () {
98
- while (left > 0) {
99
- if (!await self.#fill(1))
100
- return;
101
- const chunk = self.#take(Math.min(left, self.#buffedBytes));
102
- left -= chunk.length;
103
- yield chunk;
104
- }
105
- }, destination);
106
- return size - left;
102
+ const progress = { left: size };
103
+ await pipeline(this.#chunks(progress), destination);
104
+ return size - progress.left;
105
+ }
106
+ /** The next `progress.left` bytes, counting them down as they are yielded. */
107
+ async *#chunks(progress) {
108
+ while (progress.left > 0) {
109
+ // eslint-disable-next-line no-await-in-loop -- the stream is read in order, one chunk at a time
110
+ if (!await this.#fill(1))
111
+ return;
112
+ const chunk = this.#take(Math.min(progress.left, this.#buffedBytes));
113
+ progress.left -= chunk.length;
114
+ yield chunk;
115
+ }
107
116
  }
108
117
  /** Reads until at least `size` bytes are buffered, or the stream ends. */
109
118
  async #fill(size) {
110
119
  while (this.#buffedBytes < size && !this.#done) {
120
+ // eslint-disable-next-line no-await-in-loop -- the stream is read in order, one chunk at a time
111
121
  const { value, done } = await this.#iterator.next();
112
122
  if (done === true) {
113
123
  this.#done = true;
@@ -1,220 +1,4 @@
1
- import { spawnSync } from 'node:child_process';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { extractTarball } from './extractTarball.js';
6
- import { isMusl, platformPackageName } from './platformPackageName.js';
7
- import { downloadTarball, fetchPackument, fetchVersionMeta, registryFromEnv, useProxyFromEnv } from './registry.js';
8
- import { majorVersion, resolveVersion } from './resolveVersion.js';
9
- import { verifyRegistrySignature } from './verifySignature.js';
1
+ export { corepackDownloadOptions } from './corepack.js';
10
2
  export { downloadPnpmExecutable } from './downloadExecutable.js';
11
- export { extractTarballMember } from './extractTarballMember.js';
12
- export { isMusl, platformPackageName } from './platformPackageName.js';
13
- export { DEFAULT_REGISTRY, registryFromEnv, useProxyFromEnv } from './registry.js';
14
- export { majorVersion, resolveVersion } from './resolveVersion.js';
15
- export { verifyRegistrySignature } from './verifySignature.js';
16
- /**
17
- * The package whose dist-tags name every pnpm release. `@pnpm/exe` carries the
18
- * same versions today, but only `pnpm` is published from v12 onward, so its
19
- * tags are the ones that cannot go stale.
20
- */
21
- const CLI_PKG_NAME = 'pnpm';
22
- /** Holds the unpacked tarballs while the installation is assembled beside it. */
23
- const UNPACK_DIR = '.unpack';
24
- /** Where the `dist/` tree that ships beside the executable is published. */
25
- function wrapperPackageName(major) {
26
- return major >= 12 ? CLI_PKG_NAME : '@pnpm/exe';
27
- }
28
- const USAGE = `Usage: npx get-pnpm [version]
29
-
30
- Installs pnpm as a standalone executable and adds it to your PATH.
31
-
32
- Arguments:
33
- version An exact version (11.20.0), a major (12), or a dist-tag
34
- (latest, next-12). Defaults to $PNPM_VERSION, then "latest".
35
-
36
- Environment variables:
37
- PNPM_VERSION Version to install when no argument is given.
38
- PNPM_HOME Directory to install pnpm into.
39
- npm_config_registry Registry to download pnpm from.
40
- HTTPS_PROXY Proxy to download through (HTTP_PROXY for an http://
41
- registry); NO_PROXY names hosts to reach directly.
42
- Node 24.14 or later.
43
- `;
44
- export async function runCli(argv) {
45
- const positional = [];
46
- for (const arg of argv) {
47
- if (arg === '--help' || arg === '-h') {
48
- console.log(USAGE);
49
- return 0;
50
- }
51
- if (arg.startsWith('-')) {
52
- throw new Error(`Unknown option "${arg}".\n\n${USAGE}`);
53
- }
54
- positional.push(arg);
55
- }
56
- if (positional.length > 1) {
57
- throw new Error(`Expected at most one version, got ${positional.length}.\n\n${USAGE}`);
58
- }
59
- return installPnpm({
60
- versionSpec: positional[0] ?? process.env.PNPM_VERSION ?? 'latest',
61
- registry: registryFromEnv(),
62
- });
63
- }
64
- /**
65
- * Downloads the pnpm executable and hands over to `pnpm setup`, which installs
66
- * it globally and puts it on the PATH.
67
- *
68
- * Every download is checked against the checksum the registry published for it,
69
- * and that checksum against npm's signature — see `verifyRegistrySignature`.
70
- *
71
- * The temporary directory is assembled to look like the release tarball that
72
- * https://get.pnpm.io/install.sh downloads — the executable next to its `dist/`
73
- * tree — because `pnpm setup` installs that directory as-is.
74
- *
75
- * @returns the exit code of `pnpm setup`.
76
- */
77
- export async function installPnpm(opts) {
78
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pnpm-install-'));
79
- const removeTmpDir = () => {
80
- fs.rmSync(tmpDir, { recursive: true, force: true });
81
- };
82
- // Registered for the duration of the call and no longer: this is also a
83
- // library function, and handlers left behind would accumulate and outlive the
84
- // directory they exist to clean up.
85
- const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
86
- const onSignal = () => {
87
- removeTmpDir();
88
- process.exit(1);
89
- };
90
- for (const signal of signals) {
91
- process.once(signal, onSignal);
92
- }
93
- try {
94
- const { binPath } = await downloadPnpm({ ...opts, dest: tmpDir });
95
- const { error, status } = spawnSync(binPath, ['setup', '--force'], { stdio: 'inherit' });
96
- if (error != null)
97
- throw error;
98
- return status ?? 1;
99
- }
100
- finally {
101
- for (const signal of signals) {
102
- process.off(signal, onSignal);
103
- }
104
- removeTmpDir();
105
- }
106
- }
107
- /**
108
- * Downloads the pnpm executable into `dest`, laid out the way the release
109
- * tarball lays it out — the executable next to the `dist/` tree it loads.
110
- *
111
- * Every download is checked against the checksum the registry published for it,
112
- * and that checksum against npm's signature; see `verifyRegistrySignature`.
113
- * Nothing outside `dest` is touched, so a caller that manages its own PATH — a
114
- * CI action, say — can use this without the global install `installPnpm` does.
115
- *
116
- * @returns the version installed and the path to the executable.
117
- */
118
- export async function downloadPnpm(opts) {
119
- const restoreProxy = useProxyFromEnv();
120
- try {
121
- return await fetchPnpm(opts);
122
- }
123
- finally {
124
- restoreProxy();
125
- }
126
- }
127
- async function fetchPnpm(opts) {
128
- const packument = await fetchPackument(opts.registry, CLI_PKG_NAME);
129
- const version = resolveVersion(packument, opts.versionSpec);
130
- const major = majorVersion(version);
131
- const platformPkgName = platformPackageName({
132
- major,
133
- platform: process.platform,
134
- arch: process.arch,
135
- musl: isMusl(),
136
- });
137
- const { dest } = opts;
138
- fs.mkdirSync(dest, { recursive: true });
139
- try {
140
- console.log(`==> Downloading pnpm ${version}`);
141
- const executable = process.platform === 'win32' ? 'pnpm.exe' : 'pnpm';
142
- const fetchPackage = verifiedPackageFetcher({ dir: dest, registry: opts.registry, version, keys: opts.keys });
143
- // Settled, not `all`: a rejection there would leave the other fetch writing
144
- // into the directory the `finally` below is about to remove.
145
- const [platformResult, wrapperResult] = await Promise.allSettled([
146
- fetchPackage(platformPkgName),
147
- // v11 was the first release to keep files next to the executable; up to
148
- // v10 the executable is self-contained and ships no `dist/`.
149
- major >= 11 ? fetchPackage(wrapperPackageName(major)) : Promise.resolve(undefined),
150
- ]);
151
- if (platformResult.status === 'rejected')
152
- throw platformResult.reason;
153
- if (wrapperResult.status === 'rejected')
154
- throw wrapperResult.reason;
155
- const platformPkg = platformResult.value;
156
- const wrapperPkg = wrapperResult.value;
157
- const binPath = path.join(dest, executable);
158
- fs.renameSync(path.join(platformPkg.dir, executable), binPath);
159
- fs.chmodSync(binPath, 0o755);
160
- if (wrapperPkg != null) {
161
- fs.rmSync(path.join(dest, 'dist'), { recursive: true, force: true });
162
- fs.renameSync(path.join(wrapperPkg.dir, 'dist'), path.join(dest, 'dist'));
163
- writeManifest({ dest, executable, version, wrapperDir: wrapperPkg.dir, major });
164
- }
165
- return { version, binPath };
166
- }
167
- finally {
168
- // `pnpm setup` installs this directory as a package, so nothing may be left
169
- // in it that does not belong in the installation.
170
- fs.rmSync(path.join(dest, UNPACK_DIR), { recursive: true, force: true });
171
- }
172
- }
173
- /**
174
- * `pnpm setup` installs the directory as a package, writing a minimal manifest
175
- * when there is none — which is what the release tarball relies on. That
176
- * tarball bundles the runtime dependencies inside `dist/`; the registry copy
177
- * declares them instead, so up to v11 they have to be declared here or the
178
- * install silently loses them (`@reflink/reflink`, and with it copy-on-write
179
- * cloning). From v12 the `dist/` tree is self-contained again, so the manifest
180
- * `setup` writes is left to it.
181
- */
182
- function writeManifest(opts) {
183
- if (opts.major >= 12)
184
- return;
185
- const wrapper = JSON.parse(fs.readFileSync(path.join(opts.wrapperDir, 'package.json'), 'utf8'));
186
- if (wrapper.dependencies == null)
187
- return;
188
- fs.writeFileSync(path.join(opts.dest, 'package.json'), JSON.stringify({
189
- name: '@pnpm/exe',
190
- version: opts.version,
191
- type: 'module',
192
- bin: { pnpm: opts.executable, pn: opts.executable },
193
- dependencies: wrapper.dependencies,
194
- }));
195
- }
196
- /** Downloads packages of one version into one directory, verifying each. */
197
- function verifiedPackageFetcher(opts) {
198
- return async function fetchPackage(pkgName) {
199
- const meta = await fetchVersionMeta(opts.registry, pkgName, opts.version);
200
- if (!meta.dist.integrity) {
201
- throw new Error(`The npm registry published no checksum for ${pkgName}@${opts.version}, so it cannot be verified.`);
202
- }
203
- verifyRegistrySignature({
204
- name: pkgName,
205
- version: opts.version,
206
- integrity: meta.dist.integrity,
207
- signatures: meta.dist.signatures,
208
- keys: opts.keys,
209
- });
210
- // Unpack away from the directory being assembled: `pnpm` is both a package
211
- // name and the name of the executable that ends up beside it.
212
- const unpackDir = path.join(opts.dir, UNPACK_DIR, pkgName.replaceAll('/', '-'));
213
- const tarball = `${unpackDir}.tgz`;
214
- fs.mkdirSync(path.dirname(tarball), { recursive: true });
215
- await downloadTarball(meta, tarball, { registry: opts.registry });
216
- extractTarball(tarball, unpackDir);
217
- fs.rmSync(tarball);
218
- return { dir: path.join(unpackDir, 'package') };
219
- };
220
- }
3
+ export { downloadPnpm, runCli } from './install.js';
4
+ export { readProjectPin } from './projectPin.js';