nx 23.0.0 → 23.0.2

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 (59) hide show
  1. package/README.md +1 -2
  2. package/dist/bin/init-local.js +1 -1
  3. package/dist/release/changelog-renderer/index.d.ts +1 -0
  4. package/dist/release/changelog-renderer/index.js +27 -12
  5. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.d.ts +1 -0
  6. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +6 -1
  7. package/dist/src/command-line/init/init-v2.js +1 -1
  8. package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +3 -2
  9. package/dist/src/command-line/migrate/migrate.d.ts +2 -0
  10. package/dist/src/command-line/migrate/migrate.js +34 -7
  11. package/dist/src/command-line/migrate/resolve-package-version.js +3 -25
  12. package/dist/src/command-line/migrate/update-filters.js +2 -1
  13. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.d.ts +1 -1
  14. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +37 -20
  15. package/dist/src/command-line/release/version/resolve-current-version.js +4 -1
  16. package/dist/src/core/graph/main.js +1 -1
  17. package/dist/src/daemon/server/project-graph-incremental-recomputation.js +30 -11
  18. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  19. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  20. package/dist/src/plugins/js/lock-file/bun-parser.js +61 -38
  21. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +1 -3
  22. package/dist/src/plugins/js/package-json/create-package-json.js +22 -1
  23. package/dist/src/plugins/js/project-graph/affected/tsconfig-json-changes.js +1 -1
  24. package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.d.ts +7 -0
  25. package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +26 -1
  26. package/dist/src/plugins/js/utils/register.d.ts +31 -10
  27. package/dist/src/plugins/js/utils/register.js +87 -18
  28. package/dist/src/plugins/package-json/create-nodes.js +5 -3
  29. package/dist/src/project-graph/operators.d.ts +0 -1
  30. package/dist/src/project-graph/operators.js +0 -44
  31. package/dist/src/project-graph/project-graph-builder.js +26 -10
  32. package/dist/src/project-graph/utils/project-configuration/target-defaults.js +23 -6
  33. package/dist/src/project-graph/utils/project-configuration-utils.js +28 -28
  34. package/dist/src/tasks-runner/batch/run-batch.js +3 -0
  35. package/dist/src/tasks-runner/cache.js +6 -0
  36. package/dist/src/tasks-runner/pseudo-terminal.d.ts +1 -0
  37. package/dist/src/tasks-runner/pseudo-terminal.js +18 -4
  38. package/dist/src/tasks-runner/run-command.js +24 -8
  39. package/dist/src/tasks-runner/utils.d.ts +1 -0
  40. package/dist/src/tasks-runner/utils.js +4 -0
  41. package/dist/src/utils/ab-testing.d.ts +9 -0
  42. package/dist/src/utils/ab-testing.js +18 -4
  43. package/dist/src/utils/catalog/index.d.ts +7 -0
  44. package/dist/src/utils/catalog/index.js +31 -0
  45. package/dist/src/utils/catalog/manager-utils.d.ts +9 -0
  46. package/dist/src/utils/catalog/manager-utils.js +215 -0
  47. package/dist/src/utils/catalog/pnpm-manager.js +4 -102
  48. package/dist/src/utils/catalog/yarn-manager.js +4 -102
  49. package/dist/src/utils/is-sandbox.js +2 -0
  50. package/dist/src/utils/min-release-age/behavior/pnpm.d.ts +6 -5
  51. package/dist/src/utils/min-release-age/behavior/pnpm.js +87 -339
  52. package/dist/src/utils/package-json.d.ts +1 -0
  53. package/dist/src/utils/package-manager.d.ts +6 -3
  54. package/dist/src/utils/package-manager.js +21 -7
  55. package/dist/src/utils/spinner.d.ts +13 -1
  56. package/dist/src/utils/spinner.js +20 -4
  57. package/dist/src/utils/terminal-link.d.ts +16 -0
  58. package/dist/src/utils/terminal-link.js +87 -0
  59. package/package.json +19 -15
@@ -2,13 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.readPnpmPolicy = readPnpmPolicy;
4
4
  exports.pickPnpmVersion = pickPnpmVersion;
5
- const fs_1 = require("fs");
6
- const os_1 = require("os");
7
- const path_1 = require("path");
5
+ const child_process_1 = require("child_process");
8
6
  const semver_1 = require("semver");
9
7
  const constants_1 = require("../constants");
10
8
  const errors_1 = require("../errors");
11
- const npmrc_1 = require("../npmrc");
12
9
  const pick_1 = require("../pick");
13
10
  // Ordered newest-bound-first so the first matching row applies.
14
11
  const PNPM_BEHAVIOR_ROWS = [
@@ -19,7 +16,6 @@ const PNPM_BEHAVIOR_ROWS = [
19
16
  strictAutoOnWhenExplicit: true,
20
17
  excludeGrammar: 'v2-globs-unions',
21
18
  writesExcludes: true,
22
- uppercaseEnv: true,
23
19
  },
24
20
  {
25
21
  minVersion: '11.1.0',
@@ -28,7 +24,6 @@ const PNPM_BEHAVIOR_ROWS = [
28
24
  strictAutoOnWhenExplicit: true,
29
25
  excludeGrammar: 'v2-globs-unions',
30
26
  writesExcludes: false,
31
- uppercaseEnv: true,
32
27
  },
33
28
  {
34
29
  minVersion: '11.0.4',
@@ -37,7 +32,6 @@ const PNPM_BEHAVIOR_ROWS = [
37
32
  strictAutoOnWhenExplicit: true,
38
33
  excludeGrammar: 'v2-globs-unions',
39
34
  writesExcludes: false,
40
- uppercaseEnv: false,
41
35
  },
42
36
  {
43
37
  minVersion: '11.0.0',
@@ -46,7 +40,6 @@ const PNPM_BEHAVIOR_ROWS = [
46
40
  strictAutoOnWhenExplicit: false,
47
41
  excludeGrammar: 'v2-globs-unions',
48
42
  writesExcludes: false,
49
- uppercaseEnv: false,
50
43
  },
51
44
  {
52
45
  minVersion: '10.19.0',
@@ -55,7 +48,6 @@ const PNPM_BEHAVIOR_ROWS = [
55
48
  strictAutoOnWhenExplicit: false,
56
49
  excludeGrammar: 'v2-globs-unions',
57
50
  writesExcludes: false,
58
- uppercaseEnv: false,
59
51
  },
60
52
  {
61
53
  minVersion: '10.16.0',
@@ -64,29 +56,78 @@ const PNPM_BEHAVIOR_ROWS = [
64
56
  strictAutoOnWhenExplicit: false,
65
57
  excludeGrammar: 'v1-exact-names',
66
58
  writesExcludes: false,
67
- uppercaseEnv: false,
68
59
  },
69
60
  ];
70
61
  function findBehaviorRow(pmVersion) {
71
62
  return PNPM_BEHAVIOR_ROWS.find((row) => (0, semver_1.gte)(pmVersion, row.minVersion));
72
63
  }
73
- // Builds a resolved cooldown window, tagging it with the surface label used in
74
- // messaging. windowExplicit is false only for the invisible built-in default.
75
- function okWindow(windowMinutes, excludes, source, windowExplicit = true) {
64
+ // Asks pnpm for its own resolved config rather than re-deriving how pnpm layers
65
+ // .npmrc / env / pnpm-workspace.yaml / global config across versions. Returns
66
+ // null if pnpm cannot be run or its output is not parseable JSON, so the caller
67
+ // defers to a real install.
68
+ function readPnpmConfigList(root) {
69
+ let raw;
70
+ try {
71
+ raw = (0, child_process_1.execSync)('pnpm config list --json', {
72
+ cwd: root,
73
+ encoding: 'utf-8',
74
+ windowsHide: true,
75
+ stdio: ['ignore', 'pipe', 'ignore'],
76
+ });
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ try {
82
+ const parsed = JSON.parse(raw);
83
+ return parsed && typeof parsed === 'object'
84
+ ? parsed
85
+ : null;
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ // Extracts the cooldown keys from pnpm's reported config. pnpm reports the
92
+ // kebab-case keys; the exclude list comes back as a JSON array (set in a yaml
93
+ // surface) or a comma-joined string (set via .npmrc / env).
94
+ function parseCooldownConfig(config) {
76
95
  return {
77
- kind: 'ok',
78
- windowMinutes,
79
- excludes,
80
- sourceDescription: `pnpm minimumReleaseAge (${windowMinutes} min, ${source})`,
81
- windowExplicit,
96
+ windowMinutes: toNumber(config['minimum-release-age']) ?? undefined,
97
+ excludes: parseExcludeValue(config['minimum-release-age-exclude']),
98
+ strictExplicit: toBoolean(config['minimum-release-age-strict']),
99
+ ignoreMissingTimeExplicit: toBoolean(config['minimum-release-age-ignore-missing-time']),
82
100
  };
83
101
  }
102
+ function parseExcludeValue(value) {
103
+ const entries = Array.isArray(value)
104
+ ? value.map((entry) => String(entry))
105
+ : typeof value === 'string'
106
+ ? value.split(',')
107
+ : [];
108
+ return entries
109
+ .map((entry) => entry.trim())
110
+ .filter((entry) => entry.length > 0);
111
+ }
112
+ function toBoolean(value) {
113
+ if (typeof value === 'boolean') {
114
+ return value;
115
+ }
116
+ if (value === 'true') {
117
+ return true;
118
+ }
119
+ if (value === 'false') {
120
+ return false;
121
+ }
122
+ return undefined;
123
+ }
84
124
  /**
85
- * Reads pnpm's effective cooldown configuration once. The surface set and
86
- * precedence depend on the pnpm major: v10 layers npm-conf surfaces
87
- * (workspace yaml > npm_config env > project .npmrc > user .npmrc); v11
88
- * layers pnpm-native surfaces (pnpm_config / PNPM_CONFIG env >
89
- * pnpm-workspace.yaml > global config.yaml > built-in 1440-minute default).
125
+ * Reads pnpm's effective cooldown configuration once by asking pnpm itself
126
+ * (`pnpm config list --json`) instead of re-implementing how it layers config
127
+ * surfaces (a moving target across pnpm versions). The window and excludes come
128
+ * straight from pnpm; the strict default, exclude grammar, and loose-fallback
129
+ * semantics stay version-keyed because pnpm does not report those resolved
130
+ * values.
90
131
  */
91
132
  async function readPnpmPolicy(root, pmVersion) {
92
133
  // A pnpm major newer than the table knows can behave differently; defer to a
@@ -101,39 +142,39 @@ async function readPnpmPolicy(root, pmVersion) {
101
142
  if (!row) {
102
143
  return { outcome: 'inactive' };
103
144
  }
104
- let resolution;
105
- let strictExplicit;
106
- let ignoreMissingTimeExplicit;
107
- if (row.major === 11) {
108
- const read = readV11Surfaces(root, row);
109
- resolution = read.window;
110
- strictExplicit = read.strictExplicit;
111
- ignoreMissingTimeExplicit = read.ignoreMissingTime;
112
- }
113
- else {
114
- resolution = readV10Surfaces(root);
115
- // v10 strict is hardcoded; no surface toggles it.
116
- strictExplicit = undefined;
117
- }
118
- if (!resolution) {
119
- return { outcome: 'inactive' };
120
- }
121
- if (resolution.kind !== 'ok') {
122
- return { outcome: 'ambiguous', reason: resolution.reason };
123
- }
124
- const { windowMinutes, excludes, sourceDescription, windowExplicit } = resolution;
125
- if (!Number.isFinite(windowMinutes)) {
145
+ const config = readPnpmConfigList(root);
146
+ if (config === null) {
147
+ // Could not read pnpm's resolved config; defer to a real install rather than
148
+ // guess the policy.
126
149
  return {
127
150
  outcome: 'ambiguous',
128
- reason: `Invalid pnpm minimumReleaseAge value: ${String(windowMinutes)}.`,
151
+ reason: 'Unable to read the pnpm minimum-release-age configuration.',
129
152
  };
130
153
  }
154
+ const { windowMinutes: explicitWindow, excludes, strictExplicit, ignoreMissingTimeExplicit, } = parseCooldownConfig(config);
155
+ // An explicitly configured window (from whatever surface pnpm honors) wins;
156
+ // otherwise v11 falls back to its built-in 1440-minute (1 day) default, while
157
+ // v10 has no default (no cooldown).
158
+ let windowMinutes;
159
+ let windowExplicit;
160
+ if (explicitWindow !== undefined) {
161
+ windowMinutes = explicitWindow;
162
+ windowExplicit = true;
163
+ }
164
+ else if (row.major === 11) {
165
+ windowMinutes = 1440;
166
+ windowExplicit = false;
167
+ }
168
+ else {
169
+ return { outcome: 'inactive' };
170
+ }
131
171
  // 0 disables; negatives push the cutoff into the future -> everything passes.
132
172
  if (windowMinutes <= 0) {
133
173
  return { outcome: 'inactive' };
134
174
  }
135
175
  const windowMs = windowMinutes * constants_1.MS_PER_MINUTE;
136
176
  const cutoffMs = Date.now() - windowMs;
177
+ const sourceDescription = `pnpm minimumReleaseAge (${windowMinutes} min)`;
137
178
  const strict = resolveStrict(row, strictExplicit, windowExplicit);
138
179
  // v10 always errors on missing time; v11 defaults to skip unless a surface
139
180
  // explicitly disabled it.
@@ -179,269 +220,6 @@ function resolveStrict(row, strictExplicit, windowExplicit) {
179
220
  // so it stays loose - the normal pnpm 11 state.
180
221
  return row.strictAutoOnWhenExplicit && windowExplicit;
181
222
  }
182
- // --- v11 surfaces -----------------------------------------------------------
183
- function readV11Surfaces(root, row) {
184
- const env = process.env;
185
- const keySet = envKeySetForMajor(row);
186
- const wsRead = readYamlWindow((0, path_1.join)(root, 'pnpm-workspace.yaml'));
187
- if (wsRead && wsRead.kind === 'invalid') {
188
- return {
189
- window: wsRead,
190
- strictExplicit: undefined,
191
- ignoreMissingTime: undefined,
192
- };
193
- }
194
- const globalRead = readYamlWindow((0, path_1.join)(getConfigDir(env), 'config.yaml'));
195
- if (globalRead && globalRead.kind === 'invalid') {
196
- return {
197
- window: globalRead,
198
- strictExplicit: undefined,
199
- ignoreMissingTime: undefined,
200
- };
201
- }
202
- const wsYaml = wsRead && wsRead.kind === 'ok' ? wsRead : null;
203
- const globalYaml = globalRead && globalRead.kind === 'ok' ? globalRead : null;
204
- // pnpm v11 resolves each cooldown key independently across surfaces:
205
- // env > pnpm-workspace.yaml > global config.yaml > built-in default.
206
- const strictExplicit = readEnvBoolean(env, keySet, 'minimum_release_age_strict') ??
207
- wsYaml?.strict ??
208
- globalYaml?.strict;
209
- const ignoreMissingTime = readEnvBoolean(env, keySet, 'minimum_release_age_ignore_missing_time') ??
210
- wsYaml?.ignoreMissingTime ??
211
- globalYaml?.ignoreMissingTime;
212
- const envExcludes = readEnvArray(env, keySet, 'minimum_release_age_exclude');
213
- if (envExcludes === 'invalid') {
214
- return {
215
- window: {
216
- kind: 'invalid',
217
- reason: 'Invalid pnpm minimumReleaseAgeExclude entry.',
218
- },
219
- strictExplicit: undefined,
220
- ignoreMissingTime: undefined,
221
- };
222
- }
223
- const excludes = envExcludes ?? wsYaml?.excludes ?? globalYaml?.excludes ?? [];
224
- const envWindow = readEnvNumber(env, keySet, 'minimum_release_age');
225
- // pnpm drops a NaN env value rather than erroring; treat 'invalid' as unset
226
- // and fall through to lower surfaces.
227
- if (envWindow !== undefined && envWindow !== 'invalid') {
228
- return {
229
- window: okWindow(envWindow, excludes, 'env'),
230
- strictExplicit,
231
- ignoreMissingTime,
232
- };
233
- }
234
- if (wsYaml && wsYaml.windowMinutes !== undefined) {
235
- return {
236
- window: okWindow(wsYaml.windowMinutes, excludes, 'pnpm-workspace.yaml'),
237
- strictExplicit,
238
- ignoreMissingTime,
239
- };
240
- }
241
- if (globalYaml && globalYaml.windowMinutes !== undefined) {
242
- return {
243
- window: okWindow(globalYaml.windowMinutes, excludes, 'global config.yaml'),
244
- strictExplicit,
245
- ignoreMissingTime,
246
- };
247
- }
248
- // Built-in 1440-minute (1 day) default, injected programmatically and
249
- // invisible to `pnpm config get`. Loose unless some surface set strict.
250
- return {
251
- window: okWindow(1440, excludes, 'default', false),
252
- strictExplicit,
253
- ignoreMissingTime,
254
- };
255
- }
256
- // --- v10 surfaces -----------------------------------------------------------
257
- function readV10Surfaces(root) {
258
- // v10 layers config via @pnpm/npm-conf, per key:
259
- // workspace yaml > npm_config_* env > project .npmrc > user .npmrc.
260
- const wsRead = readYamlWindow((0, path_1.join)(root, 'pnpm-workspace.yaml'));
261
- if (wsRead && wsRead.kind === 'invalid') {
262
- return wsRead;
263
- }
264
- const wsYaml = wsRead && wsRead.kind === 'ok' ? wsRead : null;
265
- const env = process.env;
266
- const keySet = { prefix: 'npm_config_', uppercase: false };
267
- const rawEnvWindow = readEnvNumber(env, keySet, 'minimum_release_age');
268
- // npm-conf drops values that fail the Number type; treat as unset.
269
- const envWindow = rawEnvWindow === 'invalid' ? undefined : rawEnvWindow;
270
- const projectNpmrc = readNpmrcSurface((0, path_1.join)(root, '.npmrc'));
271
- const userNpmrc = readNpmrcSurface((0, path_1.join)((0, os_1.homedir)(), '.npmrc'));
272
- // v10 reads npm_config_*, which never JSON-parses, so readEnvArray can only
273
- // yield a single-entry array or undefined here - never 'invalid'.
274
- const envExcludes = readEnvArray(env, keySet, 'minimum_release_age_exclude');
275
- const excludes = wsYaml?.excludes ??
276
- (envExcludes === 'invalid' ? undefined : envExcludes) ??
277
- projectNpmrc?.excludes ??
278
- userNpmrc?.excludes ??
279
- [];
280
- if (wsYaml && wsYaml.windowMinutes !== undefined) {
281
- return okWindow(wsYaml.windowMinutes, excludes, 'pnpm-workspace.yaml');
282
- }
283
- if (envWindow !== undefined) {
284
- return okWindow(envWindow, excludes, 'env');
285
- }
286
- for (const npmrc of [projectNpmrc, userNpmrc]) {
287
- if (npmrc && npmrc.windowMinutes !== undefined) {
288
- return okWindow(npmrc.windowMinutes, excludes, '.npmrc');
289
- }
290
- }
291
- return null;
292
- }
293
- function readYamlRaw(path) {
294
- if (!(0, fs_1.existsSync)(path)) {
295
- return null;
296
- }
297
- try {
298
- const { load } = require('@zkochan/js-yaml');
299
- return load((0, fs_1.readFileSync)(path, 'utf-8')) ?? {};
300
- }
301
- catch {
302
- // The file exists but is unparseable. An absent file falls through to lower
303
- // surfaces; a corrupt one can't be reasoned about, so signal it and let the
304
- // caller defer to a real install (matching npm/yarn/bun on a read failure).
305
- return 'invalid';
306
- }
307
- }
308
- function readYamlWindow(path) {
309
- const doc = readYamlRaw(path);
310
- if (doc === 'invalid') {
311
- return { kind: 'invalid', reason: `Unable to parse ${(0, path_1.basename)(path)}.` };
312
- }
313
- if (!doc) {
314
- return null;
315
- }
316
- // Each key is read independently; pnpm merges surfaces per key.
317
- const excludes = readArrayKey(doc, 'minimumReleaseAgeExclude');
318
- const strict = readBooleanKey(doc, 'minimumReleaseAgeStrict');
319
- const ignoreMissingTime = readBooleanKey(doc, 'minimumReleaseAgeIgnoreMissingTime');
320
- const raw = doc['minimumReleaseAge'];
321
- if (raw === undefined || raw === null) {
322
- return { kind: 'ok', excludes, strict, ignoreMissingTime };
323
- }
324
- const num = toNumber(raw);
325
- if (num === null) {
326
- return {
327
- kind: 'invalid',
328
- reason: `Invalid pnpm minimumReleaseAge value: ${String(raw)}.`,
329
- };
330
- }
331
- return {
332
- kind: 'ok',
333
- windowMinutes: num,
334
- excludes,
335
- strict,
336
- ignoreMissingTime,
337
- };
338
- }
339
- function readNpmrcSurface(path) {
340
- const entries = (0, npmrc_1.readNpmrcEntries)(path);
341
- if (entries === null) {
342
- return null;
343
- }
344
- // v10 reads the kebab-case keys via npm-conf; camelCase in .npmrc is never
345
- // honored. Only the two cooldown keys are relevant here.
346
- let windowMinutes;
347
- let excludes;
348
- for (const { key, value: rawValue } of entries) {
349
- const value = stripQuotes(rawValue);
350
- if (key === 'minimum-release-age') {
351
- const num = toNumber(value);
352
- if (num !== null) {
353
- windowMinutes = num;
354
- }
355
- }
356
- else if (key === 'minimum-release-age-exclude') {
357
- // npm-conf accumulates repeated ini keys into an array.
358
- (excludes ??= []).push(value);
359
- }
360
- }
361
- return { windowMinutes, excludes };
362
- }
363
- // pnpm mirrors getConfigDir: XDG_CONFIG_HOME, else per-platform default.
364
- function getConfigDir(env) {
365
- if (env.XDG_CONFIG_HOME) {
366
- return (0, path_1.join)(env.XDG_CONFIG_HOME, 'pnpm');
367
- }
368
- if (process.platform === 'darwin') {
369
- return (0, path_1.join)((0, os_1.homedir)(), 'Library/Preferences/pnpm');
370
- }
371
- if (process.platform !== 'win32') {
372
- return (0, path_1.join)((0, os_1.homedir)(), '.config/pnpm');
373
- }
374
- if (env.LOCALAPPDATA) {
375
- return (0, path_1.join)(env.LOCALAPPDATA, 'pnpm/config');
376
- }
377
- return (0, path_1.join)((0, os_1.homedir)(), '.config/pnpm');
378
- }
379
- function envKeySetForMajor(row) {
380
- return row.major === 11
381
- ? { prefix: 'pnpm_config_', uppercase: row.uppercaseEnv }
382
- : { prefix: 'npm_config_', uppercase: false };
383
- }
384
- function envKeys(keySet, suffix) {
385
- const keys = [`${keySet.prefix}${suffix}`];
386
- if (keySet.uppercase) {
387
- keys.push(`${keySet.prefix.toUpperCase()}${suffix.toUpperCase()}`);
388
- }
389
- return keys;
390
- }
391
- function readEnvRaw(env, keySet, suffix) {
392
- for (const key of envKeys(keySet, suffix)) {
393
- if (env[key] !== undefined) {
394
- return env[key];
395
- }
396
- }
397
- return undefined;
398
- }
399
- function readEnvNumber(env, keySet, suffix) {
400
- const raw = readEnvRaw(env, keySet, suffix);
401
- if (raw === undefined) {
402
- return undefined;
403
- }
404
- const num = toNumber(raw);
405
- // pnpm parses env values via Number(); NaN drops the key.
406
- return num === null ? 'invalid' : num;
407
- }
408
- function readEnvBoolean(env, keySet, suffix) {
409
- const raw = readEnvRaw(env, keySet, suffix);
410
- // pnpm's Boolean env schema (config/reader/src/env.ts) yields only true/false
411
- // for the literals; anything else drops the key, so the value falls through to
412
- // lower surfaces / defaults instead of coercing to false.
413
- if (raw === 'true') {
414
- return true;
415
- }
416
- if (raw === 'false') {
417
- return false;
418
- }
419
- return undefined;
420
- }
421
- function readEnvArray(env, keySet, suffix) {
422
- const raw = readEnvRaw(env, keySet, suffix);
423
- if (raw === undefined) {
424
- return undefined;
425
- }
426
- // pnpm v11's [String, Array] env schema tries a JSON array first, then falls
427
- // back to the raw value as a single entry. v10 (nopt) never JSON-parses.
428
- if (keySet.prefix === 'pnpm_config_') {
429
- try {
430
- const parsed = JSON.parse(raw);
431
- if (Array.isArray(parsed)) {
432
- // pnpm passes the array verbatim, then errors at install
433
- // (ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDE) on any non-string
434
- // element; signal invalid so the caller defers to a real install.
435
- return parsed.every((e) => typeof e === 'string')
436
- ? parsed
437
- : 'invalid';
438
- }
439
- }
440
- catch { }
441
- }
442
- return [raw];
443
- }
444
- // --- value helpers ----------------------------------------------------------
445
223
  function toNumber(value) {
446
224
  if (typeof value === 'number') {
447
225
  return Number.isFinite(value) ? value : null;
@@ -456,36 +234,6 @@ function toNumber(value) {
456
234
  }
457
235
  return null;
458
236
  }
459
- function stripQuotes(value) {
460
- if ((value.startsWith('"') && value.endsWith('"')) ||
461
- (value.startsWith("'") && value.endsWith("'"))) {
462
- return value.slice(1, -1);
463
- }
464
- return value;
465
- }
466
- function readArrayKey(doc, key) {
467
- const value = doc[key];
468
- if (Array.isArray(value)) {
469
- return value.map((v) => String(v));
470
- }
471
- if (typeof value === 'string') {
472
- return [value];
473
- }
474
- return undefined;
475
- }
476
- function readBooleanKey(doc, key) {
477
- const value = doc[key];
478
- if (typeof value === 'boolean') {
479
- return value;
480
- }
481
- if (value === 'true') {
482
- return true;
483
- }
484
- if (value === 'false') {
485
- return false;
486
- }
487
- return undefined;
488
- }
489
237
  /**
490
238
  * Builds the exclude predicate matching pnpm's version-policy semantics.
491
239
  * Returns null when any entry is invalid (pnpm would error on it), so the
@@ -63,6 +63,7 @@ export interface PackageJson {
63
63
  ignoredOptionalDependencies?: string[];
64
64
  };
65
65
  overrides?: PackageOverride;
66
+ allowScripts?: Record<string, boolean>;
66
67
  bin?: Record<string, string> | string;
67
68
  workspaces?: string[] | {
68
69
  packages: string[];
@@ -78,9 +78,12 @@ export declare function modifyYarnRcToFitNewDirectory(contents: string): string;
78
78
  /**
79
79
  * We copy pnpm-workspace.yaml to the temporary directory so the workspace's
80
80
  * registry, auth and release-age settings still apply, and so `pnpm add -w`
81
- * recognizes the directory as a workspace root. `packages` (member globs) and
82
- * `patchedDependencies` (relative patch paths) only resolve in the real
83
- * workspace, so they are dropped.
81
+ * recognizes the directory as a workspace root. `patchedDependencies` (relative
82
+ * patch paths) only resolves in the real workspace, so it is dropped. The
83
+ * `packages` field is always set to a self-reference (`['.']`) - the temp dir is
84
+ * a single-package workspace - whether or not the source had one, since pnpm
85
+ * <10.5 (and corepack's bundled default pnpm) reject a workspace manifest whose
86
+ * `packages` field is missing or empty.
84
87
  *
85
88
  * Exported for testing - not meant to be used outside of this file.
86
89
  *
@@ -326,9 +326,12 @@ function modifyYarnRcToFitNewDirectory(contents) {
326
326
  /**
327
327
  * We copy pnpm-workspace.yaml to the temporary directory so the workspace's
328
328
  * registry, auth and release-age settings still apply, and so `pnpm add -w`
329
- * recognizes the directory as a workspace root. `packages` (member globs) and
330
- * `patchedDependencies` (relative patch paths) only resolve in the real
331
- * workspace, so they are dropped.
329
+ * recognizes the directory as a workspace root. `patchedDependencies` (relative
330
+ * patch paths) only resolves in the real workspace, so it is dropped. The
331
+ * `packages` field is always set to a self-reference (`['.']`) - the temp dir is
332
+ * a single-package workspace - whether or not the source had one, since pnpm
333
+ * <10.5 (and corepack's bundled default pnpm) reject a workspace manifest whose
334
+ * `packages` field is missing or empty.
332
335
  *
333
336
  * Exported for testing - not meant to be used outside of this file.
334
337
  *
@@ -337,10 +340,11 @@ function modifyYarnRcToFitNewDirectory(contents) {
337
340
  */
338
341
  function modifyPnpmWorkspaceYamlToFitNewDirectory(contents) {
339
342
  const doc = (0, yaml_1.parseDocument)(contents);
340
- if (doc.contents) {
341
- doc.delete('packages');
342
- doc.delete('patchedDependencies');
343
- }
343
+ // Set unconditionally so an empty/comments-only source (null doc.contents)
344
+ // still gets a packages field; doc.set creates the root map.
345
+ doc.set('packages', ['.']);
346
+ // Relative patch paths don't resolve in the temp dir.
347
+ doc.delete('patchedDependencies');
344
348
  return doc.toString();
345
349
  }
346
350
  function copyPackageManagerConfigurationFiles(root, destination) {
@@ -499,8 +503,15 @@ options) {
499
503
  // Quote the spec so range operators (e.g. `>=0.0.0`) are not parsed as shell
500
504
  // redirections.
501
505
  const spec = version ? `${pkg}@${version}` : pkg;
506
+ // npm enforces `devEngines.packageManager` on every command, even a read-only
507
+ // `view`; in a yarn/bun workspace we run npm, so a non-npm pin with
508
+ // `onFail: error` aborts the lookup. force downgrades that to a warning.
509
+ // Scoped to npm so a `pnpm view` spawn is left untouched.
502
510
  const { stdout } = await execAsync(`${pm} view "${spec}" ${args}`, {
503
511
  windowsHide: true,
512
+ ...(pm === 'npm'
513
+ ? { env: { ...process.env, npm_config_force: 'true' } }
514
+ : {}),
504
515
  });
505
516
  return stdout.toString().trim();
506
517
  }
@@ -516,6 +527,9 @@ async function packageRegistryPack(cwd, pkg, version) {
516
527
  const { stdout } = await execAsync(`${pm} pack ${pkg}@${version}`, {
517
528
  cwd,
518
529
  windowsHide: true,
530
+ // npm enforces `devEngines.packageManager` even on `pack`; force keeps the
531
+ // download working in workspaces that pin a non-npm manager (onFail: error).
532
+ env: { ...process.env, npm_config_force: 'true' },
519
533
  });
520
534
  const tarballPath = stdout.trim();
521
535
  return { tarballPath };
@@ -1,7 +1,19 @@
1
1
  export declare const SHOULD_SHOW_SPINNERS: boolean;
2
+ export interface StartSpinnerOptions {
3
+ /**
4
+ * When `true`, the text passed to `start`, `succeed`, and `fail` is NOT
5
+ * emitted in non-TTY environments. By default (`false`), the text is logged
6
+ * via `console.warn` so progress information isn't lost in non-interactive
7
+ * environments. Set to `true` when completion is reported through a
8
+ * different mechanism (e.g. a batched logger).
9
+ *
10
+ * Defaults to `false`.
11
+ */
12
+ skipNonTtyLogging?: boolean;
13
+ }
2
14
  declare class SpinnerManager {
3
15
  #private;
4
- start(text?: string, prefix?: string): SpinnerManager;
16
+ start(text?: string, prefix?: string, opts?: StartSpinnerOptions): SpinnerManager;
5
17
  succeed(text?: string): void;
6
18
  stop(): void;
7
19
  fail(text?: string): void;
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- var _SpinnerManager_instances, _SpinnerManager_ora, _SpinnerManager_prefix, _SpinnerManager_createOra;
2
+ var _SpinnerManager_instances, _SpinnerManager_ora, _SpinnerManager_prefix, _SpinnerManager_skipNonTtyLogging, _SpinnerManager_createOra, _SpinnerManager_handleNonTty;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.globalSpinner = exports.SHOULD_SHOW_SPINNERS = void 0;
5
5
  const tslib_1 = require("tslib");
@@ -11,9 +11,11 @@ class SpinnerManager {
11
11
  _SpinnerManager_instances.add(this);
12
12
  _SpinnerManager_ora.set(this, void 0);
13
13
  _SpinnerManager_prefix.set(this, void 0);
14
+ _SpinnerManager_skipNonTtyLogging.set(this, false);
14
15
  }
15
- start(text, prefix) {
16
- if (!exports.SHOULD_SHOW_SPINNERS) {
16
+ start(text, prefix, opts) {
17
+ tslib_1.__classPrivateFieldSet(this, _SpinnerManager_skipNonTtyLogging, opts?.skipNonTtyLogging ?? false, "f");
18
+ if (tslib_1.__classPrivateFieldGet(this, _SpinnerManager_instances, "m", _SpinnerManager_handleNonTty).call(this, text)) {
17
19
  return this;
18
20
  }
19
21
  if (prefix !== undefined) {
@@ -30,12 +32,18 @@ class SpinnerManager {
30
32
  return this;
31
33
  }
32
34
  succeed(text) {
35
+ if (tslib_1.__classPrivateFieldGet(this, _SpinnerManager_instances, "m", _SpinnerManager_handleNonTty).call(this, text)) {
36
+ return;
37
+ }
33
38
  tslib_1.__classPrivateFieldGet(this, _SpinnerManager_ora, "f")?.succeed(text);
34
39
  }
35
40
  stop() {
36
41
  tslib_1.__classPrivateFieldGet(this, _SpinnerManager_ora, "f")?.stop();
37
42
  }
38
43
  fail(text) {
44
+ if (tslib_1.__classPrivateFieldGet(this, _SpinnerManager_instances, "m", _SpinnerManager_handleNonTty).call(this, text)) {
45
+ return;
46
+ }
39
47
  tslib_1.__classPrivateFieldGet(this, _SpinnerManager_ora, "f")?.fail(text);
40
48
  }
41
49
  updateText(text) {
@@ -50,12 +58,20 @@ class SpinnerManager {
50
58
  return tslib_1.__classPrivateFieldGet(this, _SpinnerManager_ora, "f")?.isSpinning ?? false;
51
59
  }
52
60
  }
53
- _SpinnerManager_ora = new WeakMap(), _SpinnerManager_prefix = new WeakMap(), _SpinnerManager_instances = new WeakSet(), _SpinnerManager_createOra = function _SpinnerManager_createOra(text) {
61
+ _SpinnerManager_ora = new WeakMap(), _SpinnerManager_prefix = new WeakMap(), _SpinnerManager_skipNonTtyLogging = new WeakMap(), _SpinnerManager_instances = new WeakSet(), _SpinnerManager_createOra = function _SpinnerManager_createOra(text) {
54
62
  tslib_1.__classPrivateFieldSet(this, _SpinnerManager_ora, (0, ora_1.default)({
55
63
  text: text,
56
64
  prefixText: tslib_1.__classPrivateFieldGet(this, _SpinnerManager_prefix, "f"),
57
65
  hideCursor: false,
58
66
  discardStdin: false,
59
67
  }), "f");
68
+ }, _SpinnerManager_handleNonTty = function _SpinnerManager_handleNonTty(text) {
69
+ if (exports.SHOULD_SHOW_SPINNERS) {
70
+ return false;
71
+ }
72
+ if (!tslib_1.__classPrivateFieldGet(this, _SpinnerManager_skipNonTtyLogging, "f") && text) {
73
+ console.warn(text);
74
+ }
75
+ return true;
60
76
  };
61
77
  exports.globalSpinner = new SpinnerManager();