nx 23.2.0-beta.6 → 23.2.0-beta.8

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 (131) 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/command-line/completion/command-object.js +7 -12
  5. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +17 -23
  6. package/dist/src/command-line/generate/generate.js +35 -35
  7. package/dist/src/command-line/import/import.js +18 -40
  8. package/dist/src/command-line/init/ai-agent-prompts.js +5 -11
  9. package/dist/src/command-line/init/command-object.js +4 -2
  10. package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +12 -32
  11. package/dist/src/command-line/init/implementation/add-nx-to-nest.js +8 -21
  12. package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +8 -21
  13. package/dist/src/command-line/init/implementation/angular/index.js +6 -15
  14. package/dist/src/command-line/init/init-v1.js +8 -18
  15. package/dist/src/command-line/init/init-v2.js +28 -56
  16. package/dist/src/command-line/migrate/agentic/definitions.js +35 -8
  17. package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +22 -3
  18. package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +13 -10
  19. package/dist/src/command-line/migrate/agentic/handoff.d.ts +3 -1
  20. package/dist/src/command-line/migrate/agentic/handoff.js +4 -2
  21. package/dist/src/command-line/migrate/agentic/run-step.js +1 -0
  22. package/dist/src/command-line/migrate/agentic/runner.js +24 -24
  23. package/dist/src/command-line/migrate/agentic/select.js +20 -30
  24. package/dist/src/command-line/migrate/agentic/types.d.ts +19 -3
  25. package/dist/src/command-line/migrate/agentic/types.js +13 -4
  26. package/dist/src/command-line/migrate/command-object.d.ts +3 -0
  27. package/dist/src/command-line/migrate/command-object.js +13 -0
  28. package/dist/src/command-line/migrate/execute-migration.d.ts +14 -0
  29. package/dist/src/command-line/migrate/execute-migration.js +30 -9
  30. package/dist/src/command-line/migrate/migrate-analytics.d.ts +28 -0
  31. package/dist/src/command-line/migrate/migrate-analytics.js +51 -1
  32. package/dist/src/command-line/migrate/migrate-commits.d.ts +8 -2
  33. package/dist/src/command-line/migrate/migrate-commits.js +66 -19
  34. package/dist/src/command-line/migrate/migrate-config.d.ts +4 -2
  35. package/dist/src/command-line/migrate/migrate-config.js +12 -4
  36. package/dist/src/command-line/migrate/migrate.d.ts +9 -2
  37. package/dist/src/command-line/migrate/migrate.js +179 -56
  38. package/dist/src/command-line/migrate/multi-major.js +7 -10
  39. package/dist/src/command-line/migrate/resolve-package-version.js +4 -8
  40. package/dist/src/command-line/migrate/run/agent-output.d.ts +20 -0
  41. package/dist/src/command-line/migrate/run/agent-output.js +65 -0
  42. package/dist/src/command-line/migrate/run/index.d.ts +7 -0
  43. package/dist/src/command-line/migrate/run/index.js +19 -1
  44. package/dist/src/command-line/migrate/run/orchestrator.d.ts +20 -0
  45. package/dist/src/command-line/migrate/run/orchestrator.js +1200 -0
  46. package/dist/src/command-line/migrate/run/run-id.d.ts +16 -0
  47. package/dist/src/command-line/migrate/run/run-id.js +56 -0
  48. package/dist/src/command-line/migrate/run/run-state.d.ts +157 -0
  49. package/dist/src/command-line/migrate/run/run-state.js +438 -0
  50. package/dist/src/command-line/migrate/run/state-lock.d.ts +25 -0
  51. package/dist/src/command-line/migrate/run/state-lock.js +76 -0
  52. package/dist/src/command-line/migrate/run/state-machine.d.ts +84 -0
  53. package/dist/src/command-line/migrate/run/state-machine.js +315 -0
  54. package/dist/src/command-line/migrate/run/util.d.ts +58 -0
  55. package/dist/src/command-line/migrate/run/util.js +139 -7
  56. package/dist/src/command-line/migrate/run/worker.d.ts +1 -0
  57. package/dist/src/command-line/migrate/run/worker.js +308 -45
  58. package/dist/src/command-line/migrate/safe-prompt.d.ts +14 -26
  59. package/dist/src/command-line/migrate/safe-prompt.js +32 -43
  60. package/dist/src/command-line/migrate/step-actions.d.ts +5 -0
  61. package/dist/src/command-line/migrate/step-actions.js +12 -0
  62. package/dist/src/command-line/migrate/text.d.ts +18 -0
  63. package/dist/src/command-line/migrate/text.js +26 -0
  64. package/dist/src/command-line/migrate/version-skew-guard.d.ts +6 -4
  65. package/dist/src/command-line/migrate/version-skew-guard.js +34 -14
  66. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +16 -20
  67. package/dist/src/command-line/release/changelog.js +6 -17
  68. package/dist/src/command-line/release/plan.js +15 -28
  69. package/dist/src/command-line/release/release.js +6 -17
  70. package/dist/src/command-line/release/utils/remote-release-clients/github.js +16 -22
  71. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +13 -22
  72. package/dist/src/command-line/release/utils/resolve-semver-specifier.js +19 -41
  73. package/dist/src/command-line/release/version/resolve-current-version.js +7 -10
  74. package/dist/src/core/graph/main.js +1 -1
  75. package/dist/src/devkit-internals.d.ts +3 -0
  76. package/dist/src/devkit-internals.js +15 -4
  77. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +12 -0
  78. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +250 -0
  79. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +50 -0
  80. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  81. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  82. package/dist/src/plugins/js/lock-file/bun-parser.js +43 -10
  83. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +128 -2
  84. package/dist/src/tasks-runner/run-command.js +17 -32
  85. package/dist/src/tasks-runner/task-env.js +5 -2
  86. package/dist/src/tasks-runner/utils.js +2 -6
  87. package/dist/src/utils/analytics-prompt.js +4 -7
  88. package/dist/src/utils/exit-codes.d.ts +9 -0
  89. package/dist/src/utils/exit-codes.js +19 -0
  90. package/dist/src/utils/fileutils.d.ts +5 -0
  91. package/dist/src/utils/fileutils.js +7 -2
  92. package/dist/src/utils/git-utils.d.ts +33 -2
  93. package/dist/src/utils/git-utils.js +156 -8
  94. package/dist/src/utils/long-running-target.d.ts +10 -0
  95. package/dist/src/utils/long-running-target.js +19 -0
  96. package/dist/src/utils/min-release-age/behavior/bun.js +7 -11
  97. package/dist/src/utils/min-release-age/behavior/npm.js +5 -2
  98. package/dist/src/utils/min-release-age/packument.js +1 -1
  99. package/dist/src/utils/nx-console-prompt.js +3 -6
  100. package/dist/src/utils/package-manager-config/bunfig.d.ts +14 -0
  101. package/dist/src/utils/package-manager-config/bunfig.js +49 -0
  102. package/dist/src/utils/package-manager-config/npmrc.d.ts +23 -0
  103. package/dist/src/utils/package-manager-config/npmrc.js +124 -0
  104. package/dist/src/utils/package-manager-config/pnpm-config.d.ts +17 -0
  105. package/dist/src/utils/package-manager-config/pnpm-config.js +60 -0
  106. package/dist/src/utils/package-manager.d.ts +21 -2
  107. package/dist/src/utils/package-manager.js +244 -47
  108. package/dist/src/utils/params.d.ts +7 -1
  109. package/dist/src/utils/params.js +64 -6
  110. package/dist/src/utils/plugins/core-plugins.js +4 -0
  111. package/dist/src/utils/prompt-helpers.d.ts +53 -0
  112. package/dist/src/utils/prompt-helpers.js +111 -0
  113. package/dist/src/utils/provenance.js +6 -14
  114. package/dist/src/utils/registry-config/bun.d.ts +2 -0
  115. package/dist/src/utils/registry-config/bun.js +257 -0
  116. package/dist/src/utils/registry-config/index.d.ts +13 -0
  117. package/dist/src/utils/registry-config/index.js +102 -0
  118. package/dist/src/utils/registry-config/pnpm.d.ts +2 -0
  119. package/dist/src/utils/registry-config/pnpm.js +1551 -0
  120. package/dist/src/utils/registry-config/utils.d.ts +195 -0
  121. package/dist/src/utils/registry-config/utils.js +496 -0
  122. package/dist/src/utils/registry-config/yarn-berry.d.ts +2 -0
  123. package/dist/src/utils/registry-config/yarn-berry.js +612 -0
  124. package/dist/src/utils/registry-config/yarn-classic.d.ts +2 -0
  125. package/dist/src/utils/registry-config/yarn-classic.js +1001 -0
  126. package/dist/src/utils/safe-spawn.d.ts +24 -0
  127. package/dist/src/utils/safe-spawn.js +104 -0
  128. package/migrations.json +6 -0
  129. package/package.json +18 -13
  130. package/dist/src/utils/min-release-age/npmrc.d.ts +0 -15
  131. package/dist/src/utils/min-release-age/npmrc.js +0 -45
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.canPrompt = canPrompt;
4
- exports.migratePrompt = migratePrompt;
5
- const enquirer_1 = require("enquirer");
4
+ exports.migrateConfirm = migrateConfirm;
5
+ exports.migrateChoice = migrateChoice;
6
+ const exit_codes_1 = require("../../utils/exit-codes");
6
7
  const is_ci_1 = require("../../utils/is-ci");
7
8
  const output_1 = require("../../utils/output");
9
+ const prompt_helpers_1 = require("../../utils/prompt-helpers");
8
10
  /**
9
11
  * Whether `nx migrate` may show interactive prompts: requires a TTY on stdin,
10
12
  * not running in CI, and the user not having passed `--no-interactive`.
@@ -13,46 +15,33 @@ function canPrompt(interactive) {
13
15
  return !!process.stdin.isTTY && !(0, is_ci_1.isCI)() && interactive !== false;
14
16
  }
15
17
  /**
16
- * Drop-in replacement for enquirer's `prompt()` that hardens cancel
17
- * handling for every interactive prompt under `nx migrate`.
18
- *
19
- * Why: enquirer's built-in cancel path (`Prompt.cancel` → `close` →
20
- * `keypress.js` `off` → `readline.pause`) races on `Interface.pause()`
21
- * when triggered more than once in quick succession (mashed Ctrl+C),
22
- * throwing `ERR_USE_AFTER_CLOSE` from an unhandled async chain that
23
- * `await prompt(...)` cannot catch.
24
- *
25
- * In raw mode, Ctrl+C does NOT generate a SIGINT signal (the kernel's
26
- * `ISIG` flag is cleared while enquirer's keypress listener is active);
27
- * instead the 0x03 byte flows through `combos.js` and dispatches the
28
- * `cancel` action. So a `process.on('SIGINT', ...)` handler is the wrong
29
- * mechanism here — it never fires.
30
- *
31
- * The right hook is enquirer's per-prompt `options.cancel` override. When
32
- * present, `Prompt.keypress` (lib/prompt.js:42) calls our handler INSTEAD
33
- * of `this.cancel`, so enquirer's broken cleanup never runs. Both Ctrl+C
34
- * (byte 0x03) and Esc map to the `cancel` action, so this single override
35
- * covers both paths.
36
- *
37
- * The handler emits a single-line notice via `output.warn` and exits with
38
- * POSIX status 130 (128 + SIGINT). Synchronous exit is correct here: the
39
- * user explicitly asked to abort, there's no recovery state to preserve.
18
+ * Aborts the run the way `nx migrate` wants a cancel to look: one notice, then
19
+ * POSIX 130 (128 + SIGINT). The user asked to stop, so there is no state worth
20
+ * preserving.
40
21
  */
41
- async function migratePrompt(questions) {
42
- const cancel = () => {
43
- // `\n\r` → next line at column 0 (enquirer left the TTY in raw mode,
44
- // so a bare LF would not carry an implicit CR).
45
- // `\x1B[J` → clear from cursor to end of screen. Wipes enquirer's
46
- // leftover choice / input rendering below the row where we'll
47
- // write our message so the exit output isn't shadowed by orphaned
48
- // prompt fragments.
49
- process.stdout.write('\n\r\x1B[J');
50
- output_1.output.warn({ title: 'nx migrate interrupted by user.' });
51
- process.exit(130);
52
- };
53
- const withCancel = (q) => ({ ...q, cancel });
54
- const injected = Array.isArray(questions)
55
- ? questions.map(withCancel)
56
- : withCancel(questions);
57
- return (await (0, enquirer_1.prompt)(injected));
22
+ function cancelMigrate() {
23
+ process.stdout.write('\n');
24
+ output_1.output.warn({ title: 'nx migrate interrupted by user.' });
25
+ (0, exit_codes_1.exitAsInterrupted)();
26
+ }
27
+ /**
28
+ * Yes/no question for `nx migrate`, aborting the run if the user cancels.
29
+ */
30
+ async function migrateConfirm(options) {
31
+ return (0, prompt_helpers_1.confirmationPrompt)({
32
+ message: options.message,
33
+ initial: options.initial,
34
+ onCancel: cancelMigrate,
35
+ });
36
+ }
37
+ /**
38
+ * Single-choice question for `nx migrate`, aborting the run if the user cancels.
39
+ */
40
+ async function migrateChoice(options) {
41
+ return (0, prompt_helpers_1.selectPrompt)({
42
+ message: options.message,
43
+ choices: options.choices,
44
+ initial: options.initial,
45
+ onCancel: cancelMigrate,
46
+ });
58
47
  }
@@ -0,0 +1,5 @@
1
+ /** Allowed values for `--step-action`, the orchestrated reconcile's decision relay. */
2
+ export declare const STEP_ACTIONS: readonly ['retry', 'skip', 'retry-clean', 'adopt'];
3
+ export type StepAction = (typeof STEP_ACTIONS)[number];
4
+ /** Runtime guard backed by {@link STEP_ACTIONS} so the two can't drift apart. */
5
+ export declare function isStepAction(value: unknown): value is StepAction;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ // Zero-dep source of truth for --step-action: keeps migrate/run's execution
3
+ // chain out of CLI startup for callers that only need the value list or type.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.STEP_ACTIONS = void 0;
6
+ exports.isStepAction = isStepAction;
7
+ /** Allowed values for `--step-action`, the orchestrated reconcile's decision relay. */
8
+ exports.STEP_ACTIONS = ['retry', 'skip', 'retry-clean', 'adopt'];
9
+ /** Runtime guard backed by {@link STEP_ACTIONS} so the two can't drift apart. */
10
+ function isStepAction(value) {
11
+ return exports.STEP_ACTIONS.includes(value);
12
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Collapses every character a reader could take for a line break, so a value
3
+ * cannot break out of the line it is rendered on. The driving agent scans the
4
+ * same stdout for `<nx_migrate_*>` blocks, which are framed by a bare newline,
5
+ * so a value carrying its own break could open a forged one at a line start.
6
+ *
7
+ * The set is the line terminators and nothing else. U+0085, U+2028 and U+2029
8
+ * are in it because "line start" is the reader's notion, not this process's: a
9
+ * JavaScript `^` under `/m` treats the latter two as terminators, and Nx does
10
+ * not know what parses its output. Quoting is not a substitute either, since
11
+ * `JSON.stringify` leaves all three literal.
12
+ *
13
+ * Deliberately not every control character. Some of this text is already
14
+ * colored when it arrives, and stripping ESC would leave the rest of the
15
+ * escape sequence behind as visible noise without closing anything: only a
16
+ * real terminator can put a value at the start of a line.
17
+ */
18
+ export declare function singleLine(value: string): string;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ // Dependency-free on purpose. Every module that renders text an AI agent will
3
+ // read needs this, run/run-state.ts included, and that one cannot reach
4
+ // run/util.ts without a cycle through run/state-lock.ts.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.singleLine = singleLine;
7
+ /**
8
+ * Collapses every character a reader could take for a line break, so a value
9
+ * cannot break out of the line it is rendered on. The driving agent scans the
10
+ * same stdout for `<nx_migrate_*>` blocks, which are framed by a bare newline,
11
+ * so a value carrying its own break could open a forged one at a line start.
12
+ *
13
+ * The set is the line terminators and nothing else. U+0085, U+2028 and U+2029
14
+ * are in it because "line start" is the reader's notion, not this process's: a
15
+ * JavaScript `^` under `/m` treats the latter two as terminators, and Nx does
16
+ * not know what parses its output. Quoting is not a substitute either, since
17
+ * `JSON.stringify` leaves all three literal.
18
+ *
19
+ * Deliberately not every control character. Some of this text is already
20
+ * colored when it arrives, and stripping ESC would leave the rest of the
21
+ * escape sequence behind as visible noise without closing anything: only a
22
+ * real terminator can put a value at the start of a line.
23
+ */
24
+ function singleLine(value) {
25
+ return value.replace(/[\r\n\u000b\u000c\u0085\u2028\u2029]+/g, ' ');
26
+ }
@@ -1,10 +1,12 @@
1
1
  export declare const NEW_MIGRATE_FLAGS_FLOOR = "23.2.0";
2
- export declare const NEW_MIGRATE_FLAGS: readonly ['--run-migration', '--runMigration'];
2
+ export declare const NEW_MIGRATE_FLAGS: readonly ['--run-migration', '--runMigration', '--run-id', '--runId', '--step-action', '--stepAction'];
3
+ export declare function findNewMigrateFlag(argv: string[]): string | undefined;
3
4
  /**
4
- * Matches an exact token or `<flag>=<value>`. The `=` matters: a bare
5
- * `startsWith` would also match `--run-migrations` (trailing s).
5
+ * Whether the invocation names an existing orchestrated run. Such an
6
+ * invocation has to execute against the workspace-local nx that owns the run's
7
+ * state under `.nx/migrate-runs`, so it never routes to a temp installation.
6
8
  */
7
- export declare function findNewMigrateFlag(argv: string[]): string | undefined;
9
+ export declare function targetsExistingRun(argv: string[]): boolean;
8
10
  /**
9
11
  * Guard A (local side, before hop A). The current nx knows the new flags, but
10
12
  * the temp CLI 'nx migrate' is about to install may be older and would
@@ -2,36 +2,45 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NEW_MIGRATE_FLAGS = exports.NEW_MIGRATE_FLAGS_FLOOR = void 0;
4
4
  exports.findNewMigrateFlag = findNewMigrateFlag;
5
+ exports.targetsExistingRun = targetsExistingRun;
5
6
  exports.resolveNewMigrateFlagsRunTarget = resolveNewMigrateFlagsRunTarget;
6
7
  exports.assertWorkspaceNxSupportsNewMigrateFlags = assertWorkspaceNxSupportsNewMigrateFlags;
7
8
  const semver_1 = require("semver");
8
9
  const logger_1 = require("../../utils/logger");
9
10
  const output_1 = require("../../utils/output");
10
11
  const version_utils_1 = require("./version-utils");
11
- // The first stable release shipping --run-migration. Deliberately the final
12
- // release rather than its first prerelease: 23.2.0 prereleases published
13
- // before the feature landed do not carry it, so a prerelease floor would
14
- // wrongly accept them. The cost is that a later 23.2.0 prerelease that does
15
- // carry the feature is refused too, which fails toward refusal. Permanent;
16
- // never bumped at release time, but it must name the release that actually
17
- // ships the feature: if that slips past 23.2.0, versions in the gap pass
18
- // this floor and route to a temp CLI that drops the new flags and runs the
19
- // plan phase instead.
12
+ // The first stable release shipping --run-migration/--run-id. Deliberately
13
+ // the final release rather than its first prerelease: 23.2.0 prereleases
14
+ // published before the feature landed do not carry it, so a prerelease floor
15
+ // would wrongly accept them. The cost is that a later 23.2.0 prerelease that
16
+ // does carry the feature is refused too, which fails toward refusal.
17
+ // Permanent; never bumped at release time, but it must name the release that
18
+ // actually ships the feature: if that slips past 23.2.0, versions in the gap
19
+ // pass this floor and route to a temp CLI that drops the new flags and runs
20
+ // the plan phase instead.
20
21
  exports.NEW_MIGRATE_FLAGS_FLOOR = '23.2.0';
21
- // yargs accepts both spellings and the raw argv is forwarded across both
22
- // migrate hops, so detection must catch each one.
23
- exports.NEW_MIGRATE_FLAGS = ['--run-migration', '--runMigration'];
22
+ // yargs accepts both spellings of each flag and the raw argv is forwarded
23
+ // across both migrate hops, so detection must catch every one.
24
+ exports.NEW_MIGRATE_FLAGS = [
25
+ '--run-migration',
26
+ '--runMigration',
27
+ '--run-id',
28
+ '--runId',
29
+ '--step-action',
30
+ '--stepAction',
31
+ ];
32
+ const RUN_ID_FLAGS = ['--run-id', '--runId'];
24
33
  /**
25
34
  * Matches an exact token or `<flag>=<value>`. The `=` matters: a bare
26
35
  * `startsWith` would also match `--run-migrations` (trailing s).
27
36
  */
28
- function findNewMigrateFlag(argv) {
37
+ function findFlag(argv, flags) {
29
38
  for (const arg of argv) {
30
39
  // Everything after the -- separator is positional data, not options.
31
40
  if (arg === '--') {
32
41
  return undefined;
33
42
  }
34
- for (const flag of exports.NEW_MIGRATE_FLAGS) {
43
+ for (const flag of flags) {
35
44
  if (arg === flag || arg.startsWith(`${flag}=`)) {
36
45
  return flag;
37
46
  }
@@ -39,6 +48,17 @@ function findNewMigrateFlag(argv) {
39
48
  }
40
49
  return undefined;
41
50
  }
51
+ function findNewMigrateFlag(argv) {
52
+ return findFlag(argv, exports.NEW_MIGRATE_FLAGS);
53
+ }
54
+ /**
55
+ * Whether the invocation names an existing orchestrated run. Such an
56
+ * invocation has to execute against the workspace-local nx that owns the run's
57
+ * state under `.nx/migrate-runs`, so it never routes to a temp installation.
58
+ */
59
+ function targetsExistingRun(argv) {
60
+ return findFlag(argv, RUN_ID_FLAGS) !== undefined;
61
+ }
42
62
  /**
43
63
  * Guard A (local side, before hop A). The current nx knows the new flags, but
44
64
  * the temp CLI 'nx migrate' is about to install may be older and would
@@ -8,7 +8,7 @@ exports.connectExistingRepoToNxCloudPrompt = connectExistingRepoToNxCloudPrompt;
8
8
  exports.connectToNxCloudWithPrompt = connectToNxCloudWithPrompt;
9
9
  const tslib_1 = require("tslib");
10
10
  const path_1 = require("path");
11
- const handle_import_1 = require("../../../utils/handle-import");
11
+ const prompt_helpers_1 = require("../../../utils/prompt-helpers");
12
12
  const output_1 = require("../../../utils/output");
13
13
  const configuration_1 = require("../../../config/configuration");
14
14
  const tree_1 = require("../../../generators/tree");
@@ -248,23 +248,19 @@ function utmContentForCommand(command) {
248
248
  }
249
249
  async function nxCloudPrompt(key, utmContent) {
250
250
  const { message, choices, initial, footer, hint } = ab_testing_1.messages.getPrompt(key);
251
- const promptConfig = {
252
- name: 'NxCloud',
253
- message,
254
- type: 'autocomplete',
255
- choices,
256
- initial,
257
- }; // meeroslav: types in enquirer are not up to date
258
- if (footer) {
259
- promptConfig.footer = () => pc.dim(`${footer} ${(0, ab_testing_1.nxCloudHyperlink)(utmContent)}`);
260
- }
261
- if (hint) {
262
- promptConfig.hint = () => pc.dim(hint);
263
- }
264
- const enquirer = await (0, handle_import_1.handleImport)('enquirer');
265
- return await enquirer
266
- .prompt([promptConfig])
267
- .then((a) => {
268
- return a.NxCloud;
269
- });
251
+ // No separate footer/hint slot, so both are folded into the message.
252
+ const suffix = [hint, footer && `${footer} ${(0, ab_testing_1.nxCloudHyperlink)(utmContent)}`]
253
+ .filter(Boolean)
254
+ .map((t) => pc.dim(t));
255
+ return (await (0, prompt_helpers_1.selectPrompt)({
256
+ message: [message, ...suffix].join('\n'),
257
+ // These choices are `{ value, name }` where `name` is the display text,
258
+ // the inverse of enquirer's usual `{ name, message }`. Prefer `value` so
259
+ // the answer is the key the caller compares against, not the label.
260
+ choices: choices.map((c) => typeof c === 'string'
261
+ ? { value: c, label: c }
262
+ : { value: c.value ?? c.name, label: c.message ?? c.name ?? c.value }),
263
+ initial: choices[initial ?? 0]?.value ??
264
+ choices[initial ?? 0]?.name,
265
+ }));
270
266
  }
@@ -5,7 +5,7 @@ exports.isChangelogEffectivelyEnabled = isChangelogEffectivelyEnabled;
5
5
  exports.createAPI = createAPI;
6
6
  const tslib_1 = require("tslib");
7
7
  const pc = tslib_1.__importStar(require("picocolors"));
8
- const enquirer_1 = require("enquirer");
8
+ const prompt_helpers_1 = require("../../utils/prompt-helpers");
9
9
  const node_fs_1 = require("node:fs");
10
10
  const semver_1 = require("semver");
11
11
  const tmp_1 = require("tmp");
@@ -843,22 +843,11 @@ async function filterProjectCommits({ fromSHA, toSHA, projectPath, }) {
843
843
  return allCommits.filter((c) => c.affectedFiles.find((f) => f.startsWith(projectPath)));
844
844
  }
845
845
  async function promptForRemoteRelease() {
846
- try {
847
- const result = await (0, enquirer_1.prompt)([
848
- {
849
- name: 'confirmation',
850
- message: `Do you want to create a ${remoteReleaseProviderName ?? 'remote'} release anyway?`,
851
- type: 'confirm',
852
- },
853
- ]);
854
- return result.confirmation;
855
- }
856
- catch {
857
- // Ensure the cursor is always restored
858
- process.stdout.write('\u001b[?25h');
859
- // Handle the case where the user exits the prompt with ctrl+c
860
- return false;
861
- }
846
+ return (0, prompt_helpers_1.confirmationPrompt)({
847
+ message: `Do you want to create a ${remoteReleaseProviderName ?? 'remote'} release anyway?`,
848
+ // Cancelling declines the release rather than failing the run.
849
+ onCancel: () => false,
850
+ });
862
851
  }
863
852
  async function resolveWorkspaceChangelogChanges({ releaseGraph, nxReleaseConfig, args, workspacePreid, projectsPreid, useAutomaticFromRef, toSHA, fromSHA, }) {
864
853
  // NOTE: If there are multiple release groups, we'll just skip the workspace changelog anyway.
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.releasePlanCLIHandler = void 0;
4
4
  exports.createAPI = createAPI;
5
- const enquirer_1 = require("enquirer");
5
+ const prompt_helpers_1 = require("../../utils/prompt-helpers");
6
6
  const node_fs_1 = require("node:fs");
7
7
  const promises_1 = require("node:fs/promises");
8
8
  const node_path_1 = require("node:path");
@@ -199,25 +199,16 @@ async function createVersionPlanFileForBumps(args, versionPlanBumps) {
199
199
  }
200
200
  }
201
201
  async function promptForVersion(message) {
202
- try {
203
- const reply = await (0, enquirer_1.prompt)([
204
- {
205
- name: 'version',
206
- message,
207
- type: 'select',
208
- choices: [...semver_1.RELEASE_TYPES, 'none'],
209
- },
210
- ]);
211
- return reply.version;
212
- }
213
- catch {
214
- output_1.output.log({
215
- title: 'Cancelled version plan creation.',
216
- });
217
- // Ensure the cursor is always restored before exiting
218
- process.stdout.write('\u001b[?25h');
219
- process.exit(0);
220
- }
202
+ return (0, prompt_helpers_1.selectPrompt)({
203
+ message,
204
+ choices: [...semver_1.RELEASE_TYPES, 'none'],
205
+ onCancel: () => {
206
+ output_1.output.log({
207
+ title: 'Cancelled version plan creation.',
208
+ });
209
+ process.exit(0);
210
+ },
211
+ });
221
212
  }
222
213
  async function promptForMessage(versionPlanName) {
223
214
  let message;
@@ -228,14 +219,10 @@ async function promptForMessage(versionPlanName) {
228
219
  }
229
220
  async function _promptForMessage(versionPlanName) {
230
221
  try {
231
- const reply = await (0, enquirer_1.prompt)([
232
- {
233
- name: 'message',
234
- message: 'What changelog message would you like associated with this change? (Leave blank to open an external editor for multi-line messages/easier editing)',
235
- type: 'input',
236
- },
237
- ]);
238
- let message = reply.message.trim();
222
+ const reply = await (0, prompt_helpers_1.textPrompt)({
223
+ message: 'What changelog message would you like associated with this change? (Leave blank to open an external editor for multi-line messages/easier editing)',
224
+ });
225
+ let message = reply.trim();
239
226
  if (!message.length) {
240
227
  const tmpDir = (0, tmp_1.dirSync)().name;
241
228
  const messageFilePath = (0, node_path_1.join)(tmpDir, `DRAFT_MESSAGE__${versionPlanName}.md`);
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.releaseCLIHandler = void 0;
4
4
  exports.createAPI = createAPI;
5
- const enquirer_1 = require("enquirer");
5
+ const prompt_helpers_1 = require("../../utils/prompt-helpers");
6
6
  const node_fs_1 = require("node:fs");
7
7
  const nx_json_1 = require("../../config/nx-json");
8
8
  const file_map_utils_1 = require("../../project-graph/file-map-utils");
@@ -285,22 +285,11 @@ function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
285
285
  };
286
286
  }
287
287
  async function promptForPublish() {
288
- try {
289
- const reply = await (0, enquirer_1.prompt)([
290
- {
291
- name: 'confirmation',
292
- message: 'Do you want to publish these versions?',
293
- type: 'confirm',
294
- },
295
- ]);
296
- return reply.confirmation;
297
- }
298
- catch {
299
- // Ensure the cursor is always restored before exiting
300
- process.stdout.write('\u001b[?25h');
301
- // Handle the case where the user exits the prompt with ctrl+c
302
- return false;
303
- }
288
+ return (0, prompt_helpers_1.confirmationPrompt)({
289
+ message: 'Do you want to publish these versions?',
290
+ // Cancelling is a decision not to publish, not a failure.
291
+ onCancel: () => false,
292
+ });
304
293
  }
305
294
  function shouldCreateRemoteRelease(changelogConfig) {
306
295
  if (changelogConfig === false) {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GithubRemoteReleaseClient = exports.defaultCreateReleaseProvider = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const pc = tslib_1.__importStar(require("picocolors"));
6
- const enquirer_1 = require("enquirer");
6
+ const prompt_helpers_1 = require("../../../../utils/prompt-helpers");
7
7
  const node_child_process_1 = require("node:child_process");
8
8
  const node_fs_1 = require("node:fs");
9
9
  const node_os_1 = require("node:os");
@@ -104,6 +104,12 @@ class GithubRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
104
104
  await Promise.all([...authors.keys()].map(async (authorName) => {
105
105
  const meta = authors.get(authorName);
106
106
  for (const email of meta.email) {
107
+ // An empty email makes the URL `/users/find/`, so ungh attributes
108
+ // the commit to the "find" user rather than the real author.
109
+ // Non-emails just 404, but skip those too to avoid a wasted lookup.
110
+ if (!email || !email.includes('@')) {
111
+ continue;
112
+ }
107
113
  if (email.endsWith('@users.noreply.github.com')) {
108
114
  const match = email.match(/^(\d+\+)?([^@]+)@users\.noreply\.github\.com$/);
109
115
  if (match && match[2]) {
@@ -250,29 +256,17 @@ class GithubRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
250
256
  }
251
257
  async promptForContinueInGitHub() {
252
258
  try {
253
- const reply = await (0, enquirer_1.prompt)([
254
- {
255
- name: 'open',
256
- message: 'Do you want to finish creating the release manually in your browser?',
257
- type: 'autocomplete',
258
- choices: [
259
- {
260
- name: 'Yes',
261
- hint: 'It will pre-populate the form for you',
262
- },
263
- {
264
- name: 'No',
265
- },
266
- ],
267
- initial: 0,
268
- },
269
- ]);
270
- return reply.open === 'Yes';
259
+ const open = await (0, prompt_helpers_1.selectPrompt)({
260
+ message: 'Do you want to finish creating the release manually in your browser?',
261
+ choices: [
262
+ { value: 'Yes', hint: 'It will pre-populate the form for you' },
263
+ { value: 'No' },
264
+ ],
265
+ onCancel: () => process.exit(1),
266
+ });
267
+ return open === 'Yes';
271
268
  }
272
269
  catch {
273
- // Ensure the cursor is always restored before exiting
274
- process.stdout.write('\u001b[?25h');
275
- // Handle the case where the user exits the prompt with ctrl+c
276
270
  process.exit(1);
277
271
  }
278
272
  }
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GitLabRemoteReleaseClient = exports.defaultCreateReleaseProvider = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const pc = tslib_1.__importStar(require("picocolors"));
6
- const enquirer_1 = require("enquirer");
6
+ const prompt_helpers_1 = require("../../../../utils/prompt-helpers");
7
7
  const node_child_process_1 = require("node:child_process");
8
8
  const output_1 = require("../../../../utils/output");
9
9
  const extract_repo_slug_1 = require("./extract-repo-slug");
@@ -172,29 +172,20 @@ class GitLabRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
172
172
  }
173
173
  async promptForContinueInGitLab() {
174
174
  try {
175
- const reply = await (0, enquirer_1.prompt)([
176
- {
177
- name: 'open',
178
- message: 'Do you want to create the release manually in your browser?',
179
- type: 'autocomplete',
180
- choices: [
181
- {
182
- name: 'Yes',
183
- hint: 'It will open the GitLab release page for you',
184
- },
185
- {
186
- name: 'No',
187
- },
188
- ],
189
- initial: 0,
190
- },
191
- ]);
192
- return reply.open === 'Yes';
175
+ const open = await (0, prompt_helpers_1.selectPrompt)({
176
+ message: 'Do you want to create the release manually in your browser?',
177
+ choices: [
178
+ {
179
+ value: 'Yes',
180
+ hint: 'It will open the GitLab release page for you',
181
+ },
182
+ { value: 'No' },
183
+ ],
184
+ onCancel: () => process.exit(1),
185
+ });
186
+ return open === 'Yes';
193
187
  }
194
188
  catch {
195
- // Ensure the cursor is always restored before exiting
196
- process.stdout.write('\u001b[?25h');
197
- // Handle the case where the user exits the prompt with ctrl+c
198
189
  process.exit(1);
199
190
  }
200
191
  }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveSemverSpecifierFromConventionalCommits = resolveSemverSpecifierFromConventionalCommits;
4
4
  exports.resolveSemverSpecifierFromPrompt = resolveSemverSpecifierFromPrompt;
5
- const enquirer_1 = require("enquirer");
5
+ const prompt_helpers_1 = require("../../../utils/prompt-helpers");
6
6
  const semver_1 = require("semver");
7
7
  const git_1 = require("./git");
8
8
  const semver_2 = require("./semver");
@@ -19,45 +19,23 @@ releaseGroupProjects = projectNames) {
19
19
  return (0, semver_2.determineSemverChange)(relevantCommits, releaseConfig.conventionalCommits);
20
20
  }
21
21
  async function resolveSemverSpecifierFromPrompt(selectionMessage, customVersionMessage) {
22
- try {
23
- const reply = await (0, enquirer_1.prompt)([
24
- {
25
- name: 'specifier',
26
- message: selectionMessage,
27
- type: 'select',
28
- choices: [
29
- ...semver_1.RELEASE_TYPES.map((t) => ({ name: t, message: t })),
30
- {
31
- name: 'custom',
32
- message: 'Custom exact version',
33
- },
34
- ],
35
- },
36
- ]);
37
- if (reply.specifier !== 'custom') {
38
- return reply.specifier;
39
- }
40
- else {
41
- const reply = await (0, enquirer_1.prompt)([
42
- {
43
- name: 'specifier',
44
- message: customVersionMessage,
45
- type: 'input',
46
- validate: (input) => {
47
- if ((0, semver_1.valid)(input)) {
48
- return true;
49
- }
50
- return 'Please enter a valid semver version';
51
- },
52
- },
53
- ]);
54
- return reply.specifier;
55
- }
56
- }
57
- catch {
58
- // Ensure the cursor is always restored before exiting
59
- process.stdout.write('\u001b[?25h');
60
- // We need to catch the error from enquirer prompt, otherwise yargs will print its help
61
- process.exit(1);
22
+ // Cancelling exits rather than returning, so yargs never prints its help for
23
+ // what the user meant as an abort.
24
+ const abort = () => process.exit(1);
25
+ const specifier = await (0, prompt_helpers_1.selectPrompt)({
26
+ message: selectionMessage,
27
+ choices: [
28
+ ...semver_1.RELEASE_TYPES,
29
+ { value: 'custom', label: 'Custom exact version' },
30
+ ],
31
+ onCancel: abort,
32
+ });
33
+ if (specifier !== 'custom') {
34
+ return specifier;
62
35
  }
36
+ return (0, prompt_helpers_1.textPrompt)({
37
+ message: customVersionMessage,
38
+ validate: (input) => (0, semver_1.valid)(input) ? undefined : 'Please enter a valid semver version',
39
+ onCancel: abort,
40
+ });
63
41
  }