@pnpm/global.commands 1100.0.13 → 1100.0.15
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.
- package/lib/globalAdd.js +77 -16
- package/lib/installGlobalPackages.d.ts +1 -0
- package/package.json +16 -15
package/lib/globalAdd.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { linkBinsOfPackages } from '@pnpm/bins.linker';
|
|
4
4
|
import { removeBin } from '@pnpm/bins.remover';
|
|
5
|
+
import { summaryLogger } from '@pnpm/core-loggers';
|
|
5
6
|
import { cleanOrphanedInstallDirs, createGlobalCacheKey, createInstallDir, findGlobalPackage, getHashLink, getInstalledBinNames, } from '@pnpm/global.packages';
|
|
6
7
|
import { readPackageJsonFromDirRawSync } from '@pnpm/pkg-manifest.reader';
|
|
7
8
|
import { isSubdir } from 'is-subdir';
|
|
@@ -11,17 +12,9 @@ import { installGlobalPackages } from './installGlobalPackages.js';
|
|
|
11
12
|
import { promptApproveGlobalBuilds } from './promptApproveGlobalBuilds.js';
|
|
12
13
|
import { readInstalledPackages } from './readInstalledPackages.js';
|
|
13
14
|
export async function handleGlobalAdd(opts, params, commands) {
|
|
14
|
-
// Resolve relative path selectors to absolute paths before the working
|
|
15
|
-
// directory is changed to the global install dir, otherwise "." or
|
|
16
|
-
// "./foo" would resolve against the temp install directory.
|
|
17
|
-
params = params.map((param) => resolveLocalParam(param, opts.dir));
|
|
18
15
|
const globalDir = opts.globalPkgDir;
|
|
19
16
|
const globalBinDir = opts.bin;
|
|
20
17
|
cleanOrphanedInstallDirs(globalDir);
|
|
21
|
-
// Install into a new directory first, then read the resolved aliases
|
|
22
|
-
// from the resulting package.json. This is more reliable than parsing
|
|
23
|
-
// aliases from CLI params (which may be tarballs, git URLs, etc.).
|
|
24
|
-
const installDir = createInstallDir(globalDir);
|
|
25
18
|
// Convert allowBuild array to allowBuilds Record (same conversion as add.handler)
|
|
26
19
|
let allowBuilds = opts.allowBuilds ?? {};
|
|
27
20
|
if (opts.allowBuild?.length) {
|
|
@@ -30,19 +23,45 @@ export async function handleGlobalAdd(opts, params, commands) {
|
|
|
30
23
|
allowBuilds[pkg] = true;
|
|
31
24
|
}
|
|
32
25
|
}
|
|
26
|
+
// Each space-separated CLI param becomes its own isolated install group.
|
|
27
|
+
// A param containing commas is split into multiple selectors that share a
|
|
28
|
+
// single group, so `pnpm add -g foo,bar qar` installs foo+bar together
|
|
29
|
+
// and qar separately. Local paths and URLs that legitimately contain
|
|
30
|
+
// commas are detected and kept whole.
|
|
31
|
+
const groups = params
|
|
32
|
+
.map((param) => splitCommaSeparated(param, opts.dir).map((token) => resolveLocalParam(token, opts.dir)))
|
|
33
|
+
.filter((group) => group.length > 0);
|
|
34
|
+
for (const group of groups) {
|
|
35
|
+
// eslint-disable-next-line no-await-in-loop
|
|
36
|
+
await installGroup({ opts, globalDir, globalBinDir, allowBuilds, params: group }, commands);
|
|
37
|
+
}
|
|
38
|
+
// The per-group `mutateModulesInSingleProject` calls run with
|
|
39
|
+
// `omitSummaryLog: true` so the default-reporter's summary block only
|
|
40
|
+
// appears once at the end, with every installed package listed under a
|
|
41
|
+
// single "global:" heading. Without this, the reporter would print
|
|
42
|
+
// group 1's summary and then ignore later groups, because its summary
|
|
43
|
+
// pipeline takes only the first `summary` log event.
|
|
44
|
+
summaryLogger.debug({ prefix: globalDir });
|
|
45
|
+
}
|
|
46
|
+
async function installGroup(ctx, commands) {
|
|
47
|
+
const { opts, globalDir, globalBinDir, allowBuilds, params } = ctx;
|
|
48
|
+
// Install into a new directory first, then read the resolved aliases
|
|
49
|
+
// from the resulting package.json. This is more reliable than parsing
|
|
50
|
+
// aliases from CLI params (which may be tarballs, git URLs, etc.).
|
|
51
|
+
const installDir = createInstallDir(globalDir);
|
|
33
52
|
const include = {
|
|
34
53
|
dependencies: true,
|
|
35
54
|
devDependencies: false,
|
|
36
55
|
optionalDependencies: true,
|
|
37
56
|
};
|
|
38
57
|
const fetchFullMetadata = opts.supportedArchitectures?.libc != null && true;
|
|
39
|
-
const
|
|
58
|
+
const installOpts = {
|
|
40
59
|
...opts,
|
|
41
60
|
global: false,
|
|
42
|
-
bin: path.join(
|
|
43
|
-
dir,
|
|
44
|
-
lockfileDir:
|
|
45
|
-
rootProjectManifestDir:
|
|
61
|
+
bin: path.join(installDir, 'node_modules/.bin'),
|
|
62
|
+
dir: installDir,
|
|
63
|
+
lockfileDir: installDir,
|
|
64
|
+
rootProjectManifestDir: installDir,
|
|
46
65
|
rootProjectManifest: undefined,
|
|
47
66
|
saveProd: true,
|
|
48
67
|
saveDev: false,
|
|
@@ -54,9 +73,10 @@ export async function handleGlobalAdd(opts, params, commands) {
|
|
|
54
73
|
fetchFullMetadata,
|
|
55
74
|
include,
|
|
56
75
|
includeDirect: include,
|
|
57
|
-
allowBuilds
|
|
58
|
-
|
|
59
|
-
|
|
76
|
+
allowBuilds,
|
|
77
|
+
omitSummaryLog: true,
|
|
78
|
+
};
|
|
79
|
+
const ignoredBuilds = await installGlobalPackages(installOpts, params);
|
|
60
80
|
await promptApproveGlobalBuilds({
|
|
61
81
|
globalPkgDir: globalDir,
|
|
62
82
|
installDir,
|
|
@@ -95,6 +115,47 @@ export async function handleGlobalAdd(opts, params, commands) {
|
|
|
95
115
|
// Link bins from installed packages into global bin dir
|
|
96
116
|
await linkBinsOfPackages(pkgs, globalBinDir, { excludeBins: binsToSkip });
|
|
97
117
|
}
|
|
118
|
+
function splitCommaSeparated(param, baseDir) {
|
|
119
|
+
if (!param.includes(','))
|
|
120
|
+
return [param];
|
|
121
|
+
// URLs may contain commas and are never a group of selectors.
|
|
122
|
+
if (param.includes('://'))
|
|
123
|
+
return [param];
|
|
124
|
+
// For path-like specs (relative/absolute paths, file:, link:), the
|
|
125
|
+
// commas could either be part of a single path that legitimately
|
|
126
|
+
// contains commas, or be separators between multiple distinct paths.
|
|
127
|
+
// Resolve the ambiguity by checking whether the whole param actually
|
|
128
|
+
// refers to an existing local path on disk.
|
|
129
|
+
if (refersToExistingLocalPath(param, baseDir))
|
|
130
|
+
return [param];
|
|
131
|
+
return param.split(',').map((token) => token.trim()).filter(Boolean);
|
|
132
|
+
}
|
|
133
|
+
function refersToExistingLocalPath(param, baseDir) {
|
|
134
|
+
let pathPart;
|
|
135
|
+
if (param.startsWith('file:')) {
|
|
136
|
+
pathPart = param.slice('file:'.length);
|
|
137
|
+
}
|
|
138
|
+
else if (param.startsWith('link:')) {
|
|
139
|
+
pathPart = param.slice('link:'.length);
|
|
140
|
+
}
|
|
141
|
+
else if (param[0] === '.' || param[0] === '/' || param[0] === '~') {
|
|
142
|
+
pathPart = param;
|
|
143
|
+
}
|
|
144
|
+
else if (/^[a-z]:[/\\]/i.test(param)) {
|
|
145
|
+
pathPart = param;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const resolved = path.isAbsolute(pathPart) ? pathPart : path.resolve(baseDir, pathPart);
|
|
151
|
+
try {
|
|
152
|
+
fs.statSync(resolved);
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
98
159
|
function resolveLocalParam(param, baseDir) {
|
|
99
160
|
for (const prefix of ['file:', 'link:']) {
|
|
100
161
|
if (param.startsWith(prefix)) {
|
|
@@ -10,6 +10,7 @@ export interface InstallGlobalPackagesOptions extends CreateStoreControllerOptio
|
|
|
10
10
|
include: IncludedDependencies;
|
|
11
11
|
includeDirect?: IncludedDependencies;
|
|
12
12
|
fetchFullMetadata?: boolean;
|
|
13
|
+
omitSummaryLog?: boolean;
|
|
13
14
|
rootProjectManifest?: unknown;
|
|
14
15
|
rootProjectManifestDir?: string;
|
|
15
16
|
saveDev?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/global.commands",
|
|
3
|
-
"version": "1100.0.
|
|
3
|
+
"version": "1100.0.15",
|
|
4
4
|
"description": "Global package command handlers for pnpm",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -28,24 +28,25 @@
|
|
|
28
28
|
"@pnpm/util.lex-comparator": "^3.0.2",
|
|
29
29
|
"is-subdir": "^2.0.0",
|
|
30
30
|
"symlink-dir": "^10.0.1",
|
|
31
|
-
"@pnpm/
|
|
32
|
-
"@pnpm/bins.
|
|
33
|
-
"@pnpm/
|
|
34
|
-
"@pnpm/
|
|
35
|
-
"@pnpm/
|
|
36
|
-
"@pnpm/
|
|
31
|
+
"@pnpm/bins.remover": "1100.0.3",
|
|
32
|
+
"@pnpm/bins.resolver": "1100.0.3",
|
|
33
|
+
"@pnpm/cli.utils": "1101.0.3",
|
|
34
|
+
"@pnpm/bins.linker": "1100.0.4",
|
|
35
|
+
"@pnpm/config.matcher": "1100.0.1",
|
|
36
|
+
"@pnpm/config.reader": "1101.3.0",
|
|
37
|
+
"@pnpm/core-loggers": "1100.0.2",
|
|
37
38
|
"@pnpm/error": "1100.0.0",
|
|
38
|
-
"@pnpm/
|
|
39
|
-
"@pnpm/installing.deps-installer": "1101.0
|
|
40
|
-
"@pnpm/
|
|
41
|
-
"@pnpm/
|
|
42
|
-
"@pnpm/types": "1101.
|
|
43
|
-
"@pnpm/
|
|
44
|
-
"@pnpm/
|
|
39
|
+
"@pnpm/global.packages": "1100.0.3",
|
|
40
|
+
"@pnpm/installing.deps-installer": "1101.1.0",
|
|
41
|
+
"@pnpm/pkg-manifest.reader": "1100.0.3",
|
|
42
|
+
"@pnpm/store.connection-manager": "1100.1.0",
|
|
43
|
+
"@pnpm/types": "1101.1.0",
|
|
44
|
+
"@pnpm/cli.command": "1100.0.1",
|
|
45
|
+
"@pnpm/deps.inspection.list": "1100.0.9"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"@jest/globals": "30.3.0",
|
|
48
|
-
"@pnpm/global.commands": "1100.0.
|
|
49
|
+
"@pnpm/global.commands": "1100.0.15"
|
|
49
50
|
},
|
|
50
51
|
"engines": {
|
|
51
52
|
"node": ">=22.13"
|