nx 23.2.0-beta.7 → 23.2.0-beta.9

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 (188) hide show
  1. package/dist/bin/init-local.js +3 -5
  2. package/dist/release/changelog-renderer/index.js +4 -5
  3. package/dist/src/adapter/ngcli-adapter.js +60 -1
  4. package/dist/src/ai/set-up-ai-agents/set-up-ai-agents.js +2 -2
  5. package/dist/src/command-line/completion/command-object.js +7 -12
  6. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +17 -23
  7. package/dist/src/command-line/format/format.js +84 -101
  8. package/dist/src/command-line/generate/generate.js +35 -35
  9. package/dist/src/command-line/import/import.js +30 -40
  10. package/dist/src/command-line/init/ai-agent-prompts.js +5 -11
  11. package/dist/src/command-line/init/command-object.js +4 -2
  12. package/dist/src/command-line/init/configure-plugins.js +4 -1
  13. package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +12 -32
  14. package/dist/src/command-line/init/implementation/add-nx-to-nest.js +17 -21
  15. package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +8 -21
  16. package/dist/src/command-line/init/implementation/add-nx-to-turborepo.js +3 -17
  17. package/dist/src/command-line/init/implementation/angular/index.js +10 -15
  18. package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +2 -0
  19. package/dist/src/command-line/init/implementation/angular/standalone-workspace.js +8 -2
  20. package/dist/src/command-line/init/implementation/check-compatible-with-plugins.js +2 -0
  21. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +5 -0
  22. package/dist/src/command-line/init/implementation/format.d.ts +18 -0
  23. package/dist/src/command-line/init/implementation/format.js +114 -0
  24. package/dist/src/command-line/init/implementation/utils.js +8 -0
  25. package/dist/src/command-line/init/init-v1.js +15 -18
  26. package/dist/src/command-line/init/init-v2.js +37 -56
  27. package/dist/src/command-line/migrate/agentic/definitions.js +35 -8
  28. package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +22 -3
  29. package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +13 -10
  30. package/dist/src/command-line/migrate/agentic/handoff.d.ts +3 -1
  31. package/dist/src/command-line/migrate/agentic/handoff.js +4 -2
  32. package/dist/src/command-line/migrate/agentic/run-step.js +1 -0
  33. package/dist/src/command-line/migrate/agentic/runner.js +24 -24
  34. package/dist/src/command-line/migrate/agentic/select.js +20 -30
  35. package/dist/src/command-line/migrate/agentic/types.d.ts +19 -3
  36. package/dist/src/command-line/migrate/agentic/types.js +13 -4
  37. package/dist/src/command-line/migrate/command-object.d.ts +3 -0
  38. package/dist/src/command-line/migrate/command-object.js +13 -0
  39. package/dist/src/command-line/migrate/execute-migration.d.ts +14 -0
  40. package/dist/src/command-line/migrate/execute-migration.js +30 -9
  41. package/dist/src/command-line/migrate/migrate-analytics.d.ts +28 -0
  42. package/dist/src/command-line/migrate/migrate-analytics.js +51 -1
  43. package/dist/src/command-line/migrate/migrate-commits.d.ts +8 -2
  44. package/dist/src/command-line/migrate/migrate-commits.js +66 -19
  45. package/dist/src/command-line/migrate/migrate-config.d.ts +4 -2
  46. package/dist/src/command-line/migrate/migrate-config.js +12 -4
  47. package/dist/src/command-line/migrate/migrate.d.ts +13 -2
  48. package/dist/src/command-line/migrate/migrate.js +243 -80
  49. package/dist/src/command-line/migrate/multi-major.js +7 -10
  50. package/dist/src/command-line/migrate/resolve-package-version.js +4 -8
  51. package/dist/src/command-line/migrate/run/agent-output.d.ts +20 -0
  52. package/dist/src/command-line/migrate/run/agent-output.js +65 -0
  53. package/dist/src/command-line/migrate/run/index.d.ts +7 -0
  54. package/dist/src/command-line/migrate/run/index.js +19 -1
  55. package/dist/src/command-line/migrate/run/orchestrator.d.ts +20 -0
  56. package/dist/src/command-line/migrate/run/orchestrator.js +1200 -0
  57. package/dist/src/command-line/migrate/run/run-id.d.ts +16 -0
  58. package/dist/src/command-line/migrate/run/run-id.js +56 -0
  59. package/dist/src/command-line/migrate/run/run-state.d.ts +157 -0
  60. package/dist/src/command-line/migrate/run/run-state.js +438 -0
  61. package/dist/src/command-line/migrate/run/state-lock.d.ts +25 -0
  62. package/dist/src/command-line/migrate/run/state-lock.js +76 -0
  63. package/dist/src/command-line/migrate/run/state-machine.d.ts +84 -0
  64. package/dist/src/command-line/migrate/run/state-machine.js +315 -0
  65. package/dist/src/command-line/migrate/run/util.d.ts +58 -0
  66. package/dist/src/command-line/migrate/run/util.js +139 -7
  67. package/dist/src/command-line/migrate/run/worker.d.ts +1 -0
  68. package/dist/src/command-line/migrate/run/worker.js +308 -45
  69. package/dist/src/command-line/migrate/safe-prompt.d.ts +14 -26
  70. package/dist/src/command-line/migrate/safe-prompt.js +32 -43
  71. package/dist/src/command-line/migrate/step-actions.d.ts +5 -0
  72. package/dist/src/command-line/migrate/step-actions.js +12 -0
  73. package/dist/src/command-line/migrate/text.d.ts +18 -0
  74. package/dist/src/command-line/migrate/text.js +26 -0
  75. package/dist/src/command-line/migrate/version-skew-guard.d.ts +6 -4
  76. package/dist/src/command-line/migrate/version-skew-guard.js +34 -14
  77. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +16 -20
  78. package/dist/src/command-line/release/changelog.js +6 -17
  79. package/dist/src/command-line/release/plan.js +15 -28
  80. package/dist/src/command-line/release/release.js +6 -17
  81. package/dist/src/command-line/release/utils/remote-release-clients/github.js +16 -22
  82. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +13 -22
  83. package/dist/src/command-line/release/utils/resolve-semver-specifier.js +19 -41
  84. package/dist/src/command-line/release/version/resolve-current-version.js +7 -10
  85. package/dist/src/command-line/release/version.js +2 -2
  86. package/dist/src/core/graph/main.js +1 -1
  87. package/dist/src/daemon/client/client.js +11 -2
  88. package/dist/src/devkit-internals.d.ts +7 -2
  89. package/dist/src/devkit-internals.js +22 -6
  90. package/dist/src/generators/internal-utils/{format-changed-files-with-prettier-if-available.d.ts → format-changed-files.d.ts} +6 -6
  91. package/dist/src/generators/internal-utils/format-changed-files.js +163 -0
  92. package/dist/src/generators/testing-utils/create-tree-with-empty-workspace.d.ts +15 -0
  93. package/dist/src/generators/testing-utils/create-tree-with-empty-workspace.js +21 -3
  94. package/dist/src/migrations/update-15-0-0/prefix-outputs.js +2 -2
  95. package/dist/src/migrations/update-16-0-0/remove-nrwl-cli.js +2 -2
  96. package/dist/src/migrations/update-16-0-0/update-depends-on-to-tokens.js +2 -2
  97. package/dist/src/migrations/update-16-0-0/update-nx-cloud-runner.js +2 -2
  98. package/dist/src/migrations/update-16-2-0/remove-run-commands-output-path.js +2 -2
  99. package/dist/src/migrations/update-17-0-0/rm-default-collection-npm-scope.js +2 -2
  100. package/dist/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.js +2 -2
  101. package/dist/src/migrations/update-17-2-0/move-default-base.js +2 -2
  102. package/dist/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.js +2 -2
  103. package/dist/src/migrations/update-19-2-4/set-project-name.js +2 -2
  104. package/dist/src/migrations/update-20-0-0/move-use-daemon-process.js +2 -2
  105. package/dist/src/migrations/update-20-0-1/use-legacy-cache.js +2 -2
  106. package/dist/src/migrations/update-21-0-0/release-changelog-config-changes.js +2 -2
  107. package/dist/src/migrations/update-21-0-0/release-version-config-changes.js +2 -2
  108. package/dist/src/migrations/update-21-0-0/remove-custom-tasks-runner.js +2 -2
  109. package/dist/src/migrations/update-21-0-0/remove-legacy-cache.js +2 -2
  110. package/dist/src/migrations/update-22-0-0/consolidate-release-tag-config.js +2 -2
  111. package/dist/src/migrations/update-22-0-0/release-version-config-changes.js +2 -2
  112. package/dist/src/migrations/update-22-2-0/add-self-healing-to-gitignore.js +2 -2
  113. package/dist/src/migrations/update-22-6-0/add-claude-settings-local-to-git-ignore.js +2 -2
  114. package/dist/src/migrations/update-22-6-0/add-claude-worktrees-to-git-ignore.js +2 -2
  115. package/dist/src/migrations/update-22-7-0/add-polygraph-to-git-ignore.js +2 -2
  116. package/dist/src/migrations/update-23-0-0/add-migrate-runs-to-git-ignore.js +2 -2
  117. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +12 -0
  118. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +250 -0
  119. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +50 -0
  120. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  121. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  122. package/dist/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +3 -3
  123. package/dist/src/plugins/js/lock-file/bun-parser.js +43 -10
  124. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +128 -2
  125. package/dist/src/tasks-runner/run-command.js +17 -32
  126. package/dist/src/tasks-runner/task-env.js +5 -2
  127. package/dist/src/tasks-runner/utils.js +2 -6
  128. package/dist/src/utils/analytics-prompt.js +4 -7
  129. package/dist/src/utils/chunkify.d.ts +7 -1
  130. package/dist/src/utils/chunkify.js +10 -3
  131. package/dist/src/utils/exit-codes.d.ts +9 -0
  132. package/dist/src/utils/exit-codes.js +19 -0
  133. package/dist/src/utils/fileutils.d.ts +5 -0
  134. package/dist/src/utils/fileutils.js +7 -2
  135. package/dist/src/utils/formatters/index.d.ts +13 -0
  136. package/dist/src/utils/formatters/index.js +74 -0
  137. package/dist/src/utils/formatters/oxfmt.d.ts +32 -0
  138. package/dist/src/utils/formatters/oxfmt.js +764 -0
  139. package/dist/src/utils/formatters/prettier.d.ts +26 -0
  140. package/dist/src/utils/formatters/prettier.js +201 -0
  141. package/dist/src/utils/formatters/shared.d.ts +5 -0
  142. package/dist/src/utils/formatters/shared.js +8 -0
  143. package/dist/src/utils/git-utils.d.ts +33 -2
  144. package/dist/src/utils/git-utils.js +156 -8
  145. package/dist/src/utils/ignore.d.ts +47 -5
  146. package/dist/src/utils/ignore.js +93 -11
  147. package/dist/src/utils/long-running-target.d.ts +10 -0
  148. package/dist/src/utils/long-running-target.js +19 -0
  149. package/dist/src/utils/min-release-age/behavior/bun.js +7 -11
  150. package/dist/src/utils/min-release-age/behavior/npm.js +5 -2
  151. package/dist/src/utils/min-release-age/packument.js +1 -1
  152. package/dist/src/utils/nx-console-prompt.js +3 -6
  153. package/dist/src/utils/package-manager-config/bunfig.d.ts +14 -0
  154. package/dist/src/utils/package-manager-config/bunfig.js +49 -0
  155. package/dist/src/utils/package-manager-config/npmrc.d.ts +23 -0
  156. package/dist/src/utils/package-manager-config/npmrc.js +124 -0
  157. package/dist/src/utils/package-manager-config/pnpm-config.d.ts +17 -0
  158. package/dist/src/utils/package-manager-config/pnpm-config.js +60 -0
  159. package/dist/src/utils/package-manager.d.ts +21 -2
  160. package/dist/src/utils/package-manager.js +244 -47
  161. package/dist/src/utils/params.d.ts +7 -1
  162. package/dist/src/utils/params.js +64 -6
  163. package/dist/src/utils/prompt-helpers.d.ts +53 -0
  164. package/dist/src/utils/prompt-helpers.js +111 -0
  165. package/dist/src/utils/provenance.js +6 -14
  166. package/dist/src/utils/registry-config/bun.d.ts +2 -0
  167. package/dist/src/utils/registry-config/bun.js +257 -0
  168. package/dist/src/utils/registry-config/index.d.ts +13 -0
  169. package/dist/src/utils/registry-config/index.js +102 -0
  170. package/dist/src/utils/registry-config/pnpm.d.ts +2 -0
  171. package/dist/src/utils/registry-config/pnpm.js +1551 -0
  172. package/dist/src/utils/registry-config/utils.d.ts +195 -0
  173. package/dist/src/utils/registry-config/utils.js +496 -0
  174. package/dist/src/utils/registry-config/yarn-berry.d.ts +2 -0
  175. package/dist/src/utils/registry-config/yarn-berry.js +612 -0
  176. package/dist/src/utils/registry-config/yarn-classic.d.ts +2 -0
  177. package/dist/src/utils/registry-config/yarn-classic.js +1001 -0
  178. package/dist/src/utils/safe-spawn.d.ts +24 -0
  179. package/dist/src/utils/safe-spawn.js +104 -0
  180. package/dist/src/utils/write-formatted-json-file.d.ts +3 -2
  181. package/dist/src/utils/write-formatted-json-file.js +5 -4
  182. package/migrations.json +6 -0
  183. package/package.json +17 -13
  184. package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +0 -88
  185. package/dist/src/utils/is-using-prettier.d.ts +0 -3
  186. package/dist/src/utils/is-using-prettier.js +0 -62
  187. package/dist/src/utils/min-release-age/npmrc.d.ts +0 -15
  188. package/dist/src/utils/min-release-age/npmrc.js +0 -45
@@ -13,8 +13,10 @@ exports.copyPackageManagerConfigurationFiles = copyPackageManagerConfigurationFi
13
13
  exports.createTempNpmDirectory = createTempNpmDirectory;
14
14
  exports.resolvePackageVersionUsingRegistry = resolvePackageVersionUsingRegistry;
15
15
  exports.resolvePackageVersionUsingInstallation = resolvePackageVersionUsingInstallation;
16
+ exports.getWorkspaceRegistryUrlForDisplay = getWorkspaceRegistryUrlForDisplay;
16
17
  exports.packageRegistryView = packageRegistryView;
17
18
  exports.packageRegistryPack = packageRegistryPack;
19
+ exports.clearPackageManagerVersionCache = clearPackageManagerVersionCache;
18
20
  exports.getPackageWorkspaces = getPackageWorkspaces;
19
21
  exports.addPackagePathToWorkspaces = addPackagePathToWorkspaces;
20
22
  const child_process_1 = require("child_process");
@@ -30,9 +32,36 @@ const file_utils_1 = require("../project-graph/file-utils");
30
32
  const catalog_1 = require("./catalog");
31
33
  const fileutils_1 = require("./fileutils");
32
34
  const installation_directory_1 = require("./installation-directory");
35
+ const logger_1 = require("./logger");
33
36
  const package_json_1 = require("./package-json");
37
+ const shell_quoting_1 = require("./shell-quoting");
34
38
  const workspace_root_1 = require("./workspace-root");
35
39
  const execAsync = (0, util_1.promisify)(child_process_1.exec);
40
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
41
+ /**
42
+ * Shell-less spawn for the registry-bridged fetches: `exec` goes through
43
+ * /bin/sh, which is dash on Debian-family systems, and dash drops environment
44
+ * names that are not valid shell identifiers, i.e. every `//...:_authToken`
45
+ * credential and `@scope:registry` entry the overlay carries. Windows stays on
46
+ * `exec`, with every argument quoted, because Node refuses to execFile the
47
+ * package manager's `.cmd` shim without a shell.
48
+ *
49
+ * That quoting is the whole defence on Windows, so it goes through
50
+ * quoteShellArg, which throws on the one argument it cannot make safe.
51
+ */
52
+ function execPackageManagerAsync(pm, args, options) {
53
+ if (process.platform === 'win32') {
54
+ return execAsync([pm, ...args].map(shell_quoting_1.quoteShellArg).join(' '), options);
55
+ }
56
+ return execFileAsync(pm, args, options);
57
+ }
58
+ /** Same split, and the same quoting, for a blocking caller. */
59
+ function execPackageManagerSync(pm, args, options) {
60
+ if (process.platform === 'win32') {
61
+ return (0, child_process_1.execSync)([pm, ...args].map(shell_quoting_1.quoteShellArg).join(' '), options);
62
+ }
63
+ return (0, child_process_1.execFileSync)(pm, args, options);
64
+ }
36
65
  /**
37
66
  * Detects which package manager is used in the workspace based on the lock file.
38
67
  */
@@ -408,6 +437,27 @@ function copyPackageManagerConfigurationFiles(root, destination) {
408
437
  }
409
438
  }
410
439
  }
440
+ /**
441
+ * A non-JS workspace has no root package.json and keeps its package manager
442
+ * files under the Nx installation directory instead.
443
+ */
444
+ function getPackageManagerConfigRoot() {
445
+ if ((0, fs_1.existsSync)((0, path_1.join)(workspace_root_1.workspaceRoot, 'package.json'))) {
446
+ return workspace_root_1.workspaceRoot;
447
+ }
448
+ const installationPath = (0, installation_directory_1.getNxInstallationPath)(workspace_root_1.workspaceRoot);
449
+ // The installation directory can be missing or not a directory, and spawning
450
+ // with such a cwd fails outright (ENOENT/ENOTDIR).
451
+ try {
452
+ return (0, fs_1.statSync)(installationPath).isDirectory()
453
+ ? installationPath
454
+ : workspace_root_1.workspaceRoot;
455
+ }
456
+ catch (e) {
457
+ logger_1.logger.verbose(`Failed to stat the Nx installation directory at "${installationPath}".`, e);
458
+ return workspace_root_1.workspaceRoot;
459
+ }
460
+ }
411
461
  /**
412
462
  * Creates a temporary directory where you can run package manager commands safely.
413
463
  *
@@ -424,8 +474,7 @@ function createTempNpmDirectory(skipCopy = false) {
424
474
  // A package.json is needed for pnpm pack and for .npmrc to resolve
425
475
  (0, fileutils_1.writeJsonFile)(`${dir}/package.json`, {});
426
476
  if (!skipCopy) {
427
- const isNonJs = !(0, fs_1.existsSync)((0, path_1.join)(workspace_root_1.workspaceRoot, 'package.json'));
428
- copyPackageManagerConfigurationFiles(isNonJs ? (0, installation_directory_1.getNxInstallationPath)(workspace_root_1.workspaceRoot) : workspace_root_1.workspaceRoot, dir);
477
+ copyPackageManagerConfigurationFiles(getPackageManagerConfigRoot(), dir);
429
478
  }
430
479
  const cleanup = async () => {
431
480
  try {
@@ -444,7 +493,9 @@ function createTempNpmDirectory(skipCopy = false) {
444
493
  async function resolvePackageVersionUsingRegistry(packageName, version) {
445
494
  try {
446
495
  const resolvedVersion = (0, catalog_1.resolveCatalogReferenceIfNeeded)(packageName, version);
447
- const result = await packageRegistryView(packageName, resolvedVersion, 'version');
496
+ const result = await packageRegistryView(packageName, resolvedVersion, [
497
+ 'version',
498
+ ]);
448
499
  if (!result) {
449
500
  throw new Error(`Unable to resolve version ${packageName}@${resolvedVersion}.`);
450
501
  }
@@ -466,10 +517,30 @@ async function resolvePackageVersionUsingRegistry(packageName, version) {
466
517
  .replace(/'/g, '');
467
518
  return finalResolvedVersion;
468
519
  }
469
- catch {
470
- throw new Error(`Unable to resolve version ${packageName}@${version}.`);
520
+ catch (e) {
521
+ // npm masks a URL credential only in the password position, so a bare token
522
+ // in the registry URL survives into the error kept as the cause.
523
+ throw new Error(`Unable to resolve version ${packageName}@${version}.`, {
524
+ cause: redactErrorCause(e),
525
+ });
471
526
  }
472
527
  }
528
+ // Masks the userinfo in a URL: `user`, `user:pass`, or a bare token.
529
+ function redactUrlCredentials(text) {
530
+ return text.replace(/([a-z][a-z0-9+.-]*:\/\/)[^/@\s]+@/gi, '$1***@');
531
+ }
532
+ function redactErrorCause(error) {
533
+ if (error && typeof error === 'object') {
534
+ const e = error;
535
+ // An exec error carries the command and both output streams as fields.
536
+ for (const field of ['message', 'stack', 'stderr', 'stdout', 'cmd']) {
537
+ if (typeof e[field] === 'string') {
538
+ e[field] = redactUrlCredentials(e[field]);
539
+ }
540
+ }
541
+ }
542
+ return error;
543
+ }
473
544
  /**
474
545
  * Return the resolved version for a given package and version tag using by
475
546
  * installing it in a temporary directory and fetching the version from the
@@ -498,12 +569,86 @@ async function resolvePackageVersionUsingInstallation(packageName, version) {
498
569
  await cleanup();
499
570
  }
500
571
  }
501
- async function packageRegistryView(pkg, version, args,
502
- // `forceNpm` runs the view through npm even in a pnpm workspace: npm projects
503
- // a field across every matched version, whereas `pnpm view <pkg>@<range>`
504
- // collapses to the single highest match (breaks per-version field queries).
505
- options) {
506
- let pm = detectPackageManager();
572
+ /**
573
+ * What every registry-bound npm spawn resolves the same way: where npm runs,
574
+ * and an environment reproducing the workspace package manager's own registry,
575
+ * auth and TLS resolution for `pkg`. `buildEnv` adds the caller's own entries.
576
+ */
577
+ function createRegistrySpawnContext(pkg) {
578
+ // Deferred so the registry resolvers load only for the commands that spawn
579
+ // npm, not with every package-manager.ts import.
580
+ const { getNpmSpawnRegistryEnv, getPackageScope, ignoresNpmConfigEnv, mergeNpmConfigEnv, } = require('./registry-config');
581
+ const workspacePm = detectPackageManager();
582
+ const configRoot = getPackageManagerConfigRoot();
583
+ const workspacePmVersion = getPackageManagerVersionSafe(workspacePm, configRoot);
584
+ return {
585
+ workspacePm,
586
+ workspacePmVersion,
587
+ configRoot,
588
+ scope: getPackageScope(pkg),
589
+ buildEnv: (extra) => mergeNpmConfigEnv(process.env, {
590
+ ...getNpmSpawnRegistryEnv(pkg, configRoot, workspacePm, workspacePmVersion),
591
+ ...extra,
592
+ }, ignoresNpmConfigEnv(workspacePm, workspacePmVersion)),
593
+ };
594
+ }
595
+ /**
596
+ * The registry the fetch for `pkg` went to, with its userinfo masked because a
597
+ * registry URL can carry a bare token. The lookup is spawned the way
598
+ * `packageRegistryView` spawns the fetch this describes (same manager, same
599
+ * environment), so a registry the package manager keeps outside the .npmrc
600
+ * chain and a scope resolved for itself both land on the value that fetch used.
601
+ * Null where the manager yields no usable registry URL; throws where it cannot be run.
602
+ */
603
+ function getWorkspaceRegistryUrlForDisplay(pkg) {
604
+ const { workspacePm, workspacePmVersion, configRoot, scope, buildEnv } = createRegistrySpawnContext(pkg);
605
+ const { pm, env, usesNativePnpm } = resolveRegistrySpawnTarget(workspacePm, workspacePmVersion, buildEnv);
606
+ // Ask for the package scope first. Native pnpm can keep the workspace
607
+ // `registries.default` separate from the flat `registry`, so it is queried
608
+ // in between.
609
+ const keys = scope ? [`${scope}:registry`] : [];
610
+ if (usesNativePnpm) {
611
+ keys.push('registries.default');
612
+ }
613
+ keys.push('registry');
614
+ for (const key of keys) {
615
+ const value = execPackageManagerSync(pm, ['config', 'get', key], {
616
+ cwd: configRoot,
617
+ timeout: 5000,
618
+ windowsHide: true,
619
+ encoding: 'utf-8',
620
+ // The downgraded pin warns on stderr, which is noise on a path that only
621
+ // decorates an error message.
622
+ stdio: ['ignore', 'pipe', 'ignore'],
623
+ env,
624
+ }).trim();
625
+ if (!value || value === 'undefined' || value === 'null') {
626
+ continue;
627
+ }
628
+ // A present but unusable answer (a `registries:` map with a non-string
629
+ // default serializes as JSON; a non-HTTP(S) scheme) also aborted the fetch
630
+ // this describes, so a key below it was never contacted either.
631
+ if (!URL.canParse(value)) {
632
+ return null;
633
+ }
634
+ const protocol = new URL(value).protocol;
635
+ if (protocol !== 'http:' && protocol !== 'https:') {
636
+ return null;
637
+ }
638
+ return redactUrlCredentials(value);
639
+ }
640
+ return null;
641
+ }
642
+ /**
643
+ * Which manager answers a registry read for this workspace, and the environment
644
+ * it reads under. pnpm 11 reimplemented `view` natively, resolving registry and
645
+ * credentials (tokenHelper included) itself, so it runs on the untouched
646
+ * environment; pnpm 10 passed `view` through to the npm CLI, which needs the
647
+ * overlay. Shared so a lookup describing a fetch cannot resolve against a
648
+ * different environment than the fetch used.
649
+ */
650
+ function resolveRegistrySpawnTarget(workspacePm, workspacePmVersion, buildEnv, options) {
651
+ let pm = workspacePm;
507
652
  if (options?.forceNpm || pm === 'yarn' || pm === 'bun') {
508
653
  /**
509
654
  * yarn has `yarn info` but it behaves differently than (p)npm,
@@ -517,46 +662,98 @@ options) {
517
662
  */
518
663
  pm = 'npm';
519
664
  }
665
+ if (pm === 'pnpm' && ((0, semver_1.parse)(workspacePmVersion)?.major ?? 0) >= 11) {
666
+ return { pm, env: process.env, usesNativePnpm: true };
667
+ }
668
+ // npm_config_force downgrades npm's `devEngines.packageManager` enforcement,
669
+ // which otherwise aborts even a read-only lookup when the pin sets
670
+ // `onFail: error`. Only set for npm so a pnpm spawn is untouched.
671
+ return {
672
+ pm,
673
+ env: buildEnv(pm === 'npm' ? { npm_config_force: 'true' } : {}),
674
+ usesNativePnpm: false,
675
+ };
676
+ }
677
+ async function packageRegistryView(pkg, version, args,
678
+ // `forceNpm` runs the view through npm even in a pnpm workspace: npm projects
679
+ // a field across every matched version, whereas `pnpm view <pkg>@<range>`
680
+ // collapses to the single highest match (breaks per-version field queries).
681
+ options) {
682
+ const { workspacePm, workspacePmVersion, configRoot, buildEnv } = createRegistrySpawnContext(pkg);
683
+ const { pm, env } = resolveRegistrySpawnTarget(workspacePm, workspacePmVersion, buildEnv, options);
520
684
  // An empty version means we want the full packument; omit the trailing `@`.
521
- // Quote the spec so range operators (e.g. `>=0.0.0`) are not parsed as shell
522
- // redirections.
523
685
  const spec = version ? `${pkg}@${version}` : pkg;
524
- // npm enforces `devEngines.packageManager` on every command, even a read-only
525
- // `view`; in a yarn/bun workspace we run npm, so a non-npm pin with
526
- // `onFail: error` aborts the lookup. force downgrades that to a warning.
527
- // Scoped to npm so a `pnpm view` spawn is left untouched.
528
- const { stdout } = await execAsync(`${pm} view "${spec}" ${args}`, {
529
- windowsHide: true,
530
- ...(pm === 'npm'
531
- ? { env: { ...process.env, npm_config_force: 'true' } }
532
- : {}),
533
- });
534
- return stdout.toString().trim();
686
+ try {
687
+ const { stdout } = await execPackageManagerAsync(pm, ['view', spec, ...args], {
688
+ windowsHide: true,
689
+ cwd: configRoot,
690
+ env,
691
+ });
692
+ return stdout.toString().trim();
693
+ }
694
+ catch (e) {
695
+ throw redactErrorCause(e);
696
+ }
535
697
  }
536
- async function packageRegistryPack(cwd, pkg, version, options) {
537
- /**
538
- * Only `npm pack` supports downloading a tarball of a specified remote
539
- * package. `yarn` packs the active workspace, `pnpm pack` only packs
540
- * the local project, and `bun` doesn't support pack.
541
- *
542
- * @see https://github.com/nrwl/nx/pull/9667#discussion_r842553994
543
- */
698
+ /**
699
+ * Only `npm pack` supports downloading a tarball of a specified remote
700
+ * package. `yarn` packs the active workspace, `pnpm pack` only packs
701
+ * the local project, and `bun` doesn't support pack.
702
+ *
703
+ * @param packDestination Directory passed to npm's `--pack-destination`, where
704
+ * the `.tgz` is written.
705
+ * @see https://github.com/nrwl/nx/pull/9667#discussion_r842553994
706
+ */
707
+ async function packageRegistryPack(packDestination, pkg, version, options) {
544
708
  const pm = 'npm';
545
- const { stdout } = await execAsync(`${pm} pack ${pkg}@${version}`, {
546
- cwd,
547
- windowsHide: true,
548
- // npm enforces `devEngines.packageManager` even on `pack`; force keeps the
549
- // download working in workspaces that pin a non-npm manager (onFail: error).
550
- env: {
551
- ...process.env,
552
- npm_config_force: 'true',
553
- ...(options?.bypassMinReleaseAge
554
- ? { npm_config_min_release_age: '0' }
555
- : {}),
556
- },
557
- });
558
- const tarballPath = stdout.trim();
559
- return { tarballPath };
709
+ const { configRoot, buildEnv } = createRegistrySpawnContext(pkg);
710
+ // Run from the config root, not the temp dir, so npm reads the workspace
711
+ // .npmrc natively; --pack-destination still writes the tarball to the temp
712
+ // dir. npm prints the tarball basename to stdout.
713
+ try {
714
+ const { stdout } = await execPackageManagerAsync(pm, ['pack', `${pkg}@${version}`, '--pack-destination', packDestination], {
715
+ cwd: configRoot,
716
+ windowsHide: true,
717
+ env: buildEnv({
718
+ // downgrade npm's devEngines.packageManager enforcement (onFail:
719
+ // error) to a warning so pack still runs in a non-npm workspace
720
+ npm_config_force: 'true',
721
+ ...(options?.bypassMinReleaseAge
722
+ ? { npm_config_min_release_age: '0' }
723
+ : {}),
724
+ }),
725
+ });
726
+ const tarballPath = stdout.trim();
727
+ return { tarballPath };
728
+ }
729
+ catch (e) {
730
+ throw redactErrorCause(e);
731
+ }
732
+ }
733
+ // The version probe shells out when the packageManager field is absent, and
734
+ // packageRegistryView/packageRegistryPack run in tight resolution loops.
735
+ const packageManagerVersionCache = new Map();
736
+ /**
737
+ * A null version is tolerated per package manager: pnpm and yarn skip bridging,
738
+ * bun assumes a current version.
739
+ */
740
+ function getPackageManagerVersionSafe(packageManager, root) {
741
+ const key = `${packageManager}:${root}`;
742
+ if (!packageManagerVersionCache.has(key)) {
743
+ let version = null;
744
+ try {
745
+ version = getPackageManagerVersion(packageManager, root);
746
+ }
747
+ catch (e) {
748
+ logger_1.logger.verbose(`Failed to determine the ${packageManager} version in "${root}".`, e);
749
+ }
750
+ packageManagerVersionCache.set(key, version);
751
+ }
752
+ return packageManagerVersionCache.get(key);
753
+ }
754
+ // Test-only: production never re-resolves a version mid-run.
755
+ function clearPackageManagerVersionCache() {
756
+ packageManagerVersionCache.clear();
560
757
  }
561
758
  /**
562
759
  * Gets the workspaces defined in the package manager configuration.
@@ -125,16 +125,22 @@ export declare function warnDeprecations(opts: {
125
125
  export declare function convertSmartDefaultsIntoNamedParams(opts: {
126
126
  [k: string]: any;
127
127
  }, schema: Schema, defaultProjectName: string | null, relativeCwd: string | null): void;
128
- type Prompt = ConstructorParameters<typeof import('enquirer').Prompt>[0] & {
128
+ type Prompt = {
129
129
  name: string;
130
130
  type: 'input' | 'autocomplete' | 'multiselect' | 'confirm' | 'numeral';
131
131
  message: string;
132
132
  initial?: any;
133
+ /**
134
+ * Retained because {@link getPromptsForSchema} is exported and asserted on;
135
+ * the prompt itself scrolls rather than truncating.
136
+ */
133
137
  limit?: number;
134
138
  choices?: (string | {
135
139
  name: string;
136
140
  message: string;
137
141
  })[];
142
+ /** Returns `true` when valid, otherwise the message to show. */
143
+ validate?: (value: any) => boolean | string;
138
144
  };
139
145
  export declare function getPromptsForSchema(opts: Options, schema: Schema, projectsConfigurations: ProjectsConfigurations): Prompt[];
140
146
  export {};
@@ -14,7 +14,7 @@ exports.warnDeprecations = warnDeprecations;
14
14
  exports.convertSmartDefaultsIntoNamedParams = convertSmartDefaultsIntoNamedParams;
15
15
  exports.getPromptsForSchema = getPromptsForSchema;
16
16
  const logger_1 = require("./logger");
17
- const handle_import_1 = require("./handle-import");
17
+ const prompt_helpers_1 = require("./prompt-helpers");
18
18
  function camelCase(input) {
19
19
  if (input.indexOf('-') > 1) {
20
20
  return input
@@ -658,13 +658,71 @@ function getPromptsForSchema(opts, schema, projectsConfigurations) {
658
658
  return prompts;
659
659
  }
660
660
  async function promptForValues(opts, schema, projectsConfigurations) {
661
- return await (await (0, handle_import_1.handleImport)('enquirer'))
662
- .prompt(getPromptsForSchema(opts, schema, projectsConfigurations))
663
- .then((values) => ({ ...opts, ...values }))
664
- .catch((e) => {
661
+ try {
662
+ const values = {};
663
+ // Asked one at a time; a schema prompt has no cross-question state, so the
664
+ // order is just the schema's own.
665
+ for (const question of getPromptsForSchema(opts, schema, projectsConfigurations)) {
666
+ values[question.name] = await askSchemaQuestion(question);
667
+ }
668
+ return { ...opts, ...values };
669
+ }
670
+ catch (e) {
665
671
  console.error(e);
666
672
  process.exit(1);
667
- });
673
+ }
674
+ }
675
+ async function askSchemaQuestion(question) {
676
+ const validate = question.validate
677
+ ? (value) => {
678
+ const result = question.validate(value);
679
+ return result === true
680
+ ? undefined
681
+ : typeof result === 'string'
682
+ ? result
683
+ : 'Invalid value';
684
+ }
685
+ : undefined;
686
+ const choices = (question.choices ?? []).map((c) => typeof c === 'string' ? { value: c } : { value: c.name, label: c.message });
687
+ switch (question.type) {
688
+ case 'confirm':
689
+ return (0, prompt_helpers_1.confirmationPrompt)({
690
+ message: question.message,
691
+ initial: question.initial !== false,
692
+ });
693
+ case 'multiselect':
694
+ return (0, prompt_helpers_1.multiselectPrompt)({
695
+ message: question.message,
696
+ choices,
697
+ initialValues: Array.isArray(question.initial)
698
+ ? question.initial
699
+ : undefined,
700
+ });
701
+ case 'autocomplete':
702
+ return (0, prompt_helpers_1.selectPrompt)({
703
+ message: question.message,
704
+ choices,
705
+ initial: question.initial,
706
+ });
707
+ case 'numeral': {
708
+ // No numeric prompt; the answer is parsed back so the schema still
709
+ // receives a number.
710
+ const answer = await (0, prompt_helpers_1.textPrompt)({
711
+ message: question.message,
712
+ initialValue: question.initial === undefined ? undefined : String(question.initial),
713
+ validate: (value) => value !== '' && !Number.isNaN(Number(value))
714
+ ? validate?.(Number(value))
715
+ : 'Please enter a number',
716
+ });
717
+ return Number(answer);
718
+ }
719
+ default:
720
+ return (0, prompt_helpers_1.textPrompt)({
721
+ message: question.message,
722
+ initialValue: typeof question.initial === 'string' ? question.initial : undefined,
723
+ validate,
724
+ });
725
+ }
668
726
  }
669
727
  function findSchemaForProperty(propName, schema) {
670
728
  if (propName in schema.properties) {
@@ -0,0 +1,53 @@
1
+ export interface Choice<T extends string> {
2
+ value: T;
3
+ label?: string;
4
+ hint?: string;
5
+ }
6
+ /** A bare string is a choice whose label is its value. */
7
+ export type ChoiceOrValue<T extends string> = T | Choice<T>;
8
+ /**
9
+ * Ctrl+C yields a sentinel rather than throwing. `onCancel` decides what that
10
+ * means for the caller - either a fallback answer or an abort. The default
11
+ * aborts with the POSIX interrupt status.
12
+ */
13
+ export type OnCancel<T> = () => T;
14
+ export declare function selectPrompt<T extends string>(options: {
15
+ message: string;
16
+ choices: readonly ChoiceOrValue<T>[];
17
+ initial?: T;
18
+ /** Answer without prompting; defaults to the first choice. */
19
+ skip?: boolean;
20
+ skippedValue?: T;
21
+ onCancel?: OnCancel<T>;
22
+ }): Promise<T>;
23
+ export declare function confirmationPrompt(options: {
24
+ message: string;
25
+ initial?: boolean;
26
+ skip?: boolean;
27
+ skippedValue?: boolean;
28
+ onCancel?: OnCancel<boolean>;
29
+ }): Promise<boolean>;
30
+ export declare function textPrompt(options: {
31
+ message: string;
32
+ initialValue?: string;
33
+ placeholder?: string;
34
+ /**
35
+ * Runs synchronously; clack does not await validators.
36
+ *
37
+ * Always receives a string. clack validates on Enter *before* it coerces an
38
+ * empty submit to `''`, so it would otherwise hand `undefined` to a prompt
39
+ * declaring no `initialValue`; the wrapper below normalizes that, matching
40
+ * what enquirer passed.
41
+ */
42
+ validate?: (value: string) => string | undefined;
43
+ skip?: boolean;
44
+ skippedValue?: string;
45
+ onCancel?: OnCancel<string>;
46
+ }): Promise<string>;
47
+ export declare function multiselectPrompt<T extends string>(options: {
48
+ message: string;
49
+ choices: readonly ChoiceOrValue<T>[];
50
+ required?: boolean;
51
+ initialValues?: readonly T[];
52
+ onCancel?: OnCancel<T[]>;
53
+ }): Promise<T[]>;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectPrompt = selectPrompt;
4
+ exports.confirmationPrompt = confirmationPrompt;
5
+ exports.textPrompt = textPrompt;
6
+ exports.multiselectPrompt = multiselectPrompt;
7
+ const exit_codes_1 = require("./exit-codes");
8
+ /**
9
+ * `@clack/prompts` is ESM-only. A static import would compile to `require()`
10
+ * under CommonJS emit and throw ERR_REQUIRE_ESM; `module: nodenext` preserves
11
+ * this dynamic form.
12
+ */
13
+ async function prompts() {
14
+ return await import('@clack/prompts');
15
+ }
16
+ function toChoice(choice) {
17
+ return typeof choice === 'string' ? { value: choice } : choice;
18
+ }
19
+ async function selectPrompt(options) {
20
+ if (options.skip) {
21
+ return options.skippedValue ?? toChoice(options.choices[0]).value;
22
+ }
23
+ const choices = options.choices.map(toChoice);
24
+ const { autocomplete, isCancel } = await prompts();
25
+ const answer = await autocomplete({
26
+ message: options.message,
27
+ // `Option<Value>` is conditional on `Value extends Primitive`, which
28
+ // TypeScript cannot resolve while `T` is still generic.
29
+ options: choices.map((c) => ({
30
+ value: c.value,
31
+ label: c.label ?? c.value,
32
+ ...(c.hint ? { hint: c.hint } : {}),
33
+ })),
34
+ initialValue: options.initial ?? choices[0].value,
35
+ // A no-match filter leaves clack with an empty selection, and Enter then
36
+ // submits `undefined` rather than blocking. `isCancel` does not catch
37
+ // that, so downstream comparisons would silently take a wrong branch.
38
+ validate: (value) => value === undefined ? 'Pick one of the listed options' : undefined,
39
+ });
40
+ if (isCancel(answer)) {
41
+ if (options.onCancel) {
42
+ return options.onCancel();
43
+ }
44
+ // No handler means the user aborted the command.
45
+ (0, exit_codes_1.exitAsInterrupted)();
46
+ }
47
+ return answer;
48
+ }
49
+ async function confirmationPrompt(options) {
50
+ const answer = await selectPrompt({
51
+ message: options.message,
52
+ choices: [{ value: 'Yes' }, { value: 'No' }],
53
+ initial: options.initial === false ? 'No' : 'Yes',
54
+ skip: options.skip,
55
+ skippedValue: options.skippedValue === undefined
56
+ ? undefined
57
+ : options.skippedValue
58
+ ? 'Yes'
59
+ : 'No',
60
+ onCancel: options.onCancel
61
+ ? () => (options.onCancel() ? 'Yes' : 'No')
62
+ : undefined,
63
+ });
64
+ return answer === 'Yes';
65
+ }
66
+ async function textPrompt(options) {
67
+ if (options.skip) {
68
+ return options.skippedValue ?? options.initialValue ?? '';
69
+ }
70
+ const { text, isCancel } = await prompts();
71
+ const answer = await text({
72
+ message: options.message,
73
+ initialValue: options.initialValue,
74
+ placeholder: options.placeholder,
75
+ validate: options.validate
76
+ ? (value) => options.validate(value ?? '')
77
+ : undefined,
78
+ });
79
+ if (isCancel(answer)) {
80
+ if (options.onCancel) {
81
+ return options.onCancel();
82
+ }
83
+ // No handler means the user aborted the command.
84
+ (0, exit_codes_1.exitAsInterrupted)();
85
+ }
86
+ return answer;
87
+ }
88
+ async function multiselectPrompt(options) {
89
+ const choices = options.choices.map(toChoice);
90
+ const { multiselect, isCancel } = await prompts();
91
+ const answer = await multiselect({
92
+ message: options.message,
93
+ options: choices.map((c) => ({
94
+ value: c.value,
95
+ label: c.label ?? c.value,
96
+ ...(c.hint ? { hint: c.hint } : {}),
97
+ })),
98
+ required: options.required ?? false,
99
+ initialValues: options.initialValues
100
+ ? [...options.initialValues]
101
+ : undefined,
102
+ });
103
+ if (isCancel(answer)) {
104
+ if (options.onCancel) {
105
+ return options.onCancel();
106
+ }
107
+ // No handler means the user aborted the command.
108
+ (0, exit_codes_1.exitAsInterrupted)();
109
+ }
110
+ return answer;
111
+ }